diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-01-06 14:44:00 +0100 |
commit | 40736c5763bf61337c8c14e16d8587db021a87d4 (patch) | |
tree | b17a9c00042ad89cb1308e2484491799aa14e9f8 /Source/WebCore/html | |
download | qtwebkit-40736c5763bf61337c8c14e16d8587db021a87d4.tar.gz |
Imported WebKit commit 2ea9d364d0f6efa8fa64acf19f451504c59be0e4 (http://svn.webkit.org/repository/webkit/trunk@104285)
Diffstat (limited to 'Source/WebCore/html')
558 files changed, 88231 insertions, 0 deletions
diff --git a/Source/WebCore/html/BaseButtonInputType.cpp b/Source/WebCore/html/BaseButtonInputType.cpp new file mode 100644 index 000000000..11546eddd --- /dev/null +++ b/Source/WebCore/html/BaseButtonInputType.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BaseButtonInputType.h" + +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "RenderButton.h" + +namespace WebCore { + +using namespace HTMLNames; + +bool BaseButtonInputType::appendFormData(FormDataList&, bool) const +{ + // Buttons except overridden types are never successful. + return false; +} + +void BaseButtonInputType::handleKeydownEvent(KeyboardEvent* event) +{ + const String& key = event->keyIdentifier(); + if (key == "U+0020") { + element()->setActive(true, true); + // No setDefaultHandled(), because IE dispatches a keypress in this case + // and the caller will only dispatch a keypress if we don't call setDefaultHandled(). + } +} + +void BaseButtonInputType::handleKeypressEvent(KeyboardEvent* event) +{ + int charCode = event->charCode(); + if (charCode == '\r') { + element()->dispatchSimulatedClick(event); + event->setDefaultHandled(); + return; + } + if (charCode == ' ') { + // Prevent scrolling down the page. + event->setDefaultHandled(); + } +} + +void BaseButtonInputType::handleKeyupEvent(KeyboardEvent* event) +{ + const String& key = event->keyIdentifier(); + if (key != "U+0020") + return; + // Simulate mouse click for spacebar for button types. + dispatchSimulatedClickIfActive(event); +} + +RenderObject* BaseButtonInputType::createRenderer(RenderArena* arena, RenderStyle*) const +{ + return new (arena) RenderButton(element()); +} + +// FIXME: Could share this with BaseCheckableInputType and RangeInputType if we had a common base class. +void BaseButtonInputType::accessKeyAction(bool sendMouseEvents) +{ + InputType::accessKeyAction(sendMouseEvents); + + // Send mouse button events if the caller specified sendMouseEvents. + // FIXME: The comment above is no good. It says what we do, but not why. + element()->dispatchSimulatedClick(0, sendMouseEvents); +} + +bool BaseButtonInputType::storesValueSeparateFromAttribute() +{ + return false; +} + +void BaseButtonInputType::setValue(const String& sanitizedValue, bool, bool) +{ + element()->setAttribute(valueAttr, sanitizedValue); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/BaseButtonInputType.h b/Source/WebCore/html/BaseButtonInputType.h new file mode 100644 index 000000000..dabc27cac --- /dev/null +++ b/Source/WebCore/html/BaseButtonInputType.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BaseButtonInputType_h +#define BaseButtonInputType_h + +#include "InputType.h" + +namespace WebCore { + +// Base of button, file, image, reset, and submit types. +class BaseButtonInputType : public InputType { +protected: + BaseButtonInputType(HTMLInputElement* element) : InputType(element) { } + +private: + virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; + virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; + virtual void handleKeypressEvent(KeyboardEvent*) OVERRIDE; + virtual void handleKeyupEvent(KeyboardEvent*) OVERRIDE; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; + virtual void accessKeyAction(bool sendMouseEvents) OVERRIDE; + virtual bool storesValueSeparateFromAttribute() OVERRIDE; + virtual void setValue(const String&, bool, bool) OVERRIDE; +}; + +} // namespace WebCore + +#endif // BaseButtonInputType_h diff --git a/Source/WebCore/html/BaseCheckableInputType.cpp b/Source/WebCore/html/BaseCheckableInputType.cpp new file mode 100644 index 000000000..70f539d80 --- /dev/null +++ b/Source/WebCore/html/BaseCheckableInputType.cpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BaseCheckableInputType.h" + +#include "FormDataList.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "RegularExpression.h" + +namespace WebCore { + +using namespace HTMLNames; + +bool BaseCheckableInputType::saveFormControlState(String& result) const +{ + result = element()->checked() ? "on" : "off"; + return true; +} + +void BaseCheckableInputType::restoreFormControlState(const String& state) const +{ + element()->setChecked(state == "on"); +} + +bool BaseCheckableInputType::appendFormData(FormDataList& encoding, bool) const +{ + if (!element()->checked()) + return false; + encoding.appendData(element()->name(), element()->value()); + return true; +} + +void BaseCheckableInputType::handleKeydownEvent(KeyboardEvent* event) +{ + const String& key = event->keyIdentifier(); + if (key == "U+0020") { + element()->setActive(true, true); + // No setDefaultHandled(), because IE dispatches a keypress in this case + // and the caller will only dispatch a keypress if we don't call setDefaultHandled(). + } +} + +void BaseCheckableInputType::handleKeypressEvent(KeyboardEvent* event) +{ + if (event->charCode() == ' ') { + // Prevent scrolling down the page. + event->setDefaultHandled(); + } +} + +bool BaseCheckableInputType::canSetStringValue() const +{ + return false; +} + +// FIXME: Could share this with BaseButtonInputType and RangeInputType if we had a common base class. +void BaseCheckableInputType::accessKeyAction(bool sendMouseEvents) +{ + InputType::accessKeyAction(sendMouseEvents); + + // Send mouse button events if the caller specified sendMouseEvents. + // FIXME: The comment above is no good. It says what we do, but not why. + element()->dispatchSimulatedClick(0, sendMouseEvents); +} + +String BaseCheckableInputType::fallbackValue() const +{ + return "on"; +} + +bool BaseCheckableInputType::storesValueSeparateFromAttribute() +{ + return false; +} + +void BaseCheckableInputType::setValue(const String& sanitizedValue, bool, bool) +{ + element()->setAttribute(valueAttr, sanitizedValue); +} + +bool BaseCheckableInputType::isCheckable() +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/BaseCheckableInputType.h b/Source/WebCore/html/BaseCheckableInputType.h new file mode 100644 index 000000000..2c2c4ad3c --- /dev/null +++ b/Source/WebCore/html/BaseCheckableInputType.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BaseCheckableInputType_h +#define BaseCheckableInputType_h + +#include "InputType.h" + +namespace WebCore { + +// Base of checkbox and radio types. +class BaseCheckableInputType : public InputType { +protected: + BaseCheckableInputType(HTMLInputElement* element) : InputType(element) { } + virtual void handleKeydownEvent(KeyboardEvent*); + +private: + virtual bool saveFormControlState(String&) const OVERRIDE; + virtual void restoreFormControlState(const String&) const OVERRIDE; + virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; + virtual void handleKeypressEvent(KeyboardEvent*) OVERRIDE; + virtual bool canSetStringValue() const OVERRIDE; + virtual void accessKeyAction(bool sendMouseEvents) OVERRIDE; + virtual String fallbackValue() const OVERRIDE; + virtual bool storesValueSeparateFromAttribute() OVERRIDE; + virtual void setValue(const String&, bool, bool) OVERRIDE; + virtual bool isCheckable() OVERRIDE; +}; + +} // namespace WebCore + +#endif // BaseCheckableInputType_h diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.cpp b/Source/WebCore/html/BaseDateAndTimeInputType.cpp new file mode 100644 index 000000000..46366729f --- /dev/null +++ b/Source/WebCore/html/BaseDateAndTimeInputType.cpp @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "BaseDateAndTimeInputType.h" + +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "LocalizedDate.h" +#include <limits> +#include <wtf/CurrentTime.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +using namespace HTMLNames; +using namespace std; + +static const double msecPerMinute = 60 * 1000; +static const double msecPerSecond = 1000; + +double BaseDateAndTimeInputType::valueAsDate() const +{ + return parseToDouble(element()->value(), DateComponents::invalidMilliseconds()); +} + +void BaseDateAndTimeInputType::setValueAsDate(double value, ExceptionCode&) const +{ + element()->setValue(serializeWithMilliseconds(value)); +} + +double BaseDateAndTimeInputType::valueAsNumber() const +{ + return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); +} + +void BaseDateAndTimeInputType::setValueAsNumber(double newValue, bool sendChangeEvent, ExceptionCode&) const +{ + element()->setValue(serialize(newValue), sendChangeEvent); +} + +bool BaseDateAndTimeInputType::typeMismatchFor(const String& value) const +{ + return !value.isEmpty() && !parseToDateComponents(value, 0); +} + +bool BaseDateAndTimeInputType::typeMismatch() const +{ + return typeMismatchFor(element()->value()); +} + +bool BaseDateAndTimeInputType::rangeUnderflow(const String& value) const +{ + const double nan = numeric_limits<double>::quiet_NaN(); + double doubleValue = parseToDouble(value, nan); + return isfinite(doubleValue) && doubleValue < minimum(); +} + +bool BaseDateAndTimeInputType::rangeOverflow(const String& value) const +{ + const double nan = numeric_limits<double>::quiet_NaN(); + double doubleValue = parseToDouble(value, nan); + return isfinite(doubleValue) && doubleValue > maximum(); +} + +bool BaseDateAndTimeInputType::supportsRangeLimitation() const +{ + return true; +} + +double 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); +} + +bool BaseDateAndTimeInputType::isSteppable() const +{ + return true; +} + +bool BaseDateAndTimeInputType::stepMismatch(const String& value, double step) const +{ + const double nan = numeric_limits<double>::quiet_NaN(); + double doubleValue = parseToDouble(value, nan); + doubleValue = fabs(doubleValue - stepBase()); + if (!isfinite(doubleValue)) + return false; + ASSERT(round(doubleValue) == doubleValue); + ASSERT(round(step) == step); + return fmod(doubleValue, step); +} + +double BaseDateAndTimeInputType::stepBase() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase()); +} + +void BaseDateAndTimeInputType::handleKeydownEvent(KeyboardEvent* event) +{ + handleKeydownEventForSpinButton(event); + if (!event->defaultHandled()) + TextFieldInputType::handleKeydownEvent(event); +} + +void BaseDateAndTimeInputType::handleWheelEvent(WheelEvent* event) +{ + handleWheelEventForSpinButton(event); +} + +double BaseDateAndTimeInputType::parseToDouble(const String& src, double defaultValue) const +{ + DateComponents date; + if (!parseToDateComponents(src, &date)) + return defaultValue; + double msec = date.millisecondsSinceEpoch(); + ASSERT(isfinite(msec)); + return msec; +} + +bool BaseDateAndTimeInputType::parseToDateComponents(const String& source, DateComponents* out) const +{ + if (source.isEmpty()) + return false; + DateComponents ignoredResult; + if (!out) + out = &ignoredResult; + return parseToDateComponentsInternal(source.characters(), source.length(), out); +} + +String BaseDateAndTimeInputType::serialize(double value) const +{ + if (!isfinite(value)) + return String(); + DateComponents date; + if (!setMillisecondToDateComponents(value, &date)) + return String(); + return serializeWithComponents(date); +} + +String BaseDateAndTimeInputType::serializeWithComponents(const DateComponents& date) const +{ + double step; + if (!element()->getAllowedValueStep(&step)) + return date.toString(); + if (!fmod(step, msecPerMinute)) + return date.toString(DateComponents::None); + if (!fmod(step, msecPerSecond)) + return date.toString(DateComponents::Second); + return date.toString(DateComponents::Millisecond); +} + +String BaseDateAndTimeInputType::serializeWithMilliseconds(double value) const +{ + return serialize(value); +} + +String BaseDateAndTimeInputType::visibleValue() const +{ + String currentValue = element()->value(); + DateComponents date; + if (!parseToDateComponents(currentValue, &date)) + return currentValue; + + String localized = formatLocalizedDate(date); + return localized.isEmpty() ? currentValue : localized; +} + +String BaseDateAndTimeInputType::convertFromVisibleValue(const String& visibleValue) const +{ + if (visibleValue.isEmpty()) + return visibleValue; + + double parsedValue = parseLocalizedDate(visibleValue, dateType()); + if (!isfinite(parsedValue)) + return visibleValue; + + return serializeWithMilliseconds(parsedValue); +} + +String BaseDateAndTimeInputType::sanitizeValue(const String& proposedValue) const +{ + return typeMismatchFor(proposedValue) ? String() : proposedValue; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/BaseDateAndTimeInputType.h b/Source/WebCore/html/BaseDateAndTimeInputType.h new file mode 100644 index 000000000..46c0c9d8d --- /dev/null +++ b/Source/WebCore/html/BaseDateAndTimeInputType.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BaseDateAndTimeInputType_h +#define BaseDateAndTimeInputType_h + +#include "DateComponents.h" +#include "TextFieldInputType.h" +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + +// A super class of date, datetime, datetime-local, month, time, and week types. +class BaseDateAndTimeInputType : public TextFieldInputType { +protected: + BaseDateAndTimeInputType(HTMLInputElement* element) : TextFieldInputType(element) { } + virtual double parseToDouble(const String&, double) const OVERRIDE; + virtual bool parseToDateComponents(const String&, DateComponents*) const OVERRIDE; + String serializeWithComponents(const DateComponents&) const; + +private: + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const = 0; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const = 0; + 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, bool sendChangeEvent, ExceptionCode&) const OVERRIDE; + virtual bool typeMismatchFor(const String&) const OVERRIDE; + virtual bool typeMismatch() const OVERRIDE; + virtual bool rangeUnderflow(const String&) const OVERRIDE; + virtual bool rangeOverflow(const String&) const OVERRIDE; + virtual bool supportsRangeLimitation() const OVERRIDE; + virtual double defaultValueForStepUp() const OVERRIDE; + virtual bool isSteppable() const OVERRIDE; + virtual bool stepMismatch(const String&, double) const OVERRIDE; + virtual double stepBase() const OVERRIDE; + virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; + virtual void handleWheelEvent(WheelEvent*) OVERRIDE; + virtual String serialize(double) const OVERRIDE; + virtual String serializeWithMilliseconds(double) const; + virtual String visibleValue() const OVERRIDE; + virtual String convertFromVisibleValue(const String&) const OVERRIDE; + virtual String sanitizeValue(const String&) const OVERRIDE; +}; + +} // namespace WebCore + +#endif // BaseDateAndTimeInputType_h diff --git a/Source/WebCore/html/BaseTextInputType.cpp b/Source/WebCore/html/BaseTextInputType.cpp new file mode 100644 index 000000000..3eb661f06 --- /dev/null +++ b/Source/WebCore/html/BaseTextInputType.cpp @@ -0,0 +1,58 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2009 Michelangelo De Simone <micdesim@gmail.com> + * Copyright (C) 2010 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 "BaseTextInputType.h" + +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "RegularExpression.h" + +namespace WebCore { + +using namespace HTMLNames; + +bool BaseTextInputType::isTextType() const +{ + return true; +} + +bool BaseTextInputType::patternMismatch(const String& value) const +{ + const AtomicString& rawPattern = element()->fastGetAttribute(patternAttr); + // Empty values can't be mismatched + if (rawPattern.isEmpty() || value.isEmpty()) + return false; + String pattern = "^(" + rawPattern + ")$"; + int matchLength = 0; + int valueLength = value.length(); + int matchOffset = RegularExpression(pattern, TextCaseSensitive).match(value, 0, &matchLength); + return matchOffset || matchLength != valueLength; +} + +bool BaseTextInputType::supportsPlaceholder() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/BaseTextInputType.h b/Source/WebCore/html/BaseTextInputType.h new file mode 100644 index 000000000..b2a56c9a4 --- /dev/null +++ b/Source/WebCore/html/BaseTextInputType.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BaseTextInputType_h +#define BaseTextInputType_h + +#include "TextFieldInputType.h" + +namespace WebCore { + +// Base of email, password, search, tel, text, and URL types. +// They support maxlength, selection functions, and so on. +class BaseTextInputType : public TextFieldInputType { +protected: + BaseTextInputType(HTMLInputElement* element) : TextFieldInputType(element) { } + +private: + virtual bool isTextType() const OVERRIDE; + virtual bool patternMismatch(const String&) const OVERRIDE; + virtual bool supportsPlaceholder() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // BaseTextInputType_h diff --git a/Source/WebCore/html/ButtonInputType.cpp b/Source/WebCore/html/ButtonInputType.cpp new file mode 100644 index 000000000..3af472029 --- /dev/null +++ b/Source/WebCore/html/ButtonInputType.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ButtonInputType.h" + +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> ButtonInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new ButtonInputType(element)); +} + +const AtomicString& ButtonInputType::formControlType() const +{ + return InputTypeNames::button(); +} + +bool ButtonInputType::supportsValidation() const +{ + return false; +} + +bool ButtonInputType::isTextButton() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/ButtonInputType.h b/Source/WebCore/html/ButtonInputType.h new file mode 100644 index 000000000..7ce4be9fd --- /dev/null +++ b/Source/WebCore/html/ButtonInputType.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ButtonInputType_h +#define ButtonInputType_h + +#include "BaseButtonInputType.h" + +namespace WebCore { + +class ButtonInputType : public BaseButtonInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + ButtonInputType(HTMLInputElement* element) : BaseButtonInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool supportsValidation() const OVERRIDE; + virtual bool isTextButton() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // ButtonInputType_h diff --git a/Source/WebCore/html/CheckboxInputType.cpp b/Source/WebCore/html/CheckboxInputType.cpp new file mode 100644 index 000000000..dda172aec --- /dev/null +++ b/Source/WebCore/html/CheckboxInputType.cpp @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CheckboxInputType.h" + +#include "HTMLInputElement.h" +#include "KeyboardEvent.h" +#include "LocalizedStrings.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> CheckboxInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new CheckboxInputType(element)); +} + +const AtomicString& CheckboxInputType::formControlType() const +{ + return InputTypeNames::checkbox(); +} + +bool CheckboxInputType::valueMissing(const String&) const +{ + return !element()->checked(); +} + +String CheckboxInputType::valueMissingText() const +{ + return validationMessageValueMissingForCheckboxText(); +} + +void CheckboxInputType::handleKeyupEvent(KeyboardEvent* event) +{ + const String& key = event->keyIdentifier(); + if (key != "U+0020") + return; + dispatchSimulatedClickIfActive(event); +} + +PassOwnPtr<ClickHandlingState> CheckboxInputType::willDispatchClick() +{ + // An event handler can use preventDefault or "return false" to reverse the checking we do here. + // The ClickHandlingState object contains what we need to undo what we did here in didDispatchClick. + + OwnPtr<ClickHandlingState> state = adoptPtr(new ClickHandlingState); + + state->checked = element()->checked(); + state->indeterminate = element()->indeterminate(); + + if (state->indeterminate) + element()->setIndeterminate(false); + + element()->setChecked(!state->checked, true); + + return state.release(); +} + +void CheckboxInputType::didDispatchClick(Event* event, const ClickHandlingState& state) +{ + if (event->defaultPrevented() || event->defaultHandled()) { + element()->setIndeterminate(state.indeterminate); + element()->setChecked(state.checked); + } + + // The work we did in willDispatchClick was default handling. + event->setDefaultHandled(); +} + +bool CheckboxInputType::isCheckbox() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/CheckboxInputType.h b/Source/WebCore/html/CheckboxInputType.h new file mode 100644 index 000000000..160c39124 --- /dev/null +++ b/Source/WebCore/html/CheckboxInputType.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CheckboxInputType_h +#define CheckboxInputType_h + +#include "BaseCheckableInputType.h" + +namespace WebCore { + +class CheckboxInputType : public BaseCheckableInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + CheckboxInputType(HTMLInputElement* element) : BaseCheckableInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool valueMissing(const String&) const OVERRIDE; + virtual String valueMissingText() const OVERRIDE; + virtual void handleKeyupEvent(KeyboardEvent*) OVERRIDE; + virtual PassOwnPtr<ClickHandlingState> willDispatchClick() OVERRIDE; + virtual void didDispatchClick(Event*, const ClickHandlingState&) OVERRIDE; + virtual bool isCheckbox() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // CheckboxInputType_h diff --git a/Source/WebCore/html/ClassList.cpp b/Source/WebCore/html/ClassList.cpp new file mode 100644 index 000000000..349d64dce --- /dev/null +++ b/Source/WebCore/html/ClassList.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClassList.h" + +#include "Element.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "SpaceSplitString.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +using namespace HTMLNames; + +ClassList::ClassList(Element* element) + : m_element(element) +{ + if (m_element->document()->inQuirksMode()) + m_classNamesForQuirksMode.set(m_element->getAttribute(classAttr), false); +} + +void ClassList::ref() +{ + m_element->ref(); +} + +void ClassList::deref() +{ + m_element->deref(); +} + +unsigned ClassList::length() const +{ + return m_element->hasClass() ? classNames().size() : 0; +} + +const AtomicString ClassList::item(unsigned index) const +{ + if (index >= length()) + return AtomicString(); + return classNames()[index]; +} + +bool ClassList::contains(const AtomicString& token, ExceptionCode& ec) const +{ + if (!validateToken(token, ec)) + return false; + return containsInternal(token); +} + +bool ClassList::containsInternal(const AtomicString& token) const +{ + return m_element->hasClass() && classNames().contains(token); +} + +void ClassList::add(const AtomicString& token, ExceptionCode& ec) +{ + if (!validateToken(token, ec)) + return; + addInternal(token); +} + +void ClassList::addInternal(const AtomicString& token) +{ + const AtomicString& oldClassName(m_element->getAttribute(classAttr)); + if (oldClassName.isEmpty()) + m_element->setAttribute(classAttr, token); + else if (!containsInternal(token)) { + const AtomicString& newClassName(addToken(oldClassName, token)); + m_element->setAttribute(classAttr, newClassName); + } +} + +void ClassList::remove(const AtomicString& token, ExceptionCode& ec) +{ + if (!validateToken(token, ec)) + return; + removeInternal(token); +} + +void ClassList::removeInternal(const AtomicString& token) +{ + // Check using contains first since it uses AtomicString comparisons instead + // of character by character testing. + if (!containsInternal(token)) + return; + const AtomicString& newClassName(removeToken(m_element->getAttribute(classAttr), token)); + m_element->setAttribute(classAttr, newClassName); +} + +bool ClassList::toggle(const AtomicString& token, ExceptionCode& ec) +{ + if (!validateToken(token, ec)) + return false; + + if (containsInternal(token)) { + removeInternal(token); + return false; + } + addInternal(token); + return true; +} + +String ClassList::toString() const +{ + return m_element->getAttribute(classAttr); +} + +void ClassList::reset(const String& newClassName) +{ + if (!m_classNamesForQuirksMode.isNull()) + m_classNamesForQuirksMode.set(newClassName, false); +} + +const SpaceSplitString& ClassList::classNames() const +{ + ASSERT(m_element->hasClass()); + if (!m_classNamesForQuirksMode.isNull()) + return m_classNamesForQuirksMode; + return m_element->attributeMap()->classNames(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/ClassList.h b/Source/WebCore/html/ClassList.h new file mode 100644 index 000000000..992fad474 --- /dev/null +++ b/Source/WebCore/html/ClassList.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ClassList_h +#define ClassList_h + +#include "DOMTokenList.h" +#include "SpaceSplitString.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class Element; + +typedef int ExceptionCode; + +class ClassList : public DOMTokenList { +public: + static PassOwnPtr<ClassList> create(Element* element) + { + return adoptPtr(new ClassList(element)); + } + + virtual void ref(); + virtual void deref(); + + virtual unsigned length() const; + virtual const AtomicString item(unsigned index) const; + virtual bool contains(const AtomicString&, ExceptionCode&) const; + virtual void add(const AtomicString&, ExceptionCode&); + virtual void remove(const AtomicString&, ExceptionCode&); + virtual bool toggle(const AtomicString&, ExceptionCode&); + virtual String toString() const; + + virtual Element* element() { return m_element; } + + void reset(const String&); + +private: + ClassList(Element*); + + void addInternal(const AtomicString&); + bool containsInternal(const AtomicString&) const; + void removeInternal(const AtomicString&); + + const SpaceSplitString& classNames() const; + + Element* m_element; + SpaceSplitString m_classNamesForQuirksMode; +}; + +} // namespace WebCore + +#endif // ClassList_h diff --git a/Source/WebCore/html/CollectionType.h b/Source/WebCore/html/CollectionType.h new file mode 100644 index 000000000..fa348f9c3 --- /dev/null +++ b/Source/WebCore/html/CollectionType.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple 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 CollectionType_h +#define CollectionType_h + +namespace WebCore { + +enum CollectionType { + // unnamed collection types cached in the document + + DocImages, // all <img> elements in the document + DocApplets, // all <object> and <applet> elements + DocEmbeds, // all <embed> elements + DocObjects, // all <object> elements + DocForms, // all <form> elements + DocLinks, // all <a> _and_ <area> elements with a value for href + DocAnchors, // all <a> elements with a value for name + DocScripts, // all <script> elements + + DocAll, // "all" elements (IE) + + // named collection types cached in the document + + WindowNamedItems, + DocumentNamedItems, + + // types not cached in the document; these are types that can't be used on a document + + NodeChildren, // first-level children (IE) + TableTBodies, // all <tbody> elements in this table + TSectionRows, // all row elements in this table section + TRCells, // all cells in this row + SelectOptions, + DataListOptions, + MapAreas, + +#if ENABLE(MICRODATA) + ItemProperties, // Microdata item properties in the document +#endif + + OtherCollection +}; + +static const CollectionType FirstUnnamedDocumentCachedType = DocImages; +static const unsigned NumUnnamedDocumentCachedTypes = WindowNamedItems - DocImages + 1; + +static const CollectionType FirstNodeCollectionType = NodeChildren; +static const unsigned NumNodeCollectionTypes = OtherCollection - NodeChildren + 1; + +} // namespace + +#endif diff --git a/Source/WebCore/html/ColorInputType.cpp b/Source/WebCore/html/ColorInputType.cpp new file mode 100644 index 000000000..d7bd81f4c --- /dev/null +++ b/Source/WebCore/html/ColorInputType.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ColorInputType.h" + +#include "CSSPropertyNames.h" +#include "Chrome.h" +#include "Color.h" +#include "HTMLDivElement.h" +#include "HTMLInputElement.h" +#include "MouseEvent.h" +#include "ScriptController.h" +#include "ShadowRoot.h" + +#include <wtf/PassOwnPtr.h> +#include <wtf/text/WTFString.h> + +#if ENABLE(INPUT_COLOR) + +namespace WebCore { + +static bool isValidColorString(const String& value) +{ + if (value.isEmpty()) + return false; + if (value[0] != '#') + return false; + + // We don't accept #rgb and #aarrggbb formats. + if (value.length() != 7) + return false; + Color color(value); + return color.isValid() && !color.hasAlpha(); +} + +PassOwnPtr<InputType> ColorInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new ColorInputType(element)); +} + +ColorInputType::~ColorInputType() +{ + endColorChooser(); +} + +bool ColorInputType::isColorControl() const +{ + return true; +} + +const AtomicString& ColorInputType::formControlType() const +{ + return InputTypeNames::color(); +} + +bool ColorInputType::supportsRequired() const +{ + return false; +} + +String ColorInputType::fallbackValue() const +{ + return String("#000000"); +} + +String ColorInputType::sanitizeValue(const String& proposedValue) const +{ + if (!isValidColorString(proposedValue)) + return fallbackValue(); + + return proposedValue.lower(); +} + +Color ColorInputType::valueAsColor() const +{ + return Color(element()->value()); +} + +void ColorInputType::createShadowSubtree() +{ + Document* document = element()->document(); + RefPtr<HTMLDivElement> wrapperElement = HTMLDivElement::create(document); + wrapperElement->setShadowPseudoId("-webkit-color-swatch-wrapper"); + RefPtr<HTMLDivElement> colorSwatch = HTMLDivElement::create(document); + colorSwatch->setShadowPseudoId("-webkit-color-swatch"); + ExceptionCode ec = 0; + wrapperElement->appendChild(colorSwatch.release(), ec); + ASSERT(!ec); + element()->ensureShadowRoot()->appendChild(wrapperElement.release(), ec); + ASSERT(!ec); + + updateColorSwatch(); +} + +void ColorInputType::setValue(const String& value, bool valueChanged, bool sendChangeEvent) +{ + InputType::setValue(value, valueChanged, sendChangeEvent); + + if (!valueChanged) + return; + + updateColorSwatch(); + if (m_chooser) + m_chooser->setSelectedColor(valueAsColor()); +} + +void ColorInputType::handleDOMActivateEvent(Event* event) +{ + if (element()->disabled() || element()->readOnly() || !element()->renderer()) + return; + + if (!ScriptController::processingUserGesture()) + return; + + Chrome* chrome = this->chrome(); + if (chrome && !m_chooser) + m_chooser = chrome->createColorChooser(this, valueAsColor()); + + event->setDefaultHandled(); +} + +void ColorInputType::detach() +{ + endColorChooser(); +} + +void ColorInputType::didChooseColor(const Color& color) +{ + if (element()->disabled() || element()->readOnly() || color == valueAsColor()) + return; + element()->setValueFromRenderer(color.serialized()); + updateColorSwatch(); + element()->dispatchFormControlChangeEvent(); +} + +void ColorInputType::didEndChooser() +{ + m_chooser.clear(); +} + +void ColorInputType::endColorChooser() +{ + if (m_chooser) + m_chooser->endChooser(); +} + +void ColorInputType::updateColorSwatch() +{ + HTMLElement* colorSwatch = shadowColorSwatch(); + if (!colorSwatch) + return; + + colorSwatch->style()->setProperty(CSSPropertyBackgroundColor, element()->value(), false, ASSERT_NO_EXCEPTION); +} + +HTMLElement* ColorInputType::shadowColorSwatch() const +{ + ShadowRoot* shadow = element()->shadowRoot(); + return shadow ? toHTMLElement(shadow->firstChild()->firstChild()) : 0; +} + +} // namespace WebCore + +#endif // ENABLE(INPUT_COLOR) diff --git a/Source/WebCore/html/ColorInputType.h b/Source/WebCore/html/ColorInputType.h new file mode 100644 index 000000000..d51deaa7d --- /dev/null +++ b/Source/WebCore/html/ColorInputType.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ColorInputType_h +#define ColorInputType_h + +#include "ColorChooserClient.h" +#include "InputType.h" + +#if ENABLE(INPUT_COLOR) + +namespace WebCore { + +class ColorInputType : public InputType, public ColorChooserClient { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + virtual ~ColorInputType(); + + // ColorChooserClient implementation. + virtual void didChooseColor(const Color&) OVERRIDE; + virtual void didEndChooser() OVERRIDE; + +private: + ColorInputType(HTMLInputElement* element) : InputType(element) { } + virtual bool isColorControl() const OVERRIDE; + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool supportsRequired() const OVERRIDE; + virtual String fallbackValue() const OVERRIDE; + virtual String sanitizeValue(const String&) const OVERRIDE; + virtual Color valueAsColor() const OVERRIDE; + virtual void createShadowSubtree() OVERRIDE; + virtual void setValue(const String&, bool valueChanged, bool sendChangeEvent) OVERRIDE; + virtual void handleDOMActivateEvent(Event*) OVERRIDE; + virtual void detach() OVERRIDE; + + void endColorChooser(); + void updateColorSwatch(); + HTMLElement* shadowColorSwatch() const; + + OwnPtr<ColorChooser> m_chooser; +}; + +} // namespace WebCore + +#endif // ENABLE(INPUT_COLOR) + +#endif // ColorInputType_h diff --git a/Source/WebCore/html/DOMFormData.cpp b/Source/WebCore/html/DOMFormData.cpp new file mode 100644 index 000000000..a8a97184a --- /dev/null +++ b/Source/WebCore/html/DOMFormData.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DOMFormData.h" + +#include "Blob.h" +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "PlatformString.h" +#include "TextEncoding.h" + +namespace WebCore { + +DOMFormData::DOMFormData(const TextEncoding& encoding) + : FormDataList(encoding) +{ +} + +DOMFormData::DOMFormData(HTMLFormElement* form) + : FormDataList(UTF8Encoding()) +{ + if (!form) + return; + + for (unsigned i = 0; i < form->associatedElements().size(); ++i) { + FormAssociatedElement* element = form->associatedElements()[i]; + if (!toHTMLElement(element)->disabled()) + element->appendFormData(*this, true); + } +} + +void DOMFormData::append(const String& name, const String& value) +{ + if (!name.isEmpty()) + appendData(name, value); +} + +void DOMFormData::append(const String& name, Blob* blob, const String& filename) +{ + if (!name.isEmpty()) + appendBlob(name, blob, filename); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/DOMFormData.h b/Source/WebCore/html/DOMFormData.h new file mode 100644 index 000000000..842e62b04 --- /dev/null +++ b/Source/WebCore/html/DOMFormData.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DOMFormData_h +#define DOMFormData_h + +#include "FormDataList.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class Blob; +class HTMLFormElement; +class TextEncoding; + +class DOMFormData : public FormDataList, public RefCounted<DOMFormData> { +public: + static PassRefPtr<DOMFormData> create(HTMLFormElement* form) { return adoptRef(new DOMFormData(form)); } + static PassRefPtr<DOMFormData> create(const TextEncoding& encoding) { return adoptRef(new DOMFormData(encoding)); } + + void append(const String& name, const String& value); + void append(const String& name, Blob*, const String& filename = String()); + +private: + explicit DOMFormData(const TextEncoding&); + explicit DOMFormData(HTMLFormElement*); +}; + +} // namespace WebCore + +#endif // DOMFormData_h diff --git a/Source/WebCore/html/DOMFormData.idl b/Source/WebCore/html/DOMFormData.idl new file mode 100644 index 000000000..992f04766 --- /dev/null +++ b/Source/WebCore/html/DOMFormData.idl @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + CustomConstructor, + GenerateNativeConverter, + GenerateToJS + ] DOMFormData { + // void append(DOMString name, DOMString value); + // void append(DOMString name, Blob value, optional DOMString filename); + [Custom] void append(in [Optional=CallWithDefaultValue] DOMString name, + in [Optional=CallWithDefaultValue] DOMString value, + in [Optional=CallWithDefaultValue] DOMString filename); + }; + +} diff --git a/Source/WebCore/html/DOMSettableTokenList.cpp b/Source/WebCore/html/DOMSettableTokenList.cpp new file mode 100644 index 000000000..3a86e9c00 --- /dev/null +++ b/Source/WebCore/html/DOMSettableTokenList.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DOMSettableTokenList.h" + +namespace WebCore { + +DOMSettableTokenList::DOMSettableTokenList() + : m_value() + , m_tokens() +{ +} + +DOMSettableTokenList::~DOMSettableTokenList() +{ +} + +const AtomicString DOMSettableTokenList::item(unsigned index) const +{ + if (index >= length()) + return AtomicString(); + return m_tokens[index]; +} + +bool DOMSettableTokenList::contains(const AtomicString& token, ExceptionCode& ec) const +{ + if (!validateToken(token, ec)) + return false; + return m_tokens.contains(token); +} + +void DOMSettableTokenList::add(const AtomicString& token, ExceptionCode& ec) +{ + if (!validateToken(token, ec) || m_tokens.contains(token)) + return; + addInternal(token); +} + +void DOMSettableTokenList::addInternal(const AtomicString& token) +{ + m_value = addToken(m_value, token); + if (m_tokens.isNull()) + m_tokens.set(token, false); + else + m_tokens.add(token); +} + +void DOMSettableTokenList::remove(const AtomicString& token, ExceptionCode& ec) +{ + if (!validateToken(token, ec) || !m_tokens.contains(token)) + return; + removeInternal(token); +} + +void DOMSettableTokenList::removeInternal(const AtomicString& token) +{ + m_value = removeToken(m_value, token); + m_tokens.remove(token); +} + +bool DOMSettableTokenList::toggle(const AtomicString& token, ExceptionCode& ec) +{ + if (!validateToken(token, ec)) + return false; + if (m_tokens.contains(token)) { + removeInternal(token); + return false; + } + addInternal(token); + return true; +} + +void DOMSettableTokenList::setValue(const String& value) +{ + m_value = value; + m_tokens.set(value, false); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/DOMSettableTokenList.h b/Source/WebCore/html/DOMSettableTokenList.h new file mode 100644 index 000000000..0b23c6ab2 --- /dev/null +++ b/Source/WebCore/html/DOMSettableTokenList.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DOMSettableTokenList_h +#define DOMSettableTokenList_h + +#include "DOMTokenList.h" +#include "SpaceSplitString.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +typedef int ExceptionCode; + +class DOMSettableTokenList : public DOMTokenList, public RefCounted<DOMSettableTokenList> { + WTF_MAKE_FAST_ALLOCATED; +public: + static PassRefPtr<DOMSettableTokenList> create() + { + return adoptRef(new DOMSettableTokenList()); + } + virtual ~DOMSettableTokenList(); + + virtual void ref() { RefCounted<DOMSettableTokenList>::ref(); } + virtual void deref() { RefCounted<DOMSettableTokenList>::deref(); } + + virtual unsigned length() const { return m_tokens.size(); } + virtual const AtomicString item(unsigned index) const; + virtual bool contains(const AtomicString&, ExceptionCode&) const; + virtual void add(const AtomicString&, ExceptionCode&); + virtual void remove(const AtomicString&, ExceptionCode&); + virtual bool toggle(const AtomicString&, ExceptionCode&); + virtual String toString() const { return value(); } + + String value() const { return m_value; } + const SpaceSplitString& tokens() const { return m_tokens; } + void setValue(const String&); + +private: + DOMSettableTokenList(); + + void removeInternal(const AtomicString&); + void addInternal(const AtomicString&); + + String m_value; + SpaceSplitString m_tokens; +}; + +} // namespace WebCore + +#endif // DOMSettableTokenList_h diff --git a/Source/WebCore/html/DOMSettableTokenList.idl b/Source/WebCore/html/DOMSettableTokenList.idl new file mode 100644 index 000000000..6260623ee --- /dev/null +++ b/Source/WebCore/html/DOMSettableTokenList.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module core { + + interface [ + GenerateConstructor, + HasIndexGetter, + GenerateToJS + ] DOMSettableTokenList : DOMTokenList { + attribute DOMString value; + }; + +} diff --git a/Source/WebCore/html/DOMTokenList.cpp b/Source/WebCore/html/DOMTokenList.cpp new file mode 100644 index 000000000..5c41f74b5 --- /dev/null +++ b/Source/WebCore/html/DOMTokenList.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DOMTokenList.h" + +#include "ExceptionCode.h" +#include "HTMLParserIdioms.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +bool DOMTokenList::validateToken(const AtomicString& token, ExceptionCode& ec) +{ + if (token.isEmpty()) { + ec = SYNTAX_ERR; + return false; + } + + unsigned length = token.length(); + for (unsigned i = 0; i < length; ++i) { + if (isHTMLSpace(token[i])) { + ec = INVALID_CHARACTER_ERR; + return false; + } + } + + return true; +} + +String DOMTokenList::addToken(const AtomicString& input, const AtomicString& token) +{ + if (input.isEmpty()) + return token; + + StringBuilder builder; + builder.append(input); + if (input[input.length()-1] != ' ') + builder.append(' '); + builder.append(token); + return builder.toString(); +} + +String DOMTokenList::removeToken(const AtomicString& input, const AtomicString& token) +{ + // Algorithm defined at http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#remove-a-token-from-a-string + + unsigned inputLength = input.length(); + StringBuilder output; // 3 + output.reserveCapacity(inputLength); + unsigned position = 0; // 4 + + // Step 5 + while (position < inputLength) { + if (isHTMLSpace(input[position])) { // 6 + output.append(input[position++]); // 6.1, 6.2 + continue; // 6.3 + } + + // Step 7 + StringBuilder s; + while (position < inputLength && isNotHTMLSpace(input[position])) + s.append(input[position++]); + + // Step 8 + if (s.toStringPreserveCapacity() == token) { + // Step 8.1 + while (position < inputLength && isHTMLSpace(input[position])) + ++position; + + // Step 8.2 + size_t j = output.length(); + while (j > 0 && isHTMLSpace(output[j - 1])) + --j; + output.resize(j); + + // Step 8.3 + if (position < inputLength && !output.isEmpty()) + output.append(' '); + } else + output.append(s.toStringPreserveCapacity()); // Step 9 + } + + return output.toString(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/DOMTokenList.h b/Source/WebCore/html/DOMTokenList.h new file mode 100644 index 000000000..fd110b7ff --- /dev/null +++ b/Source/WebCore/html/DOMTokenList.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DOMTokenList_h +#define DOMTokenList_h + +#include <wtf/text/AtomicString.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Element; + +typedef int ExceptionCode; + +class DOMTokenList { + WTF_MAKE_NONCOPYABLE(DOMTokenList); WTF_MAKE_FAST_ALLOCATED; +public: + DOMTokenList() { } + virtual ~DOMTokenList() {}; + + virtual void ref() = 0; + virtual void deref() = 0; + + virtual unsigned length() const = 0; + virtual const AtomicString item(unsigned index) const = 0; + virtual bool contains(const AtomicString&, ExceptionCode&) const = 0; + virtual void add(const AtomicString&, ExceptionCode&) = 0; + virtual void remove(const AtomicString&, ExceptionCode&) = 0; + virtual bool toggle(const AtomicString&, ExceptionCode&) = 0; + virtual String toString() const = 0; + + virtual Element* element() { return 0; } + +protected: + static bool validateToken(const AtomicString&, ExceptionCode&); + static String addToken(const AtomicString&, const AtomicString&); + static String removeToken(const AtomicString&, const AtomicString&); +}; + +} // namespace WebCore + +#endif // DOMTokenList_h diff --git a/Source/WebCore/html/DOMTokenList.idl b/Source/WebCore/html/DOMTokenList.idl new file mode 100644 index 000000000..f94491cbe --- /dev/null +++ b/Source/WebCore/html/DOMTokenList.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module core { + + interface [ + GenerateIsReachable=ImplElementRoot, + GenerateConstructor, + HasIndexGetter + ] DOMTokenList { + readonly attribute unsigned long length; + [ConvertNullStringTo=Null] DOMString item(in unsigned long index); + boolean contains(in DOMString token) raises(DOMException); + void add(in DOMString token) raises(DOMException); + void remove(in DOMString token) raises(DOMException); + boolean toggle(in DOMString token) raises(DOMException); + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + [DontEnum] DOMString toString(); +#endif + }; + +} diff --git a/Source/WebCore/html/DOMURL.cpp b/Source/WebCore/html/DOMURL.cpp new file mode 100644 index 000000000..e42060d48 --- /dev/null +++ b/Source/WebCore/html/DOMURL.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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(BLOB) + +#include "DOMURL.h" + +#include "Blob.h" +#include "BlobURL.h" +#include "KURL.h" +#include "ScriptExecutionContext.h" +#include "ThreadableBlobRegistry.h" +#include <wtf/MainThread.h> + +#if ENABLE(MEDIA_STREAM) +#include "MediaStream.h" +#include "MediaStreamRegistry.h" +#endif + +namespace WebCore { + +DOMURL::DOMURL(ScriptExecutionContext* scriptExecutionContext) + : ContextDestructionObserver(scriptExecutionContext) +{ +} + +DOMURL::~DOMURL() +{ +} + +void DOMURL::contextDestroyed() +{ + ContextDestructionObserver::contextDestroyed(); + + HashSet<String>::iterator publicBlobURLsEnd = m_publicBlobURLs.end(); + for (HashSet<String>::iterator iter = m_publicBlobURLs.begin(); iter != publicBlobURLsEnd; ++iter) + ThreadableBlobRegistry::unregisterBlobURL(KURL(ParsedURLString, *iter)); + +#if ENABLE(MEDIA_STREAM) + HashSet<String>::iterator publicStreamURLsEnd = m_publicStreamURLs.end(); + for (HashSet<String>::iterator iter = m_publicStreamURLs.begin(); iter != publicStreamURLsEnd; ++iter) + MediaStreamRegistry::registry().unregisterMediaStreamURL(KURL(ParsedURLString, *iter)); +#endif +} + +#if ENABLE(MEDIA_STREAM) +String DOMURL::createObjectURL(MediaStream* stream) +{ + if (!m_scriptExecutionContext || !stream) + return String(); + + KURL publicURL = BlobURL::createPublicURL(scriptExecutionContext()->securityOrigin()); + if (publicURL.isEmpty()) + return String(); + + // Since WebWorkers cannot obtain Stream objects, we should be on the main thread. + ASSERT(isMainThread()); + + MediaStreamRegistry::registry().registerMediaStreamURL(publicURL, stream); + m_publicStreamURLs.add(publicURL.string()); + + return publicURL.string(); +} +#endif + +String DOMURL::createObjectURL(Blob* blob) +{ + if (!m_scriptExecutionContext || !blob) + return String(); + + KURL publicURL = BlobURL::createPublicURL(scriptExecutionContext()->securityOrigin()); + if (publicURL.isEmpty()) + return String(); + + ThreadableBlobRegistry::registerBlobURL(publicURL, blob->url()); + m_publicBlobURLs.add(publicURL.string()); + + return publicURL.string(); +} + +void DOMURL::revokeObjectURL(const String& urlString) +{ + if (!m_scriptExecutionContext) + return; + + KURL url(KURL(), urlString); + + if (m_publicBlobURLs.contains(url.string())) { + ThreadableBlobRegistry::unregisterBlobURL(url); + m_publicBlobURLs.remove(url.string()); + } + +#if ENABLE(MEDIA_STREAM) + if (m_publicStreamURLs.contains(url.string())) { + // FIXME: make sure of this assertion below. Raise a spec question if required. + // Since WebWorkers cannot obtain Stream objects, we should be on the main thread. + ASSERT(isMainThread()); + MediaStreamRegistry::registry().unregisterMediaStreamURL(url); + m_publicStreamURLs.remove(url.string()); + } +#endif +} + +} // namespace WebCore + +#endif // ENABLE(BLOB) diff --git a/Source/WebCore/html/DOMURL.h b/Source/WebCore/html/DOMURL.h new file mode 100644 index 000000000..d02fe887c --- /dev/null +++ b/Source/WebCore/html/DOMURL.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 DOMURL_h +#define DOMURL_h + +#if ENABLE(BLOB) + +#include "ActiveDOMObject.h" +#include "PlatformString.h" +#include <wtf/HashSet.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class Blob; +class MediaStream; +class ScriptExecutionContext; + +class DOMURL : public RefCounted<DOMURL>, public ContextDestructionObserver { +public: + static PassRefPtr<DOMURL> create(ScriptExecutionContext* scriptExecutionContext) { return adoptRef(new DOMURL(scriptExecutionContext)); } + ~DOMURL(); + +#if ENABLE(MEDIA_STREAM) + String createObjectURL(MediaStream*); +#endif + String createObjectURL(Blob*); + void revokeObjectURL(const String&); + +private: + explicit DOMURL(ScriptExecutionContext*); + + virtual void contextDestroyed(); + + HashSet<String> m_publicBlobURLs; +#if ENABLE(MEDIA_STREAM) + HashSet<String> m_publicStreamURLs; +#endif +}; + +} // namespace WebCore + +#endif // ENABLE(BLOB) + +#endif // DOMURL_h diff --git a/Source/WebCore/html/DOMURL.idl b/Source/WebCore/html/DOMURL.idl new file mode 100644 index 000000000..d6d55790f --- /dev/null +++ b/Source/WebCore/html/DOMURL.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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=BLOB, + OmitConstructor, + GenerateNativeConverter, + GenerateToJS, + NoStaticTables + ] DOMURL { +#if defined(ENABLE_MEDIA_STREAM) && ENABLE_MEDIA_STREAM + [ConvertNullStringTo=Undefined] DOMString createObjectURL(in MediaStream stream); +#endif + [ConvertNullStringTo=Undefined] DOMString createObjectURL(in Blob blob); + void revokeObjectURL(in DOMString url); + }; +} diff --git a/Source/WebCore/html/DateInputType.cpp b/Source/WebCore/html/DateInputType.cpp new file mode 100644 index 000000000..7b83e3dd3 --- /dev/null +++ b/Source/WebCore/html/DateInputType.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DateInputType.h" + +#include "DateComponents.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/PassOwnPtr.h> + +#if ENABLE(INPUT_TYPE_DATE) + +namespace WebCore { + +using namespace HTMLNames; + +static const double dateDefaultStep = 1.0; +static const double dateStepScaleFactor = 86400000.0; + +PassOwnPtr<InputType> DateInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new DateInputType(element)); +} + +const AtomicString& DateInputType::formControlType() const +{ + return InputTypeNames::date(); +} + +DateComponents::Type DateInputType::dateType() const +{ + return DateComponents::Date; +} + +double DateInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumDate()); +} + +double DateInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumDate()); +} + +double DateInputType::defaultStep() const +{ + return dateDefaultStep; +} + +double DateInputType::stepScaleFactor() const +{ + return dateStepScaleFactor; +} + +bool DateInputType::parsedStepValueShouldBeInteger() const +{ + return true; +} + +bool DateInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const +{ + ASSERT(out); + unsigned end; + return out->parseDate(characters, length, 0, end) && end == length; +} + +bool DateInputType::setMillisecondToDateComponents(double value, DateComponents* date) const +{ + ASSERT(date); + return date->setMillisecondsSinceEpochForDate(value); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/DateInputType.h b/Source/WebCore/html/DateInputType.h new file mode 100644 index 000000000..afb1895ae --- /dev/null +++ b/Source/WebCore/html/DateInputType.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DateInputType_h +#define DateInputType_h + +#include "BaseDateAndTimeInputType.h" + +#if ENABLE(INPUT_TYPE_DATE) + +namespace WebCore { + +class DateInputType : public BaseDateAndTimeInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + DateInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual DateComponents::Type dateType() const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual bool parsedStepValueShouldBeInteger() const OVERRIDE; + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; +}; + +} // namespace WebCore + +#endif +#endif // DateInputType_h diff --git a/Source/WebCore/html/DateTimeInputType.cpp b/Source/WebCore/html/DateTimeInputType.cpp new file mode 100644 index 000000000..35695d6a1 --- /dev/null +++ b/Source/WebCore/html/DateTimeInputType.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DateTimeInputType.h" + +#include "DateComponents.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/CurrentTime.h> +#include <wtf/PassOwnPtr.h> + +#if ENABLE(INPUT_TYPE_DATETIME) + +namespace WebCore { + +using namespace HTMLNames; + +static const double dateTimeDefaultStep = 60.0; +static const double dateTimeStepScaleFactor = 1000.0; + +PassOwnPtr<InputType> DateTimeInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new DateTimeInputType(element)); +} + +const AtomicString& DateTimeInputType::formControlType() const +{ + return InputTypeNames::datetime(); +} + +DateComponents::Type DateTimeInputType::dateType() const +{ + return DateComponents::DateTime; +} + +double DateTimeInputType::defaultValueForStepUp() const +{ + return currentTimeMS(); +} + +double DateTimeInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumDateTime()); +} + +double DateTimeInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumDateTime()); +} + +double DateTimeInputType::defaultStep() const +{ + return dateTimeDefaultStep; +} + +double DateTimeInputType::stepScaleFactor() const +{ + return dateTimeStepScaleFactor; +} + +bool DateTimeInputType::scaledStepValueShouldBeInteger() const +{ + return true; +} + +bool DateTimeInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const +{ + ASSERT(out); + unsigned end; + return out->parseDateTime(characters, length, 0, end) && end == length; +} + +bool DateTimeInputType::setMillisecondToDateComponents(double value, DateComponents* date) const +{ + ASSERT(date); + return date->setMillisecondsSinceEpochForDateTime(value); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/DateTimeInputType.h b/Source/WebCore/html/DateTimeInputType.h new file mode 100644 index 000000000..90265e953 --- /dev/null +++ b/Source/WebCore/html/DateTimeInputType.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DateTimeInputType_h +#define DateTimeInputType_h + +#include "BaseDateAndTimeInputType.h" + +#if ENABLE(INPUT_TYPE_DATETIME) + +namespace WebCore { + +class DateTimeInputType : public BaseDateAndTimeInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + DateTimeInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual DateComponents::Type dateType() const OVERRIDE; + virtual double defaultValueForStepUp() const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual bool scaledStepValueShouldBeInteger() const OVERRIDE; + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; +}; + +} // namespace WebCore + +#endif +#endif // DateTimeInputType_h diff --git a/Source/WebCore/html/DateTimeLocalInputType.cpp b/Source/WebCore/html/DateTimeLocalInputType.cpp new file mode 100644 index 000000000..e8c1fd602 --- /dev/null +++ b/Source/WebCore/html/DateTimeLocalInputType.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DateTimeLocalInputType.h" + +#include "DateComponents.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/PassOwnPtr.h> + +#if ENABLE(INPUT_TYPE_DATETIMELOCAL) + +namespace WebCore { + +using namespace HTMLNames; + +static const double dateTimeLocalDefaultStep = 60.0; +static const double dateTimeLocalStepScaleFactor = 1000.0; + +PassOwnPtr<InputType> DateTimeLocalInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new DateTimeLocalInputType(element)); +} + +const AtomicString& DateTimeLocalInputType::formControlType() const +{ + return InputTypeNames::datetimelocal(); +} + +DateComponents::Type DateTimeLocalInputType::dateType() const +{ + return DateComponents::DateTimeLocal; +} + +double DateTimeLocalInputType::valueAsDate() const +{ + // valueAsDate doesn't work for the datetime-local type according to the standard. + return DateComponents::invalidMilliseconds(); +} + +void DateTimeLocalInputType::setValueAsDate(double value, ExceptionCode& ec) const +{ + // valueAsDate doesn't work for the datetime-local type according to the standard. + InputType::setValueAsDate(value, ec); +} + +double DateTimeLocalInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumDateTime()); +} + +double DateTimeLocalInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumDateTime()); +} + +double DateTimeLocalInputType::defaultStep() const +{ + return dateTimeLocalDefaultStep; +} + +double DateTimeLocalInputType::stepScaleFactor() const +{ + return dateTimeLocalStepScaleFactor; +} + +bool DateTimeLocalInputType::scaledStepValueShouldBeInteger() const +{ + return true; +} + +bool DateTimeLocalInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const +{ + ASSERT(out); + unsigned end; + return out->parseDateTimeLocal(characters, length, 0, end) && end == length; +} + +bool DateTimeLocalInputType::setMillisecondToDateComponents(double value, DateComponents* date) const +{ + ASSERT(date); + return date->setMillisecondsSinceEpochForDateTimeLocal(value); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/DateTimeLocalInputType.h b/Source/WebCore/html/DateTimeLocalInputType.h new file mode 100644 index 000000000..2641d8439 --- /dev/null +++ b/Source/WebCore/html/DateTimeLocalInputType.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DateTimeLocalInputType_h +#define DateTimeLocalInputType_h + +#include "BaseDateAndTimeInputType.h" + +#if ENABLE(INPUT_TYPE_DATETIMELOCAL) + +namespace WebCore { + +class DateTimeLocalInputType : public BaseDateAndTimeInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + DateTimeLocalInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual DateComponents::Type dateType() const OVERRIDE; + virtual double valueAsDate() const OVERRIDE; + virtual void setValueAsDate(double, ExceptionCode&) const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual bool scaledStepValueShouldBeInteger() const OVERRIDE; + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; +}; + +} // namespace WebCore + +#endif +#endif // DateTimeLocalInputType_h diff --git a/Source/WebCore/html/EmailInputType.cpp b/Source/WebCore/html/EmailInputType.cpp new file mode 100644 index 000000000..f00ae40f7 --- /dev/null +++ b/Source/WebCore/html/EmailInputType.cpp @@ -0,0 +1,111 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2009 Michelangelo De Simone <micdesim@gmail.com> + * Copyright (C) 2010 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 "EmailInputType.h" + +#include "HTMLInputElement.h" +#include "HTMLParserIdioms.h" +#include "LocalizedStrings.h" +#include "RegularExpression.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +static const char emailPattern[] = + "[a-z0-9!#$%&'*+/=?^_`{|}~.-]+" // local part + "@" + "[a-z0-9-]+(\\.[a-z0-9-]+)*"; // domain part + +static bool isValidEmailAddress(const String& address) +{ + int addressLength = address.length(); + if (!addressLength) + return false; + + DEFINE_STATIC_LOCAL(const RegularExpression, regExp, (emailPattern, TextCaseInsensitive)); + + int matchLength; + int matchOffset = regExp.match(address, 0, &matchLength); + + return !matchOffset && matchLength == addressLength; +} + +PassOwnPtr<InputType> EmailInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new EmailInputType(element)); +} + +const AtomicString& EmailInputType::formControlType() const +{ + return InputTypeNames::email(); +} + +bool EmailInputType::typeMismatchFor(const String& value) const +{ + if (value.isEmpty()) + return false; + if (!element()->multiple()) + return !isValidEmailAddress(value); + Vector<String> addresses; + value.split(',', true, addresses); + for (unsigned i = 0; i < addresses.size(); ++i) { + if (!isValidEmailAddress(stripLeadingAndTrailingHTMLSpaces(addresses[i]))) + return true; + } + return false; +} + +bool EmailInputType::typeMismatch() const +{ + return typeMismatchFor(element()->value()); +} + +String EmailInputType::typeMismatchText() const +{ + return element()->multiple() ? validationMessageTypeMismatchForMultipleEmailText() : validationMessageTypeMismatchForEmailText(); +} + +bool EmailInputType::isEmailField() const +{ + return true; +} + +String EmailInputType::sanitizeValue(const String& proposedValue) const +{ + String noLineBreakValue = proposedValue.removeCharacters(isHTMLLineBreak); + if (!element()->multiple()) + return noLineBreakValue; + Vector<String> addresses; + noLineBreakValue.split(',', true, addresses); + StringBuilder strippedValue; + for (unsigned i = 0; i < addresses.size(); ++i) { + if (i > 0) + strippedValue.append(","); + strippedValue.append(stripLeadingAndTrailingHTMLSpaces(addresses[i])); + } + return strippedValue.toString(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/EmailInputType.h b/Source/WebCore/html/EmailInputType.h new file mode 100644 index 000000000..e9a7b9ac6 --- /dev/null +++ b/Source/WebCore/html/EmailInputType.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef EmailInputType_h +#define EmailInputType_h + +#include "BaseTextInputType.h" + +namespace WebCore { + +class EmailInputType : public BaseTextInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + EmailInputType(HTMLInputElement* element) : BaseTextInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool typeMismatchFor(const String&) const OVERRIDE; + virtual bool typeMismatch() const OVERRIDE; + virtual String typeMismatchText() const OVERRIDE; + virtual bool isEmailField() const OVERRIDE; + virtual String sanitizeValue(const String&) const OVERRIDE; +}; + +} // namespace WebCore + +#endif // ButtonInputType_h diff --git a/Source/WebCore/html/FTPDirectoryDocument.cpp b/Source/WebCore/html/FTPDirectoryDocument.cpp new file mode 100644 index 000000000..739a8f005 --- /dev/null +++ b/Source/WebCore/html/FTPDirectoryDocument.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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(FTPDIR) +#include "FTPDirectoryDocument.h" + +#include "HTMLDocumentParser.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "LocalizedStrings.h" +#include "Logging.h" +#include "FTPDirectoryParser.h" +#include "SegmentedString.h" +#include "Settings.h" +#include "SharedBuffer.h" +#include "Text.h" +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> +#include <wtf/CurrentTime.h> +#include <wtf/StdLibExtras.h> +#include <wtf/unicode/CharacterNames.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +class FTPDirectoryDocumentParser : public HTMLDocumentParser { +public: + static PassRefPtr<FTPDirectoryDocumentParser> create(HTMLDocument* document) + { + return adoptRef(new FTPDirectoryDocumentParser(document)); + } + + virtual void append(const SegmentedString&); + virtual void finish(); + + virtual bool isWaitingForScripts() const { return false; } + + inline void checkBuffer(int len = 10) + { + if ((m_dest - m_buffer) > m_size - len) { + // Enlarge buffer + int newSize = max(m_size * 2, m_size + len); + int oldOffset = m_dest - m_buffer; + m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); + m_dest = m_buffer + oldOffset; + m_size = newSize; + } + } + +private: + FTPDirectoryDocumentParser(HTMLDocument*); + + // The parser will attempt to load the document template specified via the preference + // Failing that, it will fall back and create the basic document which will have a minimal + // table for presenting the FTP directory in a useful manner + bool loadDocumentTemplate(); + void createBasicDocument(); + + void parseAndAppendOneLine(const String&); + void appendEntry(const String& name, const String& size, const String& date, bool isDirectory); + PassRefPtr<Element> createTDForFilename(const String&); + + RefPtr<HTMLTableElement> m_tableElement; + + bool m_skipLF; + bool m_parsedTemplate; + + int m_size; + UChar* m_buffer; + UChar* m_dest; + String m_carryOver; + + ListState m_listState; +}; + +FTPDirectoryDocumentParser::FTPDirectoryDocumentParser(HTMLDocument* document) + : HTMLDocumentParser(document, false) + , m_skipLF(false) + , m_parsedTemplate(false) + , m_size(254) + , m_buffer(static_cast<UChar*>(fastMalloc(sizeof(UChar) * m_size))) + , m_dest(m_buffer) +{ +} + +void FTPDirectoryDocumentParser::appendEntry(const String& filename, const String& size, const String& date, bool isDirectory) +{ + ExceptionCode ec; + + RefPtr<Element> rowElement = m_tableElement->insertRow(-1, ec); + rowElement->setAttribute("class", "ftpDirectoryEntryRow", ec); + + RefPtr<Element> element = document()->createElement(tdTag, false); + element->appendChild(Text::create(document(), String(&noBreakSpace, 1)), ec); + if (isDirectory) + element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeDirectory", ec); + else + element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeFile", ec); + rowElement->appendChild(element, ec); + + element = createTDForFilename(filename); + element->setAttribute("class", "ftpDirectoryFileName", ec); + rowElement->appendChild(element, ec); + + element = document()->createElement(tdTag, false); + element->appendChild(Text::create(document(), date), ec); + element->setAttribute("class", "ftpDirectoryFileDate", ec); + rowElement->appendChild(element, ec); + + element = document()->createElement(tdTag, false); + element->appendChild(Text::create(document(), size), ec); + element->setAttribute("class", "ftpDirectoryFileSize", ec); + rowElement->appendChild(element, ec); +} + +PassRefPtr<Element> FTPDirectoryDocumentParser::createTDForFilename(const String& filename) +{ + ExceptionCode ec; + + String fullURL = document()->baseURL().string(); + if (fullURL[fullURL.length() - 1] == '/') + fullURL.append(filename); + else + fullURL.append("/" + filename); + + RefPtr<Element> anchorElement = document()->createElement(aTag, false); + anchorElement->setAttribute("href", fullURL, ec); + anchorElement->appendChild(Text::create(document(), filename), ec); + + RefPtr<Element> tdElement = document()->createElement(tdTag, false); + tdElement->appendChild(anchorElement, ec); + + return tdElement.release(); +} + +static String processFilesizeString(const String& size, bool isDirectory) +{ + if (isDirectory) + return "--"; + + bool valid; + int64_t bytes = size.toUInt64(&valid); + if (!valid) + return unknownFileSizeText(); + + if (bytes < 1000000) + return String::format("%.2f KB", static_cast<float>(bytes)/1000); + + if (bytes < 1000000000) + return String::format("%.2f MB", static_cast<float>(bytes)/1000000); + + return String::format("%.2f GB", static_cast<float>(bytes)/1000000000); +} + +static bool wasLastDayOfMonth(int year, int month, int day) +{ + static int lastDays[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if (month < 0 || month > 11) + return false; + + if (month == 2) { + if (year % 4 == 0 && (year % 100 || year % 400 == 0)) { + if (day == 29) + return true; + return false; + } + + if (day == 28) + return true; + return false; + } + + return lastDays[month] == day; +} + +static String processFileDateString(const FTPTime& fileTime) +{ + // FIXME: Need to localize this string? + + String timeOfDay; + + if (!(fileTime.tm_hour == 0 && fileTime.tm_min == 0 && fileTime.tm_sec == 0)) { + int hour = fileTime.tm_hour; + ASSERT(hour >= 0 && hour < 24); + + if (hour < 12) { + if (hour == 0) + hour = 12; + timeOfDay = String::format(", %i:%02i AM", hour, fileTime.tm_min); + } else { + hour = hour - 12; + if (hour == 0) + hour = 12; + timeOfDay = String::format(", %i:%02i PM", hour, fileTime.tm_min); + } + } + + // If it was today or yesterday, lets just do that - but we have to compare to the current time + struct tm now; + time_t now_t = time(NULL); + getLocalTime(&now_t, &now); + + // localtime does "year = current year - 1900", compensate for that for readability and comparison purposes + now.tm_year += 1900; + + if (fileTime.tm_year == now.tm_year) { + if (fileTime.tm_mon == now.tm_mon) { + if (fileTime.tm_mday == now.tm_mday) + return "Today" + timeOfDay; + if (fileTime.tm_mday == now.tm_mday - 1) + return "Yesterday" + timeOfDay; + } + + if (now.tm_mday == 1 && (now.tm_mon == fileTime.tm_mon + 1 || (now.tm_mon == 0 && fileTime.tm_mon == 11)) && + wasLastDayOfMonth(fileTime.tm_year, fileTime.tm_mon, fileTime.tm_mday)) + return "Yesterday" + timeOfDay; + } + + if (fileTime.tm_year == now.tm_year - 1 && fileTime.tm_mon == 12 && fileTime.tm_mday == 31 && now.tm_mon == 1 && now.tm_mday == 1) + return "Yesterday" + timeOfDay; + + static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" }; + + int month = fileTime.tm_mon; + if (month < 0 || month > 11) + month = 12; + + String dateString; + + if (fileTime.tm_year > -1) + dateString = String(months[month]) + " " + String::number(fileTime.tm_mday) + ", " + String::number(fileTime.tm_year); + else + dateString = String(months[month]) + " " + String::number(fileTime.tm_mday) + ", " + String::number(now.tm_year); + + return dateString + timeOfDay; +} + +void FTPDirectoryDocumentParser::parseAndAppendOneLine(const String& inputLine) +{ + ListResult result; + CString latin1Input = inputLine.latin1(); + + FTPEntryType typeResult = parseOneFTPLine(latin1Input.data(), m_listState, result); + + // FTPMiscEntry is a comment or usage statistic which we don't care about, and junk is invalid data - bail in these 2 cases + if (typeResult == FTPMiscEntry || typeResult == FTPJunkEntry) + return; + + String filename(result.filename, result.filenameLength); + if (result.type == FTPDirectoryEntry) { + filename.append("/"); + + // We have no interest in linking to "current directory" + if (filename == "./") + return; + } + + LOG(FTP, "Appending entry - %s, %s", filename.ascii().data(), result.fileSize.ascii().data()); + + appendEntry(filename, processFilesizeString(result.fileSize, result.type == FTPDirectoryEntry), processFileDateString(result.modifiedTime), result.type == FTPDirectoryEntry); +} + +static inline PassRefPtr<SharedBuffer> createTemplateDocumentData(Settings* settings) +{ + RefPtr<SharedBuffer> buffer = 0; + if (settings) + buffer = SharedBuffer::createWithContentsOfFile(settings->ftpDirectoryTemplatePath()); + if (buffer) + LOG(FTP, "Loaded FTPDirectoryTemplate of length %i\n", buffer->size()); + return buffer.release(); +} + +bool FTPDirectoryDocumentParser::loadDocumentTemplate() +{ + DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, templateDocumentData, (createTemplateDocumentData(document()->settings()))); + // FIXME: Instead of storing the data, we'd rather actually parse the template data into the template Document once, + // store that document, then "copy" it whenever we get an FTP directory listing. There are complexities with this + // approach that make it worth putting this off. + + if (!templateDocumentData) { + LOG_ERROR("Could not load templateData"); + return false; + } + + HTMLDocumentParser::insert(String(templateDocumentData->data(), templateDocumentData->size())); + + RefPtr<Element> tableElement = document()->getElementById("ftpDirectoryTable"); + if (!tableElement) + LOG_ERROR("Unable to find element by id \"ftpDirectoryTable\" in the template document."); + else if (!tableElement->hasTagName(tableTag)) + LOG_ERROR("Element of id \"ftpDirectoryTable\" is not a table element"); + else + m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + + // Bail if we found the table element + if (m_tableElement) + return true; + + // Otherwise create one manually + tableElement = document()->createElement(tableTag, false); + m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + ExceptionCode ec; + m_tableElement->setAttribute("id", "ftpDirectoryTable", ec); + + // If we didn't find the table element, lets try to append our own to the body + // If that fails for some reason, cram it on the end of the document as a last + // ditch effort + if (Element* body = document()->body()) + body->appendChild(m_tableElement, ec); + else + document()->appendChild(m_tableElement, ec); + + return true; +} + +void FTPDirectoryDocumentParser::createBasicDocument() +{ + LOG(FTP, "Creating a basic FTP document structure as no template was loaded"); + + // FIXME: Make this "basic document" more acceptable + + RefPtr<Element> bodyElement = document()->createElement(bodyTag, false); + + ExceptionCode ec; + document()->appendChild(bodyElement, ec); + + RefPtr<Element> tableElement = document()->createElement(tableTag, false); + m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + m_tableElement->setAttribute("id", "ftpDirectoryTable", ec); + + bodyElement->appendChild(m_tableElement, ec); +} + +void FTPDirectoryDocumentParser::append(const SegmentedString& source) +{ + // Make sure we have the table element to append to by loading the template set in the pref, or + // creating a very basic document with the appropriate table + if (!m_tableElement) { + if (!loadDocumentTemplate()) + createBasicDocument(); + ASSERT(m_tableElement); + } + + bool foundNewLine = false; + + m_dest = m_buffer; + SegmentedString str = source; + while (!str.isEmpty()) { + UChar c = *str; + + if (c == '\r') { + *m_dest++ = '\n'; + foundNewLine = true; + // possibly skip an LF in the case of an CRLF sequence + m_skipLF = true; + } else if (c == '\n') { + if (!m_skipLF) + *m_dest++ = c; + else + m_skipLF = false; + } else { + *m_dest++ = c; + m_skipLF = false; + } + + str.advance(); + + // Maybe enlarge the buffer + checkBuffer(); + } + + if (!foundNewLine) { + m_dest = m_buffer; + return; + } + + UChar* start = m_buffer; + UChar* cursor = start; + + while (cursor < m_dest) { + if (*cursor == '\n') { + m_carryOver.append(String(start, cursor - start)); + LOG(FTP, "%s", m_carryOver.ascii().data()); + parseAndAppendOneLine(m_carryOver); + m_carryOver = String(); + + start = ++cursor; + } else + cursor++; + } + + // Copy the partial line we have left to the carryover buffer + if (cursor - start > 1) + m_carryOver.append(String(start, cursor - start - 1)); +} + +void FTPDirectoryDocumentParser::finish() +{ + // Possible the last line in the listing had no newline, so try to parse it now + if (!m_carryOver.isEmpty()) { + parseAndAppendOneLine(m_carryOver); + m_carryOver = String(); + } + + m_tableElement = 0; + fastFree(m_buffer); + + HTMLDocumentParser::finish(); +} + +FTPDirectoryDocument::FTPDirectoryDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) +{ +#ifndef NDEBUG + LogFTP.state = WTFLogChannelOn; +#endif +} + +PassRefPtr<DocumentParser> FTPDirectoryDocument::createParser() +{ + return FTPDirectoryDocumentParser::create(this); +} + +} + +#endif // ENABLE(FTPDIR) diff --git a/Source/WebCore/html/FTPDirectoryDocument.h b/Source/WebCore/html/FTPDirectoryDocument.h new file mode 100644 index 000000000..e7e52f7a7 --- /dev/null +++ b/Source/WebCore/html/FTPDirectoryDocument.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 FTPDirectoryDocument_h +#define FTPDirectoryDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class DOMImplementation; + +class FTPDirectoryDocument : public HTMLDocument { +public: + static PassRefPtr<FTPDirectoryDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new FTPDirectoryDocument(frame, url)); + } + +private: + FTPDirectoryDocument(Frame*, const KURL&); + virtual PassRefPtr<DocumentParser> createParser(); +}; + +} // namespace WebCore + +#endif // FTPDirectoryDocument_h diff --git a/Source/WebCore/html/FileInputType.cpp b/Source/WebCore/html/FileInputType.cpp new file mode 100644 index 000000000..655e81ead --- /dev/null +++ b/Source/WebCore/html/FileInputType.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 "FileInputType.h" + +#include "Chrome.h" +#include "Event.h" +#include "File.h" +#include "FileList.h" +#include "FileSystem.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "Icon.h" +#include "LocalizedStrings.h" +#include "RenderFileUploadControl.h" +#include "ScriptController.h" +#include "ShadowRoot.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +using namespace HTMLNames; + +class UploadButtonElement : public HTMLInputElement { +public: + static PassRefPtr<UploadButtonElement> create(Document*); + static PassRefPtr<UploadButtonElement> createForMultiple(Document*); + +private: + UploadButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +PassRefPtr<UploadButtonElement> UploadButtonElement::create(Document* document) +{ + RefPtr<UploadButtonElement> button = adoptRef(new UploadButtonElement(document)); + button->setType("button"); + button->setValue(fileButtonChooseFileLabel()); + return button.release(); +} + +PassRefPtr<UploadButtonElement> UploadButtonElement::createForMultiple(Document* document) +{ + RefPtr<UploadButtonElement> button = adoptRef(new UploadButtonElement(document)); + button->setType("button"); + button->setValue(fileButtonChooseMultipleFilesLabel()); + return button.release(); +} + +UploadButtonElement::UploadButtonElement(Document* document) + : HTMLInputElement(inputTag, document, 0, false) +{ +} + +const AtomicString& UploadButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-file-upload-button")); + return pseudoId; +} + +inline FileInputType::FileInputType(HTMLInputElement* element) + : BaseButtonInputType(element) + , m_fileList(FileList::create()) +{ +} + +PassOwnPtr<InputType> FileInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new FileInputType(element)); +} + +const AtomicString& FileInputType::formControlType() const +{ + return InputTypeNames::file(); +} + +bool FileInputType::appendFormData(FormDataList& encoding, bool multipart) const +{ + FileList* fileList = element()->files(); + unsigned numFiles = fileList->length(); + if (!multipart) { + // Send only the basenames. + // 4.10.16.4 and 4.10.16.6 sections in HTML5. + + // Unlike the multipart case, we have no special handling for the empty + // fileList because Netscape doesn't support for non-multipart + // submission of file inputs, and Firefox doesn't add "name=" query + // parameter. + for (unsigned i = 0; i < numFiles; ++i) + encoding.appendData(element()->name(), fileList->item(i)->fileName()); + return true; + } + + // If no filename at all is entered, return successful but empty. + // Null would be more logical, but Netscape posts an empty file. Argh. + if (!numFiles) { + encoding.appendBlob(element()->name(), File::create("")); + return true; + } + + for (unsigned i = 0; i < numFiles; ++i) + encoding.appendBlob(element()->name(), fileList->item(i)); + return true; +} + +bool FileInputType::valueMissing(const String& value) const +{ + return value.isEmpty(); +} + +String FileInputType::valueMissingText() const +{ + return element()->multiple() ? validationMessageValueMissingForMultipleFileText() : validationMessageValueMissingForFileText(); +} + +void FileInputType::handleDOMActivateEvent(Event* event) +{ + if (element()->disabled() || !element()->renderer()) + return; + + if (!ScriptController::processingUserGesture()) + return; + + if (Chrome* chrome = this->chrome()) { + FileChooserSettings settings; + HTMLInputElement* input = element(); +#if ENABLE(DIRECTORY_UPLOAD) + settings.allowsDirectoryUpload = input->fastHasAttribute(webkitdirectoryAttr); + settings.allowsMultipleFiles = settings.allowsDirectoryUpload || input->fastHasAttribute(multipleAttr); +#else + settings.allowsMultipleFiles = input->fastHasAttribute(multipleAttr); +#endif + settings.acceptMIMETypes = input->acceptMIMETypes(); + settings.selectedFiles = m_fileList->paths(); + chrome->runOpenPanel(input->document()->frame(), newFileChooser(settings)); + } + event->setDefaultHandled(); +} + +RenderObject* FileInputType::createRenderer(RenderArena* arena, RenderStyle*) const +{ + return new (arena) RenderFileUploadControl(element()); +} + +bool FileInputType::canSetStringValue() const +{ + return false; +} + +bool FileInputType::canChangeFromAnotherType() const +{ + // Don't allow the type to be changed to file after the first type change. + // In other engines this might mean a JavaScript programmer could set a text + // field's value to something like /etc/passwd and then change it to a file input. + // I don't think this would actually occur in WebKit, but this rule still may be + // important for compatibility. + return false; +} + +FileList* FileInputType::files() +{ + return m_fileList.get(); +} + +bool FileInputType::canSetValue(const String& value) +{ + // For security reasons, we don't allow setting the filename, but we do allow clearing it. + // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't + // applicable to the file upload control at all, but for now we are keeping this behavior + // to avoid breaking existing websites that may be relying on this. + return value.isEmpty(); +} + +bool FileInputType::getTypeSpecificValue(String& value) +{ + if (m_fileList->isEmpty()) { + value = String(); + return true; + } + + // HTML5 tells us that we're supposed to use this goofy value for + // file input controls. Historically, browsers revealed the real + // file path, but that's a privacy problem. Code on the web + // decided to try to parse the value by looking for backslashes + // (because that's what Windows file paths use). To be compatible + // with that code, we make up a fake path for the file. + value = "C:\\fakepath\\" + m_fileList->item(0)->fileName(); + return true; +} + +bool FileInputType::storesValueSeparateFromAttribute() +{ + return true; +} + +void FileInputType::setValue(const String&, bool, bool) +{ + m_fileList->clear(); + m_icon.clear(); + element()->setNeedsStyleRecalc(); +} + +void FileInputType::setFileList(const Vector<String>& paths) +{ + m_fileList->clear(); + size_t size = paths.size(); + +#if ENABLE(DIRECTORY_UPLOAD) + // If a directory is being selected, the UI allows a directory to be chosen + // and the paths provided here share a root directory somewhere up the tree; + // we want to store only the relative paths from that point. + if (size && element()->fastHasAttribute(webkitdirectoryAttr)) { + // Find the common root path. + String rootPath = directoryName(paths[0]); + for (size_t i = 1; i < size; i++) { + while (!paths[i].startsWith(rootPath)) + rootPath = directoryName(rootPath); + } + rootPath = directoryName(rootPath); + ASSERT(rootPath.length()); + int rootLength = rootPath.length(); + if (rootPath[rootLength - 1] != '\\' && rootPath[rootLength - 1] != '/') + rootLength += 1; + for (size_t i = 0; i < size; i++) { + // Normalize backslashes to slashes before exposing the relative path to script. + String relativePath = paths[i].substring(rootLength).replace('\\', '/'); + m_fileList->append(File::createWithRelativePath(paths[i], relativePath)); + } + return; + } +#endif + + for (size_t i = 0; i < size; i++) + m_fileList->append(File::create(paths[i])); +} + +bool FileInputType::isFileUpload() const +{ + return true; +} + +void FileInputType::createShadowSubtree() +{ + ExceptionCode ec = 0; + element()->ensureShadowRoot()->appendChild(element()->multiple() ? UploadButtonElement::createForMultiple(element()->document()): UploadButtonElement::create(element()->document()), ec); +} + +void FileInputType::multipleAttributeChanged() +{ + UploadButtonElement* button = static_cast<UploadButtonElement*>(element()->ensureShadowRoot()->firstChild()); + if (button) + button->setValue(element()->multiple() ? fileButtonChooseMultipleFilesLabel() : fileButtonChooseFileLabel()); +} + +void FileInputType::requestIcon(const Vector<String>& paths) +{ + if (!paths.size()) + return; + + if (Chrome* chrome = this->chrome()) + chrome->loadIconForFiles(paths, newFileIconLoader()); +} + +void FileInputType::filesChosen(const Vector<String>& paths) +{ + RefPtr<HTMLInputElement> input = element(); + + bool pathsChanged = false; + if (paths.size() != m_fileList->length()) + pathsChanged = true; + else { + for (unsigned i = 0; i < paths.size(); ++i) { + if (paths[i] != m_fileList->item(i)->path()) { + pathsChanged = true; + break; + } + } + } + + setFileList(paths); + + input->setFormControlValueMatchesRenderer(true); + input->notifyFormStateChanged(); + input->setNeedsValidityCheck(); + + requestIcon(paths); + + if (input->renderer()) + input->renderer()->repaint(); + + if (pathsChanged) { + // This call may cause destruction of this instance. + // input instance is safe since it is ref-counted. + input->HTMLElement::dispatchChangeEvent(); + } + input->setChangedSinceLastFormControlChangeEvent(false); +} + +#if ENABLE(DIRECTORY_UPLOAD) +void FileInputType::receiveDropForDirectoryUpload(const Vector<String>& paths) +{ + if (Chrome* chrome = this->chrome()) { + FileChooserSettings settings; + HTMLInputElement* input = element(); + settings.allowsDirectoryUpload = true; + settings.allowsMultipleFiles = true; + settings.selectedFiles.append(paths[0]); + settings.acceptMIMETypes = input->acceptMIMETypes(); + chrome->enumerateChosenDirectory(newFileChooser(settings)); + } +} +#endif + +void FileInputType::updateRendering(PassRefPtr<Icon> icon) +{ + if (m_icon == icon) + return; + + m_icon = icon; + if (element()->renderer()) + element()->renderer()->repaint(); +} + +void FileInputType::receiveDroppedFiles(const Vector<String>& paths) +{ + HTMLInputElement* input = element(); +#if ENABLE(DIRECTORY_UPLOAD) + if (input->fastHasAttribute(webkitdirectoryAttr)) { + receiveDropForDirectoryUpload(paths); + return; + } +#endif + + if (input->fastHasAttribute(multipleAttr)) + filesChosen(paths); + else { + Vector<String> firstPathOnly; + firstPathOnly.append(paths[0]); + filesChosen(firstPathOnly); + } +} + +Icon* FileInputType::icon() const +{ + return m_icon.get(); +} + +String FileInputType::defaultToolTip() const +{ + FileList* fileList = m_fileList.get(); + unsigned listSize = fileList->length(); + if (!listSize) { + if (element()->multiple()) + return fileButtonNoFilesSelectedLabel(); + return fileButtonNoFileSelectedLabel(); + } + + StringBuilder names; + for (size_t i = 0; i < listSize; ++i) { + names.append(fileList->item(i)->fileName()); + if (i != listSize - 1) + names.append('\n'); + } + return names.toString(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/FileInputType.h b/Source/WebCore/html/FileInputType.h new file mode 100644 index 000000000..b5a5d196d --- /dev/null +++ b/Source/WebCore/html/FileInputType.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FileInputType_h +#define FileInputType_h + +#include "BaseButtonInputType.h" +#include "FileChooser.h" +#include "FileIconLoader.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class FileList; + +class FileInputType : public BaseButtonInputType, private FileChooserClient, private FileIconLoaderClient { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + FileInputType(HTMLInputElement*); + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; + virtual bool valueMissing(const String&) const OVERRIDE; + virtual String valueMissingText() const OVERRIDE; + virtual void handleDOMActivateEvent(Event*) OVERRIDE; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; + virtual bool canSetStringValue() const OVERRIDE; + virtual bool canChangeFromAnotherType() const OVERRIDE; + virtual FileList* files() OVERRIDE; + virtual bool canSetValue(const String&) OVERRIDE; + virtual bool getTypeSpecificValue(String&) OVERRIDE; // Checked first, before internal storage or the value attribute. + virtual bool storesValueSeparateFromAttribute() OVERRIDE; + virtual void setValue(const String&, bool valueChanged, bool sendChangeEvent) OVERRIDE; + virtual void receiveDroppedFiles(const Vector<String>&) OVERRIDE; + virtual Icon* icon() const OVERRIDE; + virtual bool isFileUpload() const OVERRIDE; + virtual void createShadowSubtree() OVERRIDE; + virtual void multipleAttributeChanged() OVERRIDE; + virtual String defaultToolTip() const OVERRIDE; + + // FileChooserClient implementation. + virtual void filesChosen(const Vector<String>&) OVERRIDE; + + // FileIconLoaderClient implementation. + virtual void updateRendering(PassRefPtr<Icon>) OVERRIDE; + + void setFileList(const Vector<String>& paths); +#if ENABLE(DIRECTORY_UPLOAD) + void receiveDropForDirectoryUpload(const Vector<String>&); +#endif + void requestIcon(const Vector<String>&); + + RefPtr<FileList> m_fileList; + RefPtr<Icon> m_icon; +}; + +} // namespace WebCore + +#endif // FileInputType_h diff --git a/Source/WebCore/html/FormAssociatedElement.cpp b/Source/WebCore/html/FormAssociatedElement.cpp new file mode 100644 index 000000000..feb1d0d7a --- /dev/null +++ b/Source/WebCore/html/FormAssociatedElement.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "FormAssociatedElement.h" + +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "ValidityState.h" + +namespace WebCore { + +using namespace HTMLNames; + +FormAssociatedElement::FormAssociatedElement(HTMLFormElement* form) + : m_form(form) +{ +} + +FormAssociatedElement::~FormAssociatedElement() +{ +} + +ValidityState* FormAssociatedElement::validity() +{ + if (!m_validityState) + m_validityState = ValidityState::create(this); + + return m_validityState.get(); +} + +void FormAssociatedElement::didMoveToNewDocument(Document* oldDocument) +{ + HTMLElement* element = toHTMLElement(this); + if (oldDocument && element->fastHasAttribute(formAttr)) + oldDocument->unregisterFormElementWithFormAttribute(this); +} + +void FormAssociatedElement::insertedIntoDocument() +{ + HTMLElement* element = toHTMLElement(this); + if (element->fastHasAttribute(formAttr)) + element->document()->registerFormElementWithFormAttribute(this); +} + +void FormAssociatedElement::removedFromDocument() +{ + HTMLElement* element = toHTMLElement(this); + if (element->fastHasAttribute(formAttr)) + element->document()->unregisterFormElementWithFormAttribute(this); +} + +void FormAssociatedElement::insertedIntoTree() +{ + HTMLElement* element = toHTMLElement(this); + if (element->fastHasAttribute(formAttr)) { + // Resets the form owner at first to make sure the element don't + // associate any form elements when there is no element which has + // the given ID. + if (m_form) { + m_form->removeFormElement(this); + m_form = 0; + } + Element* formElement = element->treeScope()->getElementById(element->fastGetAttribute(formAttr)); + if (formElement && formElement->hasTagName(formTag)) { + m_form = static_cast<HTMLFormElement*>(formElement); + m_form->registerFormElement(this); + } + return; + } + if (!m_form) { + // This handles the case of a new form element being created by + // JavaScript and inserted inside a form. In the case of the parser + // setting a form, we will already have a non-null value for m_form, + // and so we don't need to do anything. + m_form = element->findFormAncestor(); + if (m_form) + m_form->registerFormElement(this); + } +} + +static inline Node* findRoot(Node* n) +{ + Node* root = n; + for (; n; n = n->parentNode()) + root = n; + return root; +} + +void FormAssociatedElement::removedFromTree() +{ + HTMLElement* element = toHTMLElement(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 && findRoot(element) != findRoot(m_form)) + removeFromForm(); +} + +void FormAssociatedElement::removeFromForm() +{ + if (!m_form) + return; + m_form->removeFormElement(this); + m_form = 0; +} + +void FormAssociatedElement::resetFormOwner() +{ + HTMLElement* element = toHTMLElement(this); + const AtomicString& formId(element->fastGetAttribute(formAttr)); + if (m_form) { + if (formId.isNull()) + return; + m_form->removeFormElement(this); + } + m_form = 0; + if (!formId.isNull() && element->inDocument()) { + // The HTML5 spec says that the element should be associated with + // the first element in the document to have an ID that equal to + // the value of form attribute, so we put the result of + // treeScope()->getElementById() over the given element. + Element* firstElement = element->treeScope()->getElementById(formId); + if (firstElement && firstElement->hasTagName(formTag)) + m_form = static_cast<HTMLFormElement*>(firstElement); + } else + m_form = element->findFormAncestor(); + if (m_form) + m_form->registerFormElement(this); +} + +void FormAssociatedElement::formAttributeChanged() +{ + HTMLElement* element = toHTMLElement(this); + if (!element->fastHasAttribute(formAttr)) { + // The form attribute removed. We need to reset form owner here. + if (m_form) + m_form->removeFormElement(this); + m_form = element->findFormAncestor(); + if (m_form) + form()->registerFormElement(this); + element->document()->unregisterFormElementWithFormAttribute(this); + } else + resetFormOwner(); +} + +const HTMLElement* toHTMLElement(const FormAssociatedElement* associatedElement) +{ + if (associatedElement->isFormControlElement()) + return static_cast<const HTMLFormControlElement*>(associatedElement); + // Assumes the element is an HTMLObjectElement + const HTMLElement* element = static_cast<const HTMLObjectElement*>(associatedElement); + ASSERT(element->hasTagName(objectTag)); + return element; +} + +HTMLElement* toHTMLElement(FormAssociatedElement* associatedElement) +{ + return const_cast<HTMLElement*>(toHTMLElement(static_cast<const FormAssociatedElement*>(associatedElement))); +} + +} // namespace Webcore diff --git a/Source/WebCore/html/FormAssociatedElement.h b/Source/WebCore/html/FormAssociatedElement.h new file mode 100644 index 000000000..1a8e1657e --- /dev/null +++ b/Source/WebCore/html/FormAssociatedElement.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple 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 FormAssociatedElement_h +#define FormAssociatedElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class FormDataList; +class HTMLFormElement; +class ValidationMessage; +class ValidityState; +class VisibleSelection; + +class FormAssociatedElement { +public: + virtual ~FormAssociatedElement(); + + void ref() { refFormAssociatedElement(); } + void deref() { derefFormAssociatedElement(); } + + HTMLFormElement* form() const { return m_form; } + ValidityState* validity(); + + virtual bool isFormControlElement() const = 0; + virtual bool isEnumeratable() const = 0; + + const AtomicString& name() const { return formControlName(); } + + // Override in derived classes to get the encoded name=value pair for submitting. + // Return true for a successful control (see HTML4-17.13.2). + virtual bool appendFormData(FormDataList&, bool) { return false; } + + virtual void formDestroyed() { m_form = 0; } + + void resetFormOwner(); + +protected: + FormAssociatedElement(HTMLFormElement*); + + void insertedIntoTree(); + void removedFromTree(); + void insertedIntoDocument(); + void removedFromDocument(); + void didMoveToNewDocument(Document* oldDocument); + + void setForm(HTMLFormElement* form) { m_form = form; } + void removeFromForm(); + void formAttributeChanged(); + +private: + virtual const AtomicString& formControlName() const = 0; + + virtual void refFormAssociatedElement() = 0; + virtual void derefFormAssociatedElement() = 0; + + HTMLFormElement* m_form; + OwnPtr<ValidityState> m_validityState; +}; + +HTMLElement* toHTMLElement(FormAssociatedElement*); +const HTMLElement* toHTMLElement(const FormAssociatedElement*); + +} // namespace + +#endif // FormAssociatedElement_h diff --git a/Source/WebCore/html/FormDataList.cpp b/Source/WebCore/html/FormDataList.cpp new file mode 100644 index 000000000..040c350d2 --- /dev/null +++ b/Source/WebCore/html/FormDataList.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2005, 2006, 2008 Apple 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 "FormDataList.h" + +#include "LineEnding.h" + +namespace WebCore { + +FormDataList::FormDataList(const TextEncoding& c) + : m_encoding(c) +{ +} + +void FormDataList::appendString(const String& s) +{ + CString cstr = m_encoding.encode(s.characters(), s.length(), EntitiesForUnencodables); + m_items.append(normalizeLineEndingsToCRLF(cstr)); +} + +void FormDataList::appendString(const CString& s) +{ + m_items.append(s); +} + +void FormDataList::appendBlob(PassRefPtr<Blob> blob, const String& filename) +{ + m_items.append(Item(blob, filename)); +} + +} // namespace diff --git a/Source/WebCore/html/FormDataList.h b/Source/WebCore/html/FormDataList.h new file mode 100644 index 000000000..24f544990 --- /dev/null +++ b/Source/WebCore/html/FormDataList.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005, 2006, 2008 Apple 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 FormDataList_h +#define FormDataList_h + +#include "Blob.h" +#include "TextEncoding.h" +#include <wtf/Forward.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +class FormDataList { +public: + class Item { + public: + Item() { } + Item(const WTF::CString& data) : m_data(data) { } + Item(PassRefPtr<Blob> blob, const String& filename) : m_blob(blob), m_filename(filename) { } + + const WTF::CString& data() const { return m_data; } + Blob* blob() const { return m_blob.get(); } + const String& filename() const { return m_filename; } + + private: + WTF::CString m_data; + RefPtr<Blob> m_blob; + String m_filename; + }; + + FormDataList(const TextEncoding&); + + void appendData(const String& key, const String& value) + { + appendString(key); + appendString(value); + } + void appendData(const String& key, const CString& value) + { + appendString(key); + appendString(value); + } + void appendData(const String& key, int value) + { + appendString(key); + appendString(String::number(value)); + } + void appendBlob(const String& key, PassRefPtr<Blob> blob, const String& filename = String()) + { + appendString(key); + appendBlob(blob, filename); + } + + const Vector<Item>& items() const { return m_items; } + const TextEncoding& encoding() const { return m_encoding; } + +private: + void appendString(const CString&); + void appendString(const String&); + void appendBlob(PassRefPtr<Blob>, const String& filename); + + TextEncoding m_encoding; + Vector<Item> m_items; +}; + +} // namespace WebCore + +#endif // FormDataList_h diff --git a/Source/WebCore/html/HTMLAllCollection.cpp b/Source/WebCore/html/HTMLAllCollection.cpp new file mode 100644 index 000000000..3dc07a388 --- /dev/null +++ b/Source/WebCore/html/HTMLAllCollection.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009, 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLAllCollection.h" + +#include "Element.h" + +namespace WebCore { + +PassRefPtr<HTMLAllCollection> HTMLAllCollection::create(Document* document) +{ + return adoptRef(new HTMLAllCollection(document)); +} + +HTMLAllCollection::HTMLAllCollection(Document* document) + : HTMLCollection(document, DocAll) +{ +} + +HTMLAllCollection::~HTMLAllCollection() +{ +} + +Node* HTMLAllCollection::namedItemWithIndex(const AtomicString& name, unsigned index) const +{ + if (!base()) + return 0; + + invalidateCacheIfNeeded(); + updateNameCache(); + + if (Vector<Element*>* idCache = m_cache.idCache.get(name.impl())) { + if (index < idCache->size()) + return idCache->at(index); + index -= idCache->size(); + } + + if (Vector<Element*>* nameCache = m_cache.nameCache.get(name.impl())) { + if (index < nameCache->size()) + return nameCache->at(index); + } + + return 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLAllCollection.h b/Source/WebCore/html/HTMLAllCollection.h new file mode 100644 index 000000000..ae97a53ac --- /dev/null +++ b/Source/WebCore/html/HTMLAllCollection.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 HTMLAllCollection_h +#define HTMLAllCollection_h + +#include "HTMLCollection.h" + +namespace WebCore { + +class HTMLAllCollection : public HTMLCollection { +public: + static PassRefPtr<HTMLAllCollection> create(Document*); + virtual ~HTMLAllCollection(); + + Node* namedItemWithIndex(const AtomicString& name, unsigned index) const; + +private: + HTMLAllCollection(Document*); +}; + +} // namespace WebCore + +#endif // HTMLAllCollection_h diff --git a/Source/WebCore/html/HTMLAllCollection.idl b/Source/WebCore/html/HTMLAllCollection.idl new file mode 100644 index 000000000..33e459b6a --- /dev/null +++ b/Source/WebCore/html/HTMLAllCollection.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 [ + HasIndexGetter, + HasNameGetter, + CustomCall, + MasqueradesAsUndefined, + GenerateIsReachable + ] HTMLAllCollection { + readonly attribute unsigned long length; + [Custom] Node item(in [Optional=CallWithDefaultValue] unsigned long index); + [Custom] Node namedItem(in DOMString name); + + // FIXME: This should return an HTMLAllCollection. + NodeList tags(in DOMString name); + }; + +} diff --git a/Source/WebCore/html/HTMLAnchorElement.cpp b/Source/WebCore/html/HTMLAnchorElement.cpp new file mode 100644 index 000000000..d60185282 --- /dev/null +++ b/Source/WebCore/html/HTMLAnchorElement.cpp @@ -0,0 +1,596 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * (C) 2006 Graham Dennis (graham.dennis@gmail.com) + * + * 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 "HTMLAnchorElement.h" + +#include "Attribute.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "FrameLoaderTypes.h" +#include "HTMLImageElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "Page.h" +#include "PingLoader.h" +#include "RenderImage.h" +#include "ResourceHandle.h" +#include "SecurityOrigin.h" +#include "SecurityPolicy.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLAnchorElement::HTMLAnchorElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_wasShiftKeyDownOnMouseDown(false) + , m_linkRelations(0) + , m_cachedVisitedLinkHash(0) +{ +} + +PassRefPtr<HTMLAnchorElement> HTMLAnchorElement::create(Document* document) +{ + return adoptRef(new HTMLAnchorElement(aTag, document)); +} + +PassRefPtr<HTMLAnchorElement> HTMLAnchorElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLAnchorElement(tagName, document)); +} + +// This function does not allow leading spaces before the port number. +static unsigned parsePortFromStringPosition(const String& value, unsigned portStart, unsigned& portEnd) +{ + portEnd = portStart; + while (isASCIIDigit(value[portEnd])) + ++portEnd; + return value.substring(portStart, portEnd - portStart).toUInt(); +} + +bool HTMLAnchorElement::supportsFocus() const +{ + if (rendererIsEditable()) + return HTMLElement::supportsFocus(); + // If not a link we should still be able to focus the element if it has tabIndex. + return isLink() || HTMLElement::supportsFocus(); +} + +bool HTMLAnchorElement::isMouseFocusable() const +{ + // Anchor elements should be mouse focusable, https://bugs.webkit.org/show_bug.cgi?id=26856 +#if !PLATFORM(GTK) && !PLATFORM(QT) && !PLATFORM(EFL) + if (isLink()) + // Only allow links with tabIndex or contentEditable to be mouse focusable. + return HTMLElement::supportsFocus(); +#endif + + // Allow tab index etc to control focus. + return HTMLElement::isMouseFocusable(); +} + +bool HTMLAnchorElement::isKeyboardFocusable(KeyboardEvent* event) const +{ + if (!isLink()) + return HTMLElement::isKeyboardFocusable(event); + + if (!isFocusable()) + return false; + + if (!document()->frame()) + return false; + + if (!document()->frame()->eventHandler()->tabsToLinks(event)) + return false; + + return hasNonEmptyBoundingBox(); +} + +static void appendServerMapMousePosition(String& url, Event* event) +{ + if (!event->isMouseEvent()) + return; + + ASSERT(event->target()); + Node* target = event->target()->toNode(); + ASSERT(target); + if (!target->hasTagName(imgTag)) + return; + + HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(event->target()->toNode()); + if (!imageElement || !imageElement->isServerMap()) + return; + + RenderImage* renderer = toRenderImage(imageElement->renderer()); + if (!renderer) + return; + + // FIXME: This should probably pass true for useTransforms. + FloatPoint absolutePosition = renderer->absoluteToLocal(FloatPoint(static_cast<MouseEvent*>(event)->pageX(), static_cast<MouseEvent*>(event)->pageY())); + int x = absolutePosition.x(); + int y = absolutePosition.y(); + url += "?"; + url += String::number(x); + url += ","; + url += String::number(y); +} + +void HTMLAnchorElement::defaultEventHandler(Event* event) +{ + if (isLink()) { + if (focused() && isEnterKeyKeydownEvent(event) && treatLinkAsLiveForEventType(NonMouseEvent)) { + event->setDefaultHandled(); + dispatchSimulatedClick(event); + return; + } + + if (isLinkClick(event) && treatLinkAsLiveForEventType(eventType(event))) { + handleClick(event); + return; + } + + if (rendererIsEditable()) { + // This keeps track of the editable block that the selection was in (if it was in one) just before the link was clicked + // for the LiveWhenNotFocused editable link behavior + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() != RightButton && document()->frame() && document()->frame()->selection()) { + m_rootEditableElementForSelectionOnMouseDown = document()->frame()->selection()->rootEditableElement(); + m_wasShiftKeyDownOnMouseDown = static_cast<MouseEvent*>(event)->shiftKey(); + } else if (event->type() == eventNames().mouseoverEvent) { + // These are cleared on mouseover and not mouseout because their values are needed for drag events, + // but drag events happen after mouse out events. + m_rootEditableElementForSelectionOnMouseDown = 0; + m_wasShiftKeyDownOnMouseDown = false; + } + } + } + + HTMLElement::defaultEventHandler(event); +} + +void HTMLAnchorElement::setActive(bool down, bool pause) +{ + if (rendererIsEditable()) { + EditableLinkBehavior editableLinkBehavior = EditableLinkDefaultBehavior; + if (Settings* settings = document()->settings()) + editableLinkBehavior = settings->editableLinkBehavior(); + + switch (editableLinkBehavior) { + default: + case EditableLinkDefaultBehavior: + case EditableLinkAlwaysLive: + break; + + case EditableLinkNeverLive: + return; + + // Don't set the link to be active if the current selection is in the same editable block as + // this link + case EditableLinkLiveWhenNotFocused: + if (down && document()->frame() && document()->frame()->selection()->rootEditableElement() == rootEditableElement()) + return; + break; + + case EditableLinkOnlyLiveWithShiftKey: + return; + } + + } + + ContainerNode::setActive(down, pause); +} + +void HTMLAnchorElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == hrefAttr) { + bool wasLink = isLink(); + setIsLink(!attr->isNull()); + if (wasLink != isLink()) + setNeedsStyleRecalc(); + if (isLink()) { + String parsedURL = stripLeadingAndTrailingHTMLSpaces(attr->value()); + if (document()->isDNSPrefetchEnabled()) { + if (protocolIs(parsedURL, "http") || protocolIs(parsedURL, "https") || parsedURL.startsWith("//")) + ResourceHandle::prepareForURL(document()->completeURL(parsedURL)); + } + if (document()->page() && !document()->page()->javaScriptURLsAreAllowed() && protocolIsJavaScript(parsedURL)) { + clearIsLink(); + attr->setValue(nullAtom); + } + } + invalidateCachedVisitedLinkHash(); + } else if (attr->name() == nameAttr || attr->name() == titleAttr) { + // Do nothing. + } else if (attr->name() == relAttr) + setRel(attr->value()); + else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLAnchorElement::accessKeyAction(bool sendMouseEvents) +{ + // send the mouse button events if the caller specified sendMouseEvents + dispatchSimulatedClick(0, sendMouseEvents); +} + +bool HTMLAnchorElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == hrefAttr || HTMLElement::isURLAttribute(attr); +} + +bool HTMLAnchorElement::canStartSelection() const +{ + // FIXME: We probably want this same behavior in SVGAElement too + if (!isLink()) + return HTMLElement::canStartSelection(); + return rendererIsEditable(); +} + +bool HTMLAnchorElement::draggable() const +{ + // Should be draggable if we have an href attribute. + const AtomicString& value = getAttribute(draggableAttr); + if (equalIgnoringCase(value, "true")) + return true; + if (equalIgnoringCase(value, "false")) + return false; + return hasAttribute(hrefAttr); +} + +KURL HTMLAnchorElement::href() const +{ + return document()->completeURL(stripLeadingAndTrailingHTMLSpaces(getAttribute(hrefAttr))); +} + +void HTMLAnchorElement::setHref(const AtomicString& value) +{ + setAttribute(hrefAttr, value); +} + +bool HTMLAnchorElement::hasRel(uint32_t relation) const +{ + return m_linkRelations & relation; +} + +void HTMLAnchorElement::setRel(const String& value) +{ + m_linkRelations = 0; + SpaceSplitString newLinkRelations(value, true); + // FIXME: Add link relations as they are implemented + if (newLinkRelations.contains("noreferrer")) + m_linkRelations |= RelationNoReferrer; +} + +const AtomicString& HTMLAnchorElement::name() const +{ + return getAttribute(nameAttr); +} + +short HTMLAnchorElement::tabIndex() const +{ + // Skip the supportsFocus check in HTMLElement. + return Element::tabIndex(); +} + +String HTMLAnchorElement::target() const +{ + return getAttribute(targetAttr); +} + +String HTMLAnchorElement::hash() const +{ + String fragmentIdentifier = href().fragmentIdentifier(); + return fragmentIdentifier.isEmpty() ? emptyString() : "#" + fragmentIdentifier; +} + +void HTMLAnchorElement::setHash(const String& value) +{ + KURL url = href(); + if (value[0] == '#') + url.setFragmentIdentifier(value.substring(1)); + else + url.setFragmentIdentifier(value); + setHref(url.string()); +} + +String HTMLAnchorElement::host() const +{ + const KURL& url = href(); + if (url.hostEnd() == url.pathStart()) + return url.host(); + if (isDefaultPortForProtocol(url.port(), url.protocol())) + return url.host(); + return url.host() + ":" + String::number(url.port()); +} + +void HTMLAnchorElement::setHost(const String& value) +{ + if (value.isEmpty()) + return; + KURL url = href(); + if (!url.canSetHostOrPort()) + return; + + size_t separator = value.find(':'); + if (!separator) + return; + + if (separator == notFound) + url.setHostAndPort(value); + else { + unsigned portEnd; + unsigned port = parsePortFromStringPosition(value, separator + 1, portEnd); + if (!port) { + // http://dev.w3.org/html5/spec/infrastructure.html#url-decomposition-idl-attributes + // specifically goes against RFC 3986 (p3.2) and + // requires setting the port to "0" if it is set to empty string. + url.setHostAndPort(value.substring(0, separator + 1) + "0"); + } else { + if (isDefaultPortForProtocol(port, url.protocol())) + url.setHostAndPort(value.substring(0, separator)); + else + url.setHostAndPort(value.substring(0, portEnd)); + } + } + setHref(url.string()); +} + +String HTMLAnchorElement::hostname() const +{ + return href().host(); +} + +void HTMLAnchorElement::setHostname(const String& value) +{ + // Before setting new value: + // Remove all leading U+002F SOLIDUS ("/") characters. + unsigned i = 0; + unsigned hostLength = value.length(); + while (value[i] == '/') + i++; + + if (i == hostLength) + return; + + KURL url = href(); + if (!url.canSetHostOrPort()) + return; + + url.setHost(value.substring(i)); + setHref(url.string()); +} + +String HTMLAnchorElement::pathname() const +{ + return href().path(); +} + +void HTMLAnchorElement::setPathname(const String& value) +{ + KURL url = href(); + if (!url.canSetPathname()) + return; + + if (value[0] == '/') + url.setPath(value); + else + url.setPath("/" + value); + + setHref(url.string()); +} + +String HTMLAnchorElement::port() const +{ + if (href().hasPort()) + return String::number(href().port()); + + return emptyString(); +} + +void HTMLAnchorElement::setPort(const String& value) +{ + KURL url = href(); + if (!url.canSetHostOrPort()) + return; + + // http://dev.w3.org/html5/spec/infrastructure.html#url-decomposition-idl-attributes + // specifically goes against RFC 3986 (p3.2) and + // requires setting the port to "0" if it is set to empty string. + unsigned port = value.toUInt(); + if (isDefaultPortForProtocol(port, url.protocol())) + url.removePort(); + else + url.setPort(port); + + setHref(url.string()); +} + +String HTMLAnchorElement::protocol() const +{ + return href().protocol() + ":"; +} + +void HTMLAnchorElement::setProtocol(const String& value) +{ + KURL url = href(); + url.setProtocol(value); + setHref(url.string()); +} + +String HTMLAnchorElement::search() const +{ + String query = href().query(); + return query.isEmpty() ? emptyString() : "?" + query; +} + +String HTMLAnchorElement::origin() const +{ + RefPtr<SecurityOrigin> origin = SecurityOrigin::create(href()); + return origin->toString(); +} + +void HTMLAnchorElement::setSearch(const String& value) +{ + KURL url = href(); + String newSearch = (value[0] == '?') ? value.substring(1) : value; + // Make sure that '#' in the query does not leak to the hash. + url.setQuery(newSearch.replace('#', "%23")); + + setHref(url.string()); +} + +String HTMLAnchorElement::text() +{ + return innerText(); +} + +String HTMLAnchorElement::toString() const +{ + return href().string(); +} + +bool HTMLAnchorElement::isLiveLink() const +{ + return isLink() && treatLinkAsLiveForEventType(m_wasShiftKeyDownOnMouseDown ? MouseEventWithShiftKey : MouseEventWithoutShiftKey); +} + +void HTMLAnchorElement::sendPings(const KURL& destinationURL) +{ + if (!hasAttribute(pingAttr) || !document()->settings()->hyperlinkAuditingEnabled()) + return; + + SpaceSplitString pingURLs(getAttribute(pingAttr), true); + for (unsigned i = 0; i < pingURLs.size(); i++) + PingLoader::sendPing(document()->frame(), document()->completeURL(pingURLs[i]), destinationURL); +} + +void HTMLAnchorElement::handleClick(Event* event) +{ + event->setDefaultHandled(); + + Frame* frame = document()->frame(); + if (!frame) + return; + + String url = stripLeadingAndTrailingHTMLSpaces(fastGetAttribute(hrefAttr)); + appendServerMapMousePosition(url, event); + KURL kurl = document()->completeURL(url); + +#if ENABLE(DOWNLOAD_ATTRIBUTE) + if (hasAttribute(downloadAttr)) { + ResourceRequest request(kurl); + + if (!hasRel(RelationNoReferrer)) { + String referrer = SecurityPolicy::generateReferrerHeader(document()->referrerPolicy(), kurl, frame->loader()->outgoingReferrer()); + if (!referrer.isEmpty()) + request.setHTTPReferrer(referrer); + frame->loader()->addExtraFieldsToMainResourceRequest(request); + } + + frame->loader()->client()->startDownload(request, fastGetAttribute(downloadAttr)); + } else +#endif + frame->loader()->urlSelected(kurl, target(), event, false, false, hasRel(RelationNoReferrer) ? NeverSendReferrer : MaybeSendReferrer); + + sendPings(kurl); +} + +HTMLAnchorElement::EventType HTMLAnchorElement::eventType(Event* event) +{ + if (!event->isMouseEvent()) + return NonMouseEvent; + return static_cast<MouseEvent*>(event)->shiftKey() ? MouseEventWithShiftKey : MouseEventWithoutShiftKey; +} + +bool HTMLAnchorElement::treatLinkAsLiveForEventType(EventType eventType) const +{ + if (!rendererIsEditable()) + return true; + + Settings* settings = document()->settings(); + if (!settings) + return true; + + switch (settings->editableLinkBehavior()) { + case EditableLinkDefaultBehavior: + case EditableLinkAlwaysLive: + return true; + + case EditableLinkNeverLive: + return false; + + // If the selection prior to clicking on this link resided in the same editable block as this link, + // and the shift key isn't pressed, we don't want to follow the link. + case EditableLinkLiveWhenNotFocused: + return eventType == MouseEventWithShiftKey || (eventType == MouseEventWithoutShiftKey && m_rootEditableElementForSelectionOnMouseDown != rootEditableElement()); + + case EditableLinkOnlyLiveWithShiftKey: + return eventType == MouseEventWithShiftKey; + } + + ASSERT_NOT_REACHED(); + return false; +} + +bool isEnterKeyKeydownEvent(Event* event) +{ + return event->type() == eventNames().keydownEvent && event->isKeyboardEvent() && static_cast<KeyboardEvent*>(event)->keyIdentifier() == "Enter"; +} + +bool isMiddleMouseButtonEvent(Event* event) +{ + return event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == MiddleButton; +} + +bool isLinkClick(Event* event) +{ + return event->type() == eventNames().clickEvent && (!event->isMouseEvent() || static_cast<MouseEvent*>(event)->button() != RightButton); +} + +void handleLinkClick(Event* event, Document* document, const String& url, const String& target, bool hideReferrer) +{ + event->setDefaultHandled(); + + Frame* frame = document->frame(); + if (!frame) + return; + frame->loader()->urlSelected(document->completeURL(url), target, event, false, false, hideReferrer ? NeverSendReferrer : MaybeSendReferrer); +} + +#if ENABLE(MICRODATA) +String HTMLAnchorElement::itemValueText() const +{ + return getURLAttribute(hrefAttr); +} + +void HTMLAnchorElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(hrefAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLAnchorElement.h b/Source/WebCore/html/HTMLAnchorElement.h new file mode 100644 index 000000000..cbda84145 --- /dev/null +++ b/Source/WebCore/html/HTMLAnchorElement.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2007, 2008, 2009, 2010 Apple 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 HTMLAnchorElement_h +#define HTMLAnchorElement_h + +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "LinkHash.h" + +namespace WebCore { + +// Link relation bitmask values. +// FIXME: Uncomment as the various link relations are implemented. +enum { +// RelationAlternate = 0x00000001, +// RelationArchives = 0x00000002, +// RelationAuthor = 0x00000004, +// RelationBoomark = 0x00000008, +// RelationExternal = 0x00000010, +// RelationFirst = 0x00000020, +// RelationHelp = 0x00000040, +// RelationIndex = 0x00000080, +// RelationLast = 0x00000100, +// RelationLicense = 0x00000200, +// RelationNext = 0x00000400, +// RelationNoFolow = 0x00000800, + RelationNoReferrer = 0x00001000, +// RelationPrev = 0x00002000, +// RelationSearch = 0x00004000, +// RelationSidebar = 0x00008000, +// RelationTag = 0x00010000, +// RelationUp = 0x00020000, +}; + +class HTMLAnchorElement : public HTMLElement { +public: + static PassRefPtr<HTMLAnchorElement> create(Document*); + static PassRefPtr<HTMLAnchorElement> create(const QualifiedName&, Document*); + + KURL href() const; + void setHref(const AtomicString&); + + const AtomicString& name() const; + + String hash() const; + void setHash(const String&); + + String host() const; + void setHost(const String&); + + String hostname() const; + void setHostname(const String&); + + String pathname() const; + void setPathname(const String&); + + String port() const; + void setPort(const String&); + + String protocol() const; + void setProtocol(const String&); + + String search() const; + void setSearch(const String&); + + String origin() const; + + String text(); + + String toString() const; + + bool isLiveLink() const; + + bool hasRel(uint32_t relation) const; + void setRel(const String&); + + LinkHash visitedLinkHash() const; + void invalidateCachedVisitedLinkHash() { m_cachedVisitedLinkHash = 0; } + +protected: + HTMLAnchorElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + +private: + virtual bool supportsFocus() const; + virtual bool isMouseFocusable() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual void defaultEventHandler(Event*); + virtual void setActive(bool active = true, bool pause = false); + virtual void accessKeyAction(bool sendMouseEvents); + virtual bool isURLAttribute(Attribute*) const; + virtual bool canStartSelection() const; + virtual String target() const; + virtual short tabIndex() const; + virtual bool draggable() const; + + void sendPings(const KURL& destinationURL); + + void handleClick(Event*); + + enum EventType { + MouseEventWithoutShiftKey, + MouseEventWithShiftKey, + NonMouseEvent, + }; + static EventType eventType(Event*); + bool treatLinkAsLiveForEventType(EventType) const; + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + RefPtr<Element> m_rootEditableElementForSelectionOnMouseDown; + bool m_wasShiftKeyDownOnMouseDown : 1; + uint32_t m_linkRelations : 31; + mutable LinkHash m_cachedVisitedLinkHash; +}; + +inline LinkHash HTMLAnchorElement::visitedLinkHash() const +{ + if (!m_cachedVisitedLinkHash) + m_cachedVisitedLinkHash = WebCore::visitedLinkHash(document()->baseURL(), fastGetAttribute(HTMLNames::hrefAttr)); + return m_cachedVisitedLinkHash; +} + +// Functions shared with the other anchor elements (i.e., SVG). + +bool isEnterKeyKeydownEvent(Event*); +bool isMiddleMouseButtonEvent(Event*); +bool isLinkClick(Event*); +void handleLinkClick(Event*, Document*, const String& url, const String& target, bool hideReferrer = false); + +} // namespace WebCore + +#endif // HTMLAnchorElement_h diff --git a/Source/WebCore/html/HTMLAnchorElement.idl b/Source/WebCore/html/HTMLAnchorElement.idl new file mode 100644 index 000000000..5669a281c --- /dev/null +++ b/Source/WebCore/html/HTMLAnchorElement.idl @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLAnchorElement : HTMLElement { + attribute [Reflect] DOMString charset; + attribute [Reflect] DOMString coords; + attribute [Conditional=DOWNLOAD_ATTRIBUTE, Reflect] DOMString download; + attribute [Reflect, URL] DOMString href; + attribute [Reflect] DOMString hreflang; + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString ping; + attribute [Reflect] DOMString rel; + attribute [Reflect] DOMString rev; + attribute [Reflect] DOMString shape; + attribute [Reflect] DOMString target; + attribute [Reflect] DOMString type; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + readonly attribute DOMString hash; + readonly attribute DOMString host; + readonly attribute DOMString hostname; + readonly attribute DOMString pathname; + readonly attribute DOMString port; + readonly attribute DOMString protocol; + readonly attribute DOMString search; +#else + attribute [ConvertNullToNullString] DOMString hash; + attribute [ConvertNullToNullString] DOMString host; + attribute [ConvertNullToNullString] DOMString hostname; + attribute [ConvertNullToNullString] DOMString pathname; + attribute [ConvertNullToNullString] DOMString port; + attribute [ConvertNullToNullString] DOMString protocol; + attribute [ConvertNullToNullString] DOMString search; + + readonly attribute [ConvertNullToNullString] DOMString origin; +#endif + + readonly attribute DOMString text; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + [DontEnum] DOMString toString(); +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extension: + readonly attribute URL absoluteLinkURL; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLAppletElement.cpp b/Source/WebCore/html/HTMLAppletElement.cpp new file mode 100644 index 000000000..14ba11feb --- /dev/null +++ b/Source/WebCore/html/HTMLAppletElement.cpp @@ -0,0 +1,142 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "HTMLAppletElement.h" + +#include "Attribute.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "RenderApplet.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "Widget.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLAppletElement::HTMLAppletElement(const QualifiedName& tagName, Document* document) + : HTMLPlugInElement(tagName, document) +{ + ASSERT(hasTagName(appletTag)); +} + +PassRefPtr<HTMLAppletElement> HTMLAppletElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLAppletElement(tagName, document)); +} + +void HTMLAppletElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == altAttr || + attr->name() == archiveAttr || + attr->name() == codeAttr || + attr->name() == codebaseAttr || + attr->name() == mayscriptAttr || + attr->name() == objectAttr) { + // Do nothing. + } else + HTMLPlugInElement::parseMappedAttribute(attr); +} + +bool HTMLAppletElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + if (!fastHasAttribute(codeAttr)) + return false; + return HTMLPlugInElement::rendererIsNeeded(context); +} + +RenderObject* HTMLAppletElement::createRenderer(RenderArena*, RenderStyle* style) +{ + if (canEmbedJava()) { + HashMap<String, String> args; + + args.set("code", getAttribute(codeAttr)); + + const AtomicString& codeBase = getAttribute(codebaseAttr); + if (!codeBase.isNull()) + args.set("codeBase", codeBase); + + const AtomicString& name = document()->isHTMLDocument() ? getAttribute(nameAttr) : getIdAttribute(); + if (!name.isNull()) + args.set("name", name); + const AtomicString& archive = getAttribute(archiveAttr); + if (!archive.isNull()) + args.set("archive", archive); + + args.set("baseURL", document()->baseURL().string()); + + const AtomicString& mayScript = getAttribute(mayscriptAttr); + if (!mayScript.isNull()) + args.set("mayScript", mayScript); + + // Other arguments (from <PARAM> tags) are added later. + + return new (document()->renderArena()) RenderApplet(this, args); + } + + return RenderObject::createObject(this, style); +} + +void HTMLAppletElement::defaultEventHandler(Event* event) +{ + RenderObject* r = renderer(); + if (!r || !r->isWidget()) + return; + Widget* widget = toRenderWidget(r)->widget(); + if (!widget) + return; + widget->handleEvent(event); +} + +RenderWidget* HTMLAppletElement::renderWidgetForJSBindings() +{ + if (!canEmbedJava()) + return 0; + + RenderApplet* applet = toRenderApplet(renderer()); + if (applet) + applet->createWidgetIfNecessary(); + + return applet; +} + +bool HTMLAppletElement::canEmbedJava() const +{ + if (document()->isSandboxed(SandboxPlugins)) + return false; + + Settings* settings = document()->settings(); + return settings && settings->isJavaEnabled(); +} + +void HTMLAppletElement::finishParsingChildren() +{ + // The parser just reached </applet>, so all the params are available now. + HTMLPlugInElement::finishParsingChildren(); + if (renderer()) + renderer()->setNeedsLayout(true); // This will cause it to create its widget & the Java applet +} + +} diff --git a/Source/WebCore/html/HTMLAppletElement.h b/Source/WebCore/html/HTMLAppletElement.h new file mode 100644 index 000000000..e5387d99a --- /dev/null +++ b/Source/WebCore/html/HTMLAppletElement.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2008, 2009 Apple 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 HTMLAppletElement_h +#define HTMLAppletElement_h + +#include "HTMLPlugInElement.h" + +namespace WebCore { + +class HTMLAppletElement : public HTMLPlugInElement { +public: + static PassRefPtr<HTMLAppletElement> create(const QualifiedName&, Document*); + +private: + HTMLAppletElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void finishParsingChildren(); + + virtual void defaultEventHandler(Event*); + + virtual RenderWidget* renderWidgetForJSBindings(); + + void setupApplet() const; + bool canEmbedJava() const; + + virtual bool shouldRegisterAsNamedItem() const OVERRIDE { return true; } + virtual bool shouldRegisterAsExtraNamedItem() const OVERRIDE { return true; } +}; + +} + +#endif diff --git a/Source/WebCore/html/HTMLAppletElement.idl b/Source/WebCore/html/HTMLAppletElement.idl new file mode 100644 index 000000000..388b5cb23 --- /dev/null +++ b/Source/WebCore/html/HTMLAppletElement.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + DelegatingPutFunction, + DelegatingGetOwnPropertySlot, + CustomCall + ] HTMLAppletElement : HTMLElement { + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString alt; + attribute [Reflect] DOMString archive; + attribute [Reflect] DOMString code; + attribute [Reflect] DOMString codeBase; + attribute [Reflect] DOMString height; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + attribute [Reflect] DOMString hspace; +#else + attribute [Reflect] long hspace; +#endif + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString object; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + attribute [Reflect] DOMString vspace; +#else + attribute [Reflect] long vspace; +#endif + attribute [Reflect] DOMString width; + }; + +} diff --git a/Source/WebCore/html/HTMLAreaElement.cpp b/Source/WebCore/html/HTMLAreaElement.cpp new file mode 100644 index 000000000..d763d789a --- /dev/null +++ b/Source/WebCore/html/HTMLAreaElement.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2009, 2011 Apple 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 "HTMLAreaElement.h" + +#include "AffineTransform.h" +#include "Attribute.h" +#include "Frame.h" +#include "HTMLImageElement.h" +#include "HTMLMapElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "Path.h" +#include "RenderImage.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLAreaElement::HTMLAreaElement(const QualifiedName& tagName, Document* document) + : HTMLAnchorElement(tagName, document) + , m_coordsLen(0) + , m_lastSize(-1, -1) + , m_shape(Unknown) +{ + ASSERT(hasTagName(areaTag)); +} + +PassRefPtr<HTMLAreaElement> HTMLAreaElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLAreaElement(tagName, document)); +} + +void HTMLAreaElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == shapeAttr) { + if (equalIgnoringCase(attr->value(), "default")) + m_shape = Default; + else if (equalIgnoringCase(attr->value(), "circle")) + m_shape = Circle; + else if (equalIgnoringCase(attr->value(), "poly")) + m_shape = Poly; + else if (equalIgnoringCase(attr->value(), "rect")) + m_shape = Rect; + invalidateCachedRegion(); + } else if (attr->name() == coordsAttr) { + m_coords = newCoordsArray(attr->value().string(), m_coordsLen); + invalidateCachedRegion(); + } else if (attr->name() == altAttr || attr->name() == accesskeyAttr) { + // Do nothing. + } else + HTMLAnchorElement::parseMappedAttribute(attr); +} + +void HTMLAreaElement::invalidateCachedRegion() +{ + m_lastSize = LayoutSize(-1, -1); +} + +bool HTMLAreaElement::mapMouseEvent(LayoutPoint location, const LayoutSize& size, HitTestResult& result) +{ + if (m_lastSize != size) { + m_region = adoptPtr(new Path(getRegion(size))); + m_lastSize = size; + } + + if (!m_region->contains(location)) + return false; + + result.setInnerNode(this); + result.setURLElement(this); + return true; +} + +Path HTMLAreaElement::computePath(RenderObject* obj) const +{ + if (!obj) + return Path(); + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = obj->localToAbsolute(); + + // Default should default to the size of the containing object. + LayoutSize size = m_lastSize; + if (m_shape == Default) + size = obj->absoluteOutlineBounds().size(); + + Path p = getRegion(size); + float zoomFactor = obj->style()->effectiveZoom(); + if (zoomFactor != 1.0f) { + AffineTransform zoomTransform; + zoomTransform.scale(zoomFactor); + p.transform(zoomTransform); + } + + p.translate(absPos - FloatPoint()); + return p; +} + +LayoutRect HTMLAreaElement::computeRect(RenderObject* obj) const +{ + return enclosingLayoutRect(computePath(obj).fastBoundingRect()); +} + +Path HTMLAreaElement::getRegion(const LayoutSize& size) const +{ + if (!m_coords && m_shape != Default) + return Path(); + + LayoutUnit width = size.width(); + LayoutUnit height = size.height(); + + // If element omits the shape attribute, select shape based on number of coordinates. + Shape shape = m_shape; + if (shape == Unknown) { + if (m_coordsLen == 3) + shape = Circle; + else if (m_coordsLen == 4) + shape = Rect; + else if (m_coordsLen >= 6) + shape = Poly; + } + + Path path; + switch (shape) { + case Poly: + if (m_coordsLen >= 6) { + int numPoints = m_coordsLen / 2; + path.moveTo(FloatPoint(m_coords[0].calcMinValue(width), m_coords[1].calcMinValue(height))); + for (int i = 1; i < numPoints; ++i) + path.addLineTo(FloatPoint(m_coords[i * 2].calcMinValue(width), m_coords[i * 2 + 1].calcMinValue(height))); + path.closeSubpath(); + } + break; + case Circle: + if (m_coordsLen >= 3) { + Length radius = m_coords[2]; + int r = min(radius.calcMinValue(width), radius.calcMinValue(height)); + path.addEllipse(FloatRect(m_coords[0].calcMinValue(width) - r, m_coords[1].calcMinValue(height) - r, 2 * r, 2 * r)); + } + break; + case Rect: + if (m_coordsLen >= 4) { + int x0 = m_coords[0].calcMinValue(width); + int y0 = m_coords[1].calcMinValue(height); + int x1 = m_coords[2].calcMinValue(width); + int y1 = m_coords[3].calcMinValue(height); + path.addRect(FloatRect(x0, y0, x1 - x0, y1 - y0)); + } + break; + case Default: + path.addRect(FloatRect(0, 0, width, height)); + break; + case Unknown: + break; + } + + return path; +} + +HTMLImageElement* HTMLAreaElement::imageElement() +{ + Node* mapElement = parentNode(); + if (!mapElement || !mapElement->hasTagName(mapTag)) + return 0; + + return static_cast<HTMLMapElement*>(mapElement)->imageElement(); +} + +bool HTMLAreaElement::isKeyboardFocusable(KeyboardEvent*) const +{ + return isFocusable(); +} + +bool HTMLAreaElement::isMouseFocusable() const +{ + return isFocusable(); +} + +bool HTMLAreaElement::isFocusable() const +{ + return supportsFocus() && Element::tabIndex() >= 0; +} + +void HTMLAreaElement::setFocus(bool shouldBeFocused) +{ + if (focused() == shouldBeFocused) + return; + + HTMLAnchorElement::setFocus(shouldBeFocused); + + HTMLImageElement* imageElement = this->imageElement(); + if (!imageElement) + return; + + RenderObject* renderer = imageElement->renderer(); + if (!renderer || !renderer->isImage()) + return; + + toRenderImage(renderer)->areaElementFocusChanged(this); +} + +void HTMLAreaElement::updateFocusAppearance(bool restorePreviousSelection) +{ + if (!isFocusable()) + return; + + HTMLImageElement* imageElement = this->imageElement(); + if (!imageElement) + return; + + imageElement->updateFocusAppearance(restorePreviousSelection); +} + +bool HTMLAreaElement::supportsFocus() const +{ + // If the AREA element was a link, it should support focus. + // The inherited method is not used because it assumes that a render object must exist + // for the element to support focus. AREA elements do not have render objects. + return isLink(); +} + +String HTMLAreaElement::target() const +{ + return getAttribute(targetAttr); +} + +#if ENABLE(MICRODATA) +String HTMLAreaElement::itemValueText() const +{ + return getURLAttribute(hrefAttr); +} + +void HTMLAreaElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(hrefAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLAreaElement.h b/Source/WebCore/html/HTMLAreaElement.h new file mode 100644 index 000000000..817391861 --- /dev/null +++ b/Source/WebCore/html/HTMLAreaElement.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2008, 2009, 2011 Apple 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 HTMLAreaElement_h +#define HTMLAreaElement_h + +#include "HTMLAnchorElement.h" +#include "LayoutTypes.h" +#include <wtf/OwnArrayPtr.h> + +namespace WebCore { + +class HitTestResult; +class HTMLImageElement; +class Path; + +class HTMLAreaElement : public HTMLAnchorElement { +public: + static PassRefPtr<HTMLAreaElement> create(const QualifiedName&, Document*); + + bool isDefault() const { return m_shape == Default; } + + bool mapMouseEvent(LayoutPoint location, const LayoutSize&, HitTestResult&); + + LayoutRect computeRect(RenderObject*) const; + Path computePath(RenderObject*) const; + + // The parent map's image. + HTMLImageElement* imageElement(); + +private: + HTMLAreaElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + virtual bool supportsFocus() const; + virtual String target() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + virtual bool isFocusable() const; + virtual void updateFocusAppearance(bool /*restorePreviousSelection*/); + virtual void setFocus(bool); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + enum Shape { Default, Poly, Rect, Circle, Unknown }; + Path getRegion(const LayoutSize&) const; + void invalidateCachedRegion(); + + OwnPtr<Path> m_region; + OwnArrayPtr<Length> m_coords; + int m_coordsLen; + LayoutSize m_lastSize; + Shape m_shape; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLAreaElement.idl b/Source/WebCore/html/HTMLAreaElement.idl new file mode 100644 index 000000000..a14a4701f --- /dev/null +++ b/Source/WebCore/html/HTMLAreaElement.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLAreaElement : HTMLElement { + attribute [Reflect] DOMString alt; + attribute [Reflect] DOMString coords; + attribute [Reflect, URL] DOMString href; + attribute [Reflect] boolean noHref; + attribute [Reflect] DOMString ping; + attribute [Reflect] DOMString shape; + attribute [Reflect] DOMString target; + + // IE Extensions + readonly attribute DOMString hash; + readonly attribute DOMString host; + readonly attribute DOMString hostname; + readonly attribute DOMString pathname; + readonly attribute DOMString port; + readonly attribute DOMString protocol; + readonly attribute DOMString search; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extension: + readonly attribute URL absoluteLinkURL; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLAttributeNames.in b/Source/WebCore/html/HTMLAttributeNames.in new file mode 100644 index 000000000..776c5e9d9 --- /dev/null +++ b/Source/WebCore/html/HTMLAttributeNames.in @@ -0,0 +1,318 @@ +namespace="HTML" +namespacePrefix="xhtml" +namespaceURI="http://www.w3.org/1999/xhtml" +attrsNullNamespace + +abbr +accept_charset +accept +accesskey +action +align +alink +alt +archive +aria-activedescendant +aria-atomic +aria-busy +aria-checked +aria-controls +aria-describedby +aria-disabled +aria-dropeffect +aria-expanded +aria-flowto +aria-grabbed +aria-haspopup +aria-help +aria-hidden +aria-invalid +aria-label +aria-labeledby +aria-labelledby +aria-level +aria-live +aria-multiline +aria-multiselectable +aria-orientation +aria-owns +aria-pressed +aria-readonly +aria-relevant +aria-required +aria-selected +aria-sort +aria-valuemax +aria-valuemin +aria-valuenow +aria-valuetext +async +autocomplete +autofocus +autoplay +autosave +axis +background +behavior +bgcolor +bgproperties +border +bordercolor +cellpadding +cellspacing +char +challenge +charoff +charset +checked +cellborder +cite +class +classid +clear +code +codebase +codetype +color +cols +colspan +compact +composite +content +contenteditable +controls +coords +crossorigin +data +datetime +declare +default +defer +dir +direction +dirname +disabled +download +draggable +webkitdropzone +enctype +end +event +expanded +face +focused +for +form +formaction +formenctype +formmethod +formnovalidate +formtarget +frame +frameborder +headers +height +hidden +high +href +hreflang +hspace +http_equiv +id +incremental +indeterminate +ismap +itemid +itemprop +itemref +itemscope +itemtype +keytype +kind +label +lang +language +leftmargin +link +list +longdesc +loop +low +playcount +loopend +loopstart +lowsrc +manifest +marginheight +marginwidth +max +maxlength +mayscript +media +mediagroup +method +min +multiple +muted +name +nohref +noresize +noshade +novalidate +nowrap +object +onabort +onbeforecopy +onbeforecut +onbeforeload +onbeforepaste +onbeforeunload +onblur +oncanplay +oncanplaythrough +onchange +onclick +oncontextmenu +oncopy +oncut +ondblclick +ondrag +ondragend +ondragenter +ondragleave +ondragover +ondragstart +ondrop +ondurationchange +onemptied +onended +onerror +onfocus +onfocusin +onfocusout +onhashchange +oninput +oninvalid +onkeydown +onkeypress +onkeyup +onload +onloadeddata +onloadedmetadata +onloadstart +onmousedown +onmousemove +onmouseout +onmouseover +onmouseup +onmousewheel +ononline +onoffline +onorientationchange +onpagehide +onpageshow +onpaste +onpause +onplay +onplaying +onpopstate +onprogress +onratechange +onreset +onresize +onscroll +onsearch +onseeked +onseeking +onselect +onselectstart +onselectionchange +onwebkitspeechchange +onstalled +onstorage +onsuspend +onsubmit +ontimeupdate +ontouchstart +ontouchmove +ontouchend +ontouchcancel +onunload +onvolumechange +onwaiting +onwebkitanimationstart +onwebkitanimationiteration +onwebkitanimationend +onwebkitbeginfullscreen +onwebkitendfullscreen +onwebkitfullscreenchange +onwebkittransitionend +open +optimum +pattern +placeholder +pluginspage +pluginurl +ping +poster +precision +preload +primary +profile +progress +prompt +readonly +rel +required +results +rev +reversed +role +rows +rowspan +rules +sandbox +scheme +scope +scoped +scrollamount +scrolldelay +scrolling +selected +shape +size +sizes +sortable +sortdirection +span +x-webkit-speech +x-webkit-grammar +spellcheck +src +srclang +standby +start +step +style +summary +tabindex +tableborder +target +text +title +top +topmargin +truespeed +type +usemap +valign +value +valuetype +version +viewsource +vlink +vspace +webkitallowfullscreen +webkitdirectory +width +wrap diff --git a/Source/WebCore/html/HTMLAudioElement.cpp b/Source/WebCore/html/HTMLAudioElement.cpp new file mode 100644 index 000000000..e3dcf47f1 --- /dev/null +++ b/Source/WebCore/html/HTMLAudioElement.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "HTMLAudioElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLAudioElement::HTMLAudioElement(const QualifiedName& tagName, Document* document, bool createdByParser) + : HTMLMediaElement(tagName, document, createdByParser) +{ + ASSERT(hasTagName(audioTag)); +} + +PassRefPtr<HTMLAudioElement> HTMLAudioElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) +{ + return adoptRef(new HTMLAudioElement(tagName, document, createdByParser)); +} + +PassRefPtr<HTMLAudioElement> HTMLAudioElement::createForJSConstructor(Document* document, const String& src) +{ + RefPtr<HTMLAudioElement> audio = adoptRef(new HTMLAudioElement(audioTag, document, false)); + audio->setPreload("auto"); + if (!src.isNull()) { + audio->setSrc(src); + audio->scheduleLoad(HTMLMediaElement::MediaResource); + } + return audio.release(); +} + +} +#endif diff --git a/Source/WebCore/html/HTMLAudioElement.h b/Source/WebCore/html/HTMLAudioElement.h new file mode 100644 index 000000000..cfa276c5a --- /dev/null +++ b/Source/WebCore/html/HTMLAudioElement.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLAudioElement_h +#define HTMLAudioElement_h + +#if ENABLE(VIDEO) + +#include "HTMLMediaElement.h" + +namespace WebCore { + +class Document; + +class HTMLAudioElement : public HTMLMediaElement { +public: + static PassRefPtr<HTMLAudioElement> create(const QualifiedName&, Document*, bool); + static PassRefPtr<HTMLAudioElement> createForJSConstructor(Document*, const String& src); + + virtual bool hasPendingActivity() const { return isPlaying() || HTMLMediaElement::hasPendingActivity(); } + + virtual bool isActiveNode() const { return true; } + +private: + HTMLAudioElement(const QualifiedName&, Document*, bool); + + virtual bool isVideo() const { return false; } +}; + +} //namespace + +#endif +#endif diff --git a/Source/WebCore/html/HTMLAudioElement.idl b/Source/WebCore/html/HTMLAudioElement.idl new file mode 100644 index 000000000..109164627 --- /dev/null +++ b/Source/WebCore/html/HTMLAudioElement.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + ActiveDOMObject, + Conditional=VIDEO, + NamedConstructor=Audio(in [Optional=CallWithNullValue] DOMString src) + ] HTMLAudioElement : HTMLMediaElement { + }; +} diff --git a/Source/WebCore/html/HTMLBRElement.cpp b/Source/WebCore/html/HTMLBRElement.cpp new file mode 100644 index 000000000..263906010 --- /dev/null +++ b/Source/WebCore/html/HTMLBRElement.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2003, 2006, 2009, 2010 Apple 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 "HTMLBRElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "HTMLNames.h" +#include "RenderBR.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLBRElement::HTMLBRElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(brTag)); +} + +PassRefPtr<HTMLBRElement> HTMLBRElement::create(Document* document) +{ + return adoptRef(new HTMLBRElement(brTag, document)); +} + +PassRefPtr<HTMLBRElement> HTMLBRElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLBRElement(tagName, document)); +} + +bool HTMLBRElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == clearAttr) { + result = eUniversal; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLBRElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == clearAttr) { + // If the string is empty, then don't add the clear property. + // <br clear> and <br clear=""> are just treated like <br> by Gecko, Mac IE, etc. -dwh + const AtomicString& str = attr->value(); + if (!str.isEmpty()) { + if (equalIgnoringCase(str, "all")) + addCSSProperty(attr, CSSPropertyClear, "both"); + else + addCSSProperty(attr, CSSPropertyClear, str); + } + } else + HTMLElement::parseMappedAttribute(attr); +} + +RenderObject* HTMLBRElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + if (style->hasContent()) + return RenderObject::createObject(this, style); + + return new (arena) RenderBR(this); +} + +} diff --git a/Source/WebCore/html/HTMLBRElement.h b/Source/WebCore/html/HTMLBRElement.h new file mode 100644 index 000000000..3857a9ba6 --- /dev/null +++ b/Source/WebCore/html/HTMLBRElement.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2003, 2006, 2009, 2010 Apple 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 HTMLBRElement_h +#define HTMLBRElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLBRElement : public HTMLElement { +public: + static PassRefPtr<HTMLBRElement> create(Document*); + static PassRefPtr<HTMLBRElement> create(const QualifiedName&, Document*); + + virtual bool canContainRangeEndPoint() const { return false; } + +private: + HTMLBRElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLBRElement.idl b/Source/WebCore/html/HTMLBRElement.idl new file mode 100644 index 000000000..a6d215d53 --- /dev/null +++ b/Source/WebCore/html/HTMLBRElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2009 Apple 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. + */ + +module html { + + interface HTMLBRElement : HTMLElement { + attribute [Reflect] DOMString clear; + }; + +} diff --git a/Source/WebCore/html/HTMLBaseElement.cpp b/Source/WebCore/html/HTMLBaseElement.cpp new file mode 100644 index 000000000..9df2ad758 --- /dev/null +++ b/Source/WebCore/html/HTMLBaseElement.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2008, 2009, 2010 Apple 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 "HTMLBaseElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLBaseElement::HTMLBaseElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(baseTag)); +} + +PassRefPtr<HTMLBaseElement> HTMLBaseElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLBaseElement(tagName, document)); +} + +void HTMLBaseElement::parseMappedAttribute(Attribute* attribute) +{ + if (attribute->name() == hrefAttr || attribute->name() == targetAttr) + document()->processBaseElement(); + else + HTMLElement::parseMappedAttribute(attribute); +} + +void HTMLBaseElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + document()->processBaseElement(); +} + +void HTMLBaseElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + document()->processBaseElement(); +} + +bool HTMLBaseElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == hrefAttr || HTMLElement::isURLAttribute(attribute); +} + +String HTMLBaseElement::target() const +{ + return fastGetAttribute(targetAttr); +} + +} diff --git a/Source/WebCore/html/HTMLBaseElement.h b/Source/WebCore/html/HTMLBaseElement.h new file mode 100644 index 000000000..fd3cef616 --- /dev/null +++ b/Source/WebCore/html/HTMLBaseElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2009, 2010 Apple 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 HTMLBaseElement_h +#define HTMLBaseElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLBaseElement : public HTMLElement { +public: + static PassRefPtr<HTMLBaseElement> create(const QualifiedName&, Document*); + +private: + HTMLBaseElement(const QualifiedName&, Document*); + + virtual String target() const; + virtual bool isURLAttribute(Attribute*) const; + virtual void parseMappedAttribute(Attribute*); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLBaseElement.idl b/Source/WebCore/html/HTMLBaseElement.idl new file mode 100644 index 000000000..2750c9ee8 --- /dev/null +++ b/Source/WebCore/html/HTMLBaseElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2009, 2010 Apple 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. + */ + +module html { + + interface HTMLBaseElement : HTMLElement { + attribute [Reflect, URL] DOMString href; + attribute [Reflect] DOMString target; + }; + +} diff --git a/Source/WebCore/html/HTMLBaseFontElement.cpp b/Source/WebCore/html/HTMLBaseFontElement.cpp new file mode 100644 index 000000000..2ab681e54 --- /dev/null +++ b/Source/WebCore/html/HTMLBaseFontElement.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2010 Apple 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 "HTMLBaseFontElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLBaseFontElement::HTMLBaseFontElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(basefontTag)); +} + +PassRefPtr<HTMLBaseFontElement> HTMLBaseFontElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLBaseFontElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLBaseFontElement.h b/Source/WebCore/html/HTMLBaseFontElement.h new file mode 100644 index 000000000..7573045f3 --- /dev/null +++ b/Source/WebCore/html/HTMLBaseFontElement.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2009, 2010 Apple 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 HTMLBaseFontElement_h +#define HTMLBaseFontElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLBaseFontElement : public HTMLElement { +public: + static PassRefPtr<HTMLBaseFontElement> create(const QualifiedName&, Document*); + +private: + HTMLBaseFontElement(const QualifiedName&, Document*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLBaseFontElement.idl b/Source/WebCore/html/HTMLBaseFontElement.idl new file mode 100644 index 000000000..95bc92c37 --- /dev/null +++ b/Source/WebCore/html/HTMLBaseFontElement.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006, 2009, 2010 Apple 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. + */ + +module html { + + interface HTMLBaseFontElement : HTMLElement { + attribute [Reflect] DOMString color; + attribute [Reflect] DOMString face; +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + attribute [Reflect] DOMString size; // this changed to a long, but our existing API is a string +#else + attribute [Reflect] long size; +#endif + }; +} diff --git a/Source/WebCore/html/HTMLBodyElement.cpp b/Source/WebCore/html/HTMLBodyElement.cpp new file mode 100644 index 000000000..6558d46d6 --- /dev/null +++ b/Source/WebCore/html/HTMLBodyElement.cpp @@ -0,0 +1,331 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2007, 2008, 2009, 2010 Apple 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 "HTMLBodyElement.h" + +#include "Attribute.h" +#include "CSSParser.h" +#include "CSSValueKeywords.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLFrameElementBase.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "Page.h" +#include "ScriptEventListener.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLBodyElement::HTMLBodyElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(bodyTag)); +} + +PassRefPtr<HTMLBodyElement> HTMLBodyElement::create(Document* document) +{ + return adoptRef(new HTMLBodyElement(bodyTag, document)); +} + +PassRefPtr<HTMLBodyElement> HTMLBodyElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLBodyElement(tagName, document)); +} + +HTMLBodyElement::~HTMLBodyElement() +{ +} + +bool HTMLBodyElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == backgroundAttr) { + result = (MappedAttributeEntry)(eLastEntry + document()->docID()); + return false; + } + + if (attrName == bgcolorAttr || + attrName == textAttr || + attrName == marginwidthAttr || + attrName == leftmarginAttr || + attrName == marginheightAttr || + attrName == topmarginAttr || + attrName == bgpropertiesAttr) { + result = eUniversal; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLBodyElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == backgroundAttr) { + String url = stripLeadingAndTrailingHTMLSpaces(attr->value()); + if (!url.isEmpty()) + addCSSImageProperty(attr, CSSPropertyBackgroundImage, document()->completeURL(url).string()); + } else if (attr->name() == marginwidthAttr || attr->name() == leftmarginAttr) { + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + } else if (attr->name() == marginheightAttr || attr->name() == topmarginAttr) { + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + } else if (attr->name() == bgcolorAttr) { + addCSSColor(attr, CSSPropertyBackgroundColor, attr->value()); + } else if (attr->name() == textAttr) { + addCSSColor(attr, CSSPropertyColor, attr->value()); + } else if (attr->name() == bgpropertiesAttr) { + if (equalIgnoringCase(attr->value(), "fixed")) + addCSSProperty(attr, CSSPropertyBackgroundAttachment, CSSValueFixed); + } else if (attr->name() == vlinkAttr || + attr->name() == alinkAttr || + attr->name() == linkAttr) { + if (attr->isNull()) { + if (attr->name() == linkAttr) + document()->resetLinkColor(); + else if (attr->name() == vlinkAttr) + document()->resetVisitedLinkColor(); + else + document()->resetActiveLinkColor(); + } else { + RGBA32 color; + if (CSSParser::parseColor(color, attr->value(), !document()->inQuirksMode())) { + if (attr->name() == linkAttr) + document()->setLinkColor(color); + else if (attr->name() == vlinkAttr) + document()->setVisitedLinkColor(color); + else + document()->setActiveLinkColor(color); + } + } + + if (attached()) + document()->recalcStyle(Force); + } else if (attr->name() == onloadAttr) + document()->setWindowAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onbeforeunloadAttr) + document()->setWindowAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onunloadAttr) + document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onpagehideAttr) + document()->setWindowAttributeEventListener(eventNames().pagehideEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onpageshowAttr) + document()->setWindowAttributeEventListener(eventNames().pageshowEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onpopstateAttr) + document()->setWindowAttributeEventListener(eventNames().popstateEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onblurAttr) + document()->setWindowAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onfocusAttr) + document()->setWindowAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(document()->frame(), attr)); +#if ENABLE(ORIENTATION_EVENTS) + else if (attr->name() == onorientationchangeAttr) + document()->setWindowAttributeEventListener(eventNames().orientationchangeEvent, createAttributeEventListener(document()->frame(), attr)); +#endif + else if (attr->name() == onhashchangeAttr) + document()->setWindowAttributeEventListener(eventNames().hashchangeEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onresizeAttr) + document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onscrollAttr) + document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onselectionchangeAttr) + document()->setAttributeEventListener(eventNames().selectionchangeEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onstorageAttr) + document()->setWindowAttributeEventListener(eventNames().storageEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == ononlineAttr) + document()->setWindowAttributeEventListener(eventNames().onlineEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onofflineAttr) + document()->setWindowAttributeEventListener(eventNames().offlineEvent, createAttributeEventListener(document()->frame(), attr)); + else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLBodyElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + + // FIXME: Perhaps this code should be in attach() instead of here. + Element* ownerElement = document()->ownerElement(); + if (ownerElement && (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag))) { + HTMLFrameElementBase* ownerFrameElement = static_cast<HTMLFrameElementBase*>(ownerElement); + int marginWidth = ownerFrameElement->marginWidth(); + if (marginWidth != -1) + setAttribute(marginwidthAttr, String::number(marginWidth)); + int marginHeight = ownerFrameElement->marginHeight(); + if (marginHeight != -1) + setAttribute(marginheightAttr, String::number(marginHeight)); + } + + // FIXME: This call to scheduleRelayout should not be needed here. + // But without it we hang during WebKit tests; need to fix that and remove this. + if (FrameView* view = document()->view()) + view->scheduleRelayout(); + + if (document() && document()->page()) + document()->page()->updateViewportArguments(); +} + +bool HTMLBodyElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == backgroundAttr || HTMLElement::isURLAttribute(attr); +} + +bool HTMLBodyElement::supportsFocus() const +{ + return rendererIsEditable() || HTMLElement::supportsFocus(); +} + +String HTMLBodyElement::aLink() const +{ + return getAttribute(alinkAttr); +} + +void HTMLBodyElement::setALink(const String& value) +{ + setAttribute(alinkAttr, value); +} + +String HTMLBodyElement::bgColor() const +{ + return getAttribute(bgcolorAttr); +} + +void HTMLBodyElement::setBgColor(const String& value) +{ + setAttribute(bgcolorAttr, value); +} + +String HTMLBodyElement::link() const +{ + return getAttribute(linkAttr); +} + +void HTMLBodyElement::setLink(const String& value) +{ + setAttribute(linkAttr, value); +} + +String HTMLBodyElement::text() const +{ + return getAttribute(textAttr); +} + +void HTMLBodyElement::setText(const String& value) +{ + setAttribute(textAttr, value); +} + +String HTMLBodyElement::vLink() const +{ + return getAttribute(vlinkAttr); +} + +void HTMLBodyElement::setVLink(const String& value) +{ + setAttribute(vlinkAttr, value); +} + +static int adjustForZoom(int value, Document* document) +{ + Frame* frame = document->frame(); + float zoomFactor = frame->pageZoomFactor() * frame->frameScaleFactor(); + if (zoomFactor == 1) + return value; + // Needed because of truncation (rather than rounding) when scaling up. + if (zoomFactor > 1) + value++; + return static_cast<int>(value / zoomFactor); +} + +int HTMLBodyElement::scrollLeft() +{ + // Update the document's layout. + Document* document = this->document(); + document->updateLayoutIgnorePendingStylesheets(); + FrameView* view = document->view(); + return view ? adjustForZoom(view->scrollX(), document) : 0; +} + +void HTMLBodyElement::setScrollLeft(int scrollLeft) +{ + Document* document = this->document(); + document->updateLayoutIgnorePendingStylesheets(); + Frame* frame = document->frame(); + if (!frame) + return; + FrameView* view = frame->view(); + if (!view) + return; + view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor() * frame->frameScaleFactor()), view->scrollY())); +} + +int HTMLBodyElement::scrollTop() +{ + // Update the document's layout. + Document* document = this->document(); + document->updateLayoutIgnorePendingStylesheets(); + FrameView* view = document->view(); + return view ? adjustForZoom(view->scrollY(), document) : 0; +} + +void HTMLBodyElement::setScrollTop(int scrollTop) +{ + Document* document = this->document(); + document->updateLayoutIgnorePendingStylesheets(); + Frame* frame = document->frame(); + if (!frame) + return; + FrameView* view = frame->view(); + if (!view) + return; + view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor() * frame->frameScaleFactor()))); +} + +int HTMLBodyElement::scrollHeight() +{ + // Update the document's layout. + Document* document = this->document(); + document->updateLayoutIgnorePendingStylesheets(); + FrameView* view = document->view(); + return view ? adjustForZoom(view->contentsHeight(), document) : 0; +} + +int HTMLBodyElement::scrollWidth() +{ + // Update the document's layout. + Document* document = this->document(); + document->updateLayoutIgnorePendingStylesheets(); + FrameView* view = document->view(); + return view ? adjustForZoom(view->contentsWidth(), document) : 0; +} + +void HTMLBodyElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, document()->completeURL(getAttribute(backgroundAttr))); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLBodyElement.h b/Source/WebCore/html/HTMLBodyElement.h new file mode 100644 index 000000000..5c632649c --- /dev/null +++ b/Source/WebCore/html/HTMLBodyElement.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2009, 2010 Apple 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 HTMLBodyElement_h +#define HTMLBodyElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class Document; + +class HTMLBodyElement : public HTMLElement { +public: + static PassRefPtr<HTMLBodyElement> create(Document*); + static PassRefPtr<HTMLBodyElement> create(const QualifiedName&, Document*); + virtual ~HTMLBodyElement(); + + String aLink() const; + void setALink(const String&); + String bgColor() const; + void setBgColor(const String&); + String link() const; + void setLink(const String&); + String text() const; + void setText(const String&); + String vLink() const; + void setVLink(const String&); + + // Declared virtual in Element + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(blur); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(focus); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(load); + + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(beforeunload); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(hashchange); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(message); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(offline); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(online); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(popstate); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(resize); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(storage); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(unload); + +#if ENABLE(ORIENTATION_EVENTS) + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(orientationchange); +#endif + +private: + HTMLBodyElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + virtual void insertedIntoDocument(); + + virtual bool isURLAttribute(Attribute*) const; + + virtual bool supportsFocus() const; + + virtual int scrollLeft(); + virtual void setScrollLeft(int scrollLeft); + + virtual int scrollTop(); + virtual void setScrollTop(int scrollTop); + + virtual int scrollHeight(); + virtual int scrollWidth(); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLBodyElement.idl b/Source/WebCore/html/HTMLBodyElement.idl new file mode 100644 index 000000000..d6851d49b --- /dev/null +++ b/Source/WebCore/html/HTMLBodyElement.idl @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2006, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLBodyElement : HTMLElement { + attribute [Reflect] DOMString aLink; + attribute [Reflect] DOMString background; + attribute [Reflect] DOMString bgColor; + attribute [Reflect] DOMString link; + attribute [Reflect] DOMString text; + attribute [Reflect] DOMString vLink; + +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + // Event handler attributes + attribute [DontEnum, WindowEventListener] EventListener onbeforeunload; + attribute [DontEnum, WindowEventListener] EventListener onhashchange; + attribute [DontEnum, WindowEventListener] EventListener onmessage; + attribute [DontEnum, WindowEventListener] EventListener onoffline; + attribute [DontEnum, WindowEventListener] EventListener ononline; + attribute [DontEnum, WindowEventListener] EventListener onpopstate; + attribute [DontEnum, WindowEventListener] EventListener onresize; + attribute [DontEnum, WindowEventListener] EventListener onstorage; + attribute [DontEnum, WindowEventListener] EventListener onunload; + + attribute [Conditional=ORIENTATION_EVENTS, DontEnum, WindowEventListener] EventListener onorientationchange; + + // Overrides of Element attributes (with different implementation in bindings). + attribute [DontEnum, WindowEventListener] EventListener onblur; + attribute [DontEnum, WindowEventListener] EventListener onerror; + attribute [DontEnum, WindowEventListener] EventListener onfocus; + attribute [DontEnum, WindowEventListener] EventListener onload; + + // Not implemented yet. + // attribute [DontEnum, WindowEventListener] EventListener onafterprint; + // attribute [DontEnum, WindowEventListener] EventListener onbeforeprint; + // attribute [DontEnum, WindowEventListener] EventListener onredo; + // attribute [DontEnum, WindowEventListener] EventListener onundo; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLButtonElement.cpp b/Source/WebCore/html/HTMLButtonElement.cpp new file mode 100644 index 000000000..54c907dc0 --- /dev/null +++ b/Source/WebCore/html/HTMLButtonElement.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * + * 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 "HTMLButtonElement.h" + +#include "Attribute.h" +#include "EventNames.h" +#include "FormDataList.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "RenderButton.h" +#include "ScriptEventListener.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLButtonElement::HTMLButtonElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) + , m_type(SUBMIT) + , m_isActivatedSubmit(false) +{ + ASSERT(hasTagName(buttonTag)); +} + +PassRefPtr<HTMLButtonElement> HTMLButtonElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLButtonElement(tagName, document, form)); +} + +RenderObject* HTMLButtonElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderButton(this); +} + +const AtomicString& HTMLButtonElement::formControlType() const +{ + switch (m_type) { + case SUBMIT: { + DEFINE_STATIC_LOCAL(const AtomicString, submit, ("submit")); + return submit; + } + case BUTTON: { + DEFINE_STATIC_LOCAL(const AtomicString, button, ("button")); + return button; + } + case RESET: { + DEFINE_STATIC_LOCAL(const AtomicString, reset, ("reset")); + return reset; + } + } + + ASSERT_NOT_REACHED(); + return emptyAtom; +} + +void HTMLButtonElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == typeAttr) { + if (equalIgnoringCase(attr->value(), "reset")) + m_type = RESET; + else if (equalIgnoringCase(attr->value(), "button")) + m_type = BUTTON; + else + m_type = SUBMIT; + setNeedsWillValidateCheck(); + } else if (attr->name() == alignAttr) { + // Don't map 'align' attribute. This matches what Firefox and IE do, but not Opera. + // See http://bugs.webkit.org/show_bug.cgi?id=12071 + } else + HTMLFormControlElement::parseMappedAttribute(attr); +} + +void HTMLButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().DOMActivateEvent && !disabled()) { + if (form() && m_type == SUBMIT) { + m_isActivatedSubmit = true; + form()->prepareForSubmission(event); + m_isActivatedSubmit = false; // Do this in case submission was canceled. + } + if (form() && m_type == RESET) + form()->reset(); + } + + if (event->isKeyboardEvent()) { + if (event->type() == eventNames().keydownEvent && static_cast<KeyboardEvent*>(event)->keyIdentifier() == "U+0020") { + setActive(true, true); + // No setDefaultHandled() - IE dispatches a keypress in this case. + return; + } + if (event->type() == eventNames().keypressEvent) { + switch (static_cast<KeyboardEvent*>(event)->charCode()) { + case '\r': + dispatchSimulatedClick(event); + event->setDefaultHandled(); + return; + case ' ': + // Prevent scrolling down the page. + event->setDefaultHandled(); + return; + } + } + if (event->type() == eventNames().keyupEvent && static_cast<KeyboardEvent*>(event)->keyIdentifier() == "U+0020") { + if (active()) + dispatchSimulatedClick(event); + event->setDefaultHandled(); + return; + } + } + + HTMLFormControlElement::defaultEventHandler(event); +} + +bool HTMLButtonElement::isSuccessfulSubmitButton() const +{ + // HTML spec says that buttons must have names to be considered successful. + // However, other browsers do not impose this constraint. + return m_type == SUBMIT && !disabled(); +} + +bool HTMLButtonElement::isActivatedSubmit() const +{ + return m_isActivatedSubmit; +} + +void HTMLButtonElement::setActivatedSubmit(bool flag) +{ + m_isActivatedSubmit = flag; +} + +bool HTMLButtonElement::appendFormData(FormDataList& formData, bool) +{ + if (m_type != SUBMIT || name().isEmpty() || !m_isActivatedSubmit) + return false; + formData.appendData(name(), value()); + return true; +} + +void HTMLButtonElement::accessKeyAction(bool sendMouseEvents) +{ + focus(); + // Send the mouse button events if the caller specified sendMouseEvents + dispatchSimulatedClick(0, sendMouseEvents); +} + +bool HTMLButtonElement::isURLAttribute(Attribute* attr) const +{ + return attr->name() == formactionAttr || HTMLFormControlElement::isURLAttribute(attr); +} + +String HTMLButtonElement::value() const +{ + return getAttribute(valueAttr); +} + +bool HTMLButtonElement::recalcWillValidate() const +{ + return m_type == SUBMIT && HTMLFormControlElement::recalcWillValidate(); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLButtonElement.h b/Source/WebCore/html/HTMLButtonElement.h new file mode 100644 index 000000000..374eb3e97 --- /dev/null +++ b/Source/WebCore/html/HTMLButtonElement.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 HTMLButtonElement_h +#define HTMLButtonElement_h + +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLButtonElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLButtonElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + String value() const; + +private: + HTMLButtonElement(const QualifiedName& tagName, Document*, HTMLFormElement*); + + enum Type { SUBMIT, RESET, BUTTON }; + + virtual const AtomicString& formControlType() const; + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + virtual void parseMappedAttribute(Attribute*); + virtual void defaultEventHandler(Event*); + virtual bool appendFormData(FormDataList&, bool); + + virtual bool isEnumeratable() const { return true; } + + virtual bool isSuccessfulSubmitButton() const; + virtual bool isActivatedSubmit() const; + virtual void setActivatedSubmit(bool flag); + + virtual void accessKeyAction(bool sendMouseEvents); + virtual bool isURLAttribute(Attribute*) const; + + virtual bool canStartSelection() const { return false; } + + virtual bool isOptionalFormControl() const { return true; } + virtual bool recalcWillValidate() const; + + Type m_type; + bool m_isActivatedSubmit; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLButtonElement.idl b/Source/WebCore/html/HTMLButtonElement.idl new file mode 100644 index 000000000..ff7dab7ba --- /dev/null +++ b/Source/WebCore/html/HTMLButtonElement.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLButtonElement : HTMLElement { + readonly attribute HTMLFormElement form; + + attribute [Reflect, URL] DOMString formAction; + attribute [ConvertNullToNullString] DOMString formEnctype; + attribute [ConvertNullToNullString] DOMString formMethod; + attribute [Reflect] boolean formNoValidate; + attribute [Reflect] DOMString formTarget; + readonly attribute ValidityState validity; + + attribute [Reflect] boolean disabled; + attribute [Reflect] boolean autofocus; + attribute [Reflect] DOMString name; + readonly attribute DOMString type; + attribute [Reflect] DOMString value; + + readonly attribute boolean willValidate; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + + void click(); + + readonly attribute NodeList labels; + }; + +} diff --git a/Source/WebCore/html/HTMLCanvasElement.cpp b/Source/WebCore/html/HTMLCanvasElement.cpp new file mode 100644 index 000000000..ca993e31c --- /dev/null +++ b/Source/WebCore/html/HTMLCanvasElement.cpp @@ -0,0 +1,536 @@ +/* + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLCanvasElement.h" + +#include "Attribute.h" +#include "CanvasContextAttributes.h" +#include "CanvasGradient.h" +#include "CanvasPattern.h" +#include "CanvasRenderingContext2D.h" +#include "CanvasStyle.h" +#include "Chrome.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "ImageBuffer.h" +#include "ImageData.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "RenderHTMLCanvas.h" +#include "Settings.h" +#include <math.h> +#include <stdio.h> + +#if USE(JSC) +#include <runtime/JSLock.h> +#endif + +#if ENABLE(WEBGL) +#include "WebGLContextAttributes.h" +#include "WebGLRenderingContext.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +// These values come from the WhatWG spec. +static const int DefaultWidth = 300; +static const int DefaultHeight = 150; + +// Firefox limits width/height to 32767 pixels, but slows down dramatically before it +// reaches that limit. We limit by area instead, giving us larger maximum dimensions, +// in exchange for a smaller maximum canvas size. +static const float MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels + +//In Skia, we will also limit width/height to 32767. +static const float MaxSkiaDim = 32767.0F; // Maximum width/height in CSS pixels. + +HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_size(DefaultWidth, DefaultHeight) + , m_rendererIsCanvas(false) + , m_ignoreReset(false) + , m_deviceScaleFactor(document->frame() ? document->frame()->page()->deviceScaleFactor() : 1) + , m_originClean(true) + , m_hasCreatedImageBuffer(false) +{ + ASSERT(hasTagName(canvasTag)); +} + +PassRefPtr<HTMLCanvasElement> HTMLCanvasElement::create(Document* document) +{ + return adoptRef(new HTMLCanvasElement(canvasTag, document)); +} + +PassRefPtr<HTMLCanvasElement> HTMLCanvasElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLCanvasElement(tagName, document)); +} + +HTMLCanvasElement::~HTMLCanvasElement() +{ + HashSet<CanvasObserver*>::iterator end = m_observers.end(); + for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it) + (*it)->canvasDestroyed(this); + + m_context.clear(); // Ensure this goes away before the ImageBuffer. +} + +void HTMLCanvasElement::parseMappedAttribute(Attribute* attr) +{ + const QualifiedName& attrName = attr->name(); + if (attrName == widthAttr || attrName == heightAttr) + reset(); + HTMLElement::parseMappedAttribute(attr); +} + +RenderObject* HTMLCanvasElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + Frame* frame = document()->frame(); + if (frame && frame->script()->canExecuteScripts(NotAboutToExecuteScript)) { + m_rendererIsCanvas = true; + return new (arena) RenderHTMLCanvas(this); + } + + m_rendererIsCanvas = false; + return HTMLElement::createRenderer(arena, style); +} + +void HTMLCanvasElement::addObserver(CanvasObserver* observer) +{ + m_observers.add(observer); +} + +void HTMLCanvasElement::removeObserver(CanvasObserver* observer) +{ + m_observers.remove(observer); +} + +void HTMLCanvasElement::setHeight(int value) +{ + setAttribute(heightAttr, String::number(value)); +} + +void HTMLCanvasElement::setWidth(int value) +{ + setAttribute(widthAttr, String::number(value)); +} + +CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, CanvasContextAttributes* attrs) +{ + // A Canvas can either be "2D" or "webgl" but never both. If you request a 2D canvas and the existing + // context is already 2D, just return that. If the existing context is WebGL, then destroy it + // before creating a new 2D context. Vice versa when requesting a WebGL canvas. Requesting a + // context with any other type string will destroy any existing context. + + // FIXME - The code depends on the context not going away once created, to prevent JS from + // seeing a dangling pointer. So for now we will disallow the context from being changed + // once it is created. + if (type == "2d") { + if (m_context && !m_context->is2d()) + return 0; + if (!m_context) { + bool usesDashbardCompatibilityMode = false; +#if ENABLE(DASHBOARD_SUPPORT) + if (Settings* settings = document()->settings()) + usesDashbardCompatibilityMode = settings->usesDashboardBackwardCompatibilityMode(); +#endif + m_context = CanvasRenderingContext2D::create(this, document()->inQuirksMode(), usesDashbardCompatibilityMode); +#if USE(IOSURFACE_CANVAS_BACKING_STORE) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING)) + if (m_context) { + // Need to make sure a RenderLayer and compositing layer get created for the Canvas + setNeedsStyleRecalc(SyntheticStyleChange); + } +#endif + } + return m_context.get(); + } +#if ENABLE(WEBGL) + Settings* settings = document()->settings(); + if (settings && settings->webGLEnabled() +#if !PLATFORM(CHROMIUM) && !PLATFORM(GTK) + && settings->acceleratedCompositingEnabled() +#endif + ) { + // Accept the legacy "webkit-3d" name as well as the provisional "experimental-webgl" name. + // Once ratified, we will also accept "webgl" as the context name. + if ((type == "webkit-3d") || + (type == "experimental-webgl")) { + if (m_context && !m_context->is3d()) + return 0; + if (!m_context) { + m_context = WebGLRenderingContext::create(this, static_cast<WebGLContextAttributes*>(attrs)); + if (m_context) { + // Need to make sure a RenderLayer and compositing layer get created for the Canvas + setNeedsStyleRecalc(SyntheticStyleChange); + } + } + return m_context.get(); + } + } +#else + UNUSED_PARAM(attrs); +#endif + return 0; +} + +void HTMLCanvasElement::didDraw(const FloatRect& rect) +{ + clearCopiedImage(); + + if (RenderBox* ro = renderBox()) { + FloatRect destRect = ro->contentBoxRect(); + FloatRect r = mapRect(rect, FloatRect(0, 0, size().width(), size().height()), destRect); + r.intersect(destRect); + if (r.isEmpty() || m_dirtyRect.contains(r)) + return; + + m_dirtyRect.unite(r); + ro->repaintRectangle(enclosingIntRect(m_dirtyRect)); + } + + HashSet<CanvasObserver*>::iterator end = m_observers.end(); + for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it) + (*it)->canvasChanged(this, rect); +} + +void HTMLCanvasElement::reset() +{ + if (m_ignoreReset) + return; + + bool ok; + bool hadImageBuffer = hasCreatedImageBuffer(); + int w = getAttribute(widthAttr).toInt(&ok); + if (!ok || w < 0) + w = DefaultWidth; + int h = getAttribute(heightAttr).toInt(&ok); + if (!ok || h < 0) + h = DefaultHeight; + + if (m_context && m_context->is2d()) { + CanvasRenderingContext2D* context2D = static_cast<CanvasRenderingContext2D*>(m_context.get()); + context2D->reset(); + } + + IntSize oldSize = size(); + setSurfaceSize(IntSize(w, h)); // The image buffer gets cleared here. + +#if ENABLE(WEBGL) + if (m_context && m_context->is3d() && oldSize != size()) + static_cast<WebGLRenderingContext*>(m_context.get())->reshape(width(), height()); +#endif + + if (RenderObject* renderer = this->renderer()) { + if (m_rendererIsCanvas) { + if (oldSize != size()) + toRenderHTMLCanvas(renderer)->canvasSizeChanged(); + if (hadImageBuffer) + renderer->repaint(); + } + } + + HashSet<CanvasObserver*>::iterator end = m_observers.end(); + for (HashSet<CanvasObserver*>::iterator it = m_observers.begin(); it != end; ++it) + (*it)->canvasResized(this); +} + +void HTMLCanvasElement::paint(GraphicsContext* context, const LayoutRect& r, bool useLowQualityScale) +{ + // Clear the dirty rect + m_dirtyRect = FloatRect(); + + if (context->paintingDisabled()) + return; + + if (m_context) { + if (!m_context->paintsIntoCanvasBuffer() && !document()->printing()) + return; + m_context->paintRenderingResultsToCanvas(); + } + + if (hasCreatedImageBuffer()) { + ImageBuffer* imageBuffer = buffer(); + if (imageBuffer) { + if (m_presentedImage) + context->drawImage(m_presentedImage.get(), ColorSpaceDeviceRGB, r, CompositeSourceOver, useLowQualityScale); + else + context->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, r, CompositeSourceOver, useLowQualityScale); + } + } + +#if ENABLE(WEBGL) + if (is3D()) + static_cast<WebGLRenderingContext*>(m_context.get())->markLayerComposited(); +#endif +} + +#if ENABLE(WEBGL) +bool HTMLCanvasElement::is3D() const +{ + return m_context && m_context->is3d(); +} +#endif + +void HTMLCanvasElement::makeRenderingResultsAvailable() +{ + if (m_context) + m_context->paintRenderingResultsToCanvas(); +} + +void HTMLCanvasElement::makePresentationCopy() +{ + if (!m_presentedImage) { + // The buffer contains the last presented data, so save a copy of it. + m_presentedImage = buffer()->copyImage(CopyBackingStore); + } +} + +void HTMLCanvasElement::clearPresentationCopy() +{ + m_presentedImage.clear(); +} + +void HTMLCanvasElement::setSurfaceSize(const IntSize& size) +{ + m_size = size; + m_hasCreatedImageBuffer = false; + m_imageBuffer.clear(); + clearCopiedImage(); +} + +String HTMLCanvasElement::toEncodingMimeType(const String& mimeType) +{ + String lowercaseMimeType = mimeType.lower(); + + // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread). + if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType)) + lowercaseMimeType = "image/png"; + + return lowercaseMimeType; +} + +String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionCode& ec) +{ + if (!m_originClean) { + ec = SECURITY_ERR; + return String(); + } + + if (m_size.isEmpty() || !buffer()) + return String("data:,"); + + String encodingMimeType = toEncodingMimeType(mimeType); + +#if USE(CG) || USE(SKIA) + // Try to get ImageData first, as that may avoid lossy conversions. + RefPtr<ImageData> imageData = getImageData(); + + if (imageData) + return ImageDataToDataURL(*imageData, encodingMimeType, quality); +#endif + + makeRenderingResultsAvailable(); + + return buffer()->toDataURL(encodingMimeType, quality); +} + +PassRefPtr<ImageData> HTMLCanvasElement::getImageData() +{ + if (!m_context || !m_context->is3d()) + return 0; + +#if ENABLE(WEBGL) + WebGLRenderingContext* ctx = static_cast<WebGLRenderingContext*>(m_context.get()); + + return ctx->paintRenderingResultsToImageData(); +#else + return 0; +#endif +} + +FloatRect HTMLCanvasElement::convertLogicalToDevice(const FloatRect& logicalRect) const +{ + FloatRect deviceRect(logicalRect); + deviceRect.scale(m_deviceScaleFactor); + + float x = floorf(deviceRect.x()); + float y = floorf(deviceRect.y()); + float w = ceilf(deviceRect.maxX() - x); + float h = ceilf(deviceRect.maxY() - y); + deviceRect.setX(x); + deviceRect.setY(y); + deviceRect.setWidth(w); + deviceRect.setHeight(h); + + return deviceRect; +} + +FloatSize HTMLCanvasElement::convertLogicalToDevice(const FloatSize& logicalSize) const +{ + float width = ceilf(logicalSize.width() * m_deviceScaleFactor); + float height = ceilf(logicalSize.height() * m_deviceScaleFactor); + return FloatSize(width, height); +} + +FloatSize HTMLCanvasElement::convertDeviceToLogical(const FloatSize& deviceSize) const +{ + float width = ceilf(deviceSize.width() / m_deviceScaleFactor); + float height = ceilf(deviceSize.height() / m_deviceScaleFactor); + return FloatSize(width, height); +} + +SecurityOrigin* HTMLCanvasElement::securityOrigin() const +{ + return document()->securityOrigin(); +} + +CSSStyleSelector* HTMLCanvasElement::styleSelector() +{ + return document()->styleSelector(); +} + +bool HTMLCanvasElement::shouldAccelerate(const IntSize& size) const +{ +#if USE(IOSURFACE_CANVAS_BACKING_STORE) + UNUSED_PARAM(size); + return document()->settings()->canvasUsesAcceleratedDrawing(); +#elif ENABLE(ACCELERATED_2D_CANVAS) + if (m_context && !m_context->is2d()) + return false; + + Settings* settings = document()->settings(); + if (!settings->accelerated2dCanvasEnabled()) + return false; + + // Do not use acceleration for small canvas. + if (size.width() * size.height() < settings->minimumAccelerated2dCanvasSize()) + return false; + + return true; +#else + UNUSED_PARAM(size); + return false; +#endif +} + +void HTMLCanvasElement::createImageBuffer() const +{ + ASSERT(!m_imageBuffer); + + m_hasCreatedImageBuffer = true; + + FloatSize logicalSize(width(), height()); + FloatSize deviceSize = convertLogicalToDevice(logicalSize); + if (!deviceSize.isExpressibleAsIntSize()) + return; + + if (deviceSize.width() * deviceSize.height() > MaxCanvasArea) + return; +#if USE(SKIA) + if (deviceSize.width() > MaxSkiaDim || deviceSize.height() > MaxSkiaDim) + return; +#endif + + IntSize bufferSize(deviceSize.width(), deviceSize.height()); + if (!bufferSize.width() || !bufferSize.height()) + return; + + RenderingMode renderingMode = shouldAccelerate(bufferSize) ? Accelerated : Unaccelerated; + m_imageBuffer = ImageBuffer::create(bufferSize, ColorSpaceDeviceRGB, renderingMode); + if (!m_imageBuffer) + return; + m_imageBuffer->context()->scale(FloatSize(bufferSize.width() / logicalSize.width(), bufferSize.height() / logicalSize.height())); + m_imageBuffer->context()->setShadowsIgnoreTransforms(true); + m_imageBuffer->context()->setImageInterpolationQuality(DefaultInterpolationQuality); + m_imageBuffer->context()->setStrokeThickness(1); + +#if USE(JSC) + JSC::JSLock lock(JSC::SilenceAssertionsOnly); + scriptExecutionContext()->globalData()->heap.reportExtraMemoryCost(m_imageBuffer->dataSize()); +#endif + +#if USE(IOSURFACE_CANVAS_BACKING_STORE) || (ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING)) + if (m_context && m_context->is2d()) + // Recalculate compositing requirements if acceleration state changed. + const_cast<HTMLCanvasElement*>(this)->setNeedsStyleRecalc(SyntheticStyleChange); +#endif +} + +GraphicsContext* HTMLCanvasElement::drawingContext() const +{ + return buffer() ? m_imageBuffer->context() : 0; +} + +GraphicsContext* HTMLCanvasElement::existingDrawingContext() const +{ + if (!m_hasCreatedImageBuffer) + return 0; + + return drawingContext(); +} + +ImageBuffer* HTMLCanvasElement::buffer() const +{ + if (!m_hasCreatedImageBuffer) + createImageBuffer(); + return m_imageBuffer.get(); +} + +Image* HTMLCanvasElement::copiedImage() const +{ + if (!m_copiedImage && buffer()) { + if (m_context) + m_context->paintRenderingResultsToCanvas(); + m_copiedImage = buffer()->copyImage(CopyBackingStore); + } + return m_copiedImage.get(); +} + +void HTMLCanvasElement::clearCopiedImage() +{ + m_copiedImage.clear(); +} + +AffineTransform HTMLCanvasElement::baseTransform() const +{ + ASSERT(m_hasCreatedImageBuffer); + FloatSize unscaledSize(width(), height()); + FloatSize deviceSize = convertLogicalToDevice(unscaledSize); + IntSize size(deviceSize.width(), deviceSize.height()); + AffineTransform transform; + if (size.width() && size.height()) + transform.scaleNonUniform(size.width() / unscaledSize.width(), size.height() / unscaledSize.height()); + return m_imageBuffer->baseTransform() * transform; +} + +} diff --git a/Source/WebCore/html/HTMLCanvasElement.h b/Source/WebCore/html/HTMLCanvasElement.h new file mode 100644 index 000000000..1bfe52cfe --- /dev/null +++ b/Source/WebCore/html/HTMLCanvasElement.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2004, 2006, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLCanvasElement_h +#define HTMLCanvasElement_h + +#include "FloatRect.h" +#include "HTMLElement.h" +#include "IntSize.h" + +#if PLATFORM(CHROMIUM) || PLATFORM(QT) +#define DefaultInterpolationQuality InterpolationMedium +#elif USE(CG) +#define DefaultInterpolationQuality InterpolationLow +#else +#define DefaultInterpolationQuality InterpolationDefault +#endif + +namespace WebCore { + +class CanvasContextAttributes; +class CanvasRenderingContext; +class GraphicsContext; +class HTMLCanvasElement; +class Image; +class ImageData; +class ImageBuffer; +class IntSize; + +class CanvasObserver { +public: + virtual ~CanvasObserver() { } + + virtual void canvasChanged(HTMLCanvasElement*, const FloatRect& changedRect) = 0; + virtual void canvasResized(HTMLCanvasElement*) = 0; + virtual void canvasDestroyed(HTMLCanvasElement*) = 0; +}; + +class HTMLCanvasElement : public HTMLElement { +public: + static PassRefPtr<HTMLCanvasElement> create(Document*); + static PassRefPtr<HTMLCanvasElement> create(const QualifiedName&, Document*); + virtual ~HTMLCanvasElement(); + + void addObserver(CanvasObserver*); + void removeObserver(CanvasObserver*); + + // Attributes and functions exposed to script + int width() const { return size().width(); } + int height() const { return size().height(); } + + const IntSize& size() const { return m_size; } + + void setWidth(int); + void setHeight(int); + + void setSize(const IntSize& newSize) + { + if (newSize == size()) + return; + m_ignoreReset = true; + setWidth(newSize.width()); + setHeight(newSize.height()); + m_ignoreReset = false; + reset(); + } + + CanvasRenderingContext* getContext(const String&, CanvasContextAttributes* attributes = 0); + + static String toEncodingMimeType(const String& mimeType); + String toDataURL(const String& mimeType, const double* quality, ExceptionCode&); + String toDataURL(const String& mimeType, ExceptionCode& ec) { return toDataURL(mimeType, 0, ec); } + + // Used for rendering + void didDraw(const FloatRect&); + + void paint(GraphicsContext*, const LayoutRect&, bool useLowQualityScale = false); + + GraphicsContext* drawingContext() const; + GraphicsContext* existingDrawingContext() const; + + CanvasRenderingContext* renderingContext() const { return m_context.get(); } + + ImageBuffer* buffer() const; + Image* copiedImage() const; + void clearCopiedImage(); + PassRefPtr<ImageData> getImageData(); + void makePresentationCopy(); + void clearPresentationCopy(); + + FloatRect convertLogicalToDevice(const FloatRect&) const; + FloatSize convertLogicalToDevice(const FloatSize&) const; + + FloatSize convertDeviceToLogical(const FloatSize&) const; + + SecurityOrigin* securityOrigin() const; + void setOriginTainted() { m_originClean = false; } + bool originClean() const { return m_originClean; } + + CSSStyleSelector* styleSelector(); + + AffineTransform baseTransform() const; + +#if ENABLE(WEBGL) + bool is3D() const; +#endif + + void makeRenderingResultsAvailable(); + bool hasCreatedImageBuffer() const { return m_hasCreatedImageBuffer; } + + bool shouldAccelerate(const IntSize&) const; + +private: + HTMLCanvasElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + void reset(); + + void createImageBuffer() const; + + void setSurfaceSize(const IntSize&); + + HashSet<CanvasObserver*> m_observers; + + IntSize m_size; + + OwnPtr<CanvasRenderingContext> m_context; + + bool m_rendererIsCanvas; + + bool m_ignoreReset; + FloatRect m_dirtyRect; + + float m_deviceScaleFactor; + bool m_originClean; + + // m_createdImageBuffer means we tried to malloc the buffer. We didn't necessarily get it. + mutable bool m_hasCreatedImageBuffer; + mutable OwnPtr<ImageBuffer> m_imageBuffer; + + mutable RefPtr<Image> m_presentedImage; + mutable RefPtr<Image> m_copiedImage; // FIXME: This is temporary for platforms that have to copy the image buffer to render (and for CSSCanvasValue). +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLCanvasElement.idl b/Source/WebCore/html/HTMLCanvasElement.idl new file mode 100644 index 000000000..50d4ae461 --- /dev/null +++ b/Source/WebCore/html/HTMLCanvasElement.idl @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + GenerateNativeConverter + ] HTMLCanvasElement : HTMLElement { + + attribute long width; + attribute long height; + + [Custom] DOMString toDataURL(in [ConvertUndefinedOrNullToNullString,Optional=CallWithDefaultValue] DOMString type) + raises(DOMException); + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + // The custom binding is needed to handle context creation attributes. + [Custom] DOMObject getContext(in [Optional=CallWithDefaultValue] DOMString contextId); +#endif +#endif + + }; + +} diff --git a/Source/WebCore/html/HTMLCollection.cpp b/Source/WebCore/html/HTMLCollection.cpp new file mode 100644 index 000000000..0803fe9cc --- /dev/null +++ b/Source/WebCore/html/HTMLCollection.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple 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 "HTMLCollection.h" + +#include "HTMLDocument.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLOptionElement.h" +#include "NodeList.h" + +#include <utility> + +namespace WebCore { + +using namespace HTMLNames; + +HTMLCollection::HTMLCollection(Node* base, CollectionType type) + : m_includeChildren(shouldIncludeChildren(type)) + , m_type(type) + , m_base(base) +{ + m_cache.clear(); +} + +bool HTMLCollection::shouldIncludeChildren(CollectionType type) +{ + switch (type) { + case DocAll: + case DocAnchors: + case DocApplets: + case DocEmbeds: + case DocForms: + case DocImages: + case DocLinks: + case DocObjects: + case DocScripts: + case DocumentNamedItems: + case MapAreas: + case OtherCollection: + case SelectOptions: + case DataListOptions: + case WindowNamedItems: +#if ENABLE(MICRODATA) + case ItemProperties: +#endif + return true; + case NodeChildren: + case TRCells: + case TSectionRows: + case TableTBodies: + return false; + } + ASSERT_NOT_REACHED(); + return false; +} + +PassRefPtr<HTMLCollection> HTMLCollection::create(Node* base, CollectionType type) +{ + return adoptRef(new HTMLCollection(base, type)); +} + +HTMLCollection::~HTMLCollection() +{ +} + +void HTMLCollection::detachFromNode() +{ + m_base = 0; +} + +void HTMLCollection::invalidateCacheIfNeeded() const +{ + ASSERT(m_base); + + uint64_t docversion = static_cast<HTMLDocument*>(m_base->document())->domTreeVersion(); + + if (m_cache.version == docversion) + return; + + m_cache.clear(); + m_cache.version = docversion; +} + +inline bool HTMLCollection::isAcceptableElement(Element* element) const +{ + switch (m_type) { + case DocImages: + return element->hasLocalName(imgTag); + case DocScripts: + return element->hasLocalName(scriptTag); + case DocForms: + return element->hasLocalName(formTag); + case TableTBodies: + return element->hasLocalName(tbodyTag); + case TRCells: + return element->hasLocalName(tdTag) || element->hasLocalName(thTag); + case TSectionRows: + return element->hasLocalName(trTag); + case SelectOptions: + return element->hasLocalName(optionTag); + case DataListOptions: + if (element->hasLocalName(optionTag)) { + HTMLOptionElement* option = static_cast<HTMLOptionElement*>(element); + if (!option->disabled() && !option->value().isEmpty()) + return true; + } + return false; + case MapAreas: + return element->hasLocalName(areaTag); + case DocApplets: + return element->hasLocalName(appletTag) || (element->hasLocalName(objectTag) && static_cast<HTMLObjectElement*>(element)->containsJavaApplet()); + case DocEmbeds: + return element->hasLocalName(embedTag); + case DocObjects: + return element->hasLocalName(objectTag); + case DocLinks: + return (element->hasLocalName(aTag) || element->hasLocalName(areaTag)) && element->fastHasAttribute(hrefAttr); + case DocAnchors: + return element->hasLocalName(aTag) && element->fastHasAttribute(nameAttr); + case DocAll: + case NodeChildren: + return true; +#if ENABLE(MICRODATA) + case ItemProperties: + return element->isHTMLElement() && element->fastHasAttribute(itempropAttr); +#endif + case DocumentNamedItems: + case OtherCollection: + case WindowNamedItems: + ASSERT_NOT_REACHED(); + } + return false; +} + +static Node* nextNodeOrSibling(Node* base, Node* node, bool includeChildren) +{ + return includeChildren ? node->traverseNextNode(base) : node->traverseNextSibling(base); +} + +Element* HTMLCollection::itemAfter(Element* previous) const +{ + ASSERT(m_base); + + Node* current; + if (!previous) + current = m_base->firstChild(); + else + current = nextNodeOrSibling(m_base, previous, m_includeChildren); + + for (; current; current = nextNodeOrSibling(m_base, current, m_includeChildren)) { + if (!current->isElementNode()) + continue; + Element* element = static_cast<Element*>(current); + if (isAcceptableElement(element)) + return element; + } + + return 0; +} + +unsigned HTMLCollection::calcLength() const +{ + ASSERT(m_base); + + unsigned len = 0; + for (Element* current = itemAfter(0); current; current = itemAfter(current)) + ++len; + return len; +} + +// since the collections are to be "live", we have to do the +// calculation every time if anything has changed +unsigned HTMLCollection::length() const +{ + if (!m_base) + return 0; + + invalidateCacheIfNeeded(); + if (!m_cache.hasLength) { + m_cache.length = calcLength(); + m_cache.hasLength = true; + } + return m_cache.length; +} + +Node* HTMLCollection::item(unsigned index) const +{ + if (!m_base) + return 0; + + invalidateCacheIfNeeded(); + if (m_cache.current && m_cache.position == index) + return m_cache.current; + if (m_cache.hasLength && m_cache.length <= index) + return 0; + if (!m_cache.current || m_cache.position > index) { + m_cache.current = itemAfter(0); + m_cache.position = 0; + if (!m_cache.current) + return 0; + } + Element* e = m_cache.current; + for (unsigned pos = m_cache.position; e && pos < index; pos++) + e = itemAfter(e); + m_cache.current = e; + m_cache.position = index; + return m_cache.current; +} + +Node* HTMLCollection::firstItem() const +{ + return item(0); +} + +Node* HTMLCollection::nextItem() const +{ + ASSERT(m_base); + invalidateCacheIfNeeded(); + + // Look for the 'second' item. The first one is currentItem, already given back. + Element* retval = itemAfter(m_cache.current); + m_cache.current = retval; + m_cache.position++; + return retval; +} + +static inline bool nameShouldBeVisibleInDocumentAll(HTMLElement* element) +{ + // The document.all collection returns only certain types of elements by name, + // although it returns any type of element by id. + return element->hasLocalName(appletTag) + || element->hasLocalName(embedTag) + || element->hasLocalName(formTag) + || element->hasLocalName(imgTag) + || element->hasLocalName(inputTag) + || element->hasLocalName(objectTag) + || element->hasLocalName(selectTag); +} + +bool HTMLCollection::checkForNameMatch(Element* element, bool checkName, const AtomicString& name) const +{ + if (!element->isHTMLElement()) + return false; + + HTMLElement* e = toHTMLElement(element); + if (!checkName) + return e->getIdAttribute() == name; + + if (m_type == DocAll && !nameShouldBeVisibleInDocumentAll(e)) + return false; + + return e->getAttribute(nameAttr) == name && e->getIdAttribute() != name; +} + +Node* HTMLCollection::namedItem(const AtomicString& name) const +{ + if (!m_base) + return 0; + + // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp + // This method first searches for an object with a matching id + // attribute. If a match is not found, the method then searches for an + // object with a matching name attribute, but only on those elements + // that are allowed a name attribute. + invalidateCacheIfNeeded(); + + for (Element* e = itemAfter(0); e; e = itemAfter(e)) { + if (checkForNameMatch(e, /* checkName */ false, name)) { + m_cache.current = e; + return e; + } + } + + for (Element* e = itemAfter(0); e; e = itemAfter(e)) { + if (checkForNameMatch(e, /* checkName */ true, name)) { + m_cache.current = e; + return e; + } + } + + m_cache.current = 0; + return 0; +} + +void HTMLCollection::updateNameCache() const +{ + ASSERT(m_base); + + if (m_cache.hasNameCache) + return; + + for (Element* element = itemAfter(0); element; element = itemAfter(element)) { + if (!element->isHTMLElement()) + continue; + HTMLElement* e = toHTMLElement(element); + const AtomicString& idAttrVal = e->getIdAttribute(); + const AtomicString& nameAttrVal = e->getAttribute(nameAttr); + if (!idAttrVal.isEmpty()) + append(m_cache.idCache, idAttrVal, e); + if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && (m_type != DocAll || nameShouldBeVisibleInDocumentAll(e))) + append(m_cache.nameCache, nameAttrVal, e); + } + + m_cache.hasNameCache = true; +} + +bool HTMLCollection::hasNamedItem(const AtomicString& name) const +{ + if (!m_base) + return false; + + if (name.isEmpty()) + return false; + + invalidateCacheIfNeeded(); + updateNameCache(); + + if (Vector<Element*>* idCache = m_cache.idCache.get(name.impl())) { + if (!idCache->isEmpty()) + return true; + } + + if (Vector<Element*>* nameCache = m_cache.nameCache.get(name.impl())) { + if (!nameCache->isEmpty()) + return true; + } + + return false; +} + +void HTMLCollection::namedItems(const AtomicString& name, Vector<RefPtr<Node> >& result) const +{ + if (!m_base) + return; + + ASSERT(result.isEmpty()); + if (name.isEmpty()) + return; + + invalidateCacheIfNeeded(); + updateNameCache(); + + Vector<Element*>* idResults = m_cache.idCache.get(name.impl()); + Vector<Element*>* nameResults = m_cache.nameCache.get(name.impl()); + + for (unsigned i = 0; idResults && i < idResults->size(); ++i) + result.append(idResults->at(i)); + + for (unsigned i = 0; nameResults && i < nameResults->size(); ++i) + result.append(nameResults->at(i)); +} + +PassRefPtr<NodeList> HTMLCollection::tags(const String& name) +{ + if (!m_base) + return 0; + + return m_base->getElementsByTagName(name); +} + +void HTMLCollection::append(NodeCacheMap& map, const AtomicString& key, Element* element) +{ + OwnPtr<Vector<Element*> >& vector = map.add(key.impl(), nullptr).first->second; + if (!vector) + vector = adoptPtr(new Vector<Element*>); + vector->append(element); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLCollection.h b/Source/WebCore/html/HTMLCollection.h new file mode 100644 index 000000000..76bc24102 --- /dev/null +++ b/Source/WebCore/html/HTMLCollection.h @@ -0,0 +1,115 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2011, 2012 Apple 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 HTMLCollection_h +#define HTMLCollection_h + +#include "CollectionType.h" +#include <wtf/RefCounted.h> +#include <wtf/Forward.h> +#include <wtf/HashMap.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Document; +class Element; +class Node; +class NodeList; + +class HTMLCollection : public RefCounted<HTMLCollection> { +public: + static PassRefPtr<HTMLCollection> create(Node* base, CollectionType); + virtual ~HTMLCollection(); + + unsigned length() const; + + virtual Node* item(unsigned index) const; + virtual Node* nextItem() const; + + virtual Node* namedItem(const AtomicString& name) const; + + Node* firstItem() const; + + bool hasNamedItem(const AtomicString& name) const; + void namedItems(const AtomicString& name, Vector<RefPtr<Node> >&) const; + + PassRefPtr<NodeList> tags(const String&); + + Node* base() const { return m_base; } + CollectionType type() const { return static_cast<CollectionType>(m_type); } + + void detachFromNode(); + +protected: + HTMLCollection(Node* base, CollectionType); + + void invalidateCacheIfNeeded() const; + + virtual void updateNameCache() const; + virtual Element* itemAfter(Element*) const; + + typedef HashMap<AtomicStringImpl*, OwnPtr<Vector<Element*> > > NodeCacheMap; + static void append(NodeCacheMap&, const AtomicString&, Element*); + + mutable struct { + NodeCacheMap idCache; + NodeCacheMap nameCache; + uint64_t version; + Element* current; + unsigned position; + unsigned length; + int elementsArrayPosition; + bool hasLength; + bool hasNameCache; + + void clear() + { + idCache.clear(); + nameCache.clear(); + version = 0; + current = 0; + position = 0; + length = 0; + elementsArrayPosition = 0; + hasLength = false; + hasNameCache = false; + } + } m_cache; + +private: + static bool shouldIncludeChildren(CollectionType); + bool checkForNameMatch(Element*, bool checkName, const AtomicString& name) const; + + virtual unsigned calcLength() const; + + bool isAcceptableElement(Element*) const; + + bool m_includeChildren : 1; + unsigned m_type : 5; // CollectionType + + Node* m_base; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLCollection.idl b/Source/WebCore/html/HTMLCollection.idl new file mode 100644 index 000000000..a25f8b0d8 --- /dev/null +++ b/Source/WebCore/html/HTMLCollection.idl @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + HasIndexGetter, + HasNameGetter, + CustomToJS, + GenerateIsReachable, + Polymorphic + ] HTMLCollection { + readonly attribute unsigned long length; + [Custom] Node item(in [Optional=CallWithDefaultValue] unsigned long index); + [Custom] Node namedItem(in [Optional=CallWithDefaultValue] DOMString name); + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + NodeList tags(in [Optional=CallWithDefaultValue] DOMString name); +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLDListElement.cpp b/Source/WebCore/html/HTMLDListElement.cpp new file mode 100644 index 000000000..cc9fabbe5 --- /dev/null +++ b/Source/WebCore/html/HTMLDListElement.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 "HTMLDListElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLDListElement::HTMLDListElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(dlTag)); +} + +PassRefPtr<HTMLDListElement> HTMLDListElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLDListElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLDListElement.h b/Source/WebCore/html/HTMLDListElement.h new file mode 100644 index 000000000..25697a11c --- /dev/null +++ b/Source/WebCore/html/HTMLDListElement.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLDListElement_h +#define HTMLDListElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLDListElement : public HTMLElement { +public: + static PassRefPtr<HTMLDListElement> create(const QualifiedName&, Document*); + +private: + HTMLDListElement(const QualifiedName&, Document*); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLDListElement.idl b/Source/WebCore/html/HTMLDListElement.idl new file mode 100644 index 000000000..1a9326fbf --- /dev/null +++ b/Source/WebCore/html/HTMLDListElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLDListElement : HTMLElement { + attribute [Reflect] boolean compact; + }; + +} diff --git a/Source/WebCore/html/HTMLDataListElement.cpp b/Source/WebCore/html/HTMLDataListElement.cpp new file mode 100644 index 000000000..964d3422a --- /dev/null +++ b/Source/WebCore/html/HTMLDataListElement.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#if ENABLE(DATALIST) +#include "HTMLDataListElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +inline HTMLDataListElement::HTMLDataListElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ +} + +PassRefPtr<HTMLDataListElement> HTMLDataListElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLDataListElement(tagName, document)); +} + +PassRefPtr<HTMLCollection> HTMLDataListElement::options() +{ + return ensureCachedHTMLCollection(DataListOptions); +} + +} // namespace WebCore + +#endif // ENABLE(DATALIST) diff --git a/Source/WebCore/html/HTMLDataListElement.h b/Source/WebCore/html/HTMLDataListElement.h new file mode 100644 index 000000000..97e608f5a --- /dev/null +++ b/Source/WebCore/html/HTMLDataListElement.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2009, Google Inc. All rights reserved. + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLDataListElement_h +#define HTMLDataListElement_h + +#if ENABLE(DATALIST) + +#include "HTMLCollection.h" +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLDataListElement : public HTMLElement { +public: + static PassRefPtr<HTMLDataListElement> create(const QualifiedName&, Document*); + + PassRefPtr<HTMLCollection> options(); + +private: + HTMLDataListElement(const QualifiedName&, Document*); +}; + +} // namespace WebCore + +#endif // ENABLE(DATALIST) + +#endif // HTMLDataListElement_h diff --git a/Source/WebCore/html/HTMLDataListElement.idl b/Source/WebCore/html/HTMLDataListElement.idl new file mode 100644 index 000000000..1f38105f8 --- /dev/null +++ b/Source/WebCore/html/HTMLDataListElement.idl @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=DATALIST, + ] HTMLDataListElement : HTMLElement { + readonly attribute HTMLCollection options; + }; +} diff --git a/Source/WebCore/html/HTMLDetailsElement.cpp b/Source/WebCore/html/HTMLDetailsElement.cpp new file mode 100644 index 000000000..fad9af804 --- /dev/null +++ b/Source/WebCore/html/HTMLDetailsElement.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "HTMLDetailsElement.h" + +#if ENABLE(DETAILS) + +#include "HTMLNames.h" +#include "HTMLSummaryElement.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "RenderDetails.h" +#include "ShadowContentElement.h" +#include "ShadowRoot.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +class DetailsContentElement : public ShadowContentElement { +public: + static PassRefPtr<DetailsContentElement> create(Document*); + +private: + DetailsContentElement(Document* document) + : ShadowContentElement(HTMLNames::divTag, document) + { + } + + virtual bool shouldInclude(Node*); +}; + +PassRefPtr<DetailsContentElement> DetailsContentElement::create(Document* document) +{ + return adoptRef(new DetailsContentElement(document)); +} + +bool DetailsContentElement::shouldInclude(Node* node) +{ + HTMLDetailsElement* details = static_cast<HTMLDetailsElement*>(shadowAncestorNode()); + return details->mainSummary() != node; +} + + +class DetailsSummaryElement : public ShadowContentElement { +public: + static PassRefPtr<DetailsSummaryElement> create(Document*); + +private: + DetailsSummaryElement(Document* document) + : ShadowContentElement(HTMLNames::divTag, document) + { + } + + virtual bool shouldInclude(Node*); +}; + +PassRefPtr<DetailsSummaryElement> DetailsSummaryElement::create(Document* document) +{ + return adoptRef(new DetailsSummaryElement(document)); +} + +bool DetailsSummaryElement::shouldInclude(Node* node) +{ + HTMLDetailsElement* details = static_cast<HTMLDetailsElement*>(shadowAncestorNode()); + return details->mainSummary() == node; +} + + +PassRefPtr<HTMLDetailsElement> HTMLDetailsElement::create(const QualifiedName& tagName, Document* document) +{ + RefPtr<HTMLDetailsElement> result = adoptRef(new HTMLDetailsElement(tagName, document)); + result->ensureShadowSubtreeOf(ForwardingSummary); + return result; +} + +HTMLDetailsElement::HTMLDetailsElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_summaryType(NoSummary) + , m_mainSummary(0) + , m_isOpen(false) +{ + ASSERT(hasTagName(detailsTag)); +} + +RenderObject* HTMLDetailsElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderDetails(this); +} + +void HTMLDetailsElement::ensureShadowSubtreeOf(SummaryType type) +{ + if (type == m_summaryType) + return; + m_summaryType = type; + removeShadowRoot(); + createShadowSubtree(); +} + +static Node* findSummaryFor(PassRefPtr<ContainerNode> container) +{ + for (Node* child = container->firstChild(); child; child = child->nextSibling()) { + if (child->hasTagName(summaryTag)) + return child; + } + + return 0; +} + +Node* HTMLDetailsElement::ensureMainSummary() +{ + Node* summary = findSummaryFor(this); + if (summary) { + ensureShadowSubtreeOf(ForwardingSummary); + return summary; + } + + ensureShadowSubtreeOf(DefaultSummary); + return findSummaryFor(shadowRoot()); +} + +void HTMLDetailsElement::refreshMainSummary(RefreshRenderer refreshRenderer) +{ + RefPtr<Node> oldSummary = m_mainSummary; + m_mainSummary = ensureMainSummary(); + + if (oldSummary == m_mainSummary || !attached()) + return; + + if (oldSummary && oldSummary->parentNodeForRenderingAndStyle()) + oldSummary->reattach(); + if (m_mainSummary && refreshRenderer == RefreshRendererAllowed) + m_mainSummary->reattach(); +} + +void HTMLDetailsElement::createShadowSubtree() +{ + ASSERT(!shadowRoot()); + ExceptionCode ec = 0; + if (m_summaryType == DefaultSummary) { + RefPtr<HTMLSummaryElement> defaultSummary = HTMLSummaryElement::create(summaryTag, document()); + defaultSummary->appendChild(Text::create(document(), defaultDetailsSummaryText()), ec); + ensureShadowRoot()->appendChild(defaultSummary, ec, true); + ensureShadowRoot()->appendChild(DetailsContentElement::create(document()), ec, true); + } else { + ASSERT(m_summaryType == ForwardingSummary); + ensureShadowRoot()->appendChild(DetailsSummaryElement::create(document()), ec, true); + ensureShadowRoot()->appendChild(DetailsContentElement::create(document()), ec, true); + } +} + + +void HTMLDetailsElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + // If childCountDelta is less then zero and the main summary has changed it must be because previous main + // summary was removed. The new main summary was then inside the unrevealed content and needs to be + // reattached to create its renderer. If childCountDelta is not less then zero then a new <summary> element + // has been added and it will be attached without our help. + if (!changedByParser) + refreshMainSummary(childCountDelta < 0 ? RefreshRendererAllowed : RefreshRendererSupressed); +} + +void HTMLDetailsElement::finishParsingChildren() +{ + HTMLElement::finishParsingChildren(); + refreshMainSummary(RefreshRendererAllowed); +} + +void HTMLDetailsElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == openAttr) { + bool oldValue = m_isOpen; + m_isOpen = !attr->value().isNull(); + if (oldValue != m_isOpen) + reattachIfAttached(); + } else + HTMLElement::parseMappedAttribute(attr); +} + +bool HTMLDetailsElement::childShouldCreateRenderer(Node* child) const +{ + return m_isOpen || child == m_mainSummary; +} + +void HTMLDetailsElement::toggleOpen() +{ + setAttribute(openAttr, m_isOpen ? nullAtom : emptyAtom); +} + +} + +#endif diff --git a/Source/WebCore/html/HTMLDetailsElement.h b/Source/WebCore/html/HTMLDetailsElement.h new file mode 100644 index 000000000..0d8dda710 --- /dev/null +++ b/Source/WebCore/html/HTMLDetailsElement.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010, 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 HTMLDetailsElement_h +#define HTMLDetailsElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLDetailsElement : public HTMLElement { +public: + static PassRefPtr<HTMLDetailsElement> create(const QualifiedName& tagName, Document* document); + Node* mainSummary() const { return m_mainSummary; } + void toggleOpen(); + +private: + enum RefreshRenderer { + RefreshRendererAllowed, + RefreshRendererSupressed, + }; + + enum SummaryType { + NoSummary, + DefaultSummary, + ForwardingSummary + }; + + HTMLDetailsElement(const QualifiedName&, Document*); + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta); + virtual void finishParsingChildren(); + + void parseMappedAttribute(Attribute*); + bool childShouldCreateRenderer(Node*) const; + + Node* ensureMainSummary(); + void refreshMainSummary(RefreshRenderer); + void ensureShadowSubtreeOf(SummaryType); + void createShadowSubtree(); + + SummaryType m_summaryType; + Node* m_mainSummary; + bool m_isOpen; + +}; + +} // namespace WebCore + +#endif // HTMLDetailsElement_h diff --git a/Source/WebCore/html/HTMLDetailsElement.idl b/Source/WebCore/html/HTMLDetailsElement.idl new file mode 100644 index 000000000..5ad9508c9 --- /dev/null +++ b/Source/WebCore/html/HTMLDetailsElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * 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. + */ + +module html { + + interface HTMLDetailsElement : HTMLElement { + attribute [Reflect] boolean open; + }; + +} diff --git a/Source/WebCore/html/HTMLDirectoryElement.cpp b/Source/WebCore/html/HTMLDirectoryElement.cpp new file mode 100644 index 000000000..64cbef8ed --- /dev/null +++ b/Source/WebCore/html/HTMLDirectoryElement.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 "HTMLDirectoryElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLDirectoryElement::HTMLDirectoryElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(dirTag)); +} + +PassRefPtr<HTMLDirectoryElement> HTMLDirectoryElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLDirectoryElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLDirectoryElement.h b/Source/WebCore/html/HTMLDirectoryElement.h new file mode 100644 index 000000000..afd387608 --- /dev/null +++ b/Source/WebCore/html/HTMLDirectoryElement.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLDirectoryElement_h +#define HTMLDirectoryElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLDirectoryElement : public HTMLElement { +public: + static PassRefPtr<HTMLDirectoryElement> create(const QualifiedName& tagName, Document*); + +private: + HTMLDirectoryElement(const QualifiedName&, Document*); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLDirectoryElement.idl b/Source/WebCore/html/HTMLDirectoryElement.idl new file mode 100644 index 000000000..b0969746a --- /dev/null +++ b/Source/WebCore/html/HTMLDirectoryElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLDirectoryElement : HTMLElement { + attribute [Reflect] boolean compact; + }; + +} diff --git a/Source/WebCore/html/HTMLDivElement.cpp b/Source/WebCore/html/HTMLDivElement.cpp new file mode 100644 index 000000000..23e6585fb --- /dev/null +++ b/Source/WebCore/html/HTMLDivElement.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLDivElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLDivElement::HTMLDivElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(divTag)); +} + +PassRefPtr<HTMLDivElement> HTMLDivElement::create(Document* document) +{ + return adoptRef(new HTMLDivElement(divTag, document)); +} + +PassRefPtr<HTMLDivElement> HTMLDivElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLDivElement(tagName, document)); +} + +bool HTMLDivElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == alignAttr) { + result = eBlock; + return false; + } + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLDivElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == alignAttr) { + if (equalIgnoringCase(attr->value(), "middle") || equalIgnoringCase(attr->value(), "center")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitCenter); + else if (equalIgnoringCase(attr->value(), "left")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitLeft); + else if (equalIgnoringCase(attr->value(), "right")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitRight); + else + addCSSProperty(attr, CSSPropertyTextAlign, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLDivElement.h b/Source/WebCore/html/HTMLDivElement.h new file mode 100644 index 000000000..2e2b41710 --- /dev/null +++ b/Source/WebCore/html/HTMLDivElement.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLDivElement_h +#define HTMLDivElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLDivElement : public HTMLElement { +public: + static PassRefPtr<HTMLDivElement> create(Document*); + static PassRefPtr<HTMLDivElement> create(const QualifiedName&, Document*); + +protected: + HTMLDivElement(const QualifiedName&, Document*); + +private: + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace WebCore + +#endif // HTMLDivElement_h diff --git a/Source/WebCore/html/HTMLDivElement.idl b/Source/WebCore/html/HTMLDivElement.idl new file mode 100644 index 000000000..90fb84f8f --- /dev/null +++ b/Source/WebCore/html/HTMLDivElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLDivElement : HTMLElement { + attribute [Reflect] DOMString align; + }; + +} diff --git a/Source/WebCore/html/HTMLDocument.cpp b/Source/WebCore/html/HTMLDocument.cpp new file mode 100644 index 000000000..b67eacd5a --- /dev/null +++ b/Source/WebCore/html/HTMLDocument.cpp @@ -0,0 +1,460 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple 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. + * + * Portions are Copyright (C) 2002 Netscape Communications Corporation. + * Other contributors: David Baron <dbaron@fas.harvard.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the document type parsing portions of this file may be used + * under the terms of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deleting the provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "HTMLDocument.h" + +#include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" +#include "CookieJar.h" +#include "DocumentLoader.h" +#include "DocumentType.h" +#include "ExceptionCode.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "HashTools.h" +#include "HTMLDocumentParser.h" +#include "HTMLBodyElement.h" +#include "HTMLElementFactory.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLNames.h" +#include "InspectorInstrumentation.h" +#include "KURL.h" +#include "Page.h" +#include "Settings.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +using namespace HTMLNames; + +HTMLDocument::HTMLDocument(Frame* frame, const KURL& url) + : Document(frame, url, false, true) +{ + clearXMLVersion(); +} + +HTMLDocument::~HTMLDocument() +{ +} + +int HTMLDocument::width() +{ + updateLayoutIgnorePendingStylesheets(); + FrameView* frameView = view(); + return frameView ? frameView->contentsWidth() : 0; +} + +int HTMLDocument::height() +{ + updateLayoutIgnorePendingStylesheets(); + FrameView* frameView = view(); + return frameView ? frameView->contentsHeight() : 0; +} + +String HTMLDocument::dir() +{ + HTMLElement* b = body(); + if (!b) + return String(); + return b->getAttribute(dirAttr); +} + +void HTMLDocument::setDir(const String& value) +{ + HTMLElement* b = body(); + if (b) + b->setAttribute(dirAttr, value); +} + +String HTMLDocument::designMode() const +{ + return inDesignMode() ? "on" : "off"; +} + +void HTMLDocument::setDesignMode(const String& value) +{ + InheritedBool mode; + if (equalIgnoringCase(value, "on")) + mode = on; + else if (equalIgnoringCase(value, "off")) + mode = off; + else + mode = inherit; + Document::setDesignMode(mode); +} + +static Node* focusedFrameOwnerElement(Frame* focusedFrame, Frame* currentFrame) +{ + for (; focusedFrame; focusedFrame = focusedFrame->tree()->parent()) { + if (focusedFrame->tree()->parent() == currentFrame) + return focusedFrame->ownerElement(); + } + return 0; +} + +Element* HTMLDocument::activeElement() +{ + Node* node = focusedNode(); + if (!node && page()) + node = focusedFrameOwnerElement(page()->focusController()->focusedFrame(), frame()); + if (!node) + return body(); + ASSERT(node->document() == this); + while (node->treeScope() != this) { + node = node->parentOrHostNode(); + ASSERT(node); + } + if (node->isElementNode()) + return toElement(node); + return body(); +} + +bool HTMLDocument::hasFocus() +{ + Page* page = this->page(); + if (!page) + return false; + if (!page->focusController()->isActive()) + return false; + if (Frame* focusedFrame = page->focusController()->focusedFrame()) { + if (focusedFrame->tree()->isDescendantOf(frame())) + return true; + } + return false; +} + +String HTMLDocument::bgColor() +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (!bodyElement) + return String(); + return bodyElement->bgColor(); +} + +void HTMLDocument::setBgColor(const String& value) +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (bodyElement) + bodyElement->setBgColor(value); +} + +String HTMLDocument::fgColor() +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (!bodyElement) + return String(); + return bodyElement->text(); +} + +void HTMLDocument::setFgColor(const String& value) +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (bodyElement) + bodyElement->setText(value); +} + +String HTMLDocument::alinkColor() +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (!bodyElement) + return String(); + return bodyElement->aLink(); +} + +void HTMLDocument::setAlinkColor(const String& value) +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (bodyElement) { + // This check is a bit silly, but some benchmarks like to set the + // document's link colors over and over to the same value and we + // don't want to incur a style update each time. + if (bodyElement->aLink() != value) + bodyElement->setALink(value); + } +} + +String HTMLDocument::linkColor() +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (!bodyElement) + return String(); + return bodyElement->link(); +} + +void HTMLDocument::setLinkColor(const String& value) +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (bodyElement) { + // This check is a bit silly, but some benchmarks like to set the + // document's link colors over and over to the same value and we + // don't want to incur a style update each time. + if (bodyElement->link() != value) + bodyElement->setLink(value); + } +} + +String HTMLDocument::vlinkColor() +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (!bodyElement) + return String(); + return bodyElement->vLink(); +} + +void HTMLDocument::setVlinkColor(const String& value) +{ + HTMLElement* b = body(); + HTMLBodyElement* bodyElement = (b && b->hasTagName(bodyTag)) ? static_cast<HTMLBodyElement*>(b) : 0; + + if (bodyElement) { + // This check is a bit silly, but some benchmarks like to set the + // document's link colors over and over to the same value and we + // don't want to incur a style update each time. + if (bodyElement->vLink() != value) + bodyElement->setVLink(value); + } +} + +void HTMLDocument::captureEvents() +{ +} + +void HTMLDocument::releaseEvents() +{ +} + +PassRefPtr<DocumentParser> HTMLDocument::createParser() +{ + bool reportErrors = InspectorInstrumentation::collectingHTMLParseErrors(this->page()); + return HTMLDocumentParser::create(this, reportErrors); +} + +// -------------------------------------------------------------------------- +// not part of the DOM +// -------------------------------------------------------------------------- + +PassRefPtr<Element> HTMLDocument::createElement(const AtomicString& name, ExceptionCode& ec) +{ + if (!isValidName(name)) { + ec = INVALID_CHARACTER_ERR; + return 0; + } + return HTMLElementFactory::createHTMLElement(QualifiedName(nullAtom, name.lower(), xhtmlNamespaceURI), this, 0, false); +} + +void HTMLDocument::addItemToMap(HashCountedSet<AtomicStringImpl*>& map, const AtomicString& name) +{ + if (name.isEmpty()) + return; + map.add(name.impl()); + if (Frame* f = frame()) + f->script()->namedItemAdded(this, name); +} + +void HTMLDocument::removeItemFromMap(HashCountedSet<AtomicStringImpl*>& map, const AtomicString& name) +{ + if (name.isEmpty()) + return; + map.remove(name.impl()); + if (Frame* f = frame()) + f->script()->namedItemRemoved(this, name); +} + +void HTMLDocument::addNamedItem(const AtomicString& name) +{ + addItemToMap(m_namedItemCounts, name); +} + +void HTMLDocument::removeNamedItem(const AtomicString& name) +{ + removeItemFromMap(m_namedItemCounts, name); +} + +void HTMLDocument::addExtraNamedItem(const AtomicString& name) +{ + addItemToMap(m_extraNamedItemCounts, name); +} + +void HTMLDocument::removeExtraNamedItem(const AtomicString& name) +{ + removeItemFromMap(m_extraNamedItemCounts, name); +} + +void HTMLDocument::setCompatibilityModeFromDoctype() +{ + // There are three possible compatibility modes: + // Quirks - quirks mode emulates WinIE and NS4. CSS parsing is also relaxed in this mode, e.g., unit types can + // be omitted from numbers. + // Limited Quirks - This mode is identical to no-quirks mode except for its treatment of line-height in the inline box model. + // No Quirks - no quirks apply. Web pages will obey the specifications to the letter. + DocumentType* docType = doctype(); + if (!docType) + return; + + // Check for Quirks Mode. + const String& publicId = docType->publicId(); + if (docType->name() != "html" + || publicId.startsWith("+//Silmaril//dtd html Pro v0r11 19970101//", false) + || publicId.startsWith("-//AdvaSoft Ltd//DTD HTML 3.0 asWedit + extensions//", false) + || publicId.startsWith("-//AS//DTD HTML 3.0 asWedit + extensions//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 1//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.0 Level 2//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 1//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict Level 2//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.0 Strict//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.0//", false) + || publicId.startsWith("-//IETF//DTD HTML 2.1E//", false) + || publicId.startsWith("-//IETF//DTD HTML 3.0//", false) + || publicId.startsWith("-//IETF//DTD HTML 3.2 Final//", false) + || publicId.startsWith("-//IETF//DTD HTML 3.2//", false) + || publicId.startsWith("-//IETF//DTD HTML 3//", false) + || publicId.startsWith("-//IETF//DTD HTML Level 0//", false) + || publicId.startsWith("-//IETF//DTD HTML Level 1//", false) + || publicId.startsWith("-//IETF//DTD HTML Level 2//", false) + || publicId.startsWith("-//IETF//DTD HTML Level 3//", false) + || publicId.startsWith("-//IETF//DTD HTML Strict Level 0//", false) + || publicId.startsWith("-//IETF//DTD HTML Strict Level 1//", false) + || publicId.startsWith("-//IETF//DTD HTML Strict Level 2//", false) + || publicId.startsWith("-//IETF//DTD HTML Strict Level 3//", false) + || publicId.startsWith("-//IETF//DTD HTML Strict//", false) + || publicId.startsWith("-//IETF//DTD HTML//", false) + || publicId.startsWith("-//Metrius//DTD Metrius Presentational//", false) + || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML Strict//", false) + || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 HTML//", false) + || publicId.startsWith("-//Microsoft//DTD Internet Explorer 2.0 Tables//", false) + || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML Strict//", false) + || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 HTML//", false) + || publicId.startsWith("-//Microsoft//DTD Internet Explorer 3.0 Tables//", false) + || publicId.startsWith("-//Netscape Comm. Corp.//DTD HTML//", false) + || publicId.startsWith("-//Netscape Comm. Corp.//DTD Strict HTML//", false) + || publicId.startsWith("-//O'Reilly and Associates//DTD HTML 2.0//", false) + || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended 1.0//", false) + || publicId.startsWith("-//O'Reilly and Associates//DTD HTML Extended Relaxed 1.0//", false) + || publicId.startsWith("-//SoftQuad Software//DTD HoTMetaL PRO 6.0::19990601::extensions to HTML 4.0//", false) + || publicId.startsWith("-//SoftQuad//DTD HoTMetaL PRO 4.0::19971010::extensions to HTML 4.0//", false) + || publicId.startsWith("-//Spyglass//DTD HTML 2.0 Extended//", false) + || publicId.startsWith("-//SQ//DTD HTML 2.0 HoTMetaL + extensions//", false) + || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava HTML//", false) + || publicId.startsWith("-//Sun Microsystems Corp.//DTD HotJava Strict HTML//", false) + || publicId.startsWith("-//W3C//DTD HTML 3 1995-03-24//", false) + || publicId.startsWith("-//W3C//DTD HTML 3.2 Draft//", false) + || publicId.startsWith("-//W3C//DTD HTML 3.2 Final//", false) + || publicId.startsWith("-//W3C//DTD HTML 3.2//", false) + || publicId.startsWith("-//W3C//DTD HTML 3.2S Draft//", false) + || publicId.startsWith("-//W3C//DTD HTML 4.0 Frameset//", false) + || publicId.startsWith("-//W3C//DTD HTML 4.0 Transitional//", false) + || publicId.startsWith("-//W3C//DTD HTML Experimental 19960712//", false) + || publicId.startsWith("-//W3C//DTD HTML Experimental 970421//", false) + || publicId.startsWith("-//W3C//DTD W3 HTML//", false) + || publicId.startsWith("-//W3O//DTD W3 HTML 3.0//", false) + || equalIgnoringCase(publicId, "-//W3O//DTD W3 HTML Strict 3.0//EN//") + || publicId.startsWith("-//WebTechs//DTD Mozilla HTML 2.0//", false) + || publicId.startsWith("-//WebTechs//DTD Mozilla HTML//", false) + || equalIgnoringCase(publicId, "-/W3C/DTD HTML 4.0 Transitional/EN") + || equalIgnoringCase(publicId, "HTML") + || equalIgnoringCase(docType->systemId(), "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") + || (docType->systemId().isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false)) + || (docType->systemId().isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) { + setCompatibilityMode(QuirksMode); + return; + } + + // Check for Limited Quirks Mode. + if (publicId.startsWith("-//W3C//DTD XHTML 1.0 Frameset//", false) + || publicId.startsWith("-//W3C//DTD XHTML 1.0 Transitional//", false) + || (!docType->systemId().isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Frameset//", false)) + || (!docType->systemId().isEmpty() && publicId.startsWith("-//W3C//DTD HTML 4.01 Transitional//", false))) { + setCompatibilityMode(LimitedQuirksMode); + return; + } + + // Otherwise we are No Quirks Mode. + setCompatibilityMode(NoQuirksMode); + return; +} + +void HTMLDocument::clear() +{ + // FIXME: This does nothing, and that seems unlikely to be correct. + // We've long had a comment saying that IE doesn't support this. + // But I do see it in the documentation for Mozilla. +} + +bool HTMLDocument::isFrameSet() const +{ + HTMLElement* bodyElement = body(); + return bodyElement && bodyElement->hasTagName(framesetTag); +} + +} diff --git a/Source/WebCore/html/HTMLDocument.h b/Source/WebCore/html/HTMLDocument.h new file mode 100644 index 000000000..d39f392aa --- /dev/null +++ b/Source/WebCore/html/HTMLDocument.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2007, 2008, 2009 Apple 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 HTMLDocument_h +#define HTMLDocument_h + +#include "CachedResourceClient.h" +#include "Document.h" +#include <wtf/HashCountedSet.h> +#include <wtf/text/AtomicStringHash.h> + +namespace WebCore { + +class FrameView; +class HTMLElement; + +class HTMLDocument : public Document, public CachedResourceClient { +public: + static PassRefPtr<HTMLDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new HTMLDocument(frame, url)); + } + virtual ~HTMLDocument(); + + int width(); + int height(); + + String dir(); + void setDir(const String&); + + String designMode() const; + void setDesignMode(const String&); + + virtual void setCompatibilityModeFromDoctype(); + + Element* activeElement(); + bool hasFocus(); + + String bgColor(); + void setBgColor(const String&); + String fgColor(); + void setFgColor(const String&); + String alinkColor(); + void setAlinkColor(const String&); + String linkColor(); + void setLinkColor(const String&); + String vlinkColor(); + void setVlinkColor(const String&); + + void clear(); + + void captureEvents(); + void releaseEvents(); + + void addNamedItem(const AtomicString& name); + void removeNamedItem(const AtomicString& name); + bool hasNamedItem(AtomicStringImpl* name); + + void addExtraNamedItem(const AtomicString& name); + void removeExtraNamedItem(const AtomicString& name); + bool hasExtraNamedItem(AtomicStringImpl* name); + +protected: + HTMLDocument(Frame*, const KURL&); + +private: + virtual PassRefPtr<Element> createElement(const AtomicString& tagName, ExceptionCode&); + + virtual bool isFrameSet() const; + virtual PassRefPtr<DocumentParser> createParser(); + + void addItemToMap(HashCountedSet<AtomicStringImpl*>&, const AtomicString&); + void removeItemFromMap(HashCountedSet<AtomicStringImpl*>&, const AtomicString&); + + HashCountedSet<AtomicStringImpl*> m_namedItemCounts; + HashCountedSet<AtomicStringImpl*> m_extraNamedItemCounts; +}; + +inline bool HTMLDocument::hasNamedItem(AtomicStringImpl* name) +{ + ASSERT(name); + return m_namedItemCounts.contains(name); +} + +inline bool HTMLDocument::hasExtraNamedItem(AtomicStringImpl* name) +{ + ASSERT(name); + return m_extraNamedItemCounts.contains(name); +} + +} // namespace WebCore + +#endif // HTMLDocument_h diff --git a/Source/WebCore/html/HTMLDocument.idl b/Source/WebCore/html/HTMLDocument.idl new file mode 100644 index 000000000..d2400a884 --- /dev/null +++ b/Source/WebCore/html/HTMLDocument.idl @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + HasOverridingNameGetter + ] HTMLDocument : Document { + [Custom, NoCPPCustom] void open(); + void close(); + [Custom] void write(in [Optional=CallWithDefaultValue] DOMString text); + [Custom] void writeln(in [Optional=CallWithDefaultValue] DOMString text); + + readonly attribute HTMLCollection embeds; + readonly attribute HTMLCollection plugins; + readonly attribute HTMLCollection scripts; + + // Extensions + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // FIXME: This should eventually be available (if they are wanted) for all languages. + attribute [Custom, Deletable] HTMLAllCollection all; +#endif + + void clear(); + + void captureEvents(); + void releaseEvents(); + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + readonly attribute long width; + readonly attribute long height; +#endif + attribute [ConvertNullToNullString] DOMString dir; + attribute [ConvertNullToNullString] DOMString designMode; + readonly attribute DOMString compatMode; + + readonly attribute Element activeElement; + boolean hasFocus(); + + // Deprecated attributes + attribute [ConvertNullToNullString] DOMString bgColor; + attribute [ConvertNullToNullString] DOMString fgColor; + attribute [ConvertNullToNullString] DOMString alinkColor; + attribute [ConvertNullToNullString] DOMString linkColor; + attribute [ConvertNullToNullString] DOMString vlinkColor; + }; + +} diff --git a/Source/WebCore/html/HTMLElement.cpp b/Source/WebCore/html/HTMLElement.cpp new file mode 100644 index 000000000..573d581d4 --- /dev/null +++ b/Source/WebCore/html/HTMLElement.cpp @@ -0,0 +1,1071 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2011 Motorola Mobility. 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 "HTMLElement.h" + +#include "Attribute.h" +#include "CSSParser.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "ChildListMutationScope.h" +#include "DocumentFragment.h" +#include "Event.h" +#include "EventListener.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "HTMLBRElement.h" +#include "HTMLCollection.h" +#include "HTMLDocument.h" +#include "HTMLElementFactory.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLTextFormControlElement.h" +#include "RenderWordBreak.h" +#include "ScriptEventListener.h" +#include "Settings.h" +#include "Text.h" +#include "TextIterator.h" +#include "XMLNames.h" +#include "markup.h" +#include <wtf/StdLibExtras.h> +#include <wtf/text/CString.h> + +#if ENABLE(MICRODATA) +#include "MicroDataItemValue.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; +using namespace WTF; + +using std::min; +using std::max; + +PassRefPtr<HTMLElement> HTMLElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLElement(tagName, document)); +} + +String HTMLElement::nodeName() const +{ + // FIXME: Would be nice to have an atomicstring lookup based off uppercase + // chars that does not have to copy the string on a hit in the hash. + // FIXME: We should have a way to detect XHTML elements and replace the hasPrefix() check with it. + if (document()->isHTMLDocument() && !tagQName().hasPrefix()) + return tagQName().localNameUpper(); + return Element::nodeName(); +} + +bool HTMLElement::ieForbidsInsertHTML() const +{ + // FIXME: Supposedly IE disallows settting innerHTML, outerHTML + // and createContextualFragment on these tags. We have no tests to + // verify this however, so this list could be totally wrong. + // This list was moved from the previous endTagRequirement() implementation. + // This is also called from editing and assumed to be the list of tags + // for which no end tag should be serialized. It's unclear if the list for + // IE compat and the list for serialization sanity are the same. + if (hasLocalName(areaTag) + || hasLocalName(baseTag) + || hasLocalName(basefontTag) + || hasLocalName(brTag) + || hasLocalName(colTag) + || hasLocalName(embedTag) + || hasLocalName(frameTag) + || hasLocalName(hrTag) + || hasLocalName(imageTag) + || hasLocalName(imgTag) + || hasLocalName(inputTag) + || hasLocalName(isindexTag) + || hasLocalName(linkTag) + || hasLocalName(metaTag) + || hasLocalName(paramTag) + || hasLocalName(sourceTag) + || hasLocalName(wbrTag)) + return true; + // FIXME: I'm not sure why dashboard mode would want to change the + // serialization of <canvas>, that seems like a bad idea. +#if ENABLE(DASHBOARD_SUPPORT) + if (hasLocalName(canvasTag)) { + Settings* settings = document()->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode()) + return true; + } +#endif + return false; +} + +bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == alignAttr + || attrName == contenteditableAttr + || attrName == hiddenAttr) { + result = eUniversal; + return false; + } + if (attrName == dirAttr) { + if (hasLocalName(bdoTag)) + result = eBDO; + else if (hasLocalName(bdiTag)) + result = eBDI; + else + result = eUniversal; + return true; + } + + return StyledElement::mapToEntry(attrName, result); +} + +static inline int unicodeBidiAttributeForDirAuto(HTMLElement* element) +{ + if (element->hasLocalName(preTag) || element->hasLocalName(textareaTag)) + return CSSValueWebkitPlaintext; + // FIXME: For bdo element, dir="auto" should result in "bidi-override isolate" but we don't support having multiple values in unicode-bidi yet. + // See https://bugs.webkit.org/show_bug.cgi?id=73164. + return CSSValueWebkitIsolate; +} + +static unsigned parseBorderWidthAttribute(Attribute* attr) +{ + ASSERT(attr && attr->name() == borderAttr); + + unsigned borderWidth = 0; + if (!attr->value().isEmpty()) + parseHTMLNonNegativeInteger(attr->value(), borderWidth); + + return borderWidth; +} + +void HTMLElement::applyBorderAttribute(Attribute* attr) +{ + addCSSLength(attr, CSSPropertyBorderWidth, String::number(parseBorderWidthAttribute(attr))); + addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid); +} + +void HTMLElement::mapLanguageAttributeToLocale(Attribute* attribute) +{ + ASSERT(attribute && (attribute->name() == langAttr || attribute->name().matches(XMLNames::langAttr))); + const AtomicString& value = attribute->value(); + if (!value.isEmpty()) { + // Have to quote so the locale id is treated as a string instead of as a CSS keyword. + addCSSProperty(attribute, CSSPropertyWebkitLocale, quoteCSSString(value)); + } else { + // The empty string means the language is explicitly unknown. + addCSSProperty(attribute, CSSPropertyWebkitLocale, CSSValueAuto); + } + setNeedsStyleRecalc(); +} + +void HTMLElement::parseMappedAttribute(Attribute* attr) +{ + if (isIdAttributeName(attr->name()) || attr->name() == classAttr || attr->name() == styleAttr) + return StyledElement::parseMappedAttribute(attr); + + String indexstring; + if (attr->name() == alignAttr) { + if (equalIgnoringCase(attr->value(), "middle")) + addCSSProperty(attr, CSSPropertyTextAlign, "center"); + else + addCSSProperty(attr, CSSPropertyTextAlign, attr->value()); + } else if (attr->name() == contenteditableAttr) { + setContentEditable(attr); + } else if (attr->name() == hiddenAttr) { + addCSSProperty(attr, CSSPropertyDisplay, CSSValueNone); + } else if (attr->name() == tabindexAttr) { + indexstring = getAttribute(tabindexAttr); + int tabindex = 0; + if (!indexstring.length()) { + clearTabIndexExplicitly(); + } else if (parseHTMLInteger(indexstring, tabindex)) { + // Clamp tabindex to the range of 'short' to match Firefox's behavior. + setTabIndexExplicitly(max(static_cast<int>(std::numeric_limits<short>::min()), min(tabindex, static_cast<int>(std::numeric_limits<short>::max())))); + } + } else if (attr->name().matches(XMLNames::langAttr)) { + mapLanguageAttributeToLocale(attr); + } else if (attr->name() == langAttr) { + // xml:lang has a higher priority than lang. + if (!fastHasAttribute(XMLNames::langAttr)) + mapLanguageAttributeToLocale(attr); + } else if (attr->name() == dirAttr) { + bool dirIsAuto = equalIgnoringCase(attr->value(), "auto"); + if (!dirIsAuto) + addCSSProperty(attr, CSSPropertyDirection, attr->value()); + dirAttributeChanged(attr); + if (dirIsAuto) + addCSSProperty(attr, CSSPropertyUnicodeBidi, unicodeBidiAttributeForDirAuto(this)); + else if (!hasTagName(bdiTag) && !hasTagName(bdoTag) && !hasTagName(outputTag)) + addCSSProperty(attr, CSSPropertyUnicodeBidi, CSSValueEmbed); + } else if (attr->name() == draggableAttr) { + const AtomicString& value = attr->value(); + if (equalIgnoringCase(value, "true")) { + addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueElement); + addCSSProperty(attr, CSSPropertyWebkitUserSelect, CSSValueNone); + } else if (equalIgnoringCase(value, "false")) + addCSSProperty(attr, CSSPropertyWebkitUserDrag, CSSValueNone); +#if ENABLE(MICRODATA) + } else if (attr->name() == itempropAttr) { + setItemProp(attr->value()); + } else if (attr->name() == itemrefAttr) { + setItemRef(attr->value()); + } else if (attr->name() == itemtypeAttr) { + setItemType(attr->value()); +#endif + } +// standard events + else if (attr->name() == onclickAttr) { + setAttributeEventListener(eventNames().clickEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == oncontextmenuAttr) { + setAttributeEventListener(eventNames().contextmenuEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondblclickAttr) { + setAttributeEventListener(eventNames().dblclickEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onmousedownAttr) { + setAttributeEventListener(eventNames().mousedownEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onmousemoveAttr) { + setAttributeEventListener(eventNames().mousemoveEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onmouseoutAttr) { + setAttributeEventListener(eventNames().mouseoutEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onmouseoverAttr) { + setAttributeEventListener(eventNames().mouseoverEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onmouseupAttr) { + setAttributeEventListener(eventNames().mouseupEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onmousewheelAttr) { + setAttributeEventListener(eventNames().mousewheelEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onfocusAttr) { + setAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onfocusinAttr) { + setAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onfocusoutAttr) { + setAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onblurAttr) { + setAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onkeydownAttr) { + setAttributeEventListener(eventNames().keydownEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onkeypressAttr) { + setAttributeEventListener(eventNames().keypressEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onkeyupAttr) { + setAttributeEventListener(eventNames().keyupEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onscrollAttr) { + setAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onbeforecutAttr) { + setAttributeEventListener(eventNames().beforecutEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == oncutAttr) { + setAttributeEventListener(eventNames().cutEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onbeforecopyAttr) { + setAttributeEventListener(eventNames().beforecopyEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == oncopyAttr) { + setAttributeEventListener(eventNames().copyEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onbeforepasteAttr) { + setAttributeEventListener(eventNames().beforepasteEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onpasteAttr) { + setAttributeEventListener(eventNames().pasteEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondragenterAttr) { + setAttributeEventListener(eventNames().dragenterEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondragoverAttr) { + setAttributeEventListener(eventNames().dragoverEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondragleaveAttr) { + setAttributeEventListener(eventNames().dragleaveEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondropAttr) { + setAttributeEventListener(eventNames().dropEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondragstartAttr) { + setAttributeEventListener(eventNames().dragstartEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondragAttr) { + setAttributeEventListener(eventNames().dragEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ondragendAttr) { + setAttributeEventListener(eventNames().dragendEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onselectstartAttr) { + setAttributeEventListener(eventNames().selectstartEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onsubmitAttr) { + setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onerrorAttr) { + setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onwebkitanimationstartAttr) { + setAttributeEventListener(eventNames().webkitAnimationStartEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onwebkitanimationiterationAttr) { + setAttributeEventListener(eventNames().webkitAnimationIterationEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onwebkitanimationendAttr) { + setAttributeEventListener(eventNames().webkitAnimationEndEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == onwebkittransitionendAttr) { + setAttributeEventListener(eventNames().webkitTransitionEndEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == oninputAttr) { + setAttributeEventListener(eventNames().inputEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == oninvalidAttr) { + setAttributeEventListener(eventNames().invalidEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ontouchstartAttr) { + setAttributeEventListener(eventNames().touchstartEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ontouchmoveAttr) { + setAttributeEventListener(eventNames().touchmoveEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ontouchendAttr) { + setAttributeEventListener(eventNames().touchendEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == ontouchcancelAttr) { + setAttributeEventListener(eventNames().touchcancelEvent, createAttributeEventListener(this, attr)); +#if ENABLE(FULLSCREEN_API) + } else if (attr->name() == onwebkitfullscreenchangeAttr) { + setAttributeEventListener(eventNames().webkitfullscreenchangeEvent, createAttributeEventListener(this, attr)); +#endif + } +} + +String HTMLElement::innerHTML() const +{ + return createMarkup(this, ChildrenOnly); +} + +String HTMLElement::outerHTML() const +{ + return createMarkup(this); +} + +static inline bool hasOneChild(ContainerNode* node) +{ + Node* firstChild = node->firstChild(); + return firstChild && !firstChild->nextSibling(); +} + +static inline bool hasOneTextChild(ContainerNode* node) +{ + return hasOneChild(node) && node->firstChild()->isTextNode(); +} + +static void replaceChildrenWithFragment(HTMLElement* element, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec) +{ +#if ENABLE(MUTATION_OBSERVERS) + ChildListMutationScope mutation(element); +#endif + + if (!fragment->firstChild()) { + element->removeChildren(); + return; + } + + if (hasOneTextChild(element) && hasOneTextChild(fragment.get())) { + static_cast<Text*>(element->firstChild())->setData(static_cast<Text*>(fragment->firstChild())->data(), ec); + return; + } + + if (hasOneChild(element)) { + element->replaceChild(fragment, element->firstChild(), ec); + return; + } + + element->removeChildren(); + element->appendChild(fragment, ec); +} + +static void replaceChildrenWithText(HTMLElement* element, const String& text, ExceptionCode& ec) +{ +#if ENABLE(MUTATION_OBSERVERS) + ChildListMutationScope mutation(element); +#endif + + if (hasOneTextChild(element)) { + static_cast<Text*>(element->firstChild())->setData(text, ec); + return; + } + + RefPtr<Text> textNode = Text::create(element->document(), text); + + if (hasOneChild(element)) { + element->replaceChild(textNode.release(), element->firstChild(), ec); + return; + } + + element->removeChildren(); + element->appendChild(textNode.release(), ec); +} + +// We may want to move a version of this function into DocumentFragment.h/cpp +static PassRefPtr<DocumentFragment> createFragmentFromSource(const String& markup, Element* contextElement, ExceptionCode& ec) +{ + Document* document = contextElement->document(); + RefPtr<DocumentFragment> fragment; + + fragment = DocumentFragment::create(document); + if (document->isHTMLDocument()) { + fragment->parseHTML(markup, contextElement); + return fragment; + } + + bool wasValid = fragment->parseXML(markup, contextElement); + if (!wasValid) { + ec = INVALID_STATE_ERR; + return 0; + } + return fragment; +} + +void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec) +{ + RefPtr<DocumentFragment> fragment = createFragmentFromSource(html, this, ec); + if (fragment) + replaceChildrenWithFragment(this, fragment.release(), ec); +} + +static void mergeWithNextTextNode(PassRefPtr<Node> node, ExceptionCode& ec) +{ + ASSERT(node && node->isTextNode()); + Node* next = node->nextSibling(); + if (!next || !next->isTextNode()) + return; + + RefPtr<Text> textNode = static_cast<Text*>(node.get()); + RefPtr<Text> textNext = static_cast<Text*>(next); + textNode->appendData(textNext->data(), ec); + if (ec) + return; + if (textNext->parentNode()) // Might have been removed by mutation event. + textNext->remove(ec); +} + +void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec) +{ + Node* p = parentNode(); + if (!p || !p->isHTMLElement()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + RefPtr<HTMLElement> parent = toHTMLElement(p); + RefPtr<Node> prev = previousSibling(); + RefPtr<Node> next = nextSibling(); + + RefPtr<DocumentFragment> fragment = createFragmentFromSource(html, parent.get(), ec); + if (ec) + return; + + parent->replaceChild(fragment.release(), this, ec); + RefPtr<Node> node = next ? next->previousSibling() : 0; + if (!ec && node && node->isTextNode()) + mergeWithNextTextNode(node.release(), ec); + + if (!ec && prev && prev->isTextNode()) + mergeWithNextTextNode(prev.release(), ec); +} + +PassRefPtr<DocumentFragment> HTMLElement::textToFragment(const String& text, ExceptionCode& ec) +{ + RefPtr<DocumentFragment> fragment = DocumentFragment::create(document()); + unsigned int i, length = text.length(); + UChar c = 0; + for (unsigned int start = 0; start < length; ) { + + // Find next line break. + for (i = start; i < length; i++) { + c = text[i]; + if (c == '\r' || c == '\n') + break; + } + + fragment->appendChild(Text::create(document(), text.substring(start, i - start)), ec); + if (ec) + return 0; + + if (c == '\r' || c == '\n') { + fragment->appendChild(HTMLBRElement::create(document()), ec); + if (ec) + return 0; + // Make sure \r\n doesn't result in two line breaks. + if (c == '\r' && i + 1 < length && text[i + 1] == '\n') + i++; + } + + start = i + 1; // Character after line break. + } + + return fragment; +} + +void HTMLElement::setInnerText(const String& text, ExceptionCode& ec) +{ + if (ieForbidsInsertHTML()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || + hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || + hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) || + hasLocalName(trTag)) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + // FIXME: This doesn't take whitespace collapsing into account at all. + + if (!text.contains('\n') && !text.contains('\r')) { + if (text.isEmpty()) { + removeChildren(); + return; + } + replaceChildrenWithText(this, text, ec); + return; + } + + // FIXME: Do we need to be able to detect preserveNewline style even when there's no renderer? + // FIXME: Can the renderer be out of date here? Do we need to call updateStyleIfNeeded? + // For example, for the contents of textarea elements that are display:none? + RenderObject* r = renderer(); + if (r && r->style()->preserveNewline()) { + if (!text.contains('\r')) { + replaceChildrenWithText(this, text, ec); + return; + } + String textWithConsistentLineBreaks = text; + textWithConsistentLineBreaks.replace("\r\n", "\n"); + textWithConsistentLineBreaks.replace('\r', '\n'); + replaceChildrenWithText(this, textWithConsistentLineBreaks, ec); + return; + } + + // Add text nodes and <br> elements. + ec = 0; + RefPtr<DocumentFragment> fragment = textToFragment(text, ec); + if (!ec) + replaceChildrenWithFragment(this, fragment.release(), ec); +} + +void HTMLElement::setOuterText(const String &text, ExceptionCode& ec) +{ + if (ieForbidsInsertHTML()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || + hasLocalName(headTag) || hasLocalName(htmlTag) || hasLocalName(tableTag) || + hasLocalName(tbodyTag) || hasLocalName(tfootTag) || hasLocalName(theadTag) || + hasLocalName(trTag)) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + ContainerNode* parent = parentNode(); + if (!parent) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return; + } + + RefPtr<Node> prev = previousSibling(); + RefPtr<Node> next = nextSibling(); + RefPtr<Node> newChild; + ec = 0; + + // Convert text to fragment with <br> tags instead of linebreaks if needed. + if (text.contains('\r') || text.contains('\n')) + newChild = textToFragment(text, ec); + else + newChild = Text::create(document(), text); + + if (!this || !parentNode()) + ec = HIERARCHY_REQUEST_ERR; + if (ec) + return; + parent->replaceChild(newChild.release(), this, ec); + + RefPtr<Node> node = next ? next->previousSibling() : 0; + if (!ec && node && node->isTextNode()) + mergeWithNextTextNode(node.release(), ec); + + if (!ec && prev && prev->isTextNode()) + mergeWithNextTextNode(prev.release(), ec); +} + +Node* HTMLElement::insertAdjacent(const String& where, Node* newChild, ExceptionCode& ec) +{ + // In Internet Explorer if the element has no parent and where is "beforeBegin" or "afterEnd", + // a document fragment is created and the elements appended in the correct order. This document + // fragment isn't returned anywhere. + // + // This is impossible for us to implement as the DOM tree does not allow for such structures, + // Opera also appears to disallow such usage. + + if (equalIgnoringCase(where, "beforeBegin")) { + ContainerNode* parent = this->parentNode(); + return (parent && parent->insertBefore(newChild, this, ec)) ? newChild : 0; + } + + if (equalIgnoringCase(where, "afterBegin")) + return insertBefore(newChild, firstChild(), ec) ? newChild : 0; + + if (equalIgnoringCase(where, "beforeEnd")) + return appendChild(newChild, ec) ? newChild : 0; + + if (equalIgnoringCase(where, "afterEnd")) { + ContainerNode* parent = this->parentNode(); + return (parent && parent->insertBefore(newChild, nextSibling(), ec)) ? newChild : 0; + } + + // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative. + ec = NOT_SUPPORTED_ERR; + return 0; +} + +Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChild, ExceptionCode& ec) +{ + if (!newChild) { + // IE throws COM Exception E_INVALIDARG; this is the best DOM exception alternative. + ec = TYPE_MISMATCH_ERR; + return 0; + } + + Node* returnValue = insertAdjacent(where, newChild, ec); + ASSERT(!returnValue || returnValue->isElementNode()); + return static_cast<Element*>(returnValue); +} + +// Step 3 of http://www.whatwg.org/specs/web-apps/current-work/multipage/apis-in-html-documents.html#insertadjacenthtml() +static Element* contextElementForInsertion(const String& where, Element* element, ExceptionCode& ec) +{ + if (equalIgnoringCase(where, "beforeBegin") || equalIgnoringCase(where, "afterEnd")) { + ContainerNode* parent = element->parentNode(); + if (parent && parent->isDocumentNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + ASSERT(!parent || parent->isElementNode()); + return static_cast<Element*>(parent); + } + if (equalIgnoringCase(where, "afterBegin") || equalIgnoringCase(where, "beforeEnd")) + return element; + ec = SYNTAX_ERR; + return 0; +} + +void HTMLElement::insertAdjacentHTML(const String& where, const String& markup, ExceptionCode& ec) +{ + RefPtr<DocumentFragment> fragment = document()->createDocumentFragment(); + Element* contextElement = contextElementForInsertion(where, this, ec); + if (!contextElement) + return; + + if (document()->isHTMLDocument()) + fragment->parseHTML(markup, contextElement); + else { + if (!fragment->parseXML(markup, contextElement)) + // FIXME: We should propagate a syntax error exception out here. + return; + } + + insertAdjacent(where, fragment.get(), ec); +} + +void HTMLElement::insertAdjacentText(const String& where, const String& text, ExceptionCode& ec) +{ + RefPtr<Text> textNode = document()->createTextNode(text); + insertAdjacent(where, textNode.get(), ec); +} + +void HTMLElement::addHTMLAlignment(Attribute* attr) +{ + addHTMLAlignmentToStyledElement(this, attr); +} + +void HTMLElement::addHTMLAlignmentToStyledElement(StyledElement* element, Attribute* attr) +{ + // Vertical alignment with respect to the current baseline of the text + // right or left means floating images. + int floatValue = CSSValueInvalid; + int verticalAlignValue = CSSValueInvalid; + + const AtomicString& alignment = attr->value(); + if (equalIgnoringCase(alignment, "absmiddle")) + verticalAlignValue = CSSValueMiddle; + else if (equalIgnoringCase(alignment, "absbottom")) + verticalAlignValue = CSSValueBottom; + else if (equalIgnoringCase(alignment, "left")) { + floatValue = CSSValueLeft; + verticalAlignValue = CSSValueTop; + } else if (equalIgnoringCase(alignment, "right")) { + floatValue = CSSValueRight; + verticalAlignValue = CSSValueTop; + } else if (equalIgnoringCase(alignment, "top")) + verticalAlignValue = CSSValueTop; + else if (equalIgnoringCase(alignment, "middle")) + verticalAlignValue = CSSValueWebkitBaselineMiddle; + else if (equalIgnoringCase(alignment, "center")) + verticalAlignValue = CSSValueMiddle; + else if (equalIgnoringCase(alignment, "bottom")) + verticalAlignValue = CSSValueBaseline; + else if (equalIgnoringCase(alignment, "texttop")) + verticalAlignValue = CSSValueTextTop; + + if (floatValue != CSSValueInvalid) + element->addCSSProperty(attr, CSSPropertyFloat, floatValue); + + if (verticalAlignValue != CSSValueInvalid) + element->addCSSProperty(attr, CSSPropertyVerticalAlign, verticalAlignValue); +} + +bool HTMLElement::supportsFocus() const +{ + return Element::supportsFocus() || (rendererIsEditable() && parentNode() && !parentNode()->rendererIsEditable()); +} + +String HTMLElement::contentEditable() const +{ + const AtomicString& value = fastGetAttribute(contenteditableAttr); + + if (value.isNull()) + return "inherit"; + if (value.isEmpty() || equalIgnoringCase(value, "true")) + return "true"; + if (equalIgnoringCase(value, "false")) + return "false"; + if (equalIgnoringCase(value, "plaintext-only")) + return "plaintext-only"; + + return "inherit"; +} + +void HTMLElement::setContentEditable(Attribute* attr) +{ + const AtomicString& enabled = attr->value(); + if (enabled.isEmpty() || equalIgnoringCase(enabled, "true")) { + addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWrite); + addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord); + addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace); + addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace); + } else if (equalIgnoringCase(enabled, "false")) { + addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadOnly); + removeCSSProperty(attr, CSSPropertyWordWrap); + removeCSSProperty(attr, CSSPropertyWebkitNbspMode); + removeCSSProperty(attr, CSSPropertyWebkitLineBreak); + } else if (equalIgnoringCase(enabled, "plaintext-only")) { + addCSSProperty(attr, CSSPropertyWebkitUserModify, CSSValueReadWritePlaintextOnly); + addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord); + addCSSProperty(attr, CSSPropertyWebkitNbspMode, CSSValueSpace); + addCSSProperty(attr, CSSPropertyWebkitLineBreak, CSSValueAfterWhiteSpace); + } +} + +void HTMLElement::setContentEditable(const String& enabled, ExceptionCode& ec) +{ + if (equalIgnoringCase(enabled, "true")) + setAttribute(contenteditableAttr, "true"); + else if (equalIgnoringCase(enabled, "false")) + setAttribute(contenteditableAttr, "false"); + else if (equalIgnoringCase(enabled, "plaintext-only")) + setAttribute(contenteditableAttr, "plaintext-only"); + else if (equalIgnoringCase(enabled, "inherit")) + removeAttribute(contenteditableAttr); + else + ec = SYNTAX_ERR; +} + +bool HTMLElement::draggable() const +{ + return equalIgnoringCase(getAttribute(draggableAttr), "true"); +} + +void HTMLElement::setDraggable(bool value) +{ + setAttribute(draggableAttr, value ? "true" : "false"); +} + +bool HTMLElement::spellcheck() const +{ + return isSpellCheckingEnabled(); +} + +void HTMLElement::setSpellcheck(bool enable) +{ + setAttribute(spellcheckAttr, enable ? "true" : "false"); +} + + +void HTMLElement::click() +{ + dispatchSimulatedClick(0, false, false); +} + +void HTMLElement::accessKeyAction(bool sendMouseEvents) +{ + dispatchSimulatedClick(0, sendMouseEvents); +} + +String HTMLElement::title() const +{ + return getAttribute(titleAttr); +} + +short HTMLElement::tabIndex() const +{ + if (supportsFocus()) + return Element::tabIndex(); + return -1; +} + +void HTMLElement::setTabIndex(int value) +{ + setAttribute(tabindexAttr, String::number(value)); +} + +PassRefPtr<HTMLCollection> HTMLElement::children() +{ + return ensureCachedHTMLCollection(NodeChildren); +} + +bool HTMLElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + if (hasLocalName(noscriptTag)) { + Frame* frame = document()->frame(); + if (frame && frame->script()->canExecuteScripts(NotAboutToExecuteScript)) + return false; + } else if (hasLocalName(noembedTag)) { + Frame* frame = document()->frame(); + if (frame && frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) + return false; + } + return StyledElement::rendererIsNeeded(context); +} + +RenderObject* HTMLElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + if (hasLocalName(wbrTag)) + return new (arena) RenderWordBreak(this); + return RenderObject::createObject(this, style); +} + +HTMLFormElement* HTMLElement::findFormAncestor() const +{ + for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor->hasTagName(formTag)) + return static_cast<HTMLFormElement*>(ancestor); + } + return 0; +} + +HTMLFormElement* HTMLElement::virtualForm() const +{ + return findFormAncestor(); +} + +static void setHasDirAutoFlagRecursively(Node* firstNode, bool flag, Node* lastNode = 0) +{ + firstNode->setSelfOrAncestorHasDirAutoAttribute(flag); + + Node* node = firstNode->firstChild(); + + while (node) { + if (node->selfOrAncestorHasDirAutoAttribute() == flag) + return; + + if (node->isHTMLElement() && toElement(node)->hasAttribute(dirAttr)) { + if (node == lastNode) + return; + node = node->traverseNextSibling(firstNode); + continue; + } + node->setSelfOrAncestorHasDirAutoAttribute(flag); + if (node == lastNode) + return; + node = node->traverseNextNode(firstNode); + } +} + +void HTMLElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + StyledElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + adjustDirectionalityIfNeededAfterChildrenChanged(beforeChange, childCountDelta); +} + +TextDirection HTMLElement::directionalityIfhasDirAutoAttribute(bool& isAuto) const +{ + if (!(selfOrAncestorHasDirAutoAttribute() && equalIgnoringCase(getAttribute(dirAttr), "auto"))) { + isAuto = false; + return LTR; + } + + isAuto = true; + return directionality(); +} + +TextDirection HTMLElement::directionality(Node** strongDirectionalityTextNode) const +{ + if (HTMLTextFormControlElement* textElement = toTextFormControl(const_cast<HTMLElement*>(this))) { + bool hasStrongDirectionality; + Unicode::Direction textDirection = textElement->value().defaultWritingDirection(&hasStrongDirectionality); + if (strongDirectionalityTextNode) + *strongDirectionalityTextNode = hasStrongDirectionality ? textElement : 0; + return (textDirection == Unicode::LeftToRight) ? LTR : RTL; + } + + Node* node = firstChild(); + while (node) { + // Skip bdi, script, style and text form controls. + if (equalIgnoringCase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag) + || (node->isElementNode() && toElement(node)->isTextFormControl())) { + node = node->traverseNextSibling(this); + continue; + } + + // Skip elements with valid dir attribute + if (node->isElementNode()) { + AtomicString dirAttributeValue = toElement(node)->fastGetAttribute(dirAttr); + if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr") || equalIgnoringCase(dirAttributeValue, "auto")) { + node = node->traverseNextSibling(this); + continue; + } + } + + if (node->isTextNode()) { + bool hasStrongDirectionality; + WTF::Unicode::Direction textDirection = node->textContent(true).defaultWritingDirection(&hasStrongDirectionality); + if (hasStrongDirectionality) { + if (strongDirectionalityTextNode) + *strongDirectionalityTextNode = node; + return (textDirection == WTF::Unicode::LeftToRight) ? LTR : RTL; + } + } + node = node->traverseNextNode(this); + } + if (strongDirectionalityTextNode) + *strongDirectionalityTextNode = 0; + return LTR; +} + +void HTMLElement::dirAttributeChanged(Attribute* attribute) +{ + Element* parent = parentElement(); + + if (parent && parent->isHTMLElement() && parent->selfOrAncestorHasDirAutoAttribute()) + toHTMLElement(parent)->adjustDirectionalityIfNeededAfterChildAttributeChanged(this); + + if (equalIgnoringCase(attribute->value(), "auto")) + calculateAndAdjustDirectionality(); +} + +void HTMLElement::adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child) +{ + ASSERT(selfOrAncestorHasDirAutoAttribute()); + Node* strongDirectionalityTextNode; + TextDirection textDirection = directionality(&strongDirectionalityTextNode); + setHasDirAutoFlagRecursively(child, false); + if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection) { + Element* elementToAdjust = this; + for (; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) { + if (elementToAdjust->hasAttribute(dirAttr)) { + elementToAdjust->setNeedsStyleRecalc(); + return; + } + } + } +} + +void HTMLElement::calculateAndAdjustDirectionality() +{ + Node* strongDirectionalityTextNode; + TextDirection textDirection = directionality(&strongDirectionalityTextNode); + setHasDirAutoFlagRecursively(this, true, strongDirectionalityTextNode); + if (renderer() && renderer()->style() && renderer()->style()->direction() != textDirection) + setNeedsStyleRecalc(); +} + +void HTMLElement::adjustDirectionalityIfNeededAfterChildrenChanged(Node* beforeChange, int childCountDelta) +{ + if ((!document() || document()->renderer()) && childCountDelta < 0) { + Node* node = beforeChange ? beforeChange->traverseNextSibling() : 0; + for (int counter = 0; node && counter < childCountDelta; counter++, node = node->traverseNextSibling()) { + if (node->isElementNode() && toElement(node)->hasAttribute(dirAttr)) + continue; + + setHasDirAutoFlagRecursively(node, false); + } + } + + if (!selfOrAncestorHasDirAutoAttribute()) + return; + + Node* oldMarkedNode = beforeChange ? beforeChange->traverseNextSibling() : 0; + while (oldMarkedNode && oldMarkedNode->isHTMLElement() && toHTMLElement(oldMarkedNode)->hasAttribute(dirAttr)) + oldMarkedNode = oldMarkedNode->traverseNextSibling(this); + if (oldMarkedNode) + setHasDirAutoFlagRecursively(oldMarkedNode, false); + + for (Element* elementToAdjust = this; elementToAdjust; elementToAdjust = elementToAdjust->parentElement()) { + if (elementToAdjust->isHTMLElement() && elementToAdjust->hasAttribute(dirAttr)) { + toHTMLElement(elementToAdjust)->calculateAndAdjustDirectionality(); + return; + } + } +} + +bool HTMLElement::isURLAttribute(Attribute* attribute) const +{ +#if ENABLE(MICRODATA) + return attribute->name() == itemidAttr; +#else + UNUSED_PARAM(attribute); + return false; +#endif +} + +#if ENABLE(MICRODATA) +void HTMLElement::setItemValue(const String& value, ExceptionCode& ec) +{ + if (!hasAttribute(itempropAttr) || hasAttribute(itemscopeAttr)) { + ec = INVALID_ACCESS_ERR; + return; + } + + setItemValueText(value, ec); +} + +PassRefPtr<MicroDataItemValue> HTMLElement::itemValue() const +{ + if (!hasAttribute(itempropAttr)) + return 0; + + if (hasAttribute(itemscopeAttr)) + return MicroDataItemValue::createFromNode(const_cast<HTMLElement* const>(this)); + + return MicroDataItemValue::createFromString(itemValueText()); +} + +String HTMLElement::itemValueText() const +{ + return textContent(true); +} + +void HTMLElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setTextContent(value, ec); +} +#endif + +} // namespace WebCore + +#ifndef NDEBUG + +// For use in the debugger +void dumpInnerHTML(WebCore::HTMLElement*); + +void dumpInnerHTML(WebCore::HTMLElement* element) +{ + printf("%s\n", element->innerHTML().ascii().data()); +} +#endif diff --git a/Source/WebCore/html/HTMLElement.h b/Source/WebCore/html/HTMLElement.h new file mode 100644 index 000000000..b98b2ddc4 --- /dev/null +++ b/Source/WebCore/html/HTMLElement.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2009 Apple 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 HTMLElement_h +#define HTMLElement_h + +#include "StyledElement.h" + +namespace WebCore { + +class DocumentFragment; +class HTMLCollection; +class HTMLFormElement; + +#if ENABLE(MICRODATA) +class MicroDataItemValue; +#endif + +class HTMLElement : public StyledElement { +public: + static PassRefPtr<HTMLElement> create(const QualifiedName& tagName, Document*); + + PassRefPtr<HTMLCollection> children(); + + virtual String title() const; + + virtual short tabIndex() const; + void setTabIndex(int); + + String innerHTML() const; + String outerHTML() const; + void setInnerHTML(const String&, ExceptionCode&); + void setOuterHTML(const String&, ExceptionCode&); + void setInnerText(const String&, ExceptionCode&); + void setOuterText(const String&, ExceptionCode&); + + Element* insertAdjacentElement(const String& where, Element* newChild, ExceptionCode&); + void insertAdjacentHTML(const String& where, const String& html, ExceptionCode&); + void insertAdjacentText(const String& where, const String& text, ExceptionCode&); + + virtual bool supportsFocus() const; + + String contentEditable() const; + void setContentEditable(const String&, ExceptionCode&); + + virtual bool draggable() const; + void setDraggable(bool); + + bool spellcheck() const; + void setSpellcheck(bool); + + void click(); + + virtual void accessKeyAction(bool sendMouseEvents); + + bool ieForbidsInsertHTML() const; + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + HTMLFormElement* form() const { return virtualForm(); } + + static void addHTMLAlignmentToStyledElement(StyledElement*, Attribute*); + + HTMLFormElement* findFormAncestor() const; + + TextDirection directionalityIfhasDirAutoAttribute(bool& isAuto) const; + +#if ENABLE(MICRODATA) + void setItemValue(const String&, ExceptionCode&); + PassRefPtr<MicroDataItemValue> itemValue() const; +#endif + +protected: + HTMLElement(const QualifiedName& tagName, Document*); + + void addHTMLAlignment(Attribute*); + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(Attribute*); + void applyBorderAttribute(Attribute*); + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + void calculateAndAdjustDirectionality(); + + virtual bool isURLAttribute(Attribute*) const; + +private: + virtual String nodeName() const; + + void mapLanguageAttributeToLocale(Attribute*); + + void setContentEditable(Attribute*); + + virtual HTMLFormElement* virtualForm() const; + + Node* insertAdjacent(const String& where, Node* newChild, ExceptionCode&); + PassRefPtr<DocumentFragment> textToFragment(const String&, ExceptionCode&); + + void dirAttributeChanged(Attribute*); + void adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child); + void adjustDirectionalityIfNeededAfterChildrenChanged(Node* beforeChange, int childCountDelta); + TextDirection directionality(Node** strongDirectionalityTextNode= 0) const; + +#if ENABLE(MICRODATA) + virtual String itemValueText() const; + virtual void setItemValueText(const String&, ExceptionCode&); +#endif +}; + +inline HTMLElement* toHTMLElement(Node* node) +{ + ASSERT(!node || node->isHTMLElement()); + return static_cast<HTMLElement*>(node); +} + +inline const HTMLElement* toHTMLElement(const Node* node) +{ + ASSERT(!node || node->isHTMLElement()); + return static_cast<const HTMLElement*>(node); +} + +// This will catch anyone doing an unnecessary cast. +void toHTMLElement(const HTMLElement*); + +inline HTMLElement::HTMLElement(const QualifiedName& tagName, Document* document) + : StyledElement(tagName, document, CreateHTMLElement) +{ + ASSERT(tagName.localName().impl()); +} + +} // namespace WebCore + +#endif // HTMLElement_h diff --git a/Source/WebCore/html/HTMLElement.idl b/Source/WebCore/html/HTMLElement.idl new file mode 100644 index 000000000..7257755ae --- /dev/null +++ b/Source/WebCore/html/HTMLElement.idl @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + GenerateNativeConverter, + CustomPushEventHandlerScope + ] HTMLElement : Element { + // iht.com relies on id returning the empty string when no id is present. + // Other browsers do this as well. So we don't convert null to JS null. + attribute [Reflect] DOMString id; + attribute [Reflect] DOMString title; + attribute [Reflect] DOMString lang; + attribute [Reflect] DOMString dir; + attribute [Reflect=class] DOMString className; + readonly attribute DOMTokenList classList; + + attribute long tabIndex; + attribute boolean draggable; + attribute [Reflect] DOMString webkitdropzone; + attribute [Reflect] boolean hidden; + attribute [Reflect] DOMString accessKey; + + // Extensions + attribute [ConvertNullToNullString] DOMString innerHTML + setter raises(DOMException); + attribute [ConvertNullToNullString] DOMString innerText + setter raises(DOMException); + attribute [ConvertNullToNullString] DOMString outerHTML + setter raises(DOMException); + attribute [ConvertNullToNullString] DOMString outerText + setter raises(DOMException); + + Element insertAdjacentElement(in [Optional=CallWithDefaultValue] DOMString where, + in [Optional=CallWithDefaultValue] Element element) + raises(DOMException); + void insertAdjacentHTML(in [Optional=CallWithDefaultValue] DOMString where, + in [Optional=CallWithDefaultValue] DOMString html) + raises(DOMException); + void insertAdjacentText(in [Optional=CallWithDefaultValue] DOMString where, + in [Optional=CallWithDefaultValue] DOMString text) + raises(DOMException); + + readonly attribute HTMLCollection children; + + attribute [ConvertNullToNullString] DOMString contentEditable + setter raises(DOMException); + readonly attribute boolean isContentEditable; + + attribute boolean spellcheck; + + attribute [Conditional=MICRODATA, Reflect] boolean itemScope; + readonly attribute [Conditional=MICRODATA] DOMSettableTokenList itemType; + attribute [Conditional=MICRODATA, Reflect, URL] DOMString itemId; + + readonly attribute [Conditional=MICRODATA] DOMSettableTokenList itemRef; + readonly attribute [Conditional=MICRODATA] DOMSettableTokenList itemProp; + +#if defined(ENABLE_MICRODATA) && ENABLE_MICRODATA + readonly attribute [Conditional=MICRODATA] HTMLPropertiesCollection properties; +#endif + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + attribute [Conditional=MICRODATA, Custom] DOMObject itemValue + setter raises(DOMException); +#endif +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + readonly attribute DOMString titleDisplayString; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLElementsAllInOne.cpp b/Source/WebCore/html/HTMLElementsAllInOne.cpp new file mode 100644 index 000000000..b624e627b --- /dev/null +++ b/Source/WebCore/html/HTMLElementsAllInOne.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// This source file coalesces the HTML elements into a single object file to +// reduce bloat and allow us to link release builds on 32-bit Windows. + +#include "HTMLAnchorElement.cpp" +#include "HTMLAppletElement.cpp" +#include "HTMLAreaElement.cpp" +#include "HTMLAudioElement.cpp" +#include "HTMLBRElement.cpp" +#include "HTMLBaseElement.cpp" +#include "HTMLBaseFontElement.cpp" +#include "HTMLBodyElement.cpp" +#include "HTMLButtonElement.cpp" +#include "HTMLCanvasElement.cpp" +#include "HTMLDataListElement.cpp" +#include "HTMLDetailsElement.cpp" +#include "HTMLDListElement.cpp" +#include "HTMLDirectoryElement.cpp" +#include "HTMLDivElement.cpp" +#include "HTMLElement.cpp" +#include "HTMLEmbedElement.cpp" +#include "HTMLFieldSetElement.cpp" +#include "HTMLFontElement.cpp" +#include "HTMLFormControlElement.cpp" +#include "HTMLFormControlElementWithState.cpp" +#include "HTMLFormElement.cpp" +#include "HTMLFrameElement.cpp" +#include "HTMLFrameElementBase.cpp" +#include "HTMLFrameOwnerElement.cpp" +#include "HTMLFrameSetElement.cpp" +#include "HTMLHRElement.cpp" +#include "HTMLHeadElement.cpp" +#include "HTMLHeadingElement.cpp" +#include "HTMLHtmlElement.cpp" +#include "HTMLIFrameElement.cpp" +#include "HTMLImageElement.cpp" +#include "HTMLInputElement.cpp" +#include "HTMLIsIndexElement.cpp" +#include "HTMLKeygenElement.cpp" +#include "HTMLLIElement.cpp" +#include "HTMLLabelElement.cpp" +#include "HTMLLegendElement.cpp" +#include "HTMLLinkElement.cpp" +#include "HTMLMapElement.cpp" +#include "HTMLMarqueeElement.cpp" +#include "HTMLMediaElement.cpp" +#include "HTMLMenuElement.cpp" +#include "HTMLMetaElement.cpp" +#include "HTMLMeterElement.cpp" +#include "HTMLModElement.cpp" +#include "HTMLOListElement.cpp" +#include "HTMLObjectElement.cpp" +#include "HTMLOptGroupElement.cpp" +#include "HTMLOptionElement.cpp" +#include "HTMLParagraphElement.cpp" +#include "HTMLParamElement.cpp" +#include "HTMLPlugInElement.cpp" +#include "HTMLPlugInImageElement.cpp" +#include "HTMLPreElement.cpp" +#include "HTMLProgressElement.cpp" +#include "HTMLQuoteElement.cpp" +#include "HTMLScriptElement.cpp" +#include "HTMLSelectElement.cpp" +#include "HTMLSourceElement.cpp" +#include "HTMLSpanElement.cpp" +#include "HTMLStyleElement.cpp" +#include "HTMLSummaryElement.cpp" +#include "HTMLTableCaptionElement.cpp" +#include "HTMLTableCellElement.cpp" +#include "HTMLTableColElement.cpp" +#include "HTMLTableElement.cpp" +#include "HTMLTablePartElement.cpp" +#include "HTMLTableRowElement.cpp" +#include "HTMLTableSectionElement.cpp" +#include "HTMLTextAreaElement.cpp" +#include "HTMLTextFormControlElement.cpp" +#include "HTMLTitleElement.cpp" +#include "HTMLUListElement.cpp" +#include "HTMLVideoElement.cpp" diff --git a/Source/WebCore/html/HTMLEmbedElement.cpp b/Source/WebCore/html/HTMLEmbedElement.cpp new file mode 100644 index 000000000..d0bd344f2 --- /dev/null +++ b/Source/WebCore/html/HTMLEmbedElement.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008, 2009, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "HTMLEmbedElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "HTMLDocument.h" +#include "HTMLImageLoader.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLParserIdioms.h" +#include "MainResourceLoader.h" +#include "PluginDocument.h" +#include "RenderEmbeddedObject.h" +#include "RenderImage.h" +#include "RenderWidget.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLEmbedElement::HTMLEmbedElement(const QualifiedName& tagName, Document* document, bool createdByParser) + : HTMLPlugInImageElement(tagName, document, createdByParser, ShouldPreferPlugInsForImages) +{ + ASSERT(hasTagName(embedTag)); +} + +PassRefPtr<HTMLEmbedElement> HTMLEmbedElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) +{ + return adoptRef(new HTMLEmbedElement(tagName, document, createdByParser)); +} + +static inline RenderWidget* findWidgetRenderer(const Node* n) +{ + if (!n->renderer()) + do + n = n->parentNode(); + while (n && !n->hasTagName(objectTag)); + + if (n && n->renderer() && n->renderer()->isWidget()) + return toRenderWidget(n->renderer()); + + return 0; +} + +RenderWidget* HTMLEmbedElement::renderWidgetForJSBindings() +{ + document()->updateLayoutIgnorePendingStylesheets(); + return findWidgetRenderer(this); +} + +bool HTMLEmbedElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == hiddenAttr) { + result = eUniversal; + return false; + } + + return HTMLPlugInImageElement::mapToEntry(attrName, result); +} + +void HTMLEmbedElement::parseMappedAttribute(Attribute* attr) +{ + const AtomicString& value = attr->value(); + + if (attr->name() == typeAttr) { + m_serviceType = value.string().lower(); + size_t pos = m_serviceType.find(";"); + if (pos != notFound) + m_serviceType = m_serviceType.left(pos); + if (!isImageType() && m_imageLoader) + m_imageLoader.clear(); + } else if (attr->name() == codeAttr) + m_url = stripLeadingAndTrailingHTMLSpaces(value.string()); + else if (attr->name() == srcAttr) { + m_url = stripLeadingAndTrailingHTMLSpaces(value.string()); + if (renderer() && isImageType()) { + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(this)); + m_imageLoader->updateFromElementIgnoringPreviousError(); + } + } else if (attr->name() == hiddenAttr) { + if (equalIgnoringCase(value.string(), "yes") || equalIgnoringCase(value.string(), "true")) { + // FIXME: Not dynamic, since we add this but don't remove it, but it may be OK for now + // that this rarely-used attribute won't work properly if you remove it. + addCSSLength(attr, CSSPropertyWidth, "0"); + addCSSLength(attr, CSSPropertyHeight, "0"); + } + } else + HTMLPlugInImageElement::parseMappedAttribute(attr); +} + +void HTMLEmbedElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues) +{ + NamedNodeMap* attributes = this->attributes(true); + if (!attributes) + return; + + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + paramNames.append(it->localName().string()); + paramValues.append(it->value().string()); + } +} + +// FIXME: This should be unified with HTMLObjectElement::updateWidget and +// moved down into HTMLPluginImageElement.cpp +void HTMLEmbedElement::updateWidget(PluginCreationOption pluginCreationOption) +{ + ASSERT(!renderEmbeddedObject()->pluginCrashedOrWasMissing()); + ASSERT(needsWidgetUpdate()); + setNeedsWidgetUpdate(false); + + if (m_url.isEmpty() && m_serviceType.isEmpty()) + return; + + // Note these pass m_url and m_serviceType to allow better code sharing with + // <object> which modifies url and serviceType before calling these. + if (!allowedToLoadFrameURL(m_url)) + return; + // FIXME: It's sadness that we have this special case here. + // See http://trac.webkit.org/changeset/25128 and + // plugins/netscape-plugin-setwindow-size.html + if (pluginCreationOption == CreateOnlyNonNetscapePlugins && wouldLoadAsNetscapePlugin(m_url, m_serviceType)) + return; + + // FIXME: These should be joined into a PluginParameters class. + Vector<String> paramNames; + Vector<String> paramValues; + parametersForPlugin(paramNames, paramValues); + + ASSERT(!m_inBeforeLoadEventHandler); + m_inBeforeLoadEventHandler = true; + bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(m_url); + m_inBeforeLoadEventHandler = false; + + if (!beforeLoadAllowedLoad) { + if (document()->isPluginDocument()) { + // Plugins inside plugin documents load differently than other plugins. By the time + // we are here in a plugin document, the load of the plugin (which is the plugin document's + // main resource) has already started. We need to explicitly cancel the main resource load here. + toPluginDocument(document())->cancelManualPluginLoad(); + } + return; + } + + RefPtr<HTMLEmbedElement> protect(this); // Loading the plugin might remove us from the document. + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + // FIXME: beforeLoad could have detached the renderer! Just like in the <object> case above. + loader->requestObject(this, m_url, getAttribute(nameAttr), m_serviceType, paramNames, paramValues); +} + +bool HTMLEmbedElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + if (isImageType()) + return HTMLPlugInImageElement::rendererIsNeeded(context); + + Frame* frame = document()->frame(); + if (!frame) + return false; + + // If my parent is an <object> and is not set to use fallback content, I + // should be ignored and not get a renderer. + ContainerNode* p = parentNode(); + if (p && p->hasTagName(objectTag)) { + ASSERT(p->renderer()); + if (!static_cast<HTMLObjectElement*>(p)->useFallbackContent()) { + ASSERT(!p->renderer()->isEmbeddedObject()); + return false; + } + } + +#if ENABLE(DASHBOARD_SUPPORT) + // Workaround for <rdar://problem/6642221>. + if (Settings* settings = frame->settings()) { + if (settings->usesDashboardBackwardCompatibilityMode()) + return true; + } +#endif + + return HTMLPlugInImageElement::rendererIsNeeded(context); +} + +void HTMLEmbedElement::insertedIntoDocument() +{ + HTMLPlugInImageElement::insertedIntoDocument(); + if (!inDocument()) + return; + + String width = getAttribute(widthAttr); + String height = getAttribute(heightAttr); + if (!width.isEmpty() || !height.isEmpty()) { + Node* n = parentNode(); + while (n && !n->hasTagName(objectTag)) + n = n->parentNode(); + if (n) { + if (!width.isEmpty()) + static_cast<HTMLObjectElement*>(n)->setAttribute(widthAttr, width); + if (!height.isEmpty()) + static_cast<HTMLObjectElement*>(n)->setAttribute(heightAttr, height); + } + } +} + +void HTMLEmbedElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + HTMLPlugInImageElement::attributeChanged(attr, preserveDecls); + + if ((attr->name() == widthAttr || attr->name() == heightAttr) && !attr->isEmpty()) { + ContainerNode* n = parentNode(); + while (n && !n->hasTagName(objectTag)) + n = n->parentNode(); + if (n) + static_cast<HTMLObjectElement*>(n)->setAttribute(attr->name(), attr->value()); + } +} + +bool HTMLEmbedElement::isURLAttribute(Attribute* attr) const +{ + return attr->name() == srcAttr || HTMLPlugInImageElement::isURLAttribute(attr); +} + +const QualifiedName& HTMLEmbedElement::imageSourceAttributeName() const +{ + return srcAttr; +} + +void HTMLEmbedElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLPlugInImageElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, document()->completeURL(getAttribute(srcAttr))); +} + +#if ENABLE(MICRODATA) +String HTMLEmbedElement::itemValueText() const +{ + return getURLAttribute(srcAttr); +} + +void HTMLEmbedElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(srcAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLEmbedElement.h b/Source/WebCore/html/HTMLEmbedElement.h new file mode 100644 index 000000000..bd6764df9 --- /dev/null +++ b/Source/WebCore/html/HTMLEmbedElement.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2008, 2009 Apple 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 HTMLEmbedElement_h +#define HTMLEmbedElement_h + +#include "HTMLPlugInImageElement.h" + +namespace WebCore { + +class HTMLEmbedElement : public HTMLPlugInImageElement { +public: + static PassRefPtr<HTMLEmbedElement> create(const QualifiedName&, Document*, bool createdByParser); + +private: + HTMLEmbedElement(const QualifiedName&, Document*, bool createdByParser); + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(Attribute*); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual void insertedIntoDocument(); + virtual void attributeChanged(Attribute*, bool preserveDecls = false); + + virtual bool isURLAttribute(Attribute*) const; + virtual const QualifiedName& imageSourceAttributeName() const; + + virtual RenderWidget* renderWidgetForJSBindings(); + + virtual void updateWidget(PluginCreationOption); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + void parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + virtual bool shouldRegisterAsNamedItem() const OVERRIDE { return true; } +}; + +} + +#endif diff --git a/Source/WebCore/html/HTMLEmbedElement.idl b/Source/WebCore/html/HTMLEmbedElement.idl new file mode 100644 index 000000000..e395fc65e --- /dev/null +++ b/Source/WebCore/html/HTMLEmbedElement.idl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + DelegatingPutFunction, + DelegatingGetOwnPropertySlot, + CustomCall + ] HTMLEmbedElement : HTMLElement { + attribute [Reflect] DOMString align; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + attribute [Reflect] DOMString height; +#else + attribute [Reflect] long height; +#endif + attribute [Reflect] DOMString name; + attribute [Reflect, URL] DOMString src; + attribute [Reflect] DOMString type; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + attribute [Reflect] DOMString width; +#else + attribute [Reflect] long width; +#endif + +#if defined(ENABLE_SVG) && ENABLE_SVG +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C || defined(ENABLE_SVG_DOM_OBJC_BINDINGS) && ENABLE_SVG_DOM_OBJC_BINDINGS + [SVGCheckSecurityDocument] SVGDocument getSVGDocument() raises(DOMException); +#endif +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLFieldSetElement.cpp b/Source/WebCore/html/HTMLFieldSetElement.cpp new file mode 100644 index 000000000..4b90412bb --- /dev/null +++ b/Source/WebCore/html/HTMLFieldSetElement.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLFieldSetElement.h" + +#include "HTMLNames.h" +#include "RenderFieldset.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLFieldSetElement::HTMLFieldSetElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) +{ + ASSERT(hasTagName(fieldsetTag)); +} + +PassRefPtr<HTMLFieldSetElement> HTMLFieldSetElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLFieldSetElement(tagName, document, form)); +} + +bool HTMLFieldSetElement::supportsFocus() const +{ + return HTMLElement::supportsFocus(); +} + +const AtomicString& HTMLFieldSetElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, fieldset, ("fieldset")); + return fieldset; +} + +RenderObject* HTMLFieldSetElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderFieldset(this); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLFieldSetElement.h b/Source/WebCore/html/HTMLFieldSetElement.h new file mode 100644 index 000000000..dcc8d0c46 --- /dev/null +++ b/Source/WebCore/html/HTMLFieldSetElement.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple 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 HTMLFieldSetElement_h +#define HTMLFieldSetElement_h + +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLFieldSetElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLFieldSetElement> create(const QualifiedName&, Document*, HTMLFormElement*); + +private: + HTMLFieldSetElement(const QualifiedName&, Document*, HTMLFormElement*); + + virtual bool isEnumeratable() const { return true; } + virtual bool supportsFocus() const; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual const AtomicString& formControlType() const; + virtual bool recalcWillValidate() const { return false; } +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLFieldSetElement.idl b/Source/WebCore/html/HTMLFieldSetElement.idl new file mode 100644 index 000000000..8cffe3db0 --- /dev/null +++ b/Source/WebCore/html/HTMLFieldSetElement.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module html { + + interface HTMLFieldSetElement : HTMLElement { + readonly attribute HTMLFormElement form; + readonly attribute ValidityState validity; + readonly attribute boolean willValidate; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + }; + +} diff --git a/Source/WebCore/html/HTMLFontElement.cpp b/Source/WebCore/html/HTMLFontElement.cpp new file mode 100644 index 000000000..d0ade290b --- /dev/null +++ b/Source/WebCore/html/HTMLFontElement.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2003, 2006, 2008, 2010 Apple 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 "HTMLFontElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include <wtf/text/StringBuilder.h> + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +HTMLFontElement::HTMLFontElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(fontTag)); +} + +PassRefPtr<HTMLFontElement> HTMLFontElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLFontElement(tagName, document)); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#fonts-and-colors +static bool parseFontSize(const String& input, int& size) +{ + + // Step 1 + // Step 2 + const UChar* position = input.characters(); + const UChar* end = position + input.length(); + + // Step 3 + while (position < end) { + if (!isHTMLSpace(*position)) + break; + ++position; + } + + // Step 4 + if (position == end) + return false; + ASSERT(position < end); + + // Step 5 + enum { + RelativePlus, + RelativeMinus, + Absolute + } mode; + + switch (*position) { + case '+': + mode = RelativePlus; + ++position; + break; + case '-': + mode = RelativeMinus; + ++position; + break; + default: + mode = Absolute; + break; + } + + // Step 6 + StringBuilder digits; + digits.reserveCapacity(16); + while (position < end) { + if (!isASCIIDigit(*position)) + break; + digits.append(*position++); + } + + // Step 7 + if (digits.isEmpty()) + return false; + + // Step 8 + int value = charactersToIntStrict(digits.characters(), digits.length()); + + // Step 9 + if (mode == RelativePlus) + value += 3; + else if (mode == RelativeMinus) + value = 3 - value; + + // Step 10 + if (value > 7) + value = 7; + + // Step 11 + if (value < 1) + value = 1; + + size = value; + return true; +} + +bool HTMLFontElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == sizeAttr || + attrName == colorAttr || + attrName == faceAttr) { + result = eUniversal; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +bool HTMLFontElement::cssValueFromFontSizeNumber(const String& s, int& size) +{ + int num = 0; + if (!parseFontSize(s, num)) + return false; + + switch (num) { + case 1: + // FIXME: The spec says that we're supposed to use CSSValueXxSmall here. + size = CSSValueXSmall; + break; + case 2: + size = CSSValueSmall; + break; + case 3: + size = CSSValueMedium; + break; + case 4: + size = CSSValueLarge; + break; + case 5: + size = CSSValueXLarge; + break; + case 6: + size = CSSValueXxLarge; + break; + case 7: + size = CSSValueWebkitXxxLarge; + break; + default: + ASSERT_NOT_REACHED(); + } + return true; +} + +void HTMLFontElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == sizeAttr) { + int size = 0; + if (cssValueFromFontSizeNumber(attr->value(), size)) + addCSSProperty(attr, CSSPropertyFontSize, size); + } else if (attr->name() == colorAttr) { + addCSSColor(attr, CSSPropertyColor, attr->value()); + } else if (attr->name() == faceAttr) { + addCSSProperty(attr, CSSPropertyFontFamily, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLFontElement.h b/Source/WebCore/html/HTMLFontElement.h new file mode 100644 index 000000000..f97ab080a --- /dev/null +++ b/Source/WebCore/html/HTMLFontElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2010 Apple 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 HTMLFontElement_h +#define HTMLFontElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLFontElement : public HTMLElement { +public: + static PassRefPtr<HTMLFontElement> create(const QualifiedName&, Document*); + + static bool cssValueFromFontSizeNumber(const String&, int&); + +private: + HTMLFontElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLFontElement.idl b/Source/WebCore/html/HTMLFontElement.idl new file mode 100644 index 000000000..141816d1d --- /dev/null +++ b/Source/WebCore/html/HTMLFontElement.idl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLFontElement : HTMLElement { + attribute [Reflect] DOMString color; + attribute [Reflect] DOMString face; + attribute [Reflect] DOMString size; + }; + +} diff --git a/Source/WebCore/html/HTMLFormCollection.cpp b/Source/WebCore/html/HTMLFormCollection.cpp new file mode 100644 index 000000000..6b19d446e --- /dev/null +++ b/Source/WebCore/html/HTMLFormCollection.cpp @@ -0,0 +1,204 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010, 2011, 2012 Apple 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 "HTMLFormCollection.h" + +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLImageElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +// Since the collections are to be "live", we have to do the +// calculation every time if anything has changed. + +HTMLFormCollection::HTMLFormCollection(HTMLFormElement* form) + : HTMLCollection(form, OtherCollection) +{ +} + +PassRefPtr<HTMLFormCollection> HTMLFormCollection::create(HTMLFormElement* form) +{ + return adoptRef(new HTMLFormCollection(form)); +} + +HTMLFormCollection::~HTMLFormCollection() +{ +} + +unsigned HTMLFormCollection::calcLength() const +{ + ASSERT(base()); + return static_cast<HTMLFormElement*>(base())->length(); +} + +Node* HTMLFormCollection::item(unsigned index) const +{ + if (!base()) + return 0; + + invalidateCacheIfNeeded(); + + if (m_cache.current && m_cache.position == index) + return m_cache.current; + + if (m_cache.hasLength && m_cache.length <= index) + return 0; + + if (!m_cache.current || m_cache.position > index) { + m_cache.current = 0; + m_cache.position = 0; + m_cache.elementsArrayPosition = 0; + } + + Vector<FormAssociatedElement*>& elementsArray = static_cast<HTMLFormElement*>(base())->m_associatedElements; + unsigned currentIndex = m_cache.position; + + for (unsigned i = m_cache.elementsArrayPosition; i < elementsArray.size(); i++) { + if (elementsArray[i]->isEnumeratable()) { + HTMLElement* element = toHTMLElement(elementsArray[i]); + if (index == currentIndex) { + m_cache.position = index; + m_cache.current = element; + m_cache.elementsArrayPosition = i; + return element; + } + + currentIndex++; + } + } + + return 0; +} + +Element* HTMLFormCollection::getNamedItem(const QualifiedName& attrName, const AtomicString& name) const +{ + if (!base()) + return 0; + + m_cache.position = 0; + return getNamedFormItem(attrName, name, 0); +} + +Element* HTMLFormCollection::getNamedFormItem(const QualifiedName& attrName, const String& name, int duplicateNumber) const +{ + HTMLFormElement* form = static_cast<HTMLFormElement*>(base()); + + if (!form) + return 0; + + bool foundInputElements = false; + for (unsigned i = 0; i < form->m_associatedElements.size(); ++i) { + FormAssociatedElement* associatedElement = form->m_associatedElements[i]; + HTMLElement* element = toHTMLElement(associatedElement); + if (associatedElement->isEnumeratable() && element->getAttribute(attrName) == name) { + foundInputElements = true; + if (!duplicateNumber) + return element; + --duplicateNumber; + } + } + + if (!foundInputElements) { + for (unsigned i = 0; i < form->m_imageElements.size(); ++i) { + HTMLImageElement* element = form->m_imageElements[i]; + if (element->getAttribute(attrName) == name) { + if (!duplicateNumber) + return element; + --duplicateNumber; + } + } + } + + return 0; +} + +Node* HTMLFormCollection::nextItem() const +{ + return item(m_cache.position + 1); +} + +Node* HTMLFormCollection::namedItem(const AtomicString& name) const +{ + if (!base()) + return 0; + + // http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/nameditem.asp + // This method first searches for an object with a matching id + // attribute. If a match is not found, the method then searches for an + // object with a matching name attribute, but only on those elements + // that are allowed a name attribute. + invalidateCacheIfNeeded(); + m_cache.current = getNamedItem(idAttr, name); + if (m_cache.current) + return m_cache.current; + m_cache.current = getNamedItem(nameAttr, name); + return m_cache.current; +} + +void HTMLFormCollection::updateNameCache() const +{ + if (!base()) + return; + + if (m_cache.hasNameCache) + return; + + HashSet<AtomicStringImpl*> foundInputElements; + + HTMLFormElement* f = static_cast<HTMLFormElement*>(base()); + + for (unsigned i = 0; i < f->m_associatedElements.size(); ++i) { + FormAssociatedElement* associatedElement = f->m_associatedElements[i]; + if (associatedElement->isEnumeratable()) { + HTMLElement* element = toHTMLElement(associatedElement); + const AtomicString& idAttrVal = element->getIdAttribute(); + const AtomicString& nameAttrVal = element->getAttribute(nameAttr); + if (!idAttrVal.isEmpty()) { + append(m_cache.idCache, idAttrVal, element); + foundInputElements.add(idAttrVal.impl()); + } + if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal) { + append(m_cache.nameCache, nameAttrVal, element); + foundInputElements.add(nameAttrVal.impl()); + } + } + } + + for (unsigned i = 0; i < f->m_imageElements.size(); ++i) { + HTMLImageElement* element = f->m_imageElements[i]; + const AtomicString& idAttrVal = element->getIdAttribute(); + const AtomicString& nameAttrVal = element->getAttribute(nameAttr); + if (!idAttrVal.isEmpty() && !foundInputElements.contains(idAttrVal.impl())) + append(m_cache.idCache, idAttrVal, element); + if (!nameAttrVal.isEmpty() && idAttrVal != nameAttrVal && !foundInputElements.contains(nameAttrVal.impl())) + append(m_cache.nameCache, nameAttrVal, element); + } + + m_cache.hasNameCache = true; +} + +} diff --git a/Source/WebCore/html/HTMLFormCollection.h b/Source/WebCore/html/HTMLFormCollection.h new file mode 100644 index 000000000..7d2963820 --- /dev/null +++ b/Source/WebCore/html/HTMLFormCollection.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple 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 HTMLFormCollection_h +#define HTMLFormCollection_h + +#include "HTMLCollection.h" + +namespace WebCore { + +class HTMLFormElement; +class QualifiedName; + +// This class is just a big hack to find form elements even in malformed HTML elements. +// The famous <table><tr><form><td> problem. + +class HTMLFormCollection : public HTMLCollection { +public: + static PassRefPtr<HTMLFormCollection> create(HTMLFormElement*); + + virtual ~HTMLFormCollection(); + + virtual Node* item(unsigned index) const; + virtual Node* nextItem() const; + + virtual Node* namedItem(const AtomicString& name) const; + +private: + HTMLFormCollection(HTMLFormElement*); + + virtual void updateNameCache() const; + virtual unsigned calcLength() const; + + Element* getNamedItem(const QualifiedName& attrName, const AtomicString& name) const; + Element* getNamedFormItem(const QualifiedName& attrName, const String& name, int duplicateNumber) const; + + mutable int currentPos; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLFormControlElement.cpp b/Source/WebCore/html/HTMLFormControlElement.cpp new file mode 100644 index 000000000..71a44844b --- /dev/null +++ b/Source/WebCore/html/HTMLFormControlElement.cpp @@ -0,0 +1,503 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLFormControlElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "ElementRareData.h" +#include "Event.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "LabelsNodeList.h" +#include "RenderBox.h" +#include "RenderTheme.h" +#include "ScriptEventListener.h" +#include "ValidationMessage.h" +#include "ValidityState.h" +#include <wtf/Vector.h> + +namespace WebCore { + +using namespace HTMLNames; +using namespace std; + +HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLElement(tagName, document) + , FormAssociatedElement(form) + , m_disabled(false) + , m_readOnly(false) + , m_required(false) + , m_valueMatchesRenderer(false) + , m_willValidateInitialized(false) + , m_willValidate(true) + , m_isValid(true) + , m_wasChangedSinceLastFormControlChangeEvent(false) + , m_hasAutofocused(false) +{ + if (!this->form()) + setForm(findFormAncestor()); + if (this->form()) + this->form()->registerFormElement(this); + + setHasCustomWillOrDidRecalcStyle(); +} + +HTMLFormControlElement::~HTMLFormControlElement() +{ + if (form()) + form()->removeFormElement(this); +} + +void HTMLFormControlElement::detach() +{ + m_validationMessage = nullptr; + HTMLElement::detach(); +} + +String HTMLFormControlElement::formEnctype() const +{ + return FormSubmission::Attributes::parseEncodingType(fastGetAttribute(formenctypeAttr)); +} + +void HTMLFormControlElement::setFormEnctype(const String& value) +{ + setAttribute(formenctypeAttr, value); +} + +String HTMLFormControlElement::formMethod() const +{ + return FormSubmission::Attributes::methodString(FormSubmission::Attributes::parseMethodType(fastGetAttribute(formmethodAttr))); +} + +void HTMLFormControlElement::setFormMethod(const String& value) +{ + setAttribute(formmethodAttr, value); +} + +bool HTMLFormControlElement::formNoValidate() const +{ + return fastHasAttribute(formnovalidateAttr); +} + +void HTMLFormControlElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == formAttr) { + formAttributeChanged(); + if (!form()) + document()->checkedRadioButtons().addButton(this); + } else if (attr->name() == disabledAttr) { + bool oldDisabled = m_disabled; + m_disabled = !attr->isNull(); + if (oldDisabled != m_disabled) { + setNeedsStyleRecalc(); + if (renderer() && renderer()->style()->hasAppearance()) + renderer()->theme()->stateChanged(renderer(), EnabledState); + } + } else if (attr->name() == readonlyAttr) { + bool oldReadOnly = m_readOnly; + m_readOnly = !attr->isNull(); + if (oldReadOnly != m_readOnly) { + setNeedsStyleRecalc(); + if (renderer() && renderer()->style()->hasAppearance()) + renderer()->theme()->stateChanged(renderer(), ReadOnlyState); + } + } else if (attr->name() == requiredAttr) { + bool oldRequired = m_required; + m_required = !attr->isNull(); + if (oldRequired != m_required) { + setNeedsValidityCheck(); + setNeedsStyleRecalc(); // Updates for :required :optional classes. + } + } else + HTMLElement::parseMappedAttribute(attr); + setNeedsWillValidateCheck(); +} + +static bool shouldAutofocus(HTMLFormControlElement* element) +{ + if (!element->autofocus()) + return false; + if (!element->renderer()) + return false; + if (element->document()->ignoreAutofocus()) + return false; + if (element->hasAutofocused()) + return false; + + // FIXME: Should this set of hasTagName checks be replaced by a + // virtual member function? + if (element->hasTagName(inputTag)) + return !static_cast<HTMLInputElement*>(element)->isInputTypeHidden(); + if (element->hasTagName(selectTag)) + return true; + if (element->hasTagName(keygenTag)) + return true; + if (element->hasTagName(buttonTag)) + return true; + if (element->hasTagName(textareaTag)) + return true; + + return false; +} + +static void focusPostAttach(Node* element, unsigned) +{ + static_cast<Element*>(element)->focus(); + element->deref(); +} + +void HTMLFormControlElement::attach() +{ + ASSERT(!attached()); + + suspendPostAttachCallbacks(); + + HTMLElement::attach(); + + // The call to updateFromElement() needs to go after the call through + // to the base class's attach() because that can sometimes do a close + // on the renderer. + if (renderer()) + renderer()->updateFromElement(); + + if (shouldAutofocus(this)) { + setAutofocused(); + ref(); + queuePostAttachCallback(focusPostAttach, this); + } + + resumePostAttachCallbacks(); +} + +void HTMLFormControlElement::didMoveToNewDocument(Document* oldDocument) +{ + FormAssociatedElement::didMoveToNewDocument(oldDocument); + HTMLElement::didMoveToNewDocument(oldDocument); +} + +void HTMLFormControlElement::insertedIntoTree(bool deep) +{ + FormAssociatedElement::insertedIntoTree(); + if (!form()) + document()->checkedRadioButtons().addButton(this); + + HTMLElement::insertedIntoTree(deep); +} + +void HTMLFormControlElement::removedFromTree(bool deep) +{ + FormAssociatedElement::removedFromTree(); + HTMLElement::removedFromTree(deep); +} + +void HTMLFormControlElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + FormAssociatedElement::insertedIntoDocument(); +} + +void HTMLFormControlElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + FormAssociatedElement::removedFromDocument(); +} + +const AtomicString& HTMLFormControlElement::formControlName() const +{ + const AtomicString& name = fastGetAttribute(nameAttr); + return name.isNull() ? emptyAtom : name; +} + +void HTMLFormControlElement::setName(const AtomicString& value) +{ + setAttribute(nameAttr, value); +} + +bool HTMLFormControlElement::wasChangedSinceLastFormControlChangeEvent() const +{ + return m_wasChangedSinceLastFormControlChangeEvent; +} + +void HTMLFormControlElement::setChangedSinceLastFormControlChangeEvent(bool changed) +{ + m_wasChangedSinceLastFormControlChangeEvent = changed; +} + +void HTMLFormControlElement::dispatchFormControlChangeEvent() +{ + HTMLElement::dispatchChangeEvent(); + setChangedSinceLastFormControlChangeEvent(false); +} + +void HTMLFormControlElement::dispatchFormControlInputEvent() +{ + setChangedSinceLastFormControlChangeEvent(true); + HTMLElement::dispatchInputEvent(); +} + +void HTMLFormControlElement::setDisabled(bool b) +{ + setAttribute(disabledAttr, b ? "" : 0); +} + +bool HTMLFormControlElement::autofocus() const +{ + return hasAttribute(autofocusAttr); +} + +bool HTMLFormControlElement::required() const +{ + return m_required; +} + +static void updateFromElementCallback(Node* node, unsigned) +{ + ASSERT_ARG(node, node->isElementNode()); + ASSERT_ARG(node, static_cast<Element*>(node)->isFormControlElement()); + ASSERT(node->renderer()); + if (RenderObject* renderer = node->renderer()) + renderer->updateFromElement(); +} + +void HTMLFormControlElement::didRecalcStyle(StyleChange) +{ + // updateFromElement() can cause the selection to change, and in turn + // trigger synchronous layout, so it must not be called during style recalc. + if (renderer()) + queuePostAttachCallback(updateFromElementCallback, this); +} + +bool HTMLFormControlElement::supportsFocus() const +{ + return !m_disabled; +} + +bool HTMLFormControlElement::isFocusable() const +{ + if (!renderer() || !renderer()->isBox() || toRenderBox(renderer())->size().isEmpty()) + return false; + // HTMLElement::isFocusable handles visibility and calls suportsFocus which + // will cover the disabled case. + return HTMLElement::isFocusable(); +} + +bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const +{ + if (isFocusable()) + if (document()->frame()) + return document()->frame()->eventHandler()->tabsToAllFormControls(event); + return false; +} + +bool HTMLFormControlElement::isMouseFocusable() const +{ +#if PLATFORM(GTK) || PLATFORM(QT) + return HTMLElement::isMouseFocusable(); +#else + return false; +#endif +} + +short HTMLFormControlElement::tabIndex() const +{ + // Skip the supportsFocus check in HTMLElement. + return Element::tabIndex(); +} + +bool HTMLFormControlElement::recalcWillValidate() const +{ + // FIXME: Should return false if this element has a datalist element as an + // ancestor. See HTML5 4.10.10 'The datalist element.' + return !m_disabled && !m_readOnly; +} + +bool HTMLFormControlElement::willValidate() const +{ + if (!m_willValidateInitialized) { + m_willValidateInitialized = true; + m_willValidate = recalcWillValidate(); + } else { + // If the following assertion fails, setNeedsWillValidateCheck() is not + // called correctly when something which changes recalcWillValidate() result + // is updated. + ASSERT(m_willValidate == recalcWillValidate()); + } + return m_willValidate; +} + +void HTMLFormControlElement::setNeedsWillValidateCheck() +{ + // We need to recalculate willValidate immediately because willValidate change can causes style change. + bool newWillValidate = recalcWillValidate(); + if (m_willValidateInitialized && m_willValidate == newWillValidate) + return; + m_willValidateInitialized = true; + m_willValidate = newWillValidate; + setNeedsStyleRecalc(); + if (!m_willValidate) + hideVisibleValidationMessage(); +} + +String HTMLFormControlElement::validationMessage() +{ + return validity()->validationMessage(); +} + +void HTMLFormControlElement::updateVisibleValidationMessage() +{ + Page* page = document()->page(); + if (!page) + return; + String message; + if (renderer() && willValidate()) { + message = validationMessage().stripWhiteSpace(); + // HTML5 specification doesn't ask UA to show the title attribute value + // with the validationMessage. However, this behavior is same as Opera + // and the specification describes such behavior as an example. + const AtomicString& title = getAttribute(titleAttr); + if (!message.isEmpty() && !title.isEmpty()) { + message.append('\n'); + message.append(title); + } + } + if (message.isEmpty()) { + hideVisibleValidationMessage(); + return; + } + if (!m_validationMessage) { + m_validationMessage = ValidationMessage::create(this); + m_validationMessage->setMessage(message); + } else { + // Call setMessage() even if m_validationMesage->message() == message + // because the existing message might be to be hidden. + m_validationMessage->setMessage(message); + } +} + +void HTMLFormControlElement::hideVisibleValidationMessage() +{ + if (m_validationMessage) + m_validationMessage->requestToHideMessage(); +} + +String HTMLFormControlElement::visibleValidationMessage() const +{ + return m_validationMessage ? m_validationMessage->message() : String(); +} + +bool HTMLFormControlElement::checkValidity(Vector<RefPtr<FormAssociatedElement> >* unhandledInvalidControls) +{ + if (!willValidate() || isValidFormControlElement()) + return true; + // An event handler can deref this object. + RefPtr<HTMLFormControlElement> protector(this); + RefPtr<Document> originalDocument(document()); + bool needsDefaultAction = dispatchEvent(Event::create(eventNames().invalidEvent, false, true)); + if (needsDefaultAction && unhandledInvalidControls && inDocument() && originalDocument == document()) + unhandledInvalidControls->append(this); + return false; +} + +bool HTMLFormControlElement::isValidFormControlElement() +{ + // If the following assertion fails, setNeedsValidityCheck() is not called + // correctly when something which changes validity is updated. + ASSERT(m_isValid == validity()->valid()); + return m_isValid; +} + +void HTMLFormControlElement::setNeedsValidityCheck() +{ + bool newIsValid = validity()->valid(); + if (willValidate() && newIsValid != m_isValid) { + // Update style for pseudo classes such as :valid :invalid. + setNeedsStyleRecalc(); + } + m_isValid = newIsValid; + + // Updates only if this control already has a validtion message. + if (!visibleValidationMessage().isEmpty()) { + // Calls updateVisibleValidationMessage() even if m_isValid is not + // changed because a validation message can be chagned. + updateVisibleValidationMessage(); + } +} + +void HTMLFormControlElement::setCustomValidity(const String& error) +{ + validity()->setCustomErrorMessage(error); +} + +void HTMLFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode) +{ + HTMLElement::dispatchBlurEvent(newFocusedNode); + hideVisibleValidationMessage(); +} + +HTMLFormElement* HTMLFormControlElement::virtualForm() const +{ + return FormAssociatedElement::form(); +} + +bool HTMLFormControlElement::isDefaultButtonForForm() const +{ + return isSuccessfulSubmitButton() && form() && form()->defaultButton() == this; +} + +bool HTMLFormControlElement::isLabelable() const +{ + // FIXME: Add meterTag and outputTag to the list once we support them. + return hasTagName(buttonTag) || hasTagName(inputTag) || hasTagName(keygenTag) +#if ENABLE(METER_TAG) + || hasTagName(meterTag) +#endif +#if ENABLE(PROGRESS_TAG) + || hasTagName(progressTag) +#endif + || hasTagName(selectTag) || hasTagName(textareaTag); +} + +PassRefPtr<NodeList> HTMLFormControlElement::labels() +{ + if (!isLabelable()) + return 0; + if (!document()) + return 0; + + NodeListsNodeData* nodeLists = Node::ensureRareData()->ensureNodeLists(this); + if (nodeLists->m_labelsNodeListCache) + return nodeLists->m_labelsNodeListCache; + + RefPtr<LabelsNodeList> list = LabelsNodeList::create(this); + nodeLists->m_labelsNodeListCache = list.get(); + return list.release(); +} + +} // namespace Webcore diff --git a/Source/WebCore/html/HTMLFormControlElement.h b/Source/WebCore/html/HTMLFormControlElement.h new file mode 100644 index 000000000..f500b6df9 --- /dev/null +++ b/Source/WebCore/html/HTMLFormControlElement.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple 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 HTMLFormControlElement_h +#define HTMLFormControlElement_h + +#include "FormAssociatedElement.h" +#include "HTMLElement.h" + +namespace WebCore { + +class FormDataList; +class HTMLFormElement; +class ValidationMessage; +class ValidityState; + +// HTMLFormControlElement is the default implementation of FormAssociatedElement, +// and form-associated element implementations should use HTMLFormControlElement +// unless there is a special reason. +class HTMLFormControlElement : public HTMLElement, public FormAssociatedElement { +public: + virtual ~HTMLFormControlElement(); + + HTMLFormElement* form() const { return FormAssociatedElement::form(); } + + String formEnctype() const; + void setFormEnctype(const String&); + String formMethod() const; + void setFormMethod(const String&); + bool formNoValidate() const; + + virtual void reset() { } + + virtual bool formControlValueMatchesRenderer() const { return m_valueMatchesRenderer; } + virtual void setFormControlValueMatchesRenderer(bool b) { m_valueMatchesRenderer = b; } + + virtual bool wasChangedSinceLastFormControlChangeEvent() const; + virtual void setChangedSinceLastFormControlChangeEvent(bool); + + virtual void dispatchFormControlChangeEvent(); + virtual void dispatchFormControlInputEvent(); + + virtual bool disabled() const { return m_disabled; } + void setDisabled(bool); + + virtual bool isFocusable() const; + virtual bool isEnumeratable() const { return false; } + + // Determines whether or not a control will be automatically focused. + virtual bool autofocus() const; + + bool required() const; + + 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(); } + + virtual bool isRadioButton() const { return false; } + virtual bool canTriggerImplicitSubmission() const { return false; } + + // Override in derived classes to get the encoded name=value pair for submitting. + // Return true for a successful control (see HTML4-17.13.2). + virtual bool appendFormData(FormDataList&, bool) { return false; } + + virtual bool isSuccessfulSubmitButton() const { return false; } + virtual bool isActivatedSubmit() const { return false; } + virtual void setActivatedSubmit(bool) { } + + virtual bool willValidate() const; + String validationMessage(); + void updateVisibleValidationMessage(); + void hideVisibleValidationMessage(); + bool checkValidity(Vector<RefPtr<FormAssociatedElement> >* unhandledInvalidControls = 0); + // This must be called when a validation constraint or control value is changed. + void setNeedsValidityCheck(); + void setCustomValidity(const String&); + + bool isLabelable() const; + PassRefPtr<NodeList> labels(); + + bool readOnly() const { return m_readOnly; } + + bool hasAutofocused() { return m_hasAutofocused; } + void setAutofocused() { m_hasAutofocused = true; } + + using TreeShared<ContainerNode>::ref; + using TreeShared<ContainerNode>::deref; + +protected: + HTMLFormControlElement(const QualifiedName& tagName, Document*, HTMLFormElement*); + + virtual void parseMappedAttribute(Attribute*); + virtual void attach(); + virtual void insertedIntoTree(bool deep); + virtual void removedFromTree(bool deep); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + virtual bool supportsFocus() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + + virtual void didRecalcStyle(StyleChange); + + virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode); + virtual void detach(); + + // This must be called any time the result of willValidate() has changed. + void setNeedsWillValidateCheck(); + virtual bool recalcWillValidate() const; + +private: + virtual void refFormAssociatedElement() { ref(); } + virtual void derefFormAssociatedElement() { deref(); } + + virtual bool isFormControlElement() const { return true; } + + virtual short tabIndex() const; + + virtual HTMLFormElement* virtualForm() const; + virtual bool isDefaultButtonForForm() const; + virtual bool isValidFormControlElement(); + String visibleValidationMessage() const; + + OwnPtr<ValidationMessage> m_validationMessage; + bool m_disabled : 1; + bool m_readOnly : 1; + bool m_required : 1; + bool m_valueMatchesRenderer : 1; + + // The initial value of m_willValidate depends on the derived class. We can't + // initialize it with a virtual function in the constructor. m_willValidate + // is not deterministic as long as m_willValidateInitialized is false. + mutable bool m_willValidateInitialized: 1; + mutable bool m_willValidate : 1; + + // Cache of validity()->valid(). + // But "candidate for constraint validation" doesn't affect m_isValid. + bool m_isValid : 1; + + bool m_wasChangedSinceLastFormControlChangeEvent : 1; + + bool m_hasAutofocused : 1; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLFormControlElementWithState.cpp b/Source/WebCore/html/HTMLFormControlElementWithState.cpp new file mode 100644 index 000000000..bf94c54d9 --- /dev/null +++ b/Source/WebCore/html/HTMLFormControlElementWithState.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLFormControlElementWithState.h" + +#include "HTMLFormElement.h" + +namespace WebCore { + +HTMLFormControlElementWithState::HTMLFormControlElementWithState(const QualifiedName& tagName, Document* doc, HTMLFormElement* f) + : HTMLFormControlElement(tagName, doc, f) +{ + document()->registerFormElementWithState(this); +} + +HTMLFormControlElementWithState::~HTMLFormControlElementWithState() +{ + document()->unregisterFormElementWithState(this); +} + +void HTMLFormControlElementWithState::didMoveToNewDocument(Document* oldDocument) +{ + if (oldDocument) + oldDocument->unregisterFormElementWithState(this); + document()->registerFormElementWithState(this); + HTMLFormControlElement::didMoveToNewDocument(oldDocument); +} + +bool HTMLFormControlElementWithState::shouldAutocomplete() const +{ + if (!form()) + return true; + return form()->shouldAutocomplete(); +} + +bool HTMLFormControlElementWithState::shouldSaveAndRestoreFormControlState() const +{ + // We don't save/restore control state in a form with autocomplete=off. + return attached() && shouldAutocomplete(); +} + +void HTMLFormControlElementWithState::finishParsingChildren() +{ + HTMLFormControlElement::finishParsingChildren(); + + // We don't save state of a control with shouldSaveAndRestoreFormControlState()=false. + // But we need to skip restoring process too because a control in another + // form might have the same pair of name and type and saved its state. + if (!shouldSaveAndRestoreFormControlState()) + return; + + Document* doc = document(); + if (doc->hasStateForNewFormElements()) { + String state; + if (doc->takeStateForFormElement(name().impl(), type().impl(), state)) + restoreFormControlState(state); + } +} + +} // namespace Webcore diff --git a/Source/WebCore/html/HTMLFormControlElementWithState.h b/Source/WebCore/html/HTMLFormControlElementWithState.h new file mode 100644 index 000000000..73de3260b --- /dev/null +++ b/Source/WebCore/html/HTMLFormControlElementWithState.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple 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 HTMLFormControlElementWithState_h +#define HTMLFormControlElementWithState_h + +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLFormControlElementWithState : public HTMLFormControlElement { +public: + virtual ~HTMLFormControlElementWithState(); + + virtual bool canContainRangeEndPoint() const { return false; } + + bool shouldSaveAndRestoreFormControlState() const; + virtual bool saveFormControlState(String&) const { return false; } + virtual void restoreFormControlState(const String&) { } + +protected: + HTMLFormControlElementWithState(const QualifiedName& tagName, Document*, HTMLFormElement*); + + virtual bool shouldAutocomplete() const; + virtual void finishParsingChildren(); + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLFormElement.cpp b/Source/WebCore/html/HTMLFormElement.cpp new file mode 100644 index 000000000..dab47efff --- /dev/null +++ b/Source/WebCore/html/HTMLFormElement.cpp @@ -0,0 +1,656 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLFormElement.h" + +#include "Attribute.h" +#include "Console.h" +#include "DOMFormData.h" +#include "DOMWindow.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "FileList.h" +#include "FileSystem.h" +#include "FormData.h" +#include "FormDataList.h" +#include "FormState.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "HTMLDocument.h" +#include "HTMLFormCollection.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "MIMETypeRegistry.h" +#include "NodeRenderingContext.h" +#include "Page.h" +#include "RenderTextControl.h" +#include "ScriptEventListener.h" +#include "Settings.h" +#include "ValidityState.h" +#include <limits> + +#if PLATFORM(WX) +#include <wx/defs.h> +#include <wx/filename.h> +#endif + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_associatedElementsBeforeIndex(0) + , m_associatedElementsAfterIndex(0) + , m_wasUserSubmitted(false) + , m_isSubmittingOrPreparingForSubmission(false) + , m_shouldSubmit(false) + , m_isInResetFunction(false) + , m_wasMalformed(false) + , m_wasDemoted(false) +{ + ASSERT(hasTagName(formTag)); +} + +PassRefPtr<HTMLFormElement> HTMLFormElement::create(Document* document) +{ + return adoptRef(new HTMLFormElement(formTag, document)); +} + +PassRefPtr<HTMLFormElement> HTMLFormElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLFormElement(tagName, document)); +} + +HTMLFormElement::~HTMLFormElement() +{ + if (m_elementsCollection) + m_elementsCollection->detachFromNode(); + + if (!shouldAutocomplete()) + document()->unregisterForPageCacheSuspensionCallbacks(this); + + for (unsigned i = 0; i < m_associatedElements.size(); ++i) + m_associatedElements[i]->formDestroyed(); + for (unsigned i = 0; i < m_imageElements.size(); ++i) + m_imageElements[i]->m_form = 0; +} + +bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url) +{ + return document()->completeURL(url).protocolIs("https"); +} + +bool HTMLFormElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + if (!m_wasDemoted) + return HTMLElement::rendererIsNeeded(context); + + ContainerNode* node = parentNode(); + RenderObject* parentRenderer = node->renderer(); + bool parentIsTableElementPart = (parentRenderer->isTable() && node->hasTagName(tableTag)) + || (parentRenderer->isTableRow() && node->hasTagName(trTag)) + || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag)) + || (parentRenderer->isTableCol() && node->hasTagName(colTag)) + || (parentRenderer->isTableCell() && node->hasTagName(trTag)); + + if (!parentIsTableElementPart) + return true; + + EDisplay display = context.style()->display(); + bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP + || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW + || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL + || display == TABLE_CAPTION; + + return formIsTablePart; +} + +void HTMLFormElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + + if (hasID()) + document()->resetFormElementsOwner(); +} + +void HTMLFormElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + + if (hasID()) + document()->resetFormElementsOwner(); +} + +void HTMLFormElement::handleLocalEvents(Event* event) +{ + Node* targetNode = event->target()->toNode(); + if (event->eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) { + event->stopPropagation(); + return; + } + HTMLElement::handleLocalEvents(event); +} + +unsigned HTMLFormElement::length() const +{ + unsigned len = 0; + for (unsigned i = 0; i < m_associatedElements.size(); ++i) + if (m_associatedElements[i]->isEnumeratable()) + ++len; + return len; +} + +Node* HTMLFormElement::item(unsigned index) +{ + return elements()->item(index); +} + +void HTMLFormElement::submitImplicitly(Event* event, bool fromImplicitSubmissionTrigger) +{ + int submissionTriggerCount = 0; + for (unsigned i = 0; i < m_associatedElements.size(); ++i) { + FormAssociatedElement* formAssociatedElement = m_associatedElements[i]; + if (!formAssociatedElement->isFormControlElement()) + continue; + HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(formAssociatedElement); + if (formElement->isSuccessfulSubmitButton()) { + if (formElement->renderer()) { + formElement->dispatchSimulatedClick(event); + return; + } + } else if (formElement->canTriggerImplicitSubmission()) + ++submissionTriggerCount; + } + if (fromImplicitSubmissionTrigger && submissionTriggerCount == 1) + prepareForSubmission(event); +} + +static inline HTMLFormControlElement* submitElementFromEvent(const Event* event) +{ + Node* targetNode = event->target()->toNode(); + if (!targetNode || !targetNode->isElementNode()) + return 0; + Element* targetElement = static_cast<Element*>(targetNode); + if (!targetElement->isFormControlElement()) + return 0; + return static_cast<HTMLFormControlElement*>(targetElement); +} + +bool HTMLFormElement::validateInteractively(Event* event) +{ + ASSERT(event); + if (!document()->page() || !document()->page()->settings()->interactiveFormValidationEnabled() || noValidate()) + return true; + + HTMLFormControlElement* submitElement = submitElementFromEvent(event); + if (submitElement && submitElement->formNoValidate()) + return true; + + for (unsigned i = 0; i < m_associatedElements.size(); ++i) { + if (m_associatedElements[i]->isFormControlElement()) + static_cast<HTMLFormControlElement*>(m_associatedElements[i])->hideVisibleValidationMessage(); + } + + Vector<RefPtr<FormAssociatedElement> > unhandledInvalidControls; + if (!checkInvalidControlsAndCollectUnhandled(unhandledInvalidControls)) + return true; + // Because the form has invalid controls, we abort the form submission and + // show a validation message on a focusable form control. + + // Needs to update layout now because we'd like to call isFocusable(), which + // has !renderer()->needsLayout() assertion. + document()->updateLayoutIgnorePendingStylesheets(); + + RefPtr<HTMLFormElement> protector(this); + // Focus on the first focusable control and show a validation message. + for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) { + FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get(); + HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement); + if (unhandled->isFocusable() && unhandled->inDocument()) { + unhandled->scrollIntoViewIfNeeded(false); + unhandled->focus(); + if (unhandled->isFormControlElement()) + static_cast<HTMLFormControlElement*>(unhandled)->updateVisibleValidationMessage(); + break; + } + } + // Warn about all of unfocusable controls. + if (Frame* frame = document()->frame()) { + for (unsigned i = 0; i < unhandledInvalidControls.size(); ++i) { + FormAssociatedElement* unhandledAssociatedElement = unhandledInvalidControls[i].get(); + HTMLElement* unhandled = toHTMLElement(unhandledAssociatedElement); + if (unhandled->isFocusable() && unhandled->inDocument()) + continue; + String message("An invalid form control with name='%name' is not focusable."); + message.replace("%name", unhandledAssociatedElement->name()); + frame->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, ErrorMessageLevel, message, 0, document()->url().string()); + } + } + return false; +} + +bool HTMLFormElement::prepareForSubmission(Event* event) +{ + Frame* frame = document()->frame(); + if (m_isSubmittingOrPreparingForSubmission || !frame) + return m_isSubmittingOrPreparingForSubmission; + + m_isSubmittingOrPreparingForSubmission = true; + m_shouldSubmit = false; + + // Interactive validation must be done before dispatching the submit event. + if (!validateInteractively(event)) { + m_isSubmittingOrPreparingForSubmission = false; + return false; + } + + frame->loader()->client()->dispatchWillSendSubmitEvent(this); + + if (dispatchEvent(Event::create(eventNames().submitEvent, true, true))) + m_shouldSubmit = true; + + m_isSubmittingOrPreparingForSubmission = false; + + if (m_shouldSubmit) + submit(event, true, true, NotSubmittedByJavaScript); + + return m_shouldSubmit; +} + +void HTMLFormElement::submit() +{ + submit(0, false, true, NotSubmittedByJavaScript); +} + +void HTMLFormElement::submitFromJavaScript() +{ + submit(0, false, ScriptController::processingUserGesture(), SubmittedByJavaScript); +} + +void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger formSubmissionTrigger) +{ + FrameView* view = document()->view(); + Frame* frame = document()->frame(); + if (!view || !frame) + return; + + if (m_isSubmittingOrPreparingForSubmission) { + m_shouldSubmit = true; + return; + } + + m_isSubmittingOrPreparingForSubmission = true; + m_wasUserSubmitted = processingUserGesture; + + HTMLFormControlElement* firstSuccessfulSubmitButton = 0; + bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button? + + for (unsigned i = 0; i < m_associatedElements.size(); ++i) { + FormAssociatedElement* associatedElement = m_associatedElements[i]; + if (!associatedElement->isFormControlElement()) + continue; + if (needButtonActivation) { + HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(associatedElement); + if (control->isActivatedSubmit()) + needButtonActivation = false; + else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton()) + firstSuccessfulSubmitButton = control; + } + } + + if (needButtonActivation && firstSuccessfulSubmitButton) + firstSuccessfulSubmitButton->setActivatedSubmit(true); + + bool lockHistory = !processingUserGesture; + frame->loader()->submitForm(FormSubmission::create(this, m_attributes, event, lockHistory, formSubmissionTrigger)); + + if (needButtonActivation && firstSuccessfulSubmitButton) + firstSuccessfulSubmitButton->setActivatedSubmit(false); + + m_shouldSubmit = false; + m_isSubmittingOrPreparingForSubmission = false; +} + +void HTMLFormElement::reset() +{ + Frame* frame = document()->frame(); + if (m_isInResetFunction || !frame) + return; + + m_isInResetFunction = true; + + if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) { + m_isInResetFunction = false; + return; + } + + for (unsigned i = 0; i < m_associatedElements.size(); ++i) { + if (m_associatedElements[i]->isFormControlElement()) + static_cast<HTMLFormControlElement*>(m_associatedElements[i])->reset(); + } + + m_isInResetFunction = false; +} + +void HTMLFormElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == actionAttr) + m_attributes.parseAction(attr->value()); + else if (attr->name() == targetAttr) + m_attributes.setTarget(attr->value()); + else if (attr->name() == methodAttr) + m_attributes.updateMethodType(attr->value()); + else if (attr->name() == enctypeAttr) + m_attributes.updateEncodingType(attr->value()); + else if (attr->name() == accept_charsetAttr) + m_attributes.setAcceptCharset(attr->value()); + else if (attr->name() == autocompleteAttr) { + if (!shouldAutocomplete()) + document()->registerForPageCacheSuspensionCallbacks(this); + else + document()->unregisterForPageCacheSuspensionCallbacks(this); + } else if (attr->name() == onsubmitAttr) + setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == onresetAttr) + setAttributeEventListener(eventNames().resetEvent, createAttributeEventListener(this, attr)); + else + HTMLElement::parseMappedAttribute(attr); +} + +template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item) +{ + size_t size = vec.size(); + for (size_t i = 0; i != size; ++i) + if (vec[i] == item) { + vec.remove(i); + break; + } +} + +unsigned HTMLFormElement::formElementIndexWithFormAttribute(Element* element) +{ + // Compares the position of the form element and the inserted element. + // Updates the indeces in order to the relation of the position: + unsigned short position = compareDocumentPosition(element); + if (position & (DOCUMENT_POSITION_CONTAINS | DOCUMENT_POSITION_CONTAINED_BY)) + ++m_associatedElementsAfterIndex; + else if (position & DOCUMENT_POSITION_PRECEDING) { + ++m_associatedElementsBeforeIndex; + ++m_associatedElementsAfterIndex; + } + + if (m_associatedElements.isEmpty()) + return 0; + + // Does binary search on m_associatedElements in order to find the index + // to be inserted. + unsigned left = 0, right = m_associatedElements.size() - 1; + while (left != right) { + unsigned middle = left + ((right - left) / 2); + position = element->compareDocumentPosition(toHTMLElement(m_associatedElements[middle])); + if (position & DOCUMENT_POSITION_FOLLOWING) + right = middle; + else + left = middle + 1; + } + + position = element->compareDocumentPosition(toHTMLElement(m_associatedElements[left])); + if (position & DOCUMENT_POSITION_FOLLOWING) + return left; + return left + 1; +} + +unsigned HTMLFormElement::formElementIndex(FormAssociatedElement* associatedElement) +{ + HTMLElement* element = toHTMLElement(associatedElement); + // Treats separately the case where this element has the form attribute + // for performance consideration. + if (element->fastHasAttribute(formAttr)) + return formElementIndexWithFormAttribute(element); + + // Check for the special case where this element is the very last thing in + // the form's tree of children; we don't want to walk the entire tree in that + // common case that occurs during parsing; instead we'll just return a value + // that says "add this form element to the end of the array". + if (element->traverseNextNode(this)) { + unsigned i = m_associatedElementsBeforeIndex; + for (Node* node = this; node; node = node->traverseNextNode(this)) { + if (node == element) { + ++m_associatedElementsAfterIndex; + return i; + } + if (node->isHTMLElement() + && (static_cast<Element*>(node)->isFormControlElement() + || node->hasTagName(objectTag)) + && toHTMLElement(node)->form() == this) + ++i; + } + } + return m_associatedElementsAfterIndex++; +} + +void HTMLFormElement::registerFormElement(FormAssociatedElement* e) +{ + if (e->isFormControlElement()) { + HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(e); + document()->checkedRadioButtons().removeButton(element); + m_checkedRadioButtons.addButton(element); + } + m_associatedElements.insert(formElementIndex(e), e); +} + +void HTMLFormElement::removeFormElement(FormAssociatedElement* e) +{ + if (e->isFormControlElement()) + m_checkedRadioButtons.removeButton(static_cast<HTMLFormControlElement*>(e)); + unsigned index; + for (index = 0; index < m_associatedElements.size(); ++index) { + if (m_associatedElements[index] == e) + break; + } + ASSERT(index < m_associatedElements.size()); + if (index < m_associatedElementsBeforeIndex) + --m_associatedElementsBeforeIndex; + if (index < m_associatedElementsAfterIndex) + --m_associatedElementsAfterIndex; + removeFromVector(m_associatedElements, e); +} + +bool HTMLFormElement::isURLAttribute(Attribute* attr) const +{ + return attr->name() == actionAttr || HTMLElement::isURLAttribute(attr); +} + +void HTMLFormElement::registerImgElement(HTMLImageElement* e) +{ + ASSERT(m_imageElements.find(e) == notFound); + m_imageElements.append(e); +} + +void HTMLFormElement::removeImgElement(HTMLImageElement* e) +{ + ASSERT(m_imageElements.find(e) != notFound); + removeFromVector(m_imageElements, e); +} + +PassRefPtr<HTMLCollection> HTMLFormElement::elements() +{ + if (!m_elementsCollection) + m_elementsCollection = HTMLFormCollection::create(this); + return m_elementsCollection; +} + +String HTMLFormElement::name() const +{ + return getAttribute(nameAttr); +} + +bool HTMLFormElement::noValidate() const +{ + return fastHasAttribute(novalidateAttr); +} + +// FIXME: This function should be removed because it does not do the same thing as the +// JavaScript binding for action, which treats action as a URL attribute. Last time I +// (Darin Adler) removed this, someone added it back, so I am leaving it in for now. +String HTMLFormElement::action() const +{ + return getAttribute(actionAttr); +} + +void HTMLFormElement::setAction(const String &value) +{ + setAttribute(actionAttr, value); +} + +void HTMLFormElement::setEnctype(const String &value) +{ + setAttribute(enctypeAttr, value); +} + +String HTMLFormElement::method() const +{ + return FormSubmission::Attributes::methodString(m_attributes.method()); +} + +void HTMLFormElement::setMethod(const String &value) +{ + setAttribute(methodAttr, value); +} + +String HTMLFormElement::target() const +{ + return getAttribute(targetAttr); +} + +bool HTMLFormElement::wasUserSubmitted() const +{ + return m_wasUserSubmitted; +} + +HTMLFormControlElement* HTMLFormElement::defaultButton() const +{ + for (unsigned i = 0; i < m_associatedElements.size(); ++i) { + if (!m_associatedElements[i]->isFormControlElement()) + continue; + HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(m_associatedElements[i]); + if (control->isSuccessfulSubmitButton()) + return control; + } + + return 0; +} + +bool HTMLFormElement::checkValidity() +{ + Vector<RefPtr<FormAssociatedElement> > controls; + return !checkInvalidControlsAndCollectUnhandled(controls); +} + +bool HTMLFormElement::checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<FormAssociatedElement> >& unhandledInvalidControls) +{ + RefPtr<HTMLFormElement> protector(this); + // Copy m_associatedElements because event handlers called from + // HTMLFormControlElement::checkValidity() might change m_associatedElements. + Vector<RefPtr<FormAssociatedElement> > elements; + elements.reserveCapacity(m_associatedElements.size()); + for (unsigned i = 0; i < m_associatedElements.size(); ++i) + elements.append(m_associatedElements[i]); + bool hasInvalidControls = false; + for (unsigned i = 0; i < elements.size(); ++i) { + if (elements[i]->form() == this && elements[i]->isFormControlElement()) { + HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(elements[i].get()); + if (!control->checkValidity(&unhandledInvalidControls) && control->form() == this) + hasInvalidControls = true; + } + } + return hasInvalidControls; +} + +HTMLFormControlElement* HTMLFormElement::elementForAlias(const AtomicString& alias) +{ + if (alias.isEmpty() || !m_elementAliases) + return 0; + return m_elementAliases->get(alias.impl()).get(); +} + +void HTMLFormElement::addElementAlias(HTMLFormControlElement* element, const AtomicString& alias) +{ + if (alias.isEmpty()) + return; + if (!m_elementAliases) + m_elementAliases = adoptPtr(new AliasMap); + m_elementAliases->set(alias.impl(), element); +} + +void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) +{ + elements()->namedItems(name, namedItems); + + HTMLFormControlElement* aliasElement = elementForAlias(name); + if (aliasElement) { + if (namedItems.find(aliasElement) == notFound) { + // We have seen it before but it is gone now. Still, we need to return it. + // FIXME: The above comment is not clear enough; it does not say why we need to do this. + namedItems.append(aliasElement); + } + } + if (namedItems.size() && namedItems.first() != aliasElement) + addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name); +} + +void HTMLFormElement::documentDidResumeFromPageCache() +{ + ASSERT(!shouldAutocomplete()); + + for (unsigned i = 0; i < m_associatedElements.size(); ++i) { + if (m_associatedElements[i]->isFormControlElement()) + static_cast<HTMLFormControlElement*>(m_associatedElements[i])->reset(); + } +} + +void HTMLFormElement::didMoveToNewDocument(Document* oldDocument) +{ + if (!shouldAutocomplete()) { + if (oldDocument) + oldDocument->unregisterForPageCacheSuspensionCallbacks(this); + document()->registerForPageCacheSuspensionCallbacks(this); + } + + HTMLElement::didMoveToNewDocument(oldDocument); +} + +bool HTMLFormElement::shouldAutocomplete() const +{ + return !equalIgnoringCase(fastGetAttribute(autocompleteAttr), "off"); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLFormElement.h b/Source/WebCore/html/HTMLFormElement.h new file mode 100644 index 000000000..2dd8af1ff --- /dev/null +++ b/Source/WebCore/html/HTMLFormElement.h @@ -0,0 +1,173 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple 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 HTMLFormElement_h +#define HTMLFormElement_h + +#include "CheckedRadioButtons.h" +#include "FormState.h" +#include "FormSubmission.h" +#include "HTMLElement.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class Event; +class FormAssociatedElement; +class FormData; +class HTMLFormControlElement; +class HTMLImageElement; +class HTMLInputElement; +class HTMLFormCollection; +class TextEncoding; + +class HTMLFormElement : public HTMLElement { +public: + static PassRefPtr<HTMLFormElement> create(Document*); + static PassRefPtr<HTMLFormElement> create(const QualifiedName&, Document*); + virtual ~HTMLFormElement(); + + PassRefPtr<HTMLCollection> elements(); + void getNamedElements(const AtomicString&, Vector<RefPtr<Node> >&); + + unsigned length() const; + Node* item(unsigned index); + + String enctype() const { return m_attributes.encodingType(); } + void setEnctype(const String&); + + String encoding() const { return m_attributes.encodingType(); } + void setEncoding(const String& value) { setEnctype(value); } + + bool shouldAutocomplete() const; + + // FIXME: Should rename these two functions to say "form control" or "form-associated element" instead of "form element". + void registerFormElement(FormAssociatedElement*); + void removeFormElement(FormAssociatedElement*); + + void registerImgElement(HTMLImageElement*); + void removeImgElement(HTMLImageElement*); + + bool prepareForSubmission(Event*); + void submit(); + void submitFromJavaScript(); + void reset(); + + // Used to indicate a malformed state to keep from applying the bottom margin of the form. + // FIXME: Would probably be better to call this wasUnclosed; that's more specific. + void setMalformed(bool malformed) { m_wasMalformed = malformed; } + bool isMalformed() const { return m_wasMalformed; } + + void setDemoted(bool demoted) { m_wasDemoted = demoted; } + + void submitImplicitly(Event*, bool fromImplicitSubmissionTrigger); + bool formWouldHaveSecureSubmission(const String& url); + + String name() const; + + bool noValidate() const; + + String acceptCharset() const { return m_attributes.acceptCharset(); } + void setAcceptCharset(const String&); + + String action() const; + void setAction(const String&); + + String method() const; + void setMethod(const String&); + + virtual String target() const; + + bool wasUserSubmitted() const; + + HTMLFormControlElement* defaultButton() const; + + bool checkValidity(); + + HTMLFormControlElement* elementForAlias(const AtomicString&); + void addElementAlias(HTMLFormControlElement*, const AtomicString& alias); + + CheckedRadioButtons& checkedRadioButtons() { return m_checkedRadioButtons; } + + const Vector<FormAssociatedElement*>& associatedElements() const { return m_associatedElements; } + +private: + HTMLFormElement(const QualifiedName&, Document*); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + virtual void handleLocalEvents(Event*); + + virtual void parseMappedAttribute(Attribute*); + + virtual bool isURLAttribute(Attribute*) const; + + virtual void documentDidResumeFromPageCache(); + + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + virtual bool shouldRegisterAsNamedItem() const OVERRIDE { return true; } + + void submit(Event*, bool activateSubmitButton, bool processingUserGesture, FormSubmissionTrigger); + + unsigned formElementIndexWithFormAttribute(Element*); + unsigned formElementIndex(FormAssociatedElement*); + + // Returns true if the submission should proceed. + bool validateInteractively(Event*); + + // Validates each of the controls, and stores controls of which 'invalid' + // event was not canceled to the specified vector. Returns true if there + // are any invalid controls in this form. + bool checkInvalidControlsAndCollectUnhandled(Vector<RefPtr<FormAssociatedElement> >&); + + friend class HTMLFormCollection; + + typedef HashMap<RefPtr<AtomicStringImpl>, RefPtr<HTMLFormControlElement> > AliasMap; + + FormSubmission::Attributes m_attributes; + OwnPtr<AliasMap> m_elementAliases; + RefPtr<HTMLFormCollection> m_elementsCollection; + + CheckedRadioButtons m_checkedRadioButtons; + + unsigned m_associatedElementsBeforeIndex; + unsigned m_associatedElementsAfterIndex; + Vector<FormAssociatedElement*> m_associatedElements; + Vector<HTMLImageElement*> m_imageElements; + + bool m_wasUserSubmitted; + bool m_isSubmittingOrPreparingForSubmission; + bool m_shouldSubmit; + + bool m_isInResetFunction; + + bool m_wasMalformed; + bool m_wasDemoted; +}; + +} // namespace WebCore + +#endif // HTMLFormElement_h diff --git a/Source/WebCore/html/HTMLFormElement.idl b/Source/WebCore/html/HTMLFormElement.idl new file mode 100644 index 000000000..c6967eacf --- /dev/null +++ b/Source/WebCore/html/HTMLFormElement.idl @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + HasIndexGetter, + HasOverridingNameGetter + ] HTMLFormElement : HTMLElement { + readonly attribute HTMLCollection elements; + readonly attribute long length; + + attribute [Reflect] DOMString name; + attribute [Reflect] boolean noValidate; + attribute [Reflect=accept_charset] DOMString acceptCharset; + attribute [Reflect, URL] DOMString action; + attribute [ConvertNullToNullString] DOMString encoding; + attribute [ConvertNullToNullString] DOMString enctype; + attribute [ConvertNullToNullString] DOMString method; + attribute [Reflect] DOMString target; + attribute [Reflect] DOMString autocomplete; + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + [ImplementationFunction=submitFromJavaScript] void submit(); +#else + void submit(); +#endif + void reset(); + boolean checkValidity(); + }; + +} diff --git a/Source/WebCore/html/HTMLFrameElement.cpp b/Source/WebCore/html/HTMLFrameElement.cpp new file mode 100644 index 000000000..34ede8df8 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameElement.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2009 Apple 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 "HTMLFrameElement.h" + +#include "Attribute.h" +#include "Frame.h" +#include "HTMLFrameSetElement.h" +#include "HTMLNames.h" +#include "RenderFrame.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLFrameElement::HTMLFrameElement(const QualifiedName& tagName, Document* document) + : HTMLFrameElementBase(tagName, document) + , m_frameBorder(true) + , m_frameBorderSet(false) +{ + ASSERT(hasTagName(frameTag)); +} + +PassRefPtr<HTMLFrameElement> HTMLFrameElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLFrameElement(tagName, document)); +} + +bool HTMLFrameElement::rendererIsNeeded(const NodeRenderingContext&) +{ + // For compatibility, frames render even when display: none is set. + return isURLAllowed(); +} + +RenderObject* HTMLFrameElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderFrame(this); +} + +static inline HTMLFrameSetElement* containingFrameSetElement(Node* node) +{ + while ((node = node->parentNode())) { + if (node->hasTagName(framesetTag)) + return static_cast<HTMLFrameSetElement*>(node); + } + return 0; +} + +bool HTMLFrameElement::noResize() const +{ + return hasAttribute(noresizeAttr); +} + +void HTMLFrameElement::attach() +{ + HTMLFrameElementBase::attach(); + + if (HTMLFrameSetElement* frameSetElement = containingFrameSetElement(this)) { + if (!m_frameBorderSet) + m_frameBorder = frameSetElement->hasFrameBorder(); + } +} + +void HTMLFrameElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == frameborderAttr) { + m_frameBorder = attr->value().toInt(); + m_frameBorderSet = !attr->isNull(); + // FIXME: If we are already attached, this has no effect. + } else if (attr->name() == noresizeAttr) { + if (renderer()) + renderer()->updateFromElement(); + } else + HTMLFrameElementBase::parseMappedAttribute(attr); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLFrameElement.h b/Source/WebCore/html/HTMLFrameElement.h new file mode 100644 index 000000000..3b0761a7a --- /dev/null +++ b/Source/WebCore/html/HTMLFrameElement.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2009 Apple 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 HTMLFrameElement_h +#define HTMLFrameElement_h + +#include "HTMLFrameElementBase.h" + +namespace WebCore { + +class HTMLFrameElement : public HTMLFrameElementBase { +public: + static PassRefPtr<HTMLFrameElement> create(const QualifiedName&, Document*); + + bool hasFrameBorder() const { return m_frameBorder; } + + bool noResize() const; + +private: + HTMLFrameElement(const QualifiedName&, Document*); + + virtual void attach(); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + virtual void parseMappedAttribute(Attribute*); + +#if ENABLE(FULLSCREEN_API) + virtual bool allowFullScreen() const { return false; } +#endif + + bool m_frameBorder; + bool m_frameBorderSet; +}; + +} // namespace WebCore + +#endif // HTMLFrameElement_h diff --git a/Source/WebCore/html/HTMLFrameElement.idl b/Source/WebCore/html/HTMLFrameElement.idl new file mode 100644 index 000000000..dfe4ef89c --- /dev/null +++ b/Source/WebCore/html/HTMLFrameElement.idl @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLFrameElement : HTMLElement { + + attribute [Reflect] DOMString frameBorder; + attribute [Reflect] DOMString longDesc; + attribute [Reflect] DOMString marginHeight; + attribute [Reflect] DOMString marginWidth; + attribute [Reflect] DOMString name; + attribute [Reflect] boolean noResize; + attribute [Reflect] DOMString scrolling; + attribute [Reflect, URL] DOMString src; + + // Introduced in DOM Level 2: + readonly attribute [CheckFrameSecurity] Document contentDocument; + + // Extensions + readonly attribute DOMWindow contentWindow; + +#if defined(ENABLE_SVG) && ENABLE_SVG +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C || defined(ENABLE_SVG_DOM_OBJC_BINDINGS) && ENABLE_SVG_DOM_OBJC_BINDINGS + [SVGCheckSecurityDocument] SVGDocument getSVGDocument() + raises(DOMException); +#endif +#endif + + attribute [ConvertNullToNullString, CustomSetter] DOMString location; + + readonly attribute long width; + readonly attribute long height; + + }; + +} diff --git a/Source/WebCore/html/HTMLFrameElementBase.cpp b/Source/WebCore/html/HTMLFrameElementBase.cpp new file mode 100644 index 000000000..f5d22a418 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameElementBase.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2008, 2009 Apple 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 "HTMLFrameElementBase.h" + +#include "Attribute.h" +#include "Document.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "KURL.h" +#include "Page.h" +#include "RenderPart.h" +#include "ScriptController.h" +#include "ScriptEventListener.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +// Helper to check if the Frame's document contains elements that can instantiate plugins. +// Does a recursive check for nested Frames too. +static bool hasPluginElements(Frame* frame) +{ + if (!frame) + return false; + + // Search for a plugin element in this document. + Document* document = frame->document(); + for (Node* node = document->firstChild(); node; node = node->traverseNextNode(document)) { + if (!node->isElementNode()) + continue; + + Element* element = static_cast<Element*>(node); + if (element->hasLocalName(embedTag) || element->hasLocalName(objectTag)) + return true; + } + + // Do the same for the nested frames. + for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { + if (hasPluginElements(child)) + return true; + } + + return false; +} + +HTMLFrameElementBase::HTMLFrameElementBase(const QualifiedName& tagName, Document* document) + : HTMLFrameOwnerElement(tagName, document) + , m_scrolling(ScrollbarAuto) + , m_marginWidth(-1) + , m_marginHeight(-1) + , m_checkInDocumentTimer(this, &HTMLFrameElementBase::checkInDocumentTimerFired) + , m_viewSource(false) + , m_remainsAliveOnRemovalFromTree(false) +{ +} + +bool HTMLFrameElementBase::isURLAllowed() const +{ + if (m_URL.isEmpty()) + return true; + + const KURL& completeURL = document()->completeURL(m_URL); + + if (protocolIsJavaScript(completeURL)) { + Document* contentDoc = this->contentDocument(); + if (contentDoc && !ScriptController::canAccessFromCurrentOrigin(contentDoc->frame())) + return false; + } + + if (Frame* parentFrame = document()->frame()) { + if (parentFrame->page()->frameCount() >= Page::maxNumberOfFrames) + return false; + } + + // We allow one level of self-reference because some sites depend on that. + // But we don't allow more than one. + bool foundSelfReference = false; + for (Frame* frame = document()->frame(); frame; frame = frame->tree()->parent()) { + if (equalIgnoringFragmentIdentifier(frame->document()->url(), completeURL)) { + if (foundSelfReference) + return false; + foundSelfReference = true; + } + } + + return true; +} + +void HTMLFrameElementBase::openURL(bool lockHistory, bool lockBackForwardList) +{ + if (!isURLAllowed()) + return; + + if (m_URL.isEmpty()) + m_URL = blankURL().string(); + + Frame* parentFrame = document()->frame(); + if (!parentFrame) + return; + + parentFrame->loader()->subframeLoader()->requestFrame(this, m_URL, m_frameName, lockHistory, lockBackForwardList); + if (contentFrame()) + contentFrame()->setInViewSourceMode(viewSourceMode()); +} + +void HTMLFrameElementBase::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == srcAttr) + setLocation(stripLeadingAndTrailingHTMLSpaces(attr->value())); + else if (isIdAttributeName(attr->name())) { + // Important to call through to base for the id attribute so the hasID bit gets set. + HTMLFrameOwnerElement::parseMappedAttribute(attr); + m_frameName = attr->value(); + } else if (attr->name() == nameAttr) { + m_frameName = attr->value(); + // FIXME: If we are already attached, this doesn't actually change the frame's name. + // FIXME: If we are already attached, this doesn't check for frame name + // conflicts and generate a unique frame name. + } else if (attr->name() == marginwidthAttr) { + m_marginWidth = attr->value().toInt(); + // FIXME: If we are already attached, this has no effect. + } else if (attr->name() == marginheightAttr) { + m_marginHeight = attr->value().toInt(); + // FIXME: If we are already attached, this has no effect. + } else if (attr->name() == scrollingAttr) { + // Auto and yes both simply mean "allow scrolling." No means "don't allow scrolling." + if (equalIgnoringCase(attr->value(), "auto") || equalIgnoringCase(attr->value(), "yes")) + m_scrolling = document()->frameElementsShouldIgnoreScrolling() ? ScrollbarAlwaysOff : ScrollbarAuto; + else if (equalIgnoringCase(attr->value(), "no")) + m_scrolling = ScrollbarAlwaysOff; + // FIXME: If we are already attached, this has no effect. + } else if (attr->name() == viewsourceAttr) { + m_viewSource = !attr->isNull(); + if (contentFrame()) + contentFrame()->setInViewSourceMode(viewSourceMode()); + } else if (attr->name() == onloadAttr) + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == onbeforeloadAttr) + setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == onbeforeunloadAttr) { + // FIXME: should <frame> elements have beforeunload handlers? + setAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(this, attr)); + } else + HTMLFrameOwnerElement::parseMappedAttribute(attr); +} + +void HTMLFrameElementBase::setNameAndOpenURL() +{ + m_frameName = getAttribute(nameAttr); + if (m_frameName.isNull()) + m_frameName = getIdAttribute(); + openURL(); +} + +void HTMLFrameElementBase::updateOnReparenting() +{ + ASSERT(m_remainsAliveOnRemovalFromTree); + + if (Frame* frame = contentFrame()) + frame->transferChildFrameToNewDocument(); +} + +void HTMLFrameElementBase::insertedIntoDocument() +{ + HTMLFrameOwnerElement::insertedIntoDocument(); + + if (m_remainsAliveOnRemovalFromTree) { + updateOnReparenting(); + setRemainsAliveOnRemovalFromTree(false); + return; + } + // DocumentFragments don't kick of any loads. + if (!document()->frame()) + return; + + // Loads may cause synchronous javascript execution (e.g. beforeload or + // src=javascript), which could try to access the renderer before the normal + // parser machinery would call lazyAttach() and set us as needing style + // resolve. Any code which expects this to be attached will resolve style + // before using renderer(), so this will make sure we attach in time. + // FIXME: Normally lazyAttach marks the renderer as attached(), but we don't + // want to do that here, as as callers expect to call attach() right after + // this and attach() will ASSERT(!attached()) + ASSERT(!renderer()); // This recalc is unecessary if we already have a renderer. + lazyAttach(DoNotSetAttached); + setNameAndOpenURL(); +} + +void HTMLFrameElementBase::attach() +{ + HTMLFrameOwnerElement::attach(); + + if (RenderPart* part = renderPart()) { + if (Frame* frame = contentFrame()) + part->setWidget(frame->view()); + } +} + +KURL HTMLFrameElementBase::location() const +{ + return document()->completeURL(getAttribute(srcAttr)); +} + +void HTMLFrameElementBase::setLocation(const String& str) +{ + Settings* settings = document()->settings(); + if (settings && settings->needsAcrobatFrameReloadingQuirk() && m_URL == str) + return; + + m_URL = AtomicString(str); + + if (inDocument()) + openURL(false, false); +} + +bool HTMLFrameElementBase::supportsFocus() const +{ + return true; +} + +void HTMLFrameElementBase::setFocus(bool received) +{ + HTMLFrameOwnerElement::setFocus(received); + if (Page* page = document()->page()) { + if (received) + page->focusController()->setFocusedFrame(contentFrame()); + else if (page->focusController()->focusedFrame() == contentFrame()) // Focus may have already been given to another frame, don't take it away. + page->focusController()->setFocusedFrame(0); + } +} + +bool HTMLFrameElementBase::isURLAttribute(Attribute *attr) const +{ + return attr->name() == srcAttr || HTMLFrameOwnerElement::isURLAttribute(attr); +} + +int HTMLFrameElementBase::width() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (!renderBox()) + return 0; + return renderBox()->width(); +} + +int HTMLFrameElementBase::height() +{ + document()->updateLayoutIgnorePendingStylesheets(); + if (!renderBox()) + return 0; + return renderBox()->height(); +} + +// Some types of content can restrict the ability to move the iframes between pages. +// For example, the plugin infrastructure of an embedder may associate the plugin instances +// with the top-level Frame for tracking various resources and failure to transfer those +// resources correctly may lead to crashes and other ill effects (https://bugs.webkit.org/show_bug.cgi?id=68267) +bool HTMLFrameElementBase::canRemainAliveOnRemovalFromTree() +{ + return !hasPluginElements(contentFrame()); +} + +void HTMLFrameElementBase::setRemainsAliveOnRemovalFromTree(bool value) +{ + ASSERT(!value || canRemainAliveOnRemovalFromTree()); + m_remainsAliveOnRemovalFromTree = value; + + // There is a possibility that JS will do document.adoptNode() on this element but will not insert it into the tree. + // Start the async timer that is normally stopped by attach(). If it's not stopped and fires, it'll unload the frame. + if (value) + m_checkInDocumentTimer.startOneShot(0); + else + m_checkInDocumentTimer.stop(); +} + +void HTMLFrameElementBase::checkInDocumentTimerFired(Timer<HTMLFrameElementBase>*) +{ + ASSERT(!attached()); + ASSERT(m_remainsAliveOnRemovalFromTree); + + m_remainsAliveOnRemovalFromTree = false; + willRemove(); +} + +void HTMLFrameElementBase::willRemove() +{ + if (m_remainsAliveOnRemovalFromTree) + return; + + HTMLFrameOwnerElement::willRemove(); +} + +#if ENABLE(FULLSCREEN_API) +bool HTMLFrameElementBase::allowFullScreen() const +{ + return hasAttribute(webkitallowfullscreenAttr); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLFrameElementBase.h b/Source/WebCore/html/HTMLFrameElementBase.h new file mode 100644 index 000000000..0f587c9c8 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameElementBase.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2008, 2009 Apple 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 HTMLFrameElementBase_h +#define HTMLFrameElementBase_h + +#include "HTMLFrameOwnerElement.h" +#include "ScrollTypes.h" + +namespace WebCore { + +class HTMLFrameElementBase : public HTMLFrameOwnerElement { +public: + KURL location() const; + void setLocation(const String&); + + virtual ScrollbarMode scrollingMode() const { return m_scrolling; } + + int marginWidth() const { return m_marginWidth; } + int marginHeight() const { return m_marginHeight; } + + int width(); + int height(); + + bool canRemainAliveOnRemovalFromTree(); + void setRemainsAliveOnRemovalFromTree(bool); +#if ENABLE(FULLSCREEN_API) + virtual bool allowFullScreen() const; +#endif + + virtual bool canContainRangeEndPoint() const { return false; } + +protected: + HTMLFrameElementBase(const QualifiedName&, Document*); + + bool isURLAllowed() const; + + virtual void parseMappedAttribute(Attribute*); + virtual void insertedIntoDocument(); + virtual void attach(); + +private: + virtual bool supportsFocus() const; + virtual void setFocus(bool); + + virtual bool isURLAttribute(Attribute*) const; + virtual bool isFrameElementBase() const { return true; } + + virtual void willRemove(); + void checkInDocumentTimerFired(Timer<HTMLFrameElementBase>*); + void updateOnReparenting(); + + bool viewSourceMode() const { return m_viewSource; } + + void setNameAndOpenURL(); + void openURL(bool lockHistory = true, bool lockBackForwardList = true); + + AtomicString m_URL; + AtomicString m_frameName; + + ScrollbarMode m_scrolling; + + int m_marginWidth; + int m_marginHeight; + + // This is a performance optimization some call "magic iframe" which avoids + // tearing down the frame hierarchy when a web page calls adoptNode on a + // frame owning element but does not immediately insert it into the new + // document before JavaScript yields to WebCore. If the element is not yet + // in a document by the time this timer fires, the frame hierarchy teardown + // will continue. This can also be seen as implementation of: + // "Removing an iframe from a Document does not cause its browsing context + // to be discarded. Indeed, an iframe's browsing context can survive its + // original parent Document if its iframe is moved to another Document." + // From HTML5: http://www.whatwg.org/specs/web-apps/current-work/multipage/the-iframe-element.html#the-iframe-element + Timer<HTMLFrameElementBase> m_checkInDocumentTimer; + + bool m_viewSource; + bool m_remainsAliveOnRemovalFromTree; +}; + +} // namespace WebCore + +#endif // HTMLFrameElementBase_h diff --git a/Source/WebCore/html/HTMLFrameOwnerElement.cpp b/Source/WebCore/html/HTMLFrameOwnerElement.cpp new file mode 100644 index 000000000..b84c449b5 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameOwnerElement.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2006, 2007, 2009 Apple 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 "HTMLFrameOwnerElement.h" + +#include "DOMWindow.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "RenderPart.h" + +#if ENABLE(SVG) +#include "ExceptionCode.h" +#include "SVGDocument.h" +#endif + +namespace WebCore { + +HTMLFrameOwnerElement::HTMLFrameOwnerElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_contentFrame(0) + , m_sandboxFlags(SandboxNone) +{ +} + +RenderPart* HTMLFrameOwnerElement::renderPart() const +{ + // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers + // when using fallback content. + if (!renderer() || !renderer()->isRenderPart()) + return 0; + return toRenderPart(renderer()); +} + +void HTMLFrameOwnerElement::willRemove() +{ + // FIXME: It is unclear why this can't be moved to removedFromDocument() + // this is the only implementation of willRemove in WebCore! + if (Frame* frame = contentFrame()) { + RefPtr<Frame> protect(frame); + frame->loader()->frameDetached(); + frame->disconnectOwnerElement(); + } + + HTMLElement::willRemove(); +} + +HTMLFrameOwnerElement::~HTMLFrameOwnerElement() +{ + if (m_contentFrame) + m_contentFrame->disconnectOwnerElement(); +} + +Document* HTMLFrameOwnerElement::contentDocument() const +{ + return m_contentFrame ? m_contentFrame->document() : 0; +} + +DOMWindow* HTMLFrameOwnerElement::contentWindow() const +{ + return m_contentFrame ? m_contentFrame->domWindow() : 0; +} + +void HTMLFrameOwnerElement::setSandboxFlags(SandboxFlags flags) +{ + m_sandboxFlags = flags; +} + +bool HTMLFrameOwnerElement::isKeyboardFocusable(KeyboardEvent* event) const +{ + return m_contentFrame && HTMLElement::isKeyboardFocusable(event); +} + +#if ENABLE(SVG) +SVGDocument* HTMLFrameOwnerElement::getSVGDocument(ExceptionCode& ec) const +{ + Document* doc = contentDocument(); + if (doc && doc->isSVGDocument()) + return static_cast<SVGDocument*>(doc); + // Spec: http://www.w3.org/TR/SVG/struct.html#InterfaceGetSVGDocument + ec = NOT_SUPPORTED_ERR; + return 0; +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLFrameOwnerElement.h b/Source/WebCore/html/HTMLFrameOwnerElement.h new file mode 100644 index 000000000..de9d15320 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameOwnerElement.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2007, 2009 Apple 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 HTMLFrameOwnerElement_h +#define HTMLFrameOwnerElement_h + +#include "FrameLoaderTypes.h" +#include "HTMLElement.h" + +namespace WebCore { + +class DOMWindow; +class Frame; +class RenderPart; + +#if ENABLE(SVG) +class SVGDocument; +#endif + +class HTMLFrameOwnerElement : public HTMLElement { +public: + virtual ~HTMLFrameOwnerElement(); + + Frame* contentFrame() const { return m_contentFrame; } + DOMWindow* contentWindow() const; + Document* contentDocument() const; + + // Most subclasses use RenderPart (either RenderEmbeddedObject or RenderIFrame) + // except for HTMLObjectElement and HTMLEmbedElement which may return any + // RenderObject when using fallback content. + RenderPart* renderPart() const; + +#if ENABLE(SVG) + SVGDocument* getSVGDocument(ExceptionCode&) const; +#endif + + virtual ScrollbarMode scrollingMode() const { return ScrollbarAuto; } + + SandboxFlags sandboxFlags() const { return m_sandboxFlags; } + +protected: + HTMLFrameOwnerElement(const QualifiedName& tagName, Document*); + + void setSandboxFlags(SandboxFlags); + + virtual void willRemove(); + +private: + friend class Frame; + + virtual bool isFrameOwnerElement() const { return true; } + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + + Frame* m_contentFrame; + SandboxFlags m_sandboxFlags; +}; + +} // namespace WebCore + +#endif // HTMLFrameOwnerElement_h diff --git a/Source/WebCore/html/HTMLFrameSetElement.cpp b/Source/WebCore/html/HTMLFrameSetElement.cpp new file mode 100644 index 000000000..978a72d7a --- /dev/null +++ b/Source/WebCore/html/HTMLFrameSetElement.cpp @@ -0,0 +1,226 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2009, 2010 Apple 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 "HTMLFrameSetElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLNames.h" +#include "Length.h" +#include "MouseEvent.h" +#include "NodeRenderingContext.h" +#include "RenderFrameSet.h" +#include "ScriptEventListener.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLFrameSetElement::HTMLFrameSetElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_totalRows(1) + , m_totalCols(1) + , m_border(6) + , m_borderSet(false) + , m_borderColorSet(false) + , m_frameborder(true) + , m_frameborderSet(false) + , m_noresize(false) +{ + ASSERT(hasTagName(framesetTag)); + + setHasCustomWillOrDidRecalcStyle(); +} + +PassRefPtr<HTMLFrameSetElement> HTMLFrameSetElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLFrameSetElement(tagName, document)); +} + +bool HTMLFrameSetElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == bordercolorAttr) { + result = eUniversal; + return true; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLFrameSetElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == rowsAttr) { + if (!attr->isNull()) { + m_rowLengths = newLengthArray(attr->value().string(), m_totalRows); + setNeedsStyleRecalc(); + } + } else if (attr->name() == colsAttr) { + if (!attr->isNull()) { + m_colLengths = newLengthArray(attr->value().string(), m_totalCols); + setNeedsStyleRecalc(); + } + } else if (attr->name() == frameborderAttr) { + if (!attr->isNull()) { + // false or "no" or "0".. + if (attr->value().toInt() == 0) { + m_frameborder = false; + m_border = 0; + } + m_frameborderSet = true; + } else { + m_frameborder = false; + m_frameborderSet = false; + } + } else if (attr->name() == noresizeAttr) { + m_noresize = true; + } else if (attr->name() == borderAttr) { + if (!attr->isNull()) { + m_border = attr->value().toInt(); + if (!m_border) + m_frameborder = false; + m_borderSet = true; + } else + m_borderSet = false; + } else if (attr->name() == bordercolorAttr) { + m_borderColorSet = attr->decl(); + if (!attr->decl() && !attr->isEmpty()) { + addCSSColor(attr, CSSPropertyBorderColor, attr->value()); + m_borderColorSet = true; + } + } else if (attr->name() == onloadAttr) + document()->setWindowAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onbeforeunloadAttr) + document()->setWindowAttributeEventListener(eventNames().beforeunloadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onunloadAttr) + document()->setWindowAttributeEventListener(eventNames().unloadEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onblurAttr) + document()->setWindowAttributeEventListener(eventNames().blurEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onfocusAttr) + document()->setWindowAttributeEventListener(eventNames().focusEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onfocusinAttr) + document()->setWindowAttributeEventListener(eventNames().focusinEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onfocusoutAttr) + document()->setWindowAttributeEventListener(eventNames().focusoutEvent, createAttributeEventListener(document()->frame(), attr)); +#if ENABLE(ORIENTATION_EVENTS) + else if (attr->name() == onorientationchangeAttr) + document()->setWindowAttributeEventListener(eventNames().orientationchangeEvent, createAttributeEventListener(document()->frame(), attr)); +#endif + else if (attr->name() == onhashchangeAttr) + document()->setWindowAttributeEventListener(eventNames().hashchangeEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onresizeAttr) + document()->setWindowAttributeEventListener(eventNames().resizeEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onscrollAttr) + document()->setWindowAttributeEventListener(eventNames().scrollEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onstorageAttr) + document()->setWindowAttributeEventListener(eventNames().storageEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == ononlineAttr) + document()->setWindowAttributeEventListener(eventNames().onlineEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onofflineAttr) + document()->setWindowAttributeEventListener(eventNames().offlineEvent, createAttributeEventListener(document()->frame(), attr)); + else if (attr->name() == onpopstateAttr) + document()->setWindowAttributeEventListener(eventNames().popstateEvent, createAttributeEventListener(document()->frame(), attr)); + else + HTMLElement::parseMappedAttribute(attr); +} + +bool HTMLFrameSetElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + // For compatibility, frames render even when display: none is set. + // However, we delay creating a renderer until stylesheets have loaded. + return context.style()->isStyleAvailable(); +} + +RenderObject *HTMLFrameSetElement::createRenderer(RenderArena *arena, RenderStyle *style) +{ + if (style->hasContent()) + return RenderObject::createObject(this, style); + + return new (arena) RenderFrameSet(this); +} + +void HTMLFrameSetElement::attach() +{ + // Inherit default settings from parent frameset + // FIXME: This is not dynamic. + for (ContainerNode* node = parentNode(); node; node = node->parentNode()) { + if (node->hasTagName(framesetTag)) { + HTMLFrameSetElement* frameset = static_cast<HTMLFrameSetElement*>(node); + if (!m_frameborderSet) + m_frameborder = frameset->hasFrameBorder(); + if (m_frameborder) { + if (!m_borderSet) + m_border = frameset->border(); + if (!m_borderColorSet) + m_borderColorSet = frameset->hasBorderColor(); + } + if (!m_noresize) + m_noresize = frameset->noResize(); + break; + } + } + + HTMLElement::attach(); +} + +void HTMLFrameSetElement::defaultEventHandler(Event* evt) +{ + if (evt->isMouseEvent() && !m_noresize && renderer() && renderer()->isFrameSet()) { + if (toRenderFrameSet(renderer())->userResize(static_cast<MouseEvent*>(evt))) { + evt->setDefaultHandled(); + return; + } + } + HTMLElement::defaultEventHandler(evt); +} + +bool HTMLFrameSetElement::willRecalcStyle(StyleChange) +{ + if (needsStyleRecalc() && renderer()) { + renderer()->setNeedsLayout(true); + clearNeedsStyleRecalc(); + } + return true; +} + +void HTMLFrameSetElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + if (Frame* frame = document()->frame()) + frame->loader()->client()->dispatchDidBecomeFrameset(document()->isFrameSet()); +} + +void HTMLFrameSetElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + if (Frame* frame = document()->frame()) + frame->loader()->client()->dispatchDidBecomeFrameset(document()->isFrameSet()); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLFrameSetElement.h b/Source/WebCore/html/HTMLFrameSetElement.h new file mode 100644 index 000000000..698897408 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameSetElement.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2009, 2010 Apple 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 HTMLFrameSetElement_h +#define HTMLFrameSetElement_h + +#include <wtf/OwnArrayPtr.h> +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLFrameSetElement : public HTMLElement { +public: + static PassRefPtr<HTMLFrameSetElement> create(const QualifiedName&, Document*); + + bool hasFrameBorder() const { return m_frameborder; } + bool noResize() const { return m_noresize; } + + int totalRows() const { return m_totalRows; } + int totalCols() const { return m_totalCols; } + int border() const { return m_border; } + + bool hasBorderColor() const { return m_borderColorSet; } + + const Length* rowLengths() const { return m_rowLengths.get(); } + const Length* colLengths() const { return m_colLengths.get(); } + + // Declared virtual in Element + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(blur); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(error); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(focus); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(load); + + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(beforeunload); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(hashchange); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(message); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(offline); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(online); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(popstate); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(resize); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(storage); + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(unload); +#if ENABLE(ORIENTATION_EVENTS) + DEFINE_WINDOW_ATTRIBUTE_EVENT_LISTENER(orientationchange); +#endif + +private: + HTMLFrameSetElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(Attribute*); + + virtual void attach(); + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + virtual void defaultEventHandler(Event*); + + virtual bool willRecalcStyle(StyleChange); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + OwnArrayPtr<Length> m_rowLengths; + OwnArrayPtr<Length> m_colLengths; + + int m_totalRows; + int m_totalCols; + + int m_border; + bool m_borderSet; + + bool m_borderColorSet; + + bool m_frameborder; + bool m_frameborderSet; + bool m_noresize; +}; + +} // namespace WebCore + +#endif // HTMLFrameSetElement_h diff --git a/Source/WebCore/html/HTMLFrameSetElement.idl b/Source/WebCore/html/HTMLFrameSetElement.idl new file mode 100644 index 000000000..db26410b8 --- /dev/null +++ b/Source/WebCore/html/HTMLFrameSetElement.idl @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserve + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + HasOverridingNameGetter + ] HTMLFrameSetElement : HTMLElement { + attribute [Reflect] DOMString cols; + attribute [Reflect] DOMString rows; + +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C + // Event handler attributes + attribute [DontEnum, WindowEventListener] EventListener onbeforeunload; + attribute [DontEnum, WindowEventListener] EventListener onhashchange; + attribute [DontEnum, WindowEventListener] EventListener onmessage; + attribute [DontEnum, WindowEventListener] EventListener onoffline; + attribute [DontEnum, WindowEventListener] EventListener ononline; + attribute [DontEnum, WindowEventListener] EventListener onpopstate; + attribute [DontEnum, WindowEventListener] EventListener onresize; + attribute [DontEnum, WindowEventListener] EventListener onstorage; + attribute [DontEnum, WindowEventListener] EventListener onunload; + + attribute [Conditional=ORIENTATION_EVENTS, DontEnum] EventListener onorientationchange; + + // Overrides of Element attributes (with different implementation in bindings). + attribute [DontEnum, WindowEventListener] EventListener onblur; + attribute [DontEnum, WindowEventListener] EventListener onerror; + attribute [DontEnum, WindowEventListener] EventListener onfocus; + attribute [DontEnum, WindowEventListener] EventListener onload; + + // Not implemented yet. + // attribute [DontEnum, WindowEventListener] EventListener onafterprint; + // attribute [DontEnum, WindowEventListener] EventListener onbeforeprint; + // attribute [DontEnum, WindowEventListener] EventListener onredo; + // attribute [DontEnum, WindowEventListener] EventListener onundo; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLHRElement.cpp b/Source/WebCore/html/HTMLHRElement.cpp new file mode 100644 index 000000000..44aa05266 --- /dev/null +++ b/Source/WebCore/html/HTMLHRElement.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLHRElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLHRElement::HTMLHRElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(hrTag)); +} + +PassRefPtr<HTMLHRElement> HTMLHRElement::create(Document* document) +{ + return adoptRef(new HTMLHRElement(hrTag, document)); +} + +PassRefPtr<HTMLHRElement> HTMLHRElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLHRElement(tagName, document)); +} + +bool HTMLHRElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == alignAttr || + attrName == widthAttr || + attrName == colorAttr || + attrName == sizeAttr || + attrName == noshadeAttr) { + result = eHR; + return false; + } + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLHRElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == alignAttr) { + if (equalIgnoringCase(attr->value(), "left")) { + addCSSProperty(attr, CSSPropertyMarginLeft, "0"); + addCSSProperty(attr, CSSPropertyMarginRight, CSSValueAuto); + } else if (equalIgnoringCase(attr->value(), "right")) { + addCSSProperty(attr, CSSPropertyMarginLeft, CSSValueAuto); + addCSSProperty(attr, CSSPropertyMarginRight, "0"); + } else { + addCSSProperty(attr, CSSPropertyMarginLeft, CSSValueAuto); + addCSSProperty(attr, CSSPropertyMarginRight, CSSValueAuto); + } + } else if (attr->name() == widthAttr) { + bool ok; + int v = attr->value().toInt(&ok); + if (ok && !v) + addCSSLength(attr, CSSPropertyWidth, "1"); + else + addCSSLength(attr, CSSPropertyWidth, attr->value()); + } else if (attr->name() == colorAttr) { + addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid); + addCSSColor(attr, CSSPropertyBorderColor, attr->value()); + addCSSColor(attr, CSSPropertyBackgroundColor, attr->value()); + } else if (attr->name() == noshadeAttr) { + addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid); + addCSSColor(attr, CSSPropertyBorderColor, String("grey")); + addCSSColor(attr, CSSPropertyBackgroundColor, String("grey")); + } else if (attr->name() == sizeAttr) { + StringImpl* si = attr->value().impl(); + int size = si->toInt(); + if (size <= 1) + addCSSProperty(attr, CSSPropertyBorderBottomWidth, String("0")); + else + addCSSLength(attr, CSSPropertyHeight, String::number(size-2)); + } else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLHRElement.h b/Source/WebCore/html/HTMLHRElement.h new file mode 100644 index 000000000..37c1190f9 --- /dev/null +++ b/Source/WebCore/html/HTMLHRElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLHRElement_h +#define HTMLHRElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLHRElement : public HTMLElement { +public: + static PassRefPtr<HTMLHRElement> create(Document*); + static PassRefPtr<HTMLHRElement> create(const QualifiedName&, Document*); + + virtual bool canContainRangeEndPoint() const { return hasChildNodes(); } + +private: + HTMLHRElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace WebCore + +#endif // HTMLHRElement_h diff --git a/Source/WebCore/html/HTMLHRElement.idl b/Source/WebCore/html/HTMLHRElement.idl new file mode 100644 index 000000000..23a57da10 --- /dev/null +++ b/Source/WebCore/html/HTMLHRElement.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLHRElement : HTMLElement { + attribute [Reflect] DOMString align; + attribute [Reflect] boolean noShade; + attribute [Reflect] DOMString size; + attribute [Reflect] DOMString width; + }; + +} diff --git a/Source/WebCore/html/HTMLHeadElement.cpp b/Source/WebCore/html/HTMLHeadElement.cpp new file mode 100644 index 000000000..8218311be --- /dev/null +++ b/Source/WebCore/html/HTMLHeadElement.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2010 Apple 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 "HTMLHeadElement.h" + +#include "HTMLNames.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLHeadElement::HTMLHeadElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(headTag)); +} + +PassRefPtr<HTMLHeadElement> HTMLHeadElement::create(Document* document) +{ + return adoptRef(new HTMLHeadElement(headTag, document)); +} + +PassRefPtr<HTMLHeadElement> HTMLHeadElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLHeadElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLHeadElement.h b/Source/WebCore/html/HTMLHeadElement.h new file mode 100644 index 000000000..1a5df864c --- /dev/null +++ b/Source/WebCore/html/HTMLHeadElement.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2010 Apple 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 HTMLHeadElement_h +#define HTMLHeadElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLHeadElement : public HTMLElement { +public: + static PassRefPtr<HTMLHeadElement> create(Document*); + static PassRefPtr<HTMLHeadElement> create(const QualifiedName&, Document*); + +private: + HTMLHeadElement(const QualifiedName&, Document*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLHeadElement.idl b/Source/WebCore/html/HTMLHeadElement.idl new file mode 100644 index 000000000..59bdbf0e7 --- /dev/null +++ b/Source/WebCore/html/HTMLHeadElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLHeadElement : HTMLElement { + attribute [Reflect] DOMString profile; + }; + +} diff --git a/Source/WebCore/html/HTMLHeadingElement.cpp b/Source/WebCore/html/HTMLHeadingElement.cpp new file mode 100644 index 000000000..47a9ea378 --- /dev/null +++ b/Source/WebCore/html/HTMLHeadingElement.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLHeadingElement.h" + +namespace WebCore { + +inline HTMLHeadingElement::HTMLHeadingElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ +} + +PassRefPtr<HTMLHeadingElement> HTMLHeadingElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLHeadingElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLHeadingElement.h b/Source/WebCore/html/HTMLHeadingElement.h new file mode 100644 index 000000000..f09cfe3b1 --- /dev/null +++ b/Source/WebCore/html/HTMLHeadingElement.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLHeadingElement_h +#define HTMLHeadingElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLHeadingElement : public HTMLElement { +public: + static PassRefPtr<HTMLHeadingElement> create(const QualifiedName&, Document*); + +private: + HTMLHeadingElement(const QualifiedName&, Document*); +}; + +} // namespace WebCore + +#endif // HTMLHeadingElement_h diff --git a/Source/WebCore/html/HTMLHeadingElement.idl b/Source/WebCore/html/HTMLHeadingElement.idl new file mode 100644 index 000000000..e419c1c7d --- /dev/null +++ b/Source/WebCore/html/HTMLHeadingElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLHeadingElement : HTMLElement { + attribute [Reflect] DOMString align; + }; + +} diff --git a/Source/WebCore/html/HTMLHtmlElement.cpp b/Source/WebCore/html/HTMLHtmlElement.cpp new file mode 100644 index 000000000..a27b0dfc5 --- /dev/null +++ b/Source/WebCore/html/HTMLHtmlElement.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2010 Apple 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 "HTMLHtmlElement.h" + +#include "ApplicationCacheHost.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "DocumentParser.h" +#include "Frame.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLHtmlElement::HTMLHtmlElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(htmlTag)); +} + +PassRefPtr<HTMLHtmlElement> HTMLHtmlElement::create(Document* document) +{ + return adoptRef(new HTMLHtmlElement(htmlTag, document)); +} + +PassRefPtr<HTMLHtmlElement> HTMLHtmlElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLHtmlElement(tagName, document)); +} + +bool HTMLHtmlElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == manifestAttr || HTMLElement::isURLAttribute(attribute); +} + +void HTMLHtmlElement::insertedByParser() +{ + // When parsing a fragment, its dummy document has a null parser. + if (!document()->parser() || !document()->parser()->documentWasLoadedAsPartOfNavigation()) + return; + + if (!document()->frame()) + return; + + DocumentLoader* documentLoader = document()->frame()->loader()->documentLoader(); + if (!documentLoader) + return; + + const AtomicString& manifest = getAttribute(manifestAttr); + if (manifest.isEmpty()) + documentLoader->applicationCacheHost()->selectCacheWithoutManifest(); + else + documentLoader->applicationCacheHost()->selectCacheWithManifest(document()->completeURL(manifest)); +} + +} diff --git a/Source/WebCore/html/HTMLHtmlElement.h b/Source/WebCore/html/HTMLHtmlElement.h new file mode 100644 index 000000000..982627a5a --- /dev/null +++ b/Source/WebCore/html/HTMLHtmlElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2010 Apple 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 HTMLHtmlElement_h +#define HTMLHtmlElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLHtmlElement : public HTMLElement { +public: + static PassRefPtr<HTMLHtmlElement> create(Document*); + static PassRefPtr<HTMLHtmlElement> create(const QualifiedName&, Document*); + + void insertedByParser(); + +private: + HTMLHtmlElement(const QualifiedName&, Document*); + + virtual bool isURLAttribute(Attribute*) const; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLHtmlElement.idl b/Source/WebCore/html/HTMLHtmlElement.idl new file mode 100644 index 000000000..03c661cc8 --- /dev/null +++ b/Source/WebCore/html/HTMLHtmlElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLHtmlElement : HTMLElement { + attribute [Reflect] DOMString version; + attribute [Reflect, URL] DOMString manifest; + }; + +} diff --git a/Source/WebCore/html/HTMLIFrameElement.cpp b/Source/WebCore/html/HTMLIFrameElement.cpp new file mode 100644 index 000000000..ae60a1e74 --- /dev/null +++ b/Source/WebCore/html/HTMLIFrameElement.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann (hausmann@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Ericsson AB. 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 "HTMLIFrameElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "Frame.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "NodeRenderingContext.h" +#include "RenderIFrame.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLIFrameElement::HTMLIFrameElement(const QualifiedName& tagName, Document* document) + : HTMLFrameElementBase(tagName, document) +{ + ASSERT(hasTagName(iframeTag)); +} + +PassRefPtr<HTMLIFrameElement> HTMLIFrameElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLIFrameElement(tagName, document)); +} + +bool HTMLIFrameElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr || attrName == heightAttr) { + result = eUniversal; + return false; + } + + if (attrName == alignAttr) { + result = eReplaced; // Share with <img> since the alignment behavior is the same. + return false; + } + + if (attrName == frameborderAttr) { + result = eReplaced; + return false; + } + + return HTMLFrameElementBase::mapToEntry(attrName, result); +} + +void HTMLIFrameElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == widthAttr) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + else if (attr->name() == heightAttr) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + else if (attr->name() == alignAttr) + addHTMLAlignment(attr); + else if (attr->name() == nameAttr) { + const AtomicString& newName = attr->value(); + if (inDocument() && document()->isHTMLDocument()) { + HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); + document->removeExtraNamedItem(m_name); + document->addExtraNamedItem(newName); + } + m_name = newName; + } else if (attr->name() == frameborderAttr) { + // Frame border doesn't really match the HTML4 spec definition for iframes. It simply adds + // a presentational hint that the border should be off if set to zero. + if (!attr->isNull() && !attr->value().toInt()) + // Add a rule that nulls out our border width. + addCSSLength(attr, CSSPropertyBorderWidth, "0"); + } else if (attr->name() == sandboxAttr) + setSandboxFlags(attr->isNull() ? SandboxNone : SecurityContext::parseSandboxPolicy(attr->value())); + else + HTMLFrameElementBase::parseMappedAttribute(attr); +} + +bool HTMLIFrameElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + return isURLAllowed() && context.style()->display() != NONE; +} + +RenderObject* HTMLIFrameElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderIFrame(this); +} + +void HTMLIFrameElement::insertedIntoDocument() +{ + if (document()->isHTMLDocument()) + static_cast<HTMLDocument*>(document())->addExtraNamedItem(m_name); + + HTMLFrameElementBase::insertedIntoDocument(); +} + +void HTMLIFrameElement::removedFromDocument() +{ + if (document()->isHTMLDocument()) + static_cast<HTMLDocument*>(document())->removeExtraNamedItem(m_name); + + HTMLFrameElementBase::removedFromDocument(); +} + +#if ENABLE(MICRODATA) +String HTMLIFrameElement::itemValueText() const +{ + return getURLAttribute(srcAttr); +} + +void HTMLIFrameElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(srcAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLIFrameElement.h b/Source/WebCore/html/HTMLIFrameElement.h new file mode 100644 index 000000000..a7aecf81d --- /dev/null +++ b/Source/WebCore/html/HTMLIFrameElement.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2004, 2006, 2008, 2009 Apple 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 HTMLIFrameElement_h +#define HTMLIFrameElement_h + +#include "HTMLFrameElementBase.h" + +namespace WebCore { + +class HTMLIFrameElement : public HTMLFrameElementBase { +public: + static PassRefPtr<HTMLIFrameElement> create(const QualifiedName&, Document*); + +private: + HTMLIFrameElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + AtomicString m_name; +}; + +} // namespace WebCore + +#endif // HTMLIFrameElement_h diff --git a/Source/WebCore/html/HTMLIFrameElement.idl b/Source/WebCore/html/HTMLIFrameElement.idl new file mode 100644 index 000000000..7f9e25a97 --- /dev/null +++ b/Source/WebCore/html/HTMLIFrameElement.idl @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLIFrameElement : HTMLElement { + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString frameBorder; + attribute [Reflect] DOMString height; + attribute [Reflect] DOMString longDesc; + attribute [Reflect] DOMString marginHeight; + attribute [Reflect] DOMString marginWidth; + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString sandbox; + attribute [Reflect] DOMString scrolling; + attribute [Reflect, URL] DOMString src; + attribute [Reflect] DOMString width; + + // Introduced in DOM Level 2: + readonly attribute [CheckFrameSecurity] Document contentDocument; + + // Extensions + readonly attribute DOMWindow contentWindow; + +#if defined(ENABLE_SVG) && ENABLE_SVG +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C || defined(ENABLE_SVG_DOM_OBJC_BINDINGS) && ENABLE_SVG_DOM_OBJC_BINDINGS + [SVGCheckSecurityDocument] SVGDocument getSVGDocument() + raises(DOMException); +#endif +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLImageElement.cpp b/Source/WebCore/html/HTMLImageElement.cpp new file mode 100644 index 000000000..adc9eb904 --- /dev/null +++ b/Source/WebCore/html/HTMLImageElement.cpp @@ -0,0 +1,384 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 "HTMLImageElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "EventNames.h" +#include "FrameView.h" +#include "HTMLDocument.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "RenderImage.h" +#include "ScriptEventListener.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +HTMLImageElement::HTMLImageElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLElement(tagName, document) + , m_imageLoader(this) + , m_form(form) + , m_compositeOperator(CompositeSourceOver) +{ + ASSERT(hasTagName(imgTag)); + if (form) + form->registerImgElement(this); +} + +PassRefPtr<HTMLImageElement> HTMLImageElement::create(Document* document) +{ + return adoptRef(new HTMLImageElement(imgTag, document)); +} + +PassRefPtr<HTMLImageElement> HTMLImageElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLImageElement(tagName, document, form)); +} + +HTMLImageElement::~HTMLImageElement() +{ + if (m_form) + m_form->removeImgElement(this); +} + +PassRefPtr<HTMLImageElement> HTMLImageElement::createForJSConstructor(Document* document, const int* optionalWidth, const int* optionalHeight) +{ + RefPtr<HTMLImageElement> image = adoptRef(new HTMLImageElement(imgTag, document)); + if (optionalWidth) + image->setWidth(*optionalWidth); + if (optionalHeight > 0) + image->setHeight(*optionalHeight); + return image.release(); +} + +bool HTMLImageElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr || + attrName == heightAttr || + attrName == vspaceAttr || + attrName == hspaceAttr || + attrName == valignAttr) { + result = eUniversal; + return false; + } + + if (attrName == borderAttr || attrName == alignAttr) { + result = eReplaced; // Shared with embed and iframe elements. + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLImageElement::parseMappedAttribute(Attribute* attr) +{ + const QualifiedName& attrName = attr->name(); + if (attrName == altAttr) { + if (renderer() && renderer()->isImage()) + toRenderImage(renderer())->updateAltText(); + } else if (attrName == srcAttr) + m_imageLoader.updateFromElementIgnoringPreviousError(); + else if (attrName == widthAttr) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + else if (attrName == heightAttr) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + else if (attrName == borderAttr) { + // border="noborder" -> border="0" + applyBorderAttribute(attr); + } else if (attrName == vspaceAttr) { + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + } else if (attrName == hspaceAttr) { + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + } else if (attrName == alignAttr) + addHTMLAlignment(attr); + else if (attrName == valignAttr) + addCSSProperty(attr, CSSPropertyVerticalAlign, attr->value()); + else if (attrName == usemapAttr) + setIsLink(!attr->isNull()); + else if (attrName == onabortAttr) + setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr)); + else if (attrName == onloadAttr) + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); + else if (attrName == onbeforeloadAttr) + setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); + else if (attrName == compositeAttr) { + if (!parseCompositeOperator(attr->value(), m_compositeOperator)) + m_compositeOperator = CompositeSourceOver; + } else + HTMLElement::parseMappedAttribute(attr); +} + +String HTMLImageElement::altText() const +{ + // lets figure out the alt text.. magic stuff + // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen + // also heavily discussed by Hixie on bugzilla + String alt = getAttribute(altAttr); + // fall back to title attribute + if (alt.isNull()) + alt = getAttribute(titleAttr); + return alt; +} + +RenderObject* HTMLImageElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + if (style->hasContent()) + return RenderObject::createObject(this, style); + + RenderImage* image = new (arena) RenderImage(this); + image->setImageResource(RenderImageResource::create()); + return image; +} + +void HTMLImageElement::attach() +{ + HTMLElement::attach(); + + if (renderer() && renderer()->isImage() && m_imageLoader.haveFiredBeforeLoadEvent()) { + RenderImage* renderImage = toRenderImage(renderer()); + RenderImageResource* renderImageResource = renderImage->imageResource(); + if (renderImageResource->hasImage()) + return; + renderImageResource->setCachedImage(m_imageLoader.image()); + + // If we have no image at all because we have no src attribute, set + // image height and width for the alt text instead. + if (!m_imageLoader.image() && !renderImageResource->cachedImage()) + renderImage->setImageSizeForAltText(); + } +} + +void HTMLImageElement::insertedIntoDocument() +{ + // If we have been inserted from a renderer-less document, + // our loader may have not fetched the image, so do it now. + if (!m_imageLoader.image()) + m_imageLoader.updateFromElement(); + + HTMLElement::insertedIntoDocument(); +} + +void HTMLImageElement::insertedIntoTree(bool deep) +{ + if (!m_form) { + // m_form can be non-null if it was set in constructor. + for (ContainerNode* ancestor = parentNode(); ancestor; ancestor = ancestor->parentNode()) { + if (ancestor->hasTagName(formTag)) { + m_form = static_cast<HTMLFormElement*>(ancestor); + m_form->registerImgElement(this); + break; + } + } + } + + HTMLElement::insertedIntoTree(deep); +} + +void HTMLImageElement::removedFromTree(bool deep) +{ + if (m_form) + m_form->removeImgElement(this); + m_form = 0; + HTMLElement::removedFromTree(deep); +} + +int HTMLImageElement::width(bool ignorePendingStylesheets) +{ + if (!renderer()) { + // check the attribute first for an explicit pixel value + bool ok; + int width = getAttribute(widthAttr).toInt(&ok); + if (ok) + return width; + + // if the image is available, use its width + if (m_imageLoader.image()) + return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).width(); + } + + if (ignorePendingStylesheets) + document()->updateLayoutIgnorePendingStylesheets(); + else + document()->updateLayout(); + + RenderBox* box = renderBox(); + return box ? adjustForAbsoluteZoom(box->contentWidth(), box) : 0; +} + +int HTMLImageElement::height(bool ignorePendingStylesheets) +{ + if (!renderer()) { + // check the attribute first for an explicit pixel value + bool ok; + int height = getAttribute(heightAttr).toInt(&ok); + if (ok) + return height; + + // if the image is available, use its height + if (m_imageLoader.image()) + return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).height(); + } + + if (ignorePendingStylesheets) + document()->updateLayoutIgnorePendingStylesheets(); + else + document()->updateLayout(); + + RenderBox* box = renderBox(); + return box ? adjustForAbsoluteZoom(box->contentHeight(), box) : 0; +} + +int HTMLImageElement::naturalWidth() const +{ + if (!m_imageLoader.image()) + return 0; + + return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).width(); +} + +int HTMLImageElement::naturalHeight() const +{ + if (!m_imageLoader.image()) + return 0; + + return m_imageLoader.image()->imageSizeForRenderer(renderer(), 1.0f).height(); +} + +bool HTMLImageElement::isURLAttribute(Attribute* attr) const +{ + return attr->name() == srcAttr + || attr->name() == lowsrcAttr + || attr->name() == longdescAttr + || (attr->name() == usemapAttr && attr->value().string()[0] != '#') + || HTMLElement::isURLAttribute(attr); +} + +const AtomicString& HTMLImageElement::alt() const +{ + return getAttribute(altAttr); +} + +bool HTMLImageElement::draggable() const +{ + // Image elements are draggable by default. + return !equalIgnoringCase(getAttribute(draggableAttr), "false"); +} + +void HTMLImageElement::setHeight(int value) +{ + setAttribute(heightAttr, String::number(value)); +} + +KURL HTMLImageElement::src() const +{ + return document()->completeURL(getAttribute(srcAttr)); +} + +void HTMLImageElement::setSrc(const String& value) +{ + setAttribute(srcAttr, value); +} + +void HTMLImageElement::setWidth(int value) +{ + setAttribute(widthAttr, String::number(value)); +} + +int HTMLImageElement::x() const +{ + RenderObject* r = renderer(); + if (!r) + return 0; + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = r->localToAbsolute(); + return absPos.x(); +} + +int HTMLImageElement::y() const +{ + RenderObject* r = renderer(); + if (!r) + return 0; + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = r->localToAbsolute(); + return absPos.y(); +} + +bool HTMLImageElement::complete() const +{ + return m_imageLoader.imageComplete(); +} + +void HTMLImageElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, src()); + // FIXME: What about when the usemap attribute begins with "#"? + addSubresourceURL(urls, document()->completeURL(getAttribute(usemapAttr))); +} + +void HTMLImageElement::didMoveToNewDocument(Document* oldDocument) +{ + m_imageLoader.elementDidMoveToNewDocument(); + HTMLElement::didMoveToNewDocument(oldDocument); +} + +bool HTMLImageElement::isServerMap() const +{ + if (!fastHasAttribute(ismapAttr)) + return false; + + const AtomicString& usemap = fastGetAttribute(usemapAttr); + + // If the usemap attribute starts with '#', it refers to a map element in the document. + if (usemap.string()[0] == '#') + return false; + + return document()->completeURL(stripLeadingAndTrailingHTMLSpaces(usemap)).isEmpty(); +} + +#if ENABLE(MICRODATA) +String HTMLImageElement::itemValueText() const +{ + return getURLAttribute(srcAttr); +} + +void HTMLImageElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(srcAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLImageElement.h b/Source/WebCore/html/HTMLImageElement.h new file mode 100644 index 000000000..8cb3ee901 --- /dev/null +++ b/Source/WebCore/html/HTMLImageElement.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 HTMLImageElement_h +#define HTMLImageElement_h + +#include "GraphicsTypes.h" +#include "HTMLElement.h" +#include "HTMLImageLoader.h" + +namespace WebCore { + +class HTMLFormElement; + +class HTMLImageElement : public HTMLElement { + friend class HTMLFormElement; +public: + static PassRefPtr<HTMLImageElement> create(Document*); + static PassRefPtr<HTMLImageElement> create(const QualifiedName&, Document*, HTMLFormElement*); + static PassRefPtr<HTMLImageElement> createForJSConstructor(Document*, const int* optionalWidth, const int* optionalHeight); + + virtual ~HTMLImageElement(); + + int width(bool ignorePendingStylesheets = false); + int height(bool ignorePendingStylesheets = false); + + int naturalWidth() const; + int naturalHeight() const; + + bool isServerMap() const; + + String altText() const; + + CompositeOperator compositeOperator() const { return m_compositeOperator; } + + CachedImage* cachedImage() const { return m_imageLoader.image(); } + void setCachedImage(CachedImage* i) { m_imageLoader.setImage(i); }; + + void setLoadManually(bool loadManually) { m_imageLoader.setLoadManually(loadManually); } + + const AtomicString& alt() const; + + void setHeight(int); + + KURL src() const; + void setSrc(const String&); + + void setWidth(int); + + int x() const; + int y() const; + + bool complete() const; + + bool haveFiredLoadEvent() const { return m_imageLoader.haveFiredLoadEvent(); } + bool hasPendingActivity() const { return !m_imageLoader.haveFiredLoadEvent(); } + + virtual bool canContainRangeEndPoint() const { return false; } + +protected: + HTMLImageElement(const QualifiedName&, Document*, HTMLFormElement* = 0); + + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + +private: + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(Attribute*); + + virtual void attach(); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + virtual bool canStartSelection() const { return false; } + + virtual bool isURLAttribute(Attribute*) const; + + virtual bool draggable() const; + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + virtual void insertedIntoDocument(); + virtual void insertedIntoTree(bool deep); + virtual void removedFromTree(bool deep); + + virtual bool shouldRegisterAsNamedItem() const OVERRIDE { return true; } + virtual bool shouldRegisterAsExtraNamedItem() const OVERRIDE { return true; } + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + HTMLImageLoader m_imageLoader; + HTMLFormElement* m_form; + CompositeOperator m_compositeOperator; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLImageElement.idl b/Source/WebCore/html/HTMLImageElement.idl new file mode 100644 index 000000000..c6a88ea9d --- /dev/null +++ b/Source/WebCore/html/HTMLImageElement.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2006, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + GenerateNativeConverter + ] HTMLImageElement : HTMLElement { + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString alt; + attribute [Reflect] DOMString border; + attribute [Reflect] DOMString crossOrigin; + attribute long height; + attribute [Reflect] long hspace; + attribute [Reflect] boolean isMap; + attribute [Reflect, URL] DOMString longDesc; + attribute [Reflect, URL] DOMString src; + attribute [Reflect] DOMString useMap; + attribute [Reflect] long vspace; + attribute long width; + + // Extensions + readonly attribute boolean complete; + attribute [Reflect,URL] DOMString lowsrc; + readonly attribute long naturalHeight; + readonly attribute long naturalWidth; + readonly attribute long x; + readonly attribute long y; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extension: + readonly attribute DOMString altDisplayString; + readonly attribute URL absoluteImageURL; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLImageLoader.cpp b/Source/WebCore/html/HTMLImageLoader.cpp new file mode 100644 index 000000000..77ee1e7d8 --- /dev/null +++ b/Source/WebCore/html/HTMLImageLoader.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 "HTMLImageLoader.h" + +#include "CachedImage.h" +#include "Element.h" +#include "Event.h" +#include "EventNames.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLParserIdioms.h" +#include "Settings.h" + +#if USE(JSC) +#include "JSDOMWindowBase.h" +#include <runtime/JSLock.h> +#endif + +namespace WebCore { + +HTMLImageLoader::HTMLImageLoader(Element* node) + : ImageLoader(node) +{ +} + +HTMLImageLoader::~HTMLImageLoader() +{ +} + +void HTMLImageLoader::dispatchLoadEvent() +{ + // HTMLVideoElement uses this class to load the poster image, but it should not fire events for loading or failure. + if (element()->hasTagName(HTMLNames::videoTag)) + return; + + bool errorOccurred = image()->errorOccurred(); + if (!errorOccurred && image()->response().httpStatusCode() >= 400) + errorOccurred = element()->hasTagName(HTMLNames::objectTag); // An <object> considers a 404 to be an error and should fire onerror. + element()->dispatchEvent(Event::create(errorOccurred ? eventNames().errorEvent : eventNames().loadEvent, false, false)); +} + +String HTMLImageLoader::sourceURI(const AtomicString& attr) const +{ +#if ENABLE(DASHBOARD_SUPPORT) + Settings* settings = element()->document()->settings(); + if (settings && settings->usesDashboardBackwardCompatibilityMode() && attr.length() > 7 && attr.startsWith("url(\"") && attr.endsWith("\")")) + return attr.string().substring(5, attr.length() - 7); +#endif + + return stripLeadingAndTrailingHTMLSpaces(attr); +} + +void HTMLImageLoader::notifyFinished(CachedResource*) +{ + CachedImage* cachedImage = image(); + + Element* elem = element(); + ImageLoader::notifyFinished(cachedImage); + + bool loadError = cachedImage->errorOccurred() || cachedImage->response().httpStatusCode() >= 400; +#if USE(JSC) + if (!loadError) { + if (!elem->inDocument()) { + JSC::JSLock lock(JSC::SilenceAssertionsOnly); + JSC::JSGlobalData* globalData = JSDOMWindowBase::commonJSGlobalData(); + globalData->heap.reportExtraMemoryCost(cachedImage->encodedSize()); + } + } +#endif + + if (loadError && elem->hasTagName(HTMLNames::objectTag)) + static_cast<HTMLObjectElement*>(elem)->renderFallbackContent(); +} + +} diff --git a/Source/WebCore/html/HTMLImageLoader.h b/Source/WebCore/html/HTMLImageLoader.h new file mode 100644 index 000000000..d3b606871 --- /dev/null +++ b/Source/WebCore/html/HTMLImageLoader.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004 Apple Computer, Inc. + * + * 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 HTMLImageLoader_h +#define HTMLImageLoader_h + +#include "ImageLoader.h" + +namespace WebCore { + +class HTMLImageLoader : public ImageLoader { +public: + HTMLImageLoader(Element*); + virtual ~HTMLImageLoader(); + + virtual void dispatchLoadEvent(); + virtual String sourceURI(const AtomicString&) const; + + virtual void notifyFinished(CachedResource*); +}; + +} + +#endif diff --git a/Source/WebCore/html/HTMLInputElement.cpp b/Source/WebCore/html/HTMLInputElement.cpp new file mode 100644 index 000000000..46d1049bd --- /dev/null +++ b/Source/WebCore/html/HTMLInputElement.cpp @@ -0,0 +1,1845 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * 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 "HTMLInputElement.h" + +#include "AXObjectCache.h" +#include "BeforeTextInsertedEvent.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "Document.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FileList.h" +#include "Frame.h" +#include "HTMLCollection.h" +#include "HTMLDataListElement.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "HTMLParserIdioms.h" +#include "InputType.h" +#include "KeyboardEvent.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "NumberInputType.h" +#include "RenderTextControlSingleLine.h" +#include "RenderTheme.h" +#include "SearchInputType.h" +#include "ScriptEventListener.h" +#include "WheelEvent.h" +#include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> + +#if ENABLE(INPUT_COLOR) +#include "ColorInputType.h" +#endif + +#if ENABLE(INPUT_SPEECH) +#include "RuntimeEnabledFeatures.h" +#endif + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: According to HTML4, the length attribute's value can be arbitrarily +// large. However, due to https://bugs.webkit.org/show_bug.cgi?id=14536 things +// get rather sluggish when a text field has a larger number of characters than +// this, even when just clicking in the text field. +const int HTMLInputElement::maximumLength = 524288; +const int defaultSize = 20; +const int maxSavedResults = 256; + +HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser) + : HTMLTextFormControlElement(tagName, document, form) + , m_size(defaultSize) + , m_maxLength(maximumLength) + , m_maxResults(-1) + , m_isChecked(false) + , m_reflectsCheckedAttribute(true) + , m_isIndeterminate(false) + , m_hasType(false) + , m_isActivatedSubmit(false) + , m_autocomplete(Uninitialized) + , m_isAutofilled(false) + , m_stateRestored(false) + , m_parsingInProgress(createdByParser) + , m_wasModifiedByUser(false) + , m_canReceiveDroppedFiles(false) + , m_inputType(InputType::createText(this)) +{ + ASSERT(hasTagName(inputTag) || hasTagName(isindexTag)); +} + +PassRefPtr<HTMLInputElement> HTMLInputElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser) +{ + RefPtr<HTMLInputElement> inputElement = adoptRef(new HTMLInputElement(tagName, document, form, createdByParser)); + inputElement->createShadowSubtree(); + return inputElement.release(); +} + +void HTMLInputElement::createShadowSubtree() +{ + m_inputType->createShadowSubtree(); +} + +HTMLInputElement::~HTMLInputElement() +{ + if (needsSuspensionCallback()) + document()->unregisterForPageCacheSuspensionCallbacks(this); + + document()->checkedRadioButtons().removeButton(this); + + // Need to remove this from the form while it is still an HTMLInputElement, + // so can't wait for the base class's destructor to do it. + removeFromForm(); +} + +const AtomicString& HTMLInputElement::formControlName() const +{ + return m_name.isNull() ? emptyAtom : m_name; +} + +HTMLElement* HTMLInputElement::containerElement() const +{ + return m_inputType->containerElement(); +} + +HTMLElement* HTMLInputElement::innerTextElement() const +{ + return m_inputType->innerTextElement(); +} + +HTMLElement* HTMLInputElement::innerBlockElement() const +{ + return m_inputType->innerBlockElement(); +} + +HTMLElement* HTMLInputElement::innerSpinButtonElement() const +{ + return m_inputType->innerSpinButtonElement(); +} + +HTMLElement* HTMLInputElement::resultsButtonElement() const +{ + return m_inputType->resultsButtonElement(); +} + +HTMLElement* HTMLInputElement::cancelButtonElement() const +{ + return m_inputType->cancelButtonElement(); +} + +#if ENABLE(INPUT_SPEECH) +HTMLElement* HTMLInputElement::speechButtonElement() const +{ + return m_inputType->speechButtonElement(); +} +#endif + +HTMLElement* HTMLInputElement::placeholderElement() const +{ + return m_inputType->placeholderElement(); +} + +bool HTMLInputElement::shouldAutocomplete() const +{ + if (m_autocomplete != Uninitialized) + return m_autocomplete == On; + return HTMLTextFormControlElement::shouldAutocomplete(); +} + +void HTMLInputElement::updateCheckedRadioButtons() +{ + if (attached() && checked()) + checkedRadioButtons().addButton(this); + + if (form()) { + const Vector<FormAssociatedElement*>& controls = form()->associatedElements(); + for (unsigned i = 0; i < controls.size(); ++i) { + if (!controls[i]->isFormControlElement()) + continue; + HTMLFormControlElement* control = static_cast<HTMLFormControlElement*>(controls[i]); + if (control->name() != name()) + continue; + if (control->type() != type()) + continue; + control->setNeedsValidityCheck(); + } + } else { + typedef Document::FormElementListHashSet::const_iterator Iterator; + Iterator end = document()->formElements()->end(); + for (Iterator it = document()->formElements()->begin(); it != end; ++it) { + HTMLFormControlElementWithState* control = *it; + if (control->formControlName() != name()) + continue; + if (control->formControlType() != type()) + continue; + if (control->form()) + continue; + control->setNeedsValidityCheck(); + } + } +} + +bool HTMLInputElement::isValidValue(const String& value) const +{ + if (!m_inputType->canSetStringValue()) { + ASSERT_NOT_REACHED(); + return false; + } + return !m_inputType->typeMismatchFor(value) + && !stepMismatch(value) + && !rangeUnderflow(value) + && !rangeOverflow(value) + && !tooLong(value, IgnoreDirtyFlag) + && !patternMismatch(value) + && !valueMissing(value); +} + +bool HTMLInputElement::typeMismatch() const +{ + return m_inputType->typeMismatch(); +} + +bool HTMLInputElement::valueMissing(const String& value) const +{ + if (!isRequiredFormControl() || readOnly() || disabled()) + return false; + return m_inputType->valueMissing(value); +} + +bool HTMLInputElement::patternMismatch(const String& value) const +{ + return m_inputType->patternMismatch(value); +} + +bool HTMLInputElement::tooLong(const String& value, NeedsToCheckDirtyFlag check) const +{ + // We use isTextType() instead of supportsMaxLength() because of the + // 'virtual' overhead. + if (!isTextType()) + return false; + int max = maxLength(); + if (max < 0) + return false; + if (check == CheckDirtyFlag) { + // Return false for the default value or a value set by a script even if + // it is longer than maxLength. + if (!hasDirtyValue() || !m_wasModifiedByUser) + return false; + } + return numGraphemeClusters(value) > static_cast<unsigned>(max); +} + +bool HTMLInputElement::rangeUnderflow(const String& value) const +{ + return m_inputType->rangeUnderflow(value); +} + +bool HTMLInputElement::rangeOverflow(const String& value) const +{ + return m_inputType->rangeOverflow(value); +} + +double HTMLInputElement::minimum() const +{ + return m_inputType->minimum(); +} + +double HTMLInputElement::maximum() const +{ + return m_inputType->maximum(); +} + +bool HTMLInputElement::stepMismatch(const String& value) const +{ + double step; + if (!getAllowedValueStep(&step)) + return false; + return m_inputType->stepMismatch(value, step); +} + +String HTMLInputElement::minimumString() const +{ + return m_inputType->serialize(minimum()); +} + +String HTMLInputElement::maximumString() const +{ + return m_inputType->serialize(maximum()); +} + +String HTMLInputElement::stepBaseString() const +{ + return m_inputType->serialize(m_inputType->stepBase()); +} + +String HTMLInputElement::stepString() const +{ + double step; + if (!getAllowedValueStep(&step)) { + // stepString() should be called only if stepMismatch() can be true. + ASSERT_NOT_REACHED(); + return String(); + } + return serializeForNumberType(step / m_inputType->stepScaleFactor()); +} + +String HTMLInputElement::typeMismatchText() const +{ + return m_inputType->typeMismatchText(); +} + +String HTMLInputElement::valueMissingText() const +{ + return m_inputType->valueMissingText(); +} + +bool HTMLInputElement::getAllowedValueStep(double* step) const +{ + return getAllowedValueStepWithDecimalPlaces(RejectAny, step, 0); +} + +bool HTMLInputElement::getAllowedValueStepWithDecimalPlaces(AnyStepHandling anyStepHandling, double* step, unsigned* decimalPlaces) const +{ + ASSERT(step); + double defaultStep = m_inputType->defaultStep(); + double stepScaleFactor = m_inputType->stepScaleFactor(); + if (!isfinite(defaultStep) || !isfinite(stepScaleFactor)) + return false; + const AtomicString& stepString = fastGetAttribute(stepAttr); + if (stepString.isEmpty()) { + *step = defaultStep * stepScaleFactor; + if (decimalPlaces) + *decimalPlaces = 0; + return true; + } + + if (equalIgnoringCase(stepString, "any")) { + switch (anyStepHandling) { + case RejectAny: + return false; + case AnyIsDefaultStep: + *step = defaultStep * stepScaleFactor; + if (decimalPlaces) + *decimalPlaces = 0; + return true; + default: + ASSERT_NOT_REACHED(); + } + } + + double parsed; + if (!decimalPlaces) { + if (!parseToDoubleForNumberType(stepString, &parsed) || parsed <= 0.0) { + *step = defaultStep * stepScaleFactor; + return true; + } + } else { + if (!parseToDoubleForNumberTypeWithDecimalPlaces(stepString, &parsed, decimalPlaces) || parsed <= 0.0) { + *step = defaultStep * stepScaleFactor; + *decimalPlaces = 0; + return true; + } + } + // For date, month, week, the parsed value should be an integer for some types. + if (m_inputType->parsedStepValueShouldBeInteger()) + parsed = max(round(parsed), 1.0); + double result = parsed * stepScaleFactor; + // For datetime, datetime-local, time, the result should be an integer. + if (m_inputType->scaledStepValueShouldBeInteger()) + result = max(round(result), 1.0); + ASSERT(result > 0); + *step = result; + return true; +} + +void HTMLInputElement::applyStep(double count, AnyStepHandling anyStepHandling, bool sendChangeEvent, ExceptionCode& ec) +{ + double step; + unsigned stepDecimalPlaces, currentDecimalPlaces; + if (!getAllowedValueStepWithDecimalPlaces(anyStepHandling, &step, &stepDecimalPlaces)) { + ec = INVALID_STATE_ERR; + return; + } + + const double nan = numeric_limits<double>::quiet_NaN(); + double current = m_inputType->parseToDoubleWithDecimalPlaces(value(), nan, ¤tDecimalPlaces); + if (!isfinite(current)) { + ec = INVALID_STATE_ERR; + return; + } + double newValue = current + step * count; + if (isinf(newValue)) { + ec = INVALID_STATE_ERR; + return; + } + + double acceptableError = m_inputType->acceptableError(step); + if (newValue - m_inputType->minimum() < -acceptableError) { + ec = INVALID_STATE_ERR; + return; + } + if (newValue < m_inputType->minimum()) + newValue = m_inputType->minimum(); + + const AtomicString& stepString = fastGetAttribute(stepAttr); + if (!equalIgnoringCase(stepString, "any")) + newValue = alignValueForStep(newValue, step, currentDecimalPlaces, stepDecimalPlaces); + + if (newValue - m_inputType->maximum() > acceptableError) { + ec = INVALID_STATE_ERR; + return; + } + if (newValue > m_inputType->maximum()) + newValue = m_inputType->maximum(); + + setValueAsNumber(newValue, ec, sendChangeEvent); + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->postNotification(renderer(), AXObjectCache::AXValueChanged, true); +} + +double HTMLInputElement::alignValueForStep(double newValue, double step, unsigned currentDecimalPlaces, unsigned stepDecimalPlaces) +{ + if (newValue >= pow(10.0, 21.0)) + return newValue; + + unsigned baseDecimalPlaces; + double base = m_inputType->stepBaseWithDecimalPlaces(&baseDecimalPlaces); + baseDecimalPlaces = min(baseDecimalPlaces, 16u); + if (stepMismatch(value())) { + double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, currentDecimalPlaces))); + newValue = round(newValue * scale) / scale; + } else { + double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, baseDecimalPlaces))); + newValue = round((base + round((newValue - base) / step) * step) * scale) / scale; + } + + return newValue; +} + +void HTMLInputElement::stepUp(int n, ExceptionCode& ec) +{ + bool sendChangeEvent = false; + applyStep(n, RejectAny, sendChangeEvent, ec); +} + +void HTMLInputElement::stepDown(int n, ExceptionCode& ec) +{ + bool sendChangeEvent = false; + applyStep(-n, RejectAny, sendChangeEvent, ec); +} + +bool HTMLInputElement::isKeyboardFocusable(KeyboardEvent* event) const +{ + if (isTextField()) + return HTMLTextFormControlElement::isFocusable(); + return HTMLTextFormControlElement::isKeyboardFocusable(event) && m_inputType->isKeyboardFocusable(); +} + +bool HTMLInputElement::isMouseFocusable() const +{ + if (isTextField()) + return HTMLTextFormControlElement::isFocusable(); + return HTMLTextFormControlElement::isMouseFocusable(); +} + +void HTMLInputElement::updateFocusAppearance(bool restorePreviousSelection) +{ + if (isTextField()) { + if (!restorePreviousSelection || !hasCachedSelection()) + select(); + else + restoreCachedSelection(); + if (document()->frame()) + document()->frame()->selection()->revealSelection(); + } else + HTMLTextFormControlElement::updateFocusAppearance(restorePreviousSelection); +} + +void HTMLInputElement::aboutToUnload() +{ + if (!isTextField() || !focused()) + return; + + Frame* frame = document()->frame(); + if (!frame) + return; + + frame->editor()->textFieldDidEndEditing(this); +} + +bool HTMLInputElement::shouldUseInputMethod() +{ + return m_inputType->shouldUseInputMethod(); +} + +void HTMLInputElement::handleFocusEvent() +{ + if (!isTextField()) + return; + if (isPasswordField() && document()->frame()) + document()->setUseSecureKeyboardEntryWhenActive(true); +} + +void HTMLInputElement::handleBlurEvent() +{ + m_inputType->handleBlurEvent(); + if (!isTextField()) + return; + Frame* frame = document()->frame(); + if (!frame) + return; + if (isPasswordField()) + document()->setUseSecureKeyboardEntryWhenActive(false); + frame->editor()->textFieldDidEndEditing(this); +} + +void HTMLInputElement::setType(const String& type) +{ + // FIXME: This should just call setAttribute. No reason to handle the empty string specially. + // We should write a test case to show that setting to the empty string does not remove the + // attribute in other browsers and then fix this. Note that setting to null *does* remove + // the attribute and setAttribute implements that. + if (type.isEmpty()) + removeAttribute(typeAttr); + else + setAttribute(typeAttr, type); +} + +void HTMLInputElement::updateType() +{ + OwnPtr<InputType> newType = InputType::create(this, fastGetAttribute(typeAttr)); + bool hadType = m_hasType; + m_hasType = true; + if (m_inputType->formControlType() == newType->formControlType()) + return; + + if (hadType && !newType->canChangeFromAnotherType()) { + // Set the attribute back to the old value. + // Useful in case we were called from inside parseMappedAttribute. + setAttribute(typeAttr, type()); + return; + } + + checkedRadioButtons().removeButton(this); + + bool wasAttached = attached(); + if (wasAttached) + detach(); + + bool didStoreValue = m_inputType->storesValueSeparateFromAttribute(); + bool neededSuspensionCallback = needsSuspensionCallback(); + bool didRespectHeightAndWidth = m_inputType->shouldRespectHeightAndWidthAttributes(); + + m_inputType->destroyShadowSubtree(); + m_inputType = newType.release(); + m_inputType->createShadowSubtree(); + + setNeedsWillValidateCheck(); + + bool willStoreValue = m_inputType->storesValueSeparateFromAttribute(); + + if (didStoreValue && !willStoreValue && hasDirtyValue()) { + setAttribute(valueAttr, m_valueIfDirty); + m_valueIfDirty = String(); + } + if (!didStoreValue && willStoreValue) + m_valueIfDirty = sanitizeValue(fastGetAttribute(valueAttr)); + else + updateValueIfNeeded(); + + setFormControlValueMatchesRenderer(false); + updateInnerTextValue(); + + m_wasModifiedByUser = false; + + if (neededSuspensionCallback) + unregisterForSuspensionCallbackIfNeeded(); + else + registerForSuspensionCallbackIfNeeded(); + + if (didRespectHeightAndWidth != m_inputType->shouldRespectHeightAndWidthAttributes()) { + NamedNodeMap* map = attributeMap(); + ASSERT(map); + if (Attribute* height = map->getAttributeItem(heightAttr)) + attributeChanged(height, false); + if (Attribute* width = map->getAttributeItem(widthAttr)) + attributeChanged(width, false); + if (Attribute* align = map->getAttributeItem(alignAttr)) + attributeChanged(align, false); + } + + if (wasAttached) { + attach(); + if (document()->focusedNode() == this) + updateFocusAppearance(true); + } + + setChangedSinceLastFormControlChangeEvent(false); + + checkedRadioButtons().addButton(this); + + setNeedsValidityCheck(); + notifyFormStateChanged(); +} + +void HTMLInputElement::updateInnerTextValue() +{ + if (!isTextField()) + return; + + if (!suggestedValue().isNull()) { + setInnerTextValue(suggestedValue()); + updatePlaceholderVisibility(false); + } else if (!formControlValueMatchesRenderer()) { + // Update the renderer value if the formControlValueMatchesRenderer() flag is false. + // It protects an unacceptable renderer value from being overwritten with the DOM value. + setInnerTextValue(visibleValue()); + updatePlaceholderVisibility(false); + } +} + +void HTMLInputElement::subtreeHasChanged() +{ + ASSERT(isTextField()); + ASSERT(renderer()); + RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(renderer()); + + bool wasChanged = wasChangedSinceLastFormControlChangeEvent(); + setChangedSinceLastFormControlChangeEvent(true); + + // We don't need to call sanitizeUserInputValue() function here because + // HTMLInputElement::handleBeforeTextInsertedEvent() has already called + // sanitizeUserInputValue(). + // sanitizeValue() is needed because IME input doesn't dispatch BeforeTextInsertedEvent. + String value = innerTextValue(); + if (isAcceptableValue(value)) + setValueFromRenderer(sanitizeValue(convertFromVisibleValue(value))); + updatePlaceholderVisibility(false); + // 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(); + + if (!wasChanged && focused()) { + if (Frame* frame = document()->frame()) + frame->editor()->textFieldDidBeginEditing(this); + } + + if (focused()) { + if (Frame* frame = document()->frame()) + frame->editor()->textDidChangeInTextField(this); + } + // When typing in an input field, childrenChanged is not called, so we need to force the directionality check. + if (isTextField()) + calculateAndAdjustDirectionality(); +} + +const AtomicString& HTMLInputElement::formControlType() const +{ + return m_inputType->formControlType(); +} + +bool HTMLInputElement::saveFormControlState(String& result) const +{ + return m_inputType->saveFormControlState(result); +} + +void HTMLInputElement::restoreFormControlState(const String& state) +{ + m_inputType->restoreFormControlState(state); + m_stateRestored = true; +} + +bool HTMLInputElement::canStartSelection() const +{ + if (!isTextField()) + return false; + return HTMLTextFormControlElement::canStartSelection(); +} + +bool HTMLInputElement::canHaveSelection() const +{ + return isTextField(); +} + +void HTMLInputElement::accessKeyAction(bool sendMouseEvents) +{ + m_inputType->accessKeyAction(sendMouseEvents); +} + +bool HTMLInputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (((attrName == heightAttr || attrName == widthAttr) && m_inputType->shouldRespectHeightAndWidthAttributes()) + || attrName == vspaceAttr + || attrName == hspaceAttr) { + result = eUniversal; + return false; + } + + if (attrName == alignAttr && m_inputType->shouldRespectAlignAttribute()) { + // Share with <img> since the alignment behavior is the same. + result = eReplaced; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLInputElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == nameAttr) { + checkedRadioButtons().removeButton(this); + m_name = attr->value(); + checkedRadioButtons().addButton(this); + HTMLTextFormControlElement::parseMappedAttribute(attr); + } else if (attr->name() == autocompleteAttr) { + if (equalIgnoringCase(attr->value(), "off")) { + m_autocomplete = Off; + registerForSuspensionCallbackIfNeeded(); + } else { + bool needsToUnregister = m_autocomplete == Off; + + if (attr->isEmpty()) + m_autocomplete = Uninitialized; + else + m_autocomplete = On; + + if (needsToUnregister) + unregisterForSuspensionCallbackIfNeeded(); + } + } else if (attr->name() == typeAttr) { + updateType(); + } else if (attr->name() == valueAttr) { + // We only need to setChanged if the form is looking at the default value right now. + if (!hasDirtyValue()) { + updatePlaceholderVisibility(false); + setNeedsStyleRecalc(); + } + setFormControlValueMatchesRenderer(false); + setNeedsValidityCheck(); + } else if (attr->name() == checkedAttr) { + // Another radio button in the same group might be checked by state + // restore. We shouldn't call setChecked() even if this has the checked + // attribute. So, delay the setChecked() call until + // finishParsingChildren() is called if parsing is in progress. + if (!m_parsingInProgress && m_reflectsCheckedAttribute) { + setChecked(!attr->isNull()); + m_reflectsCheckedAttribute = true; + } + } else if (attr->name() == maxlengthAttr) + parseMaxLengthAttribute(attr); + else if (attr->name() == sizeAttr) { + int oldSize = m_size; + int value = attr->value().toInt(); + m_size = value > 0 ? value : defaultSize; + if (m_size != oldSize && renderer()) + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + } else if (attr->name() == altAttr) + m_inputType->altAttributeChanged(); + else if (attr->name() == srcAttr) + m_inputType->srcAttributeChanged(); + else if (attr->name() == usemapAttr || attr->name() == accesskeyAttr) { + // FIXME: ignore for the moment + } else if (attr->name() == vspaceAttr) { + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + } else if (attr->name() == hspaceAttr) { + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + } else if (attr->name() == alignAttr) { + if (m_inputType->shouldRespectAlignAttribute()) + addHTMLAlignment(attr); + } else if (attr->name() == widthAttr) { + if (m_inputType->shouldRespectHeightAndWidthAttributes()) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + } else if (attr->name() == heightAttr) { + if (m_inputType->shouldRespectHeightAndWidthAttributes()) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + } else if (attr->name() == borderAttr && isImageButton()) { + applyBorderAttribute(attr); + } else if (attr->name() == onsearchAttr) { + // Search field and slider attributes all just cause updateFromElement to be called through style recalcing. + setAttributeEventListener(eventNames().searchEvent, createAttributeEventListener(this, attr)); + } else if (attr->name() == resultsAttr) { + int oldResults = m_maxResults; + m_maxResults = !attr->isNull() ? std::min(attr->value().toInt(), maxSavedResults) : -1; + // FIXME: Detaching just for maxResults change is not ideal. We should figure out the right + // time to relayout for this change. + if (m_maxResults != oldResults && (m_maxResults <= 0 || oldResults <= 0)) + reattachIfAttached(); + setNeedsStyleRecalc(); + } else if (attr->name() == autosaveAttr || attr->name() == incrementalAttr) + setNeedsStyleRecalc(); + else if (attr->name() == minAttr || attr->name() == maxAttr) { + m_inputType->minOrMaxAttributeChanged(); + setNeedsValidityCheck(); + } else if (attr->name() == multipleAttr) { + m_inputType->multipleAttributeChanged(); + setNeedsValidityCheck(); + } else if (attr->name() == stepAttr) { + m_inputType->stepAttributeChanged(); + setNeedsValidityCheck(); + } else if (attr->name() == patternAttr || attr->name() == precisionAttr) + setNeedsValidityCheck(); + else if (attr->name() == disabledAttr) { + m_inputType->disabledAttributeChanged(); + HTMLTextFormControlElement::parseMappedAttribute(attr); + } else if (attr->name() == readonlyAttr) { + m_inputType->readonlyAttributeChanged(); + HTMLTextFormControlElement::parseMappedAttribute(attr); + } +#if ENABLE(DATALIST) + else if (attr->name() == listAttr) + m_hasNonEmptyList = !attr->isEmpty(); + // FIXME: we need to tell this change to a renderer if the attribute affects the appearance. +#endif +#if ENABLE(INPUT_SPEECH) + else if (attr->name() == webkitspeechAttr) { + if (renderer()) { + // This renderer and its children have quite different layouts and styles depending on + // whether the speech button is visible or not. So we reset the whole thing and recreate + // to get the right styles and layout. + detach(); + m_inputType->destroyShadowSubtree(); + m_inputType->createShadowSubtree(); + attach(); + } else { + m_inputType->destroyShadowSubtree(); + m_inputType->createShadowSubtree(); + } + setFormControlValueMatchesRenderer(false); + setNeedsStyleRecalc(); + } else if (attr->name() == onwebkitspeechchangeAttr) + setAttributeEventListener(eventNames().webkitspeechchangeEvent, createAttributeEventListener(this, attr)); +#endif + else + HTMLTextFormControlElement::parseMappedAttribute(attr); + updateInnerTextValue(); +} + +void HTMLInputElement::finishParsingChildren() +{ + m_parsingInProgress = false; + HTMLTextFormControlElement::finishParsingChildren(); + if (!m_stateRestored) { + bool checked = hasAttribute(checkedAttr); + if (checked) + setChecked(checked); + m_reflectsCheckedAttribute = true; + } +} + +bool HTMLInputElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + return m_inputType->rendererIsNeeded() && HTMLTextFormControlElement::rendererIsNeeded(context); +} + +RenderObject* HTMLInputElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + return m_inputType->createRenderer(arena, style); +} + +void HTMLInputElement::attach() +{ + suspendPostAttachCallbacks(); + + if (!m_hasType) + updateType(); + + HTMLTextFormControlElement::attach(); + + m_inputType->attach(); + + if (document()->focusedNode() == this) + document()->updateFocusAppearanceSoon(true /* restore selection */); + + resumePostAttachCallbacks(); +} + +void HTMLInputElement::detach() +{ + HTMLTextFormControlElement::detach(); + setFormControlValueMatchesRenderer(false); + m_inputType->detach(); +} + +String HTMLInputElement::altText() const +{ + // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen + // also heavily discussed by Hixie on bugzilla + // note this is intentionally different to HTMLImageElement::altText() + String alt = fastGetAttribute(altAttr); + // fall back to title attribute + if (alt.isNull()) + alt = getAttribute(titleAttr); + if (alt.isNull()) + alt = getAttribute(valueAttr); + if (alt.isEmpty()) + alt = inputElementAltText(); + return alt; +} + +bool HTMLInputElement::isSuccessfulSubmitButton() const +{ + // HTML spec says that buttons must have names to be considered successful. + // However, other browsers do not impose this constraint. So we do not. + return !disabled() && m_inputType->canBeSuccessfulSubmitButton(); +} + +bool HTMLInputElement::isActivatedSubmit() const +{ + return m_isActivatedSubmit; +} + +void HTMLInputElement::setActivatedSubmit(bool flag) +{ + m_isActivatedSubmit = flag; +} + +bool HTMLInputElement::appendFormData(FormDataList& encoding, bool multipart) +{ + return m_inputType->isFormDataAppendable() && m_inputType->appendFormData(encoding, multipart); +} + +void HTMLInputElement::reset() +{ + if (m_inputType->storesValueSeparateFromAttribute()) + setValue(String()); + + setAutofilled(false); + setChecked(hasAttribute(checkedAttr)); + m_reflectsCheckedAttribute = true; +} + +bool HTMLInputElement::isTextField() const +{ + return m_inputType->isTextField(); +} + +bool HTMLInputElement::isTextType() const +{ + return m_inputType->isTextType(); +} + +void HTMLInputElement::setChecked(bool nowChecked, bool sendChangeEvent) +{ + if (checked() == nowChecked) + return; + + checkedRadioButtons().removeButton(this); + + m_reflectsCheckedAttribute = false; + m_isChecked = nowChecked; + setNeedsStyleRecalc(); + if (isRadioButton()) + updateCheckedRadioButtons(); + if (renderer() && renderer()->style()->hasAppearance()) + renderer()->theme()->stateChanged(renderer(), CheckedState); + setNeedsValidityCheck(); + + // Ideally we'd do this from the render tree (matching + // RenderTextView), but it's not possible to do it at the moment + // because of the way the code is structured. + if (renderer() && AXObjectCache::accessibilityEnabled()) + renderer()->document()->axObjectCache()->checkedStateChanged(renderer()); + + // Only send a change event for items in the document (avoid firing during + // parsing) and don't send a change event for a radio button that's getting + // unchecked to match other browsers. DOM is not a useful standard for this + // because it says only to fire change events at "lose focus" time, which is + // definitely wrong in practice for these types of elements. + if (sendChangeEvent && inDocument() && m_inputType->shouldSendChangeEventAfterCheckedChanged()) { + setTextAsOfLastFormControlChangeEvent(String()); + dispatchFormControlChangeEvent(); + } +} + +void HTMLInputElement::setIndeterminate(bool newValue) +{ + if (!m_inputType->isCheckable() || indeterminate() == newValue) + return; + + m_isIndeterminate = newValue; + + setNeedsStyleRecalc(); + + if (renderer() && renderer()->style()->hasAppearance()) + renderer()->theme()->stateChanged(renderer(), CheckedState); +} + +int HTMLInputElement::size() const +{ + return m_size; +} + +bool HTMLInputElement::sizeShouldIncludeDecoration(int& preferredSize) const +{ + return m_inputType->sizeShouldIncludeDecoration(defaultSize, preferredSize); +} + +void HTMLInputElement::copyNonAttributeProperties(const Element* source) +{ + const HTMLInputElement* sourceElement = static_cast<const HTMLInputElement*>(source); + + m_valueIfDirty = sourceElement->m_valueIfDirty; + m_wasModifiedByUser = false; + setChecked(sourceElement->m_isChecked); + m_reflectsCheckedAttribute = sourceElement->m_reflectsCheckedAttribute; + m_isIndeterminate = sourceElement->m_isIndeterminate; + + HTMLTextFormControlElement::copyNonAttributeProperties(source); + + setFormControlValueMatchesRenderer(false); + updateInnerTextValue(); +} + +String HTMLInputElement::value() const +{ + String value; + if (m_inputType->getTypeSpecificValue(value)) + return value; + + value = m_valueIfDirty; + if (!value.isNull()) + return value; + + value = sanitizeValue(fastGetAttribute(valueAttr)); + if (!value.isNull()) + return value; + + return m_inputType->fallbackValue(); +} + +String HTMLInputElement::valueWithDefault() const +{ + String value = this->value(); + if (!value.isNull()) + return value; + + return m_inputType->defaultValue(); +} + +void HTMLInputElement::setValueForUser(const String& value) +{ + // Call setValue and make it send a change event. + setValue(value, true); +} + +const String& HTMLInputElement::suggestedValue() const +{ + return m_suggestedValue; +} + +void HTMLInputElement::setSuggestedValue(const String& value) +{ + if (!m_inputType->canSetSuggestedValue()) + return; + setFormControlValueMatchesRenderer(false); + m_suggestedValue = sanitizeValue(value); + setNeedsStyleRecalc(); + updateInnerTextValue(); +} + +void HTMLInputElement::setValue(const String& value, bool sendChangeEvent) +{ + if (!m_inputType->canSetValue(value)) + return; + + RefPtr<HTMLInputElement> protector(this); + String sanitizedValue = sanitizeValue(value); + bool valueChanged = sanitizedValue != this->value(); + + setLastChangeWasNotUserEdit(); + setFormControlValueMatchesRenderer(false); + m_suggestedValue = String(); // Prevent TextFieldInputType::setValue from using the suggested value. + m_inputType->setValue(sanitizedValue, valueChanged, sendChangeEvent); + + if (!valueChanged) + return; + + if (sendChangeEvent) + m_inputType->dispatchChangeEventInResponseToSetValue(); + + // FIXME: Why do we do this when !sendChangeEvent? + if (isTextField() && (!focused() || !sendChangeEvent)) + setTextAsOfLastFormControlChangeEvent(value); + + notifyFormStateChanged(); +} + +void HTMLInputElement::setValueInternal(const String& sanitizedValue, bool sendChangeEvent) +{ + m_valueIfDirty = sanitizedValue; + m_wasModifiedByUser = sendChangeEvent; + setNeedsValidityCheck(); +} + +double HTMLInputElement::valueAsDate() const +{ + return m_inputType->valueAsDate(); +} + +void HTMLInputElement::setValueAsDate(double value, ExceptionCode& ec) +{ + m_inputType->setValueAsDate(value, ec); +} + +double HTMLInputElement::valueAsNumber() const +{ + return m_inputType->valueAsNumber(); +} + +void HTMLInputElement::setValueAsNumber(double newValue, ExceptionCode& ec, bool sendChangeEvent) +{ + if (!isfinite(newValue)) { + ec = NOT_SUPPORTED_ERR; + return; + } + m_inputType->setValueAsNumber(newValue, sendChangeEvent, ec); +} + +String HTMLInputElement::placeholder() const +{ + return fastGetAttribute(placeholderAttr).string(); +} + +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. + ASSERT(!isFileUpload()); + + m_suggestedValue = String(); + + // Renderer and our event handler are responsible for sanitizing values. + ASSERT(value == sanitizeValue(value) || sanitizeValue(value).isEmpty()); + + // Workaround for bug where trailing \n is included in the result of textContent. + // The assert macro above may also be simplified to: value == constrainValue(value) + // http://bugs.webkit.org/show_bug.cgi?id=9661 + m_valueIfDirty = value == "\n" ? String("") : value; + + setFormControlValueMatchesRenderer(true); + m_wasModifiedByUser = true; + + // Input event is fired by the Node::defaultEventHandler for editable controls. + if (!isTextField()) + dispatchInputEvent(); + notifyFormStateChanged(); + + setNeedsValidityCheck(); + + // Clear autofill flag (and yellow background) on user edit. + setAutofilled(false); +} + +void* HTMLInputElement::preDispatchEventHandler(Event* event) +{ + if (event->type() == eventNames().textInputEvent && m_inputType->shouldSubmitImplicitly(event)) { + event->stopPropagation(); + return 0; + } + if (event->type() != eventNames().clickEvent) + return 0; + if (!event->isMouseEvent() || static_cast<MouseEvent*>(event)->button() != LeftButton) + return 0; + // FIXME: Check whether there are any cases where this actually ends up leaking. + return m_inputType->willDispatchClick().leakPtr(); +} + +void HTMLInputElement::postDispatchEventHandler(Event* event, void* dataFromPreDispatch) +{ + OwnPtr<ClickHandlingState> state = adoptPtr(static_cast<ClickHandlingState*>(dataFromPreDispatch)); + if (!state) + return; + m_inputType->didDispatchClick(event, *state); +} + +void HTMLInputElement::defaultEventHandler(Event* evt) +{ + if (evt->isMouseEvent() && evt->type() == eventNames().clickEvent && static_cast<MouseEvent*>(evt)->button() == LeftButton) { + m_inputType->handleClickEvent(static_cast<MouseEvent*>(evt)); + if (evt->defaultHandled()) + return; + } + + if (evt->isKeyboardEvent() && evt->type() == eventNames().keydownEvent) { + m_inputType->handleKeydownEvent(static_cast<KeyboardEvent*>(evt)); + if (evt->defaultHandled()) + return; + } + + // Call the base event handler before any of our own event handling for almost all events in text fields. + // Makes editing keyboard handling take precedence over the keydown and keypress handling in this function. + bool callBaseClassEarly = isTextField() && (evt->type() == eventNames().keydownEvent || evt->type() == eventNames().keypressEvent); + if (callBaseClassEarly) { + HTMLTextFormControlElement::defaultEventHandler(evt); + if (evt->defaultHandled()) + return; + } + + // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means + // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks + // on the element, or presses enter while it is the active element. JavaScript code wishing to activate the element + // must dispatch a DOMActivate event - a click event will not do the job. + if (evt->type() == eventNames().DOMActivateEvent) { + m_inputType->handleDOMActivateEvent(evt); + if (evt->defaultHandled()) + return; + } + + // Use key press event here since sending simulated mouse events + // on key down blocks the proper sending of the key press event. + if (evt->isKeyboardEvent() && evt->type() == eventNames().keypressEvent) { + m_inputType->handleKeypressEvent(static_cast<KeyboardEvent*>(evt)); + if (evt->defaultHandled()) + return; + } + + if (evt->isKeyboardEvent() && evt->type() == eventNames().keyupEvent) { + m_inputType->handleKeyupEvent(static_cast<KeyboardEvent*>(evt)); + if (evt->defaultHandled()) + return; + } + + if (m_inputType->shouldSubmitImplicitly(evt)) { + if (isSearchField()) { + addSearchResult(); + onSearch(); + } + // Form submission finishes editing, just as loss of focus does. + // If there was a change, send the event now. + if (wasChangedSinceLastFormControlChangeEvent()) + dispatchFormControlChangeEvent(); + + RefPtr<HTMLFormElement> formForSubmission = m_inputType->formForSubmission(); + // Form may never have been present, or may have been destroyed by code responding to the change event. + if (formForSubmission) + formForSubmission->submitImplicitly(evt, canTriggerImplicitSubmission()); + + evt->setDefaultHandled(); + return; + } + + if (evt->isBeforeTextInsertedEvent()) + m_inputType->handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(evt)); + + if (evt->hasInterface(eventNames().interfaceForWheelEvent)) { + m_inputType->handleWheelEvent(static_cast<WheelEvent*>(evt)); + if (evt->defaultHandled()) + return; + } + + if (evt->isMouseEvent() && evt->type() == eventNames().mousedownEvent) { + m_inputType->handleMouseDownEvent(static_cast<MouseEvent*>(evt)); + if (evt->defaultHandled()) + return; + } + + m_inputType->forwardEvent(evt); + + if (!callBaseClassEarly && !evt->defaultHandled()) + HTMLTextFormControlElement::defaultEventHandler(evt); +} + +bool HTMLInputElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == srcAttr || attr->name() == formactionAttr || HTMLTextFormControlElement::isURLAttribute(attr); +} + +String HTMLInputElement::defaultValue() const +{ + return fastGetAttribute(valueAttr); +} + +void HTMLInputElement::setDefaultValue(const String &value) +{ + setAttribute(valueAttr, value); +} + +void HTMLInputElement::setDefaultName(const AtomicString& name) +{ + m_name = name; +} + +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) +{ + size_t slashPosition = type.find('/'); + if (slashPosition == notFound || !slashPosition || slashPosition == type.length() - 1) + return false; + for (size_t i = 0; i < type.length(); ++i) { + if (!isRFC2616TokenCharacter(type[i]) && i != slashPosition) + return false; + } + return true; +} + +Vector<String> HTMLInputElement::acceptMIMETypes() +{ + Vector<String> mimeTypes; + + String acceptString = accept(); + if (acceptString.isEmpty()) + return mimeTypes; + + Vector<String> splitTypes; + acceptString.split(',', false, splitTypes); + for (size_t i = 0; i < splitTypes.size(); ++i) { + String trimmedMimeType = stripLeadingAndTrailingHTMLSpaces(splitTypes[i]); + if (trimmedMimeType.isEmpty()) + continue; + if (!isValidMIMEType(trimmedMimeType)) + continue; + mimeTypes.append(trimmedMimeType.lower()); + } + + return mimeTypes; +} + +String HTMLInputElement::accept() const +{ + return fastGetAttribute(acceptAttr); +} + +String HTMLInputElement::alt() const +{ + return fastGetAttribute(altAttr); +} + +int HTMLInputElement::maxLength() const +{ + return m_maxLength; +} + +void HTMLInputElement::setMaxLength(int maxLength, ExceptionCode& ec) +{ + if (maxLength < 0) + ec = INDEX_SIZE_ERR; + else + setAttribute(maxlengthAttr, String::number(maxLength)); +} + +bool HTMLInputElement::multiple() const +{ + return fastHasAttribute(multipleAttr); +} + +void HTMLInputElement::setSize(unsigned size) +{ + setAttribute(sizeAttr, String::number(size)); +} + +KURL HTMLInputElement::src() const +{ + return document()->completeURL(fastGetAttribute(srcAttr)); +} + +void HTMLInputElement::setAutofilled(bool autofilled) +{ + if (autofilled == m_isAutofilled) + return; + + m_isAutofilled = autofilled; + setNeedsStyleRecalc(); +} + +FileList* HTMLInputElement::files() +{ + return m_inputType->files(); +} + +void HTMLInputElement::receiveDroppedFiles(const Vector<String>& filenames) +{ + m_inputType->receiveDroppedFiles(filenames); +} + +Icon* HTMLInputElement::icon() const +{ + return m_inputType->icon(); +} + +bool HTMLInputElement::canReceiveDroppedFiles() const +{ + return m_canReceiveDroppedFiles; +} + +void HTMLInputElement::setCanReceiveDroppedFiles(bool canReceiveDroppedFiles) +{ + if (m_canReceiveDroppedFiles == canReceiveDroppedFiles) + return; + m_canReceiveDroppedFiles = canReceiveDroppedFiles; + renderer()->updateFromElement(); +} + +String HTMLInputElement::visibleValue() const +{ + return m_inputType->visibleValue(); +} + +String HTMLInputElement::convertFromVisibleValue(const String& visibleValue) const +{ + return m_inputType->convertFromVisibleValue(visibleValue); +} + +bool HTMLInputElement::isAcceptableValue(const String& proposedValue) const +{ + return m_inputType->isAcceptableValue(proposedValue); +} + +String HTMLInputElement::sanitizeValue(const String& proposedValue) const +{ + if (proposedValue.isNull()) + return proposedValue; + return m_inputType->sanitizeValue(proposedValue); +} + +bool HTMLInputElement::hasUnacceptableValue() const +{ + return m_inputType->hasUnacceptableValue(); +} + +bool HTMLInputElement::isInRange() const +{ + return m_inputType->supportsRangeLimitation() && !rangeUnderflow(value()) && !rangeOverflow(value()); +} + +bool HTMLInputElement::isOutOfRange() const +{ + return m_inputType->supportsRangeLimitation() && (rangeUnderflow(value()) || rangeOverflow(value())); +} + +bool HTMLInputElement::needsSuspensionCallback() +{ + return m_autocomplete == Off || m_inputType->shouldResetOnDocumentActivation(); +} + +void HTMLInputElement::registerForSuspensionCallbackIfNeeded() +{ + if (needsSuspensionCallback()) + document()->registerForPageCacheSuspensionCallbacks(this); +} + +void HTMLInputElement::unregisterForSuspensionCallbackIfNeeded() +{ + if (!needsSuspensionCallback()) + document()->unregisterForPageCacheSuspensionCallbacks(this); +} + +bool HTMLInputElement::isRequiredFormControl() const +{ + return m_inputType->supportsRequired() && required(); +} + +void HTMLInputElement::addSearchResult() +{ + ASSERT(isSearchField()); + if (renderer()) + toRenderTextControlSingleLine(renderer())->addSearchResult(); +} + +void HTMLInputElement::onSearch() +{ + ASSERT(isSearchField()); + if (m_inputType) + static_cast<SearchInputType*>(m_inputType.get())->stopSearchEventTimer(); + dispatchEvent(Event::create(eventNames().searchEvent, true, false)); +} + +void HTMLInputElement::documentDidResumeFromPageCache() +{ + ASSERT(needsSuspensionCallback()); + reset(); +} + +void HTMLInputElement::didMoveToNewDocument(Document* oldDocument) +{ + m_inputType->willMoveToNewOwnerDocument(); + bool needsSuspensionCallback = this->needsSuspensionCallback(); + if (oldDocument) { + // Always unregister for cache callbacks when leaving a document, even if we would otherwise like to be registered + if (needsSuspensionCallback) + oldDocument->unregisterForPageCacheSuspensionCallbacks(this); + oldDocument->checkedRadioButtons().removeButton(this); + } + + if (needsSuspensionCallback) + document()->registerForPageCacheSuspensionCallbacks(this); + + HTMLTextFormControlElement::didMoveToNewDocument(oldDocument); +} + +void HTMLInputElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLTextFormControlElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, src()); +} + +bool HTMLInputElement::recalcWillValidate() const +{ + return m_inputType->supportsValidation() && HTMLTextFormControlElement::recalcWillValidate(); +} + +#if ENABLE(INPUT_COLOR) +void HTMLInputElement::selectColorInColorChooser(const Color& color) +{ + if (!m_inputType->isColorControl()) + return; + static_cast<ColorInputType*>(m_inputType.get())->didChooseColor(color); +} +#endif + +#if ENABLE(DATALIST) + +HTMLElement* HTMLInputElement::list() const +{ + return dataList(); +} + +HTMLDataListElement* HTMLInputElement::dataList() const +{ + if (!m_hasNonEmptyList) + return 0; + + if (!m_inputType->shouldRespectListAttribute()) + return 0; + + Element* element = treeScope()->getElementById(fastGetAttribute(listAttr)); + if (!element) + return 0; + if (!element->hasTagName(datalistTag)) + return 0; + + return static_cast<HTMLDataListElement*>(element); +} + +HTMLOptionElement* HTMLInputElement::selectedOption() const +{ + String value = this->value(); + + // The empty string never matches to a datalist option because it + // doesn't represent a suggestion according to the standard. + if (value.isEmpty()) + return 0; + + HTMLDataListElement* sourceElement = dataList(); + if (!sourceElement) + return 0; + RefPtr<HTMLCollection> options = sourceElement->options(); + if (!options) + return 0; + unsigned length = options->length(); + for (unsigned i = 0; i < length; ++i) { + HTMLOptionElement* option = static_cast<HTMLOptionElement*>(options->item(i)); + if (!option->disabled() && value == option->value()) + return option; + } + return 0; +} + +#endif // ENABLE(DATALIST) + +bool HTMLInputElement::isSteppable() const +{ + return m_inputType->isSteppable(); +} + +void HTMLInputElement::stepUpFromRenderer(int n) +{ + // The differences from stepUp()/stepDown(): + // + // Difference 1: the current value + // If the current value is not a number, including empty, the current value is assumed as 0. + // * If 0 is in-range, and matches to step value + // - The value should be the +step if n > 0 + // - The value should be the -step if n < 0 + // If -step or +step is out of range, new value should be 0. + // * If 0 is smaller than the minimum value + // - The value should be the minimum value for any n + // * If 0 is larger than the maximum value + // - The value should be the maximum value for any n + // * If 0 is in-range, but not matched to step value + // - The value should be the larger matched value nearest to 0 if n > 0 + // e.g. <input type=number min=-100 step=3> -> 2 + // - The value should be the smaler matched value nearest to 0 if n < 0 + // e.g. <input type=number min=-100 step=3> -> -1 + // As for date/datetime-local/month/time/week types, the current value is assumed as "the current local date/time". + // As for datetime type, the current value is assumed as "the current date/time in UTC". + // If the current value is smaller than the minimum value: + // - The value should be the minimum value if n > 0 + // - Nothing should happen if n < 0 + // If the current value is larger than the maximum value: + // - The value should be the maximum value if n < 0 + // - Nothing should happen if n > 0 + // + // Difference 2: clamping steps + // If the current value is not matched to step value: + // - The value should be the larger matched value nearest to 0 if n > 0 + // e.g. <input type=number value=3 min=-100 step=3> -> 5 + // - The value should be the smaler matched value nearest to 0 if n < 0 + // e.g. <input type=number value=3 min=-100 step=3> -> 2 + // + // n is assumed as -n if step < 0. + + ASSERT(isSteppable()); + if (!isSteppable()) + return; + ASSERT(n); + if (!n) + return; + + unsigned stepDecimalPlaces, baseDecimalPlaces; + double step, base; + // FIXME: Not any changes after stepping, even if it is an invalid value, may be better. + // (e.g. Stepping-up for <input type="number" value="foo" step="any" /> => "foo") + if (!getAllowedValueStepWithDecimalPlaces(AnyIsDefaultStep, &step, &stepDecimalPlaces)) + return; + base = m_inputType->stepBaseWithDecimalPlaces(&baseDecimalPlaces); + baseDecimalPlaces = min(baseDecimalPlaces, 16u); + + int sign; + if (step > 0) + sign = n; + else if (step < 0) + sign = -n; + else + sign = 0; + + const double nan = numeric_limits<double>::quiet_NaN(); + String currentStringValue = value(); + double current = m_inputType->parseToDouble(currentStringValue, nan); + const bool sendChangeEvent = true; + if (!isfinite(current)) { + ExceptionCode ec; + current = m_inputType->defaultValueForStepUp(); + double nextDiff = step * n; + if (current < m_inputType->minimum() - nextDiff) + current = m_inputType->minimum() - nextDiff; + if (current > m_inputType->maximum() - nextDiff) + current = m_inputType->maximum() - nextDiff; + setValueAsNumber(current, ec, sendChangeEvent); + } + if ((sign > 0 && current < m_inputType->minimum()) || (sign < 0 && current > m_inputType->maximum())) + setValue(m_inputType->serialize(sign > 0 ? m_inputType->minimum() : m_inputType->maximum()), sendChangeEvent); + else { + ExceptionCode ec; + if (stepMismatch(value())) { + ASSERT(step); + double newValue; + double scale = pow(10.0, static_cast<double>(max(stepDecimalPlaces, baseDecimalPlaces))); + + if (sign < 0) + newValue = round((base + floor((current - base) / step) * step) * scale) / scale; + else if (sign > 0) + newValue = round((base + ceil((current - base) / step) * step) * scale) / scale; + else + newValue = current; + + if (newValue < m_inputType->minimum()) + newValue = m_inputType->minimum(); + if (newValue > m_inputType->maximum()) + newValue = m_inputType->maximum(); + + setValueAsNumber(newValue, ec, n == 1 || n == -1); + current = newValue; + if (n > 1) + applyStep(n - 1, AnyIsDefaultStep, sendChangeEvent, ec); + else if (n < -1) + applyStep(n + 1, AnyIsDefaultStep, sendChangeEvent, ec); + } else + applyStep(n, AnyIsDefaultStep, sendChangeEvent, ec); + } +} + +#if ENABLE(INPUT_SPEECH) + +bool HTMLInputElement::isSpeechEnabled() const +{ + // FIXME: Add support for RANGE, EMAIL, URL, COLOR and DATE/TIME input types. + return m_inputType->shouldRespectSpeechAttribute() && RuntimeEnabledFeatures::speechInputEnabled() && hasAttribute(webkitspeechAttr); +} + +#endif + +bool HTMLInputElement::isTextButton() const +{ + return m_inputType->isTextButton(); +} + +bool HTMLInputElement::isRadioButton() const +{ + return m_inputType->isRadioButton(); +} + +bool HTMLInputElement::isSearchField() const +{ + return m_inputType->isSearchField(); +} + +bool HTMLInputElement::isInputTypeHidden() const +{ + return m_inputType->isHiddenType(); +} + +bool HTMLInputElement::isPasswordField() const +{ + return m_inputType->isPasswordField(); +} + +bool HTMLInputElement::isCheckbox() const +{ + return m_inputType->isCheckbox(); +} + +bool HTMLInputElement::isRangeControl() const +{ + return m_inputType->isRangeControl(); +} + +bool HTMLInputElement::isText() const +{ + return m_inputType->isTextType(); +} + +bool HTMLInputElement::isEmailField() const +{ + return m_inputType->isEmailField(); +} + +bool HTMLInputElement::isFileUpload() const +{ + return m_inputType->isFileUpload(); +} + +bool HTMLInputElement::isImageButton() const +{ + return m_inputType->isImageButton(); +} + +bool HTMLInputElement::isNumberField() const +{ + return m_inputType->isNumberField(); +} + +bool HTMLInputElement::isSubmitButton() const +{ + return m_inputType->isSubmitButton(); +} + +bool HTMLInputElement::isTelephoneField() const +{ + return m_inputType->isTelephoneField(); +} + +bool HTMLInputElement::isURLField() const +{ + return m_inputType->isURLField(); +} + +bool HTMLInputElement::isEnumeratable() const +{ + return m_inputType->isEnumeratable(); +} + +bool HTMLInputElement::shouldAppearChecked() const +{ + return checked() && m_inputType->isCheckable(); +} + +bool HTMLInputElement::supportsPlaceholder() const +{ + return m_inputType->supportsPlaceholder(); +} + +void HTMLInputElement::updatePlaceholderText() +{ + return m_inputType->updatePlaceholderText(); +} + +CheckedRadioButtons& HTMLInputElement::checkedRadioButtons() const +{ + if (HTMLFormElement* formElement = form()) + return formElement->checkedRadioButtons(); + return document()->checkedRadioButtons(); +} + +void HTMLInputElement::parseMaxLengthAttribute(Attribute* attribute) +{ + int maxLength; + if (!parseHTMLInteger(attribute->value(), maxLength)) + maxLength = maximumLength; + if (maxLength < 0 || maxLength > maximumLength) + maxLength = maximumLength; + int oldMaxLength = m_maxLength; + m_maxLength = maxLength; + if (oldMaxLength != maxLength) + updateValueIfNeeded(); + setNeedsStyleRecalc(); + setNeedsValidityCheck(); +} + +void HTMLInputElement::updateValueIfNeeded() +{ + String newValue = sanitizeValue(m_valueIfDirty); + ASSERT(!m_valueIfDirty.isNull() || newValue.isNull()); + if (newValue != m_valueIfDirty) + setValue(newValue); +} + +String HTMLInputElement::defaultToolTip() const +{ + return m_inputType->defaultToolTip(); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLInputElement.h b/Source/WebCore/html/HTMLInputElement.h new file mode 100644 index 000000000..054436dd4 --- /dev/null +++ b/Source/WebCore/html/HTMLInputElement.h @@ -0,0 +1,363 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 HTMLInputElement_h +#define HTMLInputElement_h + +#include "HTMLTextFormControlElement.h" + +namespace WebCore { + +class FileList; +class HTMLDataListElement; +class HTMLOptionElement; +class Icon; +class InputType; +class KURL; + +class HTMLInputElement : public HTMLTextFormControlElement { +public: + static PassRefPtr<HTMLInputElement> create(const QualifiedName&, Document*, HTMLFormElement*, bool createdByParser); + virtual ~HTMLInputElement(); + + DEFINE_ATTRIBUTE_EVENT_LISTENER(webkitspeechchange); + + virtual HTMLInputElement* toInputElement() { return this; } + + virtual bool shouldAutocomplete() const; + + // For ValidityState + bool typeMismatch() const; + // valueMissing() ignores the specified string value for CHECKBOX and RADIO. + bool valueMissing(const String&) const; + bool patternMismatch(const String&) const; + bool tooLong(const String&, NeedsToCheckDirtyFlag) const; + bool rangeUnderflow(const String&) const; + bool rangeOverflow(const String&) const; + // Returns the minimum value for type=date, number, or range. Don't call this for other types. + double minimum() const; + // Returns the maximum value for type=date, number, or range. Don't call this for other types. + // This always returns a value which is >= minimum(). + 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; + + // For ValidityState. + bool stepMismatch(const String&) const; + String minimumString() const; + String maximumString() const; + String stepBaseString() const; + String stepString() const; + String typeMismatchText() const; + String valueMissingText() const; + + // Implementations of HTMLInputElement::stepUp() and stepDown(). + void stepUp(int, ExceptionCode&); + void stepDown(int, ExceptionCode&); + void stepUp(ExceptionCode& ec) { stepUp(1, ec); } + void stepDown(ExceptionCode& ec) { stepDown(1, ec); } + // stepUp()/stepDown() for user-interaction. + bool isSteppable() const; + void stepUpFromRenderer(int); + + bool isTextButton() const; + + virtual bool isRadioButton() const; + bool isTextField() const; + bool isSearchField() const; + bool isInputTypeHidden() const; + bool isPasswordField() const; + bool isCheckbox() const; + bool isRangeControl() const; + + // FIXME: It's highly likely that any call site calling this function should instead + // be using a different one. Many input elements behave like text fields, and in addition + // any unknown input type is treated as text. Consider, for example, isTextField or + // isTextField && !isPasswordField. + bool isText() const; + + bool isEmailField() const; + bool isFileUpload() const; + bool isImageButton() const; + bool isNumberField() const; + bool isSubmitButton() const; + bool isTelephoneField() const; + bool isURLField() const; + +#if ENABLE(INPUT_SPEECH) + bool isSpeechEnabled() const; +#endif + + HTMLElement* containerElement() const; + virtual HTMLElement* innerTextElement() const; + HTMLElement* innerBlockElement() const; + HTMLElement* innerSpinButtonElement() const; + HTMLElement* resultsButtonElement() const; + HTMLElement* cancelButtonElement() const; +#if ENABLE(INPUT_SPEECH) + HTMLElement* speechButtonElement() const; +#endif + virtual HTMLElement* placeholderElement() const; + + bool checked() const { return m_isChecked; } + void setChecked(bool, bool sendChangeEvent = false); + + // 'indeterminate' is a state independent of the checked state that causes the control to draw in a way that hides the actual state. + bool indeterminate() const { return m_isIndeterminate; } + void setIndeterminate(bool); + // shouldAppearChecked is used by the rendering tree/CSS while checked() is used by JS to determine checked state + bool shouldAppearChecked() const; + virtual bool isIndeterminate() const { return indeterminate(); } + + int size() const; + bool sizeShouldIncludeDecoration(int& preferredSize) const; + + void setType(const String&); + + String value() const; + void setValue(const String&, bool sendChangeEvent = false); + void setValueForUser(const String&); + // Checks if the specified string would be a valid value. + // We should not call this for types with no string value such as CHECKBOX and RADIO. + bool isValidValue(const String&) const; + bool hasDirtyValue() const { return !m_valueIfDirty.isNull(); }; + + String sanitizeValue(const String&) const; + + void updateInnerTextValue(); + + // The value which is drawn by a renderer. + String visibleValue() const; + String convertFromVisibleValue(const String&) const; + // Returns true if the specified string can be set as the value of HTMLInputElement. + bool isAcceptableValue(const String&) const; + + const String& suggestedValue() const; + void setSuggestedValue(const String&); + + double valueAsDate() const; + void setValueAsDate(double, ExceptionCode&); + + double valueAsNumber() const; + void setValueAsNumber(double, ExceptionCode&, bool sendChangeEvent = false); + + virtual String placeholder() const; + virtual void setPlaceholder(const String&); + + String valueWithDefault() const; + + void setValueFromRenderer(const String&); + + bool canHaveSelection() const; + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void detach(); + + // FIXME: For isActivatedSubmit and setActivatedSubmit, we should use the NVI-idiom here by making + // it private virtual in all classes and expose a public method in HTMLFormControlElement to call + // the private virtual method. + virtual bool isActivatedSubmit() const; + virtual void setActivatedSubmit(bool flag); + + String altText() const; + + int maxResults() const { return m_maxResults; } + + String defaultValue() const; + void setDefaultValue(const String&); + + void setDefaultName(const AtomicString&); + + Vector<String> acceptMIMETypes(); + String accept() const; + String alt() const; + + void setSize(unsigned); + + KURL src() const; + + virtual int maxLength() const; + void setMaxLength(int, ExceptionCode&); + + bool multiple() const; + + bool isAutofilled() const { return m_isAutofilled; } + void setAutofilled(bool = true); + + FileList* files(); + void receiveDroppedFiles(const Vector<String>&); + Icon* icon() const; + // These functions are used for rendering the input active during a + // drag-and-drop operation. + bool canReceiveDroppedFiles() const; + void setCanReceiveDroppedFiles(bool); + + void addSearchResult(); + void onSearch(); + bool searchEventsShouldBeDispatched() const; + +#if ENABLE(DATALIST) + HTMLElement* list() const; + HTMLOptionElement* selectedOption() const; +#endif + + // These functions are public so they can be used in InputType classes. + // Otherwise, they would be private. + CheckedRadioButtons& checkedRadioButtons() const; + void updateCheckedRadioButtons(); + void setValueInternal(const String&, bool sendChangeEvent); + + void cacheSelectionInResponseToSetValue(int caretOffset) { cacheSelection(caretOffset, caretOffset, SelectionHasNoDirection); } + +#if ENABLE(INPUT_COLOR) + // For test purposes. + void selectColorInColorChooser(const Color&); +#endif + + String defaultToolTip() const; + + static const int maximumLength; + +protected: + HTMLInputElement(const QualifiedName&, Document*, HTMLFormElement*, bool createdByParser); + void createShadowSubtree(); + virtual void defaultEventHandler(Event*); + +private: + enum AutoCompleteSetting { Uninitialized, On, Off }; + enum AnyStepHandling { RejectAny, AnyIsDefaultStep }; + + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + virtual bool isEnumeratable() const; + virtual void updateFocusAppearance(bool restorePreviousSelection); + 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 bool canStartSelection() const; + + virtual void accessKeyAction(bool sendMouseEvents); + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(Attribute*); + virtual void finishParsingChildren(); + + virtual void copyNonAttributeProperties(const Element* source); + + virtual void attach(); + + virtual bool appendFormData(FormDataList&, bool); + + virtual bool isSuccessfulSubmitButton() const; + + virtual void reset(); + + virtual void* preDispatchEventHandler(Event*); + virtual void postDispatchEventHandler(Event*, void* dataFromPreDispatch); + + virtual bool isURLAttribute(Attribute*) const; + + virtual bool hasUnacceptableValue() const; + + virtual bool isInRange() const; + virtual bool isOutOfRange() const; + + virtual void documentDidResumeFromPageCache(); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + bool needsSuspensionCallback(); + void registerForSuspensionCallbackIfNeeded(); + void unregisterForSuspensionCallbackIfNeeded(); + + bool supportsMaxLength() const { return isTextType(); } + bool isTextType() const; + + virtual bool supportsPlaceholder() const; + virtual void updatePlaceholderText(); + virtual bool isEmptyValue() const OVERRIDE { return innerTextValue().isEmpty(); } + virtual bool isEmptySuggestedValue() const { return suggestedValue().isEmpty(); } + virtual void handleFocusEvent(); + virtual void handleBlurEvent(); + + virtual bool isOptionalFormControl() const { return !isRequiredFormControl(); } + virtual bool isRequiredFormControl() const; + virtual bool recalcWillValidate() const; + + void updateType(); + + virtual void subtreeHasChanged(); + + bool getAllowedValueStepWithDecimalPlaces(AnyStepHandling, double*, unsigned*) const; + + // Helper for stepUp()/stepDown(). Adds step value * count to the current value. + void applyStep(double count, AnyStepHandling, bool sendChangeEvent, ExceptionCode&); + double alignValueForStep(double value, double step, unsigned currentDecimalPlaces, unsigned stepDecimalPlaces); + +#if ENABLE(DATALIST) + HTMLDataListElement* dataList() const; +#endif + void parseMaxLengthAttribute(Attribute*); + void updateValueIfNeeded(); + + AtomicString m_name; + String m_valueIfDirty; + String m_suggestedValue; + int m_size; + int m_maxLength; + short m_maxResults; + bool m_isChecked : 1; + bool m_reflectsCheckedAttribute : 1; + bool m_isIndeterminate : 1; + bool m_hasType : 1; + bool m_isActivatedSubmit : 1; + unsigned m_autocomplete : 2; // AutoCompleteSetting + bool m_isAutofilled : 1; +#if ENABLE(DATALIST) + bool m_hasNonEmptyList : 1; +#endif + bool m_stateRestored : 1; + bool m_parsingInProgress : 1; + bool m_wasModifiedByUser : 1; + bool m_canReceiveDroppedFiles : 1; + OwnPtr<InputType> m_inputType; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLInputElement.idl b/Source/WebCore/html/HTMLInputElement.idl new file mode 100644 index 000000000..7463334bb --- /dev/null +++ b/Source/WebCore/html/HTMLInputElement.idl @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLInputElement : HTMLElement { + attribute [ConvertNullToNullString] DOMString defaultValue; + attribute [Reflect=checked] boolean defaultChecked; + attribute [Reflect] DOMString dirName; + readonly attribute HTMLFormElement form; + attribute [Reflect, URL] DOMString formAction; + attribute [ConvertNullToNullString] DOMString formEnctype; + attribute [ConvertNullToNullString] DOMString formMethod; + attribute [Reflect] boolean formNoValidate; + attribute [Reflect] DOMString formTarget; + readonly attribute ValidityState validity; + attribute [Reflect] DOMString accept; + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString alt; + attribute boolean checked; + attribute [Reflect] boolean disabled; + attribute [Reflect] boolean autofocus; + attribute [Reflect] DOMString autocomplete; + readonly attribute [Conditional=DATALIST] HTMLElement list; + attribute [Reflect] DOMString max; + attribute long maxLength setter raises(DOMException); + attribute [Reflect] DOMString min; + attribute [Reflect] boolean multiple; + attribute [Conditional=DIRECTORY_UPLOAD, Reflect] boolean webkitdirectory; + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString pattern; + attribute [Reflect] DOMString placeholder; + attribute [Reflect] boolean readOnly; + attribute [Reflect] boolean required; +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + attribute [ConvertToString] DOMString size; // DOM level 2 changed this to a long, but our existing API is a string +#else + // FIXME: The spec says this should be a long, not an unsigned long. + attribute unsigned long size; // Changed string -> long as part of DOM level 2 +#endif + attribute [Reflect, URL] DOMString src; + attribute [Reflect] DOMString step; + attribute [ConvertNullToNullString] DOMString type; // readonly dropped as part of DOM level 2 + attribute [Reflect] DOMString useMap; + attribute [ConvertNullToNullString] DOMString value; +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP + attribute Date valueAsDate setter raises(DOMException); +#endif + attribute double valueAsNumber setter raises(DOMException); + readonly attribute [Conditional=DATALIST] HTMLOptionElement selectedOption; + attribute [Reflect] boolean incremental; + + void stepUp(in [Optional] long n) raises(DOMException); + void stepDown(in [Optional] long n) raises(DOMException); + + readonly attribute boolean willValidate; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + void select(); + void click(); + +#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT + void setValueForUser(in [ConvertNullToNullString] DOMString value); +#endif + + // WinIE extension: + attribute boolean indeterminate; + + attribute [Custom] long selectionStart; + attribute [Custom] long selectionEnd; + attribute [Custom] DOMString selectionDirection; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + [Custom] void setSelectionRange(in long start, in long end); +#else + [Custom] void setSelectionRange(in [Optional=CallWithDefaultValue] long start, + in [Optional=CallWithDefaultValue] long end, + in [Optional] DOMString direction); +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) + // Objective-C extension: + readonly attribute DOMString altDisplayString; + readonly attribute URL absoluteImageURL; +#endif + + readonly attribute FileList files; + readonly attribute NodeList labels; + + attribute [Conditional=INPUT_SPEECH, Reflect, EnabledAtRuntime] boolean webkitSpeech; + attribute [Conditional=INPUT_SPEECH, Reflect, EnabledAtRuntime] boolean webkitGrammar; + attribute [Conditional=INPUT_SPEECH, DontEnum] EventListener onwebkitspeechchange; + }; + +} diff --git a/Source/WebCore/html/HTMLIsIndexElement.cpp b/Source/WebCore/html/HTMLIsIndexElement.cpp new file mode 100644 index 000000000..f7cf5ecd1 --- /dev/null +++ b/Source/WebCore/html/HTMLIsIndexElement.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLIsIndexElement.h" + +#include "Attribute.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLIsIndexElement::HTMLIsIndexElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLInputElement(tagName, document, form, false) +{ + ASSERT(hasTagName(isindexTag)); + setDefaultName(isindexTag.localName()); +} + +PassRefPtr<HTMLIsIndexElement> HTMLIsIndexElement::create(Document* document, HTMLFormElement* form) +{ + RefPtr<HTMLIsIndexElement> element = adoptRef(new HTMLIsIndexElement(isindexTag, document, form)); + element->createShadowSubtree(); + return element.release(); +} + +PassRefPtr<HTMLIsIndexElement> HTMLIsIndexElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + RefPtr<HTMLIsIndexElement> element = adoptRef(new HTMLIsIndexElement(tagName, document, form)); + element->createShadowSubtree(); + return element.release(); +} + +void HTMLIsIndexElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == promptAttr) + setValue(attr->value()); + else if (attr->name() == placeholderAttr) + updatePlaceholderVisibility(true); + else + // don't call HTMLInputElement::parseMappedAttribute here, as it would + // accept attributes this element does not support + HTMLFormControlElement::parseMappedAttribute(attr); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLIsIndexElement.h b/Source/WebCore/html/HTMLIsIndexElement.h new file mode 100644 index 000000000..857ef7578 --- /dev/null +++ b/Source/WebCore/html/HTMLIsIndexElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple 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 HTMLIsIndexElement_h +#define HTMLIsIndexElement_h + +#include "HTMLInputElement.h" + +namespace WebCore { + +class HTMLIsIndexElement : public HTMLInputElement { +public: + static PassRefPtr<HTMLIsIndexElement> create(Document*, HTMLFormElement*); + static PassRefPtr<HTMLIsIndexElement> create(const QualifiedName&, Document*, HTMLFormElement*); + +private: + HTMLIsIndexElement(const QualifiedName&, Document*, HTMLFormElement*); + + virtual bool canTriggerImplicitSubmission() const { return true; } + + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLIsIndexElement.idl b/Source/WebCore/html/HTMLIsIndexElement.idl new file mode 100644 index 000000000..028a1800a --- /dev/null +++ b/Source/WebCore/html/HTMLIsIndexElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLIsIndexElement : HTMLInputElement { + readonly attribute HTMLFormElement form; + attribute [Reflect] DOMString prompt; + }; + +} diff --git a/Source/WebCore/html/HTMLKeygenElement.cpp b/Source/WebCore/html/HTMLKeygenElement.cpp new file mode 100644 index 000000000..11b7e92ef --- /dev/null +++ b/Source/WebCore/html/HTMLKeygenElement.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLKeygenElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "FormDataList.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.h" +#include "HTMLOptionElement.h" +#include "SSLKeyGenerator.h" +#include "ShadowRoot.h" +#include "Text.h" +#include <wtf/StdLibExtras.h> + +using namespace WebCore; + +namespace WebCore { + +using namespace HTMLNames; + +class KeygenSelectElement : public HTMLSelectElement { +public: + static PassRefPtr<KeygenSelectElement> create(Document* document) + { + return adoptRef(new KeygenSelectElement(document)); + } + + virtual const AtomicString& shadowPseudoId() const + { + DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-keygen-select")); + return pseudoId; + } + +protected: + KeygenSelectElement(Document* document) + : HTMLSelectElement(selectTag, document, 0) + { + } + +private: + virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren() + { + return create(document()); + } +}; + +inline HTMLKeygenElement::HTMLKeygenElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElementWithState(tagName, document, form) +{ + ASSERT(hasTagName(keygenTag)); + + // Create a select element with one option element for each key size. + Vector<String> keys; + getSupportedKeySizes(keys); + + RefPtr<HTMLSelectElement> select = KeygenSelectElement::create(document); + ExceptionCode ec = 0; + for (size_t i = 0; i < keys.size(); ++i) { + RefPtr<HTMLOptionElement> option = HTMLOptionElement::create(document, this->form()); + select->appendChild(option, ec); + option->appendChild(Text::create(document, keys[i]), ec); + } + + ensureShadowRoot()->appendChild(select, ec); +} + +PassRefPtr<HTMLKeygenElement> HTMLKeygenElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLKeygenElement(tagName, document, form)); +} + +void HTMLKeygenElement::parseMappedAttribute(Attribute* attr) +{ + // Reflect disabled attribute on the shadow select element + if (attr->name() == disabledAttr) + shadowSelect()->setAttribute(attr->name(), attr->value()); + + HTMLFormControlElement::parseMappedAttribute(attr); +} + +bool HTMLKeygenElement::appendFormData(FormDataList& encoded_values, bool) +{ + // Only RSA is supported at this time. + const AtomicString& keyType = fastGetAttribute(keytypeAttr); + if (!keyType.isNull() && !equalIgnoringCase(keyType, "rsa")) + return false; + String value = signedPublicKeyAndChallengeString(shadowSelect()->selectedIndex(), fastGetAttribute(challengeAttr), document()->baseURL()); + if (value.isNull()) + return false; + encoded_values.appendData(name(), value.utf8()); + return true; +} + +const AtomicString& HTMLKeygenElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, keygen, ("keygen")); + return keygen; +} + +void HTMLKeygenElement::reset() +{ + static_cast<HTMLFormControlElement*>(shadowSelect())->reset(); +} + +HTMLSelectElement* HTMLKeygenElement::shadowSelect() const +{ + ShadowRoot* shadow = shadowRoot(); + ASSERT(shadow); + return shadow ? toHTMLSelectElement(shadow->firstChild()) : 0; +} + +} // namespace diff --git a/Source/WebCore/html/HTMLKeygenElement.h b/Source/WebCore/html/HTMLKeygenElement.h new file mode 100644 index 000000000..cc477d692 --- /dev/null +++ b/Source/WebCore/html/HTMLKeygenElement.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple 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 HTMLKeygenElement_h +#define HTMLKeygenElement_h + +#include "HTMLFormControlElementWithState.h" + +namespace WebCore { + +class HTMLSelectElement; + +class HTMLKeygenElement : public HTMLFormControlElementWithState { +public: + static PassRefPtr<HTMLKeygenElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + virtual bool willValidate() const { return false; } + +private: + HTMLKeygenElement(const QualifiedName&, Document*, HTMLFormElement*); + + virtual bool canStartSelection() const { return false; } + + virtual void parseMappedAttribute(Attribute*); + + virtual bool appendFormData(FormDataList&, bool); + virtual const AtomicString& formControlType() const; + virtual bool isOptionalFormControl() const { return false; } + + virtual bool isEnumeratable() const { return true; } + + virtual void reset(); + + HTMLSelectElement* shadowSelect() const; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLKeygenElement.idl b/Source/WebCore/html/HTMLKeygenElement.idl new file mode 100644 index 000000000..c34a92582 --- /dev/null +++ b/Source/WebCore/html/HTMLKeygenElement.idl @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface HTMLKeygenElement : HTMLElement { + attribute [Reflect] boolean autofocus; + attribute [Reflect] DOMString challenge; + attribute [Reflect] boolean disabled; + readonly attribute HTMLFormElement form; + attribute [Reflect] DOMString keytype; + attribute DOMString name; + + readonly attribute DOMString type; + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + + readonly attribute NodeList labels; + }; + +} diff --git a/Source/WebCore/html/HTMLLIElement.cpp b/Source/WebCore/html/HTMLLIElement.cpp new file mode 100644 index 000000000..32886cbe9 --- /dev/null +++ b/Source/WebCore/html/HTMLLIElement.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2006, 2007, 2010 Apple 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 "HTMLLIElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" +#include "RenderListItem.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLLIElement::HTMLLIElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(liTag)); +} + +PassRefPtr<HTMLLIElement> HTMLLIElement::create(Document* document) +{ + return adoptRef(new HTMLLIElement(liTag, document)); +} + +PassRefPtr<HTMLLIElement> HTMLLIElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLLIElement(tagName, document)); +} + +bool HTMLLIElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == typeAttr) { + result = eListItem; // Share with <ol> since all the values are the same + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLLIElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == valueAttr) { + if (renderer() && renderer()->isListItem()) + parseValue(attr->value()); + } else if (attr->name() == typeAttr) { + if (attr->value() == "a") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueLowerAlpha); + else if (attr->value() == "A") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueUpperAlpha); + else if (attr->value() == "i") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueLowerRoman); + else if (attr->value() == "I") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueUpperRoman); + else if (attr->value() == "1") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueDecimal); + else + addCSSProperty(attr, CSSPropertyListStyleType, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLLIElement::attach() +{ + ASSERT(!attached()); + + HTMLElement::attach(); + + if (renderer() && renderer()->isListItem()) { + RenderListItem* render = toRenderListItem(renderer()); + + // Find the enclosing list node. + Node* listNode = 0; + Node* n = this; + while (!listNode && (n = n->parentNode())) { + if (n->hasTagName(ulTag) || n->hasTagName(olTag)) + listNode = n; + } + + // If we are not in a list, tell the renderer so it can position us inside. + // We don't want to change our style to say "inside" since that would affect nested nodes. + if (!listNode) + render->setNotInList(true); + + parseValue(fastGetAttribute(valueAttr)); + } +} + +inline void HTMLLIElement::parseValue(const AtomicString& value) +{ + ASSERT(renderer() && renderer()->isListItem()); + + bool valueOK; + int requestedValue = value.toInt(&valueOK); + if (valueOK) + toRenderListItem(renderer())->setExplicitValue(requestedValue); + else + toRenderListItem(renderer())->clearExplicitValue(); +} + +} diff --git a/Source/WebCore/html/HTMLLIElement.h b/Source/WebCore/html/HTMLLIElement.h new file mode 100644 index 000000000..7e6a95370 --- /dev/null +++ b/Source/WebCore/html/HTMLLIElement.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLLIElement_h +#define HTMLLIElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLLIElement : public HTMLElement { +public: + static PassRefPtr<HTMLLIElement> create(Document*); + static PassRefPtr<HTMLLIElement> create(const QualifiedName&, Document*); + +private: + HTMLLIElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + virtual void attach(); + + void parseValue(const AtomicString&); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLLIElement.idl b/Source/WebCore/html/HTMLLIElement.idl new file mode 100644 index 000000000..2dc541b18 --- /dev/null +++ b/Source/WebCore/html/HTMLLIElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLLIElement : HTMLElement { + attribute [Reflect] DOMString type; + attribute [Reflect] long value; + }; + +} diff --git a/Source/WebCore/html/HTMLLabelElement.cpp b/Source/WebCore/html/HTMLLabelElement.cpp new file mode 100644 index 000000000..6cbd21900 --- /dev/null +++ b/Source/WebCore/html/HTMLLabelElement.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLLabelElement.h" + +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "HTMLFormControlElement.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +static HTMLFormControlElement* nodeAsLabelableFormControl(Node* node) +{ + if (!node || !node->isElementNode() || !static_cast<Element*>(node)->isFormControlElement()) + return 0; + + HTMLFormControlElement* formControlElement = static_cast<HTMLFormControlElement*>(node); + if (!formControlElement->isLabelable()) + return 0; + + return formControlElement; +} + +inline HTMLLabelElement::HTMLLabelElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(labelTag)); +} + +PassRefPtr<HTMLLabelElement> HTMLLabelElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLLabelElement(tagName, document)); +} + +bool HTMLLabelElement::isFocusable() const +{ + return false; +} + +HTMLFormControlElement* HTMLLabelElement::control() +{ + const AtomicString& controlId = getAttribute(forAttr); + if (controlId.isNull()) { + // Search the children and descendants of the label element for a form element. + // per http://dev.w3.org/html5/spec/Overview.html#the-label-element + // the form element must be "labelable form-associated element". + Node* node = this; + while ((node = node->traverseNextNode(this))) { + if (HTMLFormControlElement* formControlElement = nodeAsLabelableFormControl(node)) + return formControlElement; + } + return 0; + } + + // Find the first element whose id is controlId. If it is found and it is a labelable form control, + // return it, otherwise return 0. + return nodeAsLabelableFormControl(treeScope()->getElementById(controlId)); +} + +void HTMLLabelElement::setActive(bool down, bool pause) +{ + if (down == active()) + return; + + // Update our status first. + HTMLElement::setActive(down, pause); + + // Also update our corresponding control. + if (HTMLElement* element = control()) + element->setActive(down, pause); +} + +void HTMLLabelElement::setHovered(bool over) +{ + if (over == hovered()) + return; + + // Update our status first. + HTMLElement::setHovered(over); + + // Also update our corresponding control. + if (HTMLElement* element = control()) + element->setHovered(over); +} + +void HTMLLabelElement::defaultEventHandler(Event* evt) +{ + static bool processingClick = false; + + if (evt->type() == eventNames().clickEvent && !processingClick) { + RefPtr<HTMLElement> element = control(); + + // If we can't find a control or if the control received the click + // event, then there's no need for us to do anything. + if (!element || (evt->target() && element->containsIncludingShadowDOM(evt->target()->toNode()))) + return; + + processingClick = true; + + // Click the corresponding control. + element->dispatchSimulatedClick(evt); + + // If the control can be focused via the mouse, then do that too. + if (element->isMouseFocusable()) + element->focus(); + + processingClick = false; + + evt->setDefaultHandled(); + } + + HTMLElement::defaultEventHandler(evt); +} + +void HTMLLabelElement::focus(bool) +{ + // to match other browsers, always restore previous selection + if (HTMLElement* element = control()) + element->focus(); +} + +void HTMLLabelElement::accessKeyAction(bool sendMouseEvents) +{ + if (HTMLElement* element = control()) + element->accessKeyAction(sendMouseEvents); + else + HTMLElement::accessKeyAction(sendMouseEvents); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLLabelElement.h b/Source/WebCore/html/HTMLLabelElement.h new file mode 100644 index 000000000..32a55e449 --- /dev/null +++ b/Source/WebCore/html/HTMLLabelElement.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 HTMLLabelElement_h +#define HTMLLabelElement_h + +#include "HTMLElement.h" +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLLabelElement : public HTMLElement { +public: + static PassRefPtr<HTMLLabelElement> create(const QualifiedName&, Document*); + + HTMLFormControlElement* control(); + +private: + HTMLLabelElement(const QualifiedName&, Document*); + + virtual bool isFocusable() const; + + virtual void accessKeyAction(bool sendMouseEvents); + + // Overridden to update the hover/active state of the corresponding control. + virtual void setActive(bool = true, bool pause = false); + virtual void setHovered(bool = true); + + // Overridden to either click() or focus() the corresponding control. + virtual void defaultEventHandler(Event*); + + void focus(bool restorePreviousSelection = true); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLLabelElement.idl b/Source/WebCore/html/HTMLLabelElement.idl new file mode 100644 index 000000000..9460418cd --- /dev/null +++ b/Source/WebCore/html/HTMLLabelElement.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLLabelElement : HTMLElement { + readonly attribute HTMLFormElement form; + attribute [Reflect=for] DOMString htmlFor; + readonly attribute HTMLElement control; + }; + +} diff --git a/Source/WebCore/html/HTMLLegendElement.cpp b/Source/WebCore/html/HTMLLegendElement.cpp new file mode 100644 index 000000000..61d2db31b --- /dev/null +++ b/Source/WebCore/html/HTMLLegendElement.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLLegendElement.h" + +#include "HTMLNames.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLLegendElement::HTMLLegendElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) +{ + ASSERT(hasTagName(legendTag)); +} + +PassRefPtr<HTMLLegendElement> HTMLLegendElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLLegendElement(tagName, document, form)); +} + +bool HTMLLegendElement::supportsFocus() const +{ + return HTMLElement::supportsFocus(); +} + +const AtomicString& HTMLLegendElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, legend, ("legend")); + return legend; +} + +HTMLFormControlElement* HTMLLegendElement::associatedControl() +{ + // Check if there's a fieldset belonging to this legend. + ContainerNode* fieldset = parentNode(); + while (fieldset && !fieldset->hasTagName(fieldsetTag)) + fieldset = fieldset->parentNode(); + if (!fieldset) + return 0; + + // Find first form element inside the fieldset that is not a legend element. + // FIXME: Should we consider tabindex? + Node* node = fieldset; + while ((node = node->traverseNextNode(fieldset))) { + if (node->isElementNode()) { + Element* element = static_cast<Element*>(node); + if (!element->hasLocalName(legendTag) && element->isFormControlElement()) + return static_cast<HTMLFormControlElement*>(element); + } + } + + return 0; +} + +void HTMLLegendElement::focus(bool) +{ + if (isFocusable()) + Element::focus(); + + // To match other browsers' behavior, never restore previous selection. + if (HTMLFormControlElement* control = associatedControl()) + control->focus(false); +} + +void HTMLLegendElement::accessKeyAction(bool sendMouseEvents) +{ + if (HTMLFormControlElement* control = associatedControl()) + control->accessKeyAction(sendMouseEvents); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLLegendElement.h b/Source/WebCore/html/HTMLLegendElement.h new file mode 100644 index 000000000..24d8fce82 --- /dev/null +++ b/Source/WebCore/html/HTMLLegendElement.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010 Apple 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 HTMLLegendElement_h +#define HTMLLegendElement_h + +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLLegendElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLLegendElement> create(const QualifiedName&, Document*, HTMLFormElement*); + +private: + HTMLLegendElement(const QualifiedName&, Document*, HTMLFormElement*); + + // Control in the legend's fieldset that gets focus and access key. + HTMLFormControlElement* associatedControl(); + + virtual bool supportsFocus() const; + virtual const AtomicString& formControlType() const; + virtual void accessKeyAction(bool sendMouseEvents); + virtual void focus(bool restorePreviousSelection = true); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLLegendElement.idl b/Source/WebCore/html/HTMLLegendElement.idl new file mode 100644 index 000000000..8cda34f58 --- /dev/null +++ b/Source/WebCore/html/HTMLLegendElement.idl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLLegendElement : HTMLElement { + readonly attribute HTMLFormElement form; + attribute [Reflect] DOMString align; + }; + +} diff --git a/Source/WebCore/html/HTMLLinkElement.cpp b/Source/WebCore/html/HTMLLinkElement.cpp new file mode 100644 index 000000000..124163d93 --- /dev/null +++ b/Source/WebCore/html/HTMLLinkElement.cpp @@ -0,0 +1,463 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Rob Buis (rwlbuis@gmail.com) + * Copyright (C) 2011 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 "HTMLLinkElement.h" + +#include "Attribute.h" +#include "CachedCSSStyleSheet.h" +#include "CachedResource.h" +#include "CachedResourceLoader.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "MediaList.h" +#include "MediaQueryEvaluator.h" +#include "Page.h" +#include "ResourceHandle.h" +#include "ScriptEventListener.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLLinkElement::HTMLLinkElement(const QualifiedName& tagName, Document* document, bool createdByParser) + : HTMLElement(tagName, document) + , m_linkLoader(this) + , m_sizes(DOMSettableTokenList::create()) + , m_disabledState(Unset) + , m_loading(false) + , m_createdByParser(createdByParser) + , m_isInShadowTree(false) + , m_pendingSheetType(None) +{ + ASSERT(hasTagName(linkTag)); +} + +PassRefPtr<HTMLLinkElement> HTMLLinkElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) +{ + return adoptRef(new HTMLLinkElement(tagName, document, createdByParser)); +} + +HTMLLinkElement::~HTMLLinkElement() +{ + if (m_sheet) + m_sheet->clearOwnerNode(); + + if (m_cachedSheet) { + m_cachedSheet->removeClient(this); + removePendingSheet(); + } + + if (inDocument()) + document()->removeStyleSheetCandidateNode(this); +} + +void HTMLLinkElement::setDisabledState(bool disabled) +{ + DisabledState oldDisabledState = m_disabledState; + m_disabledState = disabled ? Disabled : EnabledViaScript; + if (oldDisabledState != m_disabledState) { + // If we change the disabled state while the sheet is still loading, then we have to + // perform three checks: + if (styleSheetIsLoading()) { + // Check #1: The sheet becomes disabled while loading. + if (m_disabledState == Disabled) + removePendingSheet(); + + // Check #2: An alternate sheet becomes enabled while it is still loading. + if (m_relAttribute.m_isAlternate && m_disabledState == EnabledViaScript) + addPendingSheet(Blocking); + + // Check #3: A main sheet becomes enabled while it was still loading and + // after it was disabled via script. It takes really terrible code to make this + // happen (a double toggle for no reason essentially). This happens on + // virtualplastic.net, which manages to do about 12 enable/disables on only 3 + // sheets. :) + if (!m_relAttribute.m_isAlternate && m_disabledState == EnabledViaScript && oldDisabledState == Disabled) + addPendingSheet(Blocking); + + // If the sheet is already loading just bail. + return; + } + + // Load the sheet, since it's never been loaded before. + if (!m_sheet && m_disabledState == EnabledViaScript) + process(); + else + document()->styleSelectorChanged(DeferRecalcStyle); // Update the style selector. + } +} + +void HTMLLinkElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == relAttr) { + m_relAttribute = LinkRelAttribute(attr->value()); + process(); + } else if (attr->name() == hrefAttr) { + String url = stripLeadingAndTrailingHTMLSpaces(attr->value()); + m_url = url.isEmpty() ? KURL() : document()->completeURL(url); + process(); + } else if (attr->name() == typeAttr) { + m_type = attr->value(); + process(); + } else if (attr->name() == sizesAttr) { + setSizes(attr->value()); + process(); + } else if (attr->name() == mediaAttr) { + m_media = attr->value().string().lower(); + process(); + } else if (attr->name() == disabledAttr) + setDisabledState(!attr->isNull()); + else if (attr->name() == onbeforeloadAttr) + setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); +#if ENABLE(LINK_PREFETCH) + else if (attr->name() == onloadAttr) + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == onerrorAttr) + setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); +#endif + else { + if (attr->name() == titleAttr && m_sheet) + m_sheet->setTitle(attr->value()); + HTMLElement::parseMappedAttribute(attr); + } +} + +bool HTMLLinkElement::shouldLoadLink() +{ + RefPtr<Document> originalDocument = document(); + if (!dispatchBeforeLoadEvent(m_url)) + return false; + // A beforeload handler might have removed us from the document or changed the document. + if (!inDocument() || document() != originalDocument) + return false; + return true; +} + +void HTMLLinkElement::process() +{ + if (!inDocument() || m_isInShadowTree) { + ASSERT(!m_sheet); + return; + } + + String type = m_type.lower(); + + if (!m_linkLoader.loadLink(m_relAttribute, type, m_sizes->toString(), m_url, document())) + return; + + bool acceptIfTypeContainsTextCSS = document()->page() && document()->page()->settings() && document()->page()->settings()->treatsAnyTextCSSLinkAsStylesheet(); + + if (m_disabledState != Disabled && (m_relAttribute.m_isStyleSheet || (acceptIfTypeContainsTextCSS && type.contains("text/css"))) + && document()->frame() && m_url.isValid()) { + + String charset = getAttribute(charsetAttr); + if (charset.isEmpty() && document()->frame()) + charset = document()->charset(); + + if (m_cachedSheet) { + removePendingSheet(); + m_cachedSheet->removeClient(this); + m_cachedSheet = 0; + } + + if (!shouldLoadLink()) + return; + + m_loading = true; + + bool mediaQueryMatches = true; + if (!m_media.isEmpty()) { + RefPtr<RenderStyle> documentStyle = CSSStyleSelector::styleForDocument(document()); + RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(m_media); + MediaQueryEvaluator evaluator(document()->frame()->view()->mediaType(), document()->frame(), documentStyle.get()); + mediaQueryMatches = evaluator.eval(media.get()); + } + + // Don't hold up render tree construction and script execution on stylesheets + // that are not needed for the rendering at the moment. + bool blocking = mediaQueryMatches && !isAlternate(); + addPendingSheet(blocking ? Blocking : NonBlocking); + + // Load stylesheets that are not needed for the rendering immediately with low priority. + ResourceLoadPriority priority = blocking ? ResourceLoadPriorityUnresolved : ResourceLoadPriorityVeryLow; + ResourceRequest request(document()->completeURL(m_url)); + m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(request, charset, priority); + + if (m_cachedSheet) + m_cachedSheet->addClient(this); + else { + // The request may have been denied if (for example) the stylesheet is local and the document is remote. + m_loading = false; + removePendingSheet(); + } + } else if (m_sheet) { + // we no longer contain a stylesheet, e.g. perhaps rel or type was changed + m_sheet = 0; + document()->styleSelectorChanged(DeferRecalcStyle); + } +} + +void HTMLLinkElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + + m_isInShadowTree = isInShadowTree(); + if (m_isInShadowTree) + return; + + document()->addStyleSheetCandidateNode(this, m_createdByParser); + + process(); +} + +void HTMLLinkElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + + if (m_isInShadowTree) { + ASSERT(!m_sheet); + return; + } + document()->removeStyleSheetCandidateNode(this); + + if (m_sheet) { + ASSERT(m_sheet->ownerNode() == this); + m_sheet->clearOwnerNode(); + m_sheet = 0; + } + + if (document()->renderer()) + document()->styleSelectorChanged(DeferRecalcStyle); +} + +void HTMLLinkElement::finishParsingChildren() +{ + m_createdByParser = false; + HTMLElement::finishParsingChildren(); +} + +void HTMLLinkElement::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet) +{ + if (!inDocument()) { + ASSERT(!m_sheet); + return; + } + + m_sheet = CSSStyleSheet::create(this, href, baseURL, charset); + + bool strictParsing = !document()->inQuirksMode(); + bool enforceMIMEType = strictParsing; + bool crossOriginCSS = false; + bool validMIMEType = false; + bool needsSiteSpecificQuirks = document()->page() && document()->page()->settings()->needsSiteSpecificQuirks(); + + // Check to see if we should enforce the MIME type of the CSS resource in strict mode. + // Running in iWeb 2 is one example of where we don't want to - <rdar://problem/6099748> + if (enforceMIMEType && document()->page() && !document()->page()->settings()->enforceCSSMIMETypeInNoQuirksMode()) + enforceMIMEType = false; + +#ifdef BUILDING_ON_LEOPARD + if (enforceMIMEType && needsSiteSpecificQuirks) { + // Covers both http and https, with or without "www." + if (baseURL.string().contains("mcafee.com/japan/", false)) + enforceMIMEType = false; + } +#endif + + String sheetText = sheet->sheetText(enforceMIMEType, &validMIMEType); + m_sheet->parseString(sheetText, strictParsing); + + // If we're loading a stylesheet cross-origin, and the MIME type is not + // standard, require the CSS to at least start with a syntactically + // valid CSS rule. + // This prevents an attacker playing games by injecting CSS strings into + // HTML, XML, JSON, etc. etc. + if (!document()->securityOrigin()->canRequest(baseURL)) + crossOriginCSS = true; + + if (crossOriginCSS && !validMIMEType && !m_sheet->hasSyntacticallyValidCSSHeader()) + m_sheet = CSSStyleSheet::create(this, href, baseURL, charset); + + if (strictParsing && needsSiteSpecificQuirks) { + // Work around <https://bugs.webkit.org/show_bug.cgi?id=28350>. + DEFINE_STATIC_LOCAL(const String, slashKHTMLFixesDotCss, ("/KHTMLFixes.css")); + DEFINE_STATIC_LOCAL(const String, mediaWikiKHTMLFixesStyleSheet, ("/* KHTML fix stylesheet */\n/* work around the horizontal scrollbars */\n#column-content { margin-left: 0; }\n\n")); + // There are two variants of KHTMLFixes.css. One is equal to mediaWikiKHTMLFixesStyleSheet, + // while the other lacks the second trailing newline. + if (baseURL.string().endsWith(slashKHTMLFixesDotCss) && !sheetText.isNull() && mediaWikiKHTMLFixesStyleSheet.startsWith(sheetText) + && sheetText.length() >= mediaWikiKHTMLFixesStyleSheet.length() - 1) { + ASSERT(m_sheet->length() == 1); + ExceptionCode ec; + m_sheet->deleteRule(0, ec); + } + } + + m_sheet->setTitle(title()); + + RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(m_media); + m_sheet->setMedia(media.get()); + + m_loading = false; + m_sheet->checkLoaded(); +} + +bool HTMLLinkElement::styleSheetIsLoading() const +{ + if (m_loading) + return true; + if (!m_sheet) + return false; + return m_sheet->isLoading(); +} + +void HTMLLinkElement::linkLoaded() +{ + dispatchEvent(Event::create(eventNames().loadEvent, false, false)); +} + +void HTMLLinkElement::linkLoadingErrored() +{ + dispatchEvent(Event::create(eventNames().errorEvent, false, false)); +} + +bool HTMLLinkElement::sheetLoaded() +{ + if (!styleSheetIsLoading()) { + removePendingSheet(); + return true; + } + return false; +} + +void HTMLLinkElement::startLoadingDynamicSheet() +{ + // We don't support multiple blocking sheets. + ASSERT(m_pendingSheetType < Blocking); + addPendingSheet(Blocking); +} + +bool HTMLLinkElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == hrefAttr || HTMLElement::isURLAttribute(attr); +} + +KURL HTMLLinkElement::href() const +{ + return document()->completeURL(getAttribute(hrefAttr)); +} + +String HTMLLinkElement::rel() const +{ + return getAttribute(relAttr); +} + +String HTMLLinkElement::target() const +{ + return getAttribute(targetAttr); +} + +String HTMLLinkElement::type() const +{ + return getAttribute(typeAttr); +} + +void HTMLLinkElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + // Favicons are handled by a special case in LegacyWebArchive::create() + if (m_relAttribute.m_iconType != InvalidIcon) + return; + + if (!m_relAttribute.m_isStyleSheet) + return; + + // Append the URL of this link element. + addSubresourceURL(urls, href()); + + // Walk the URLs linked by the linked-to stylesheet. + if (CSSStyleSheet* styleSheet = const_cast<HTMLLinkElement*>(this)->sheet()) + styleSheet->addSubresourceStyleURLs(urls); +} + +void HTMLLinkElement::addPendingSheet(PendingSheetType type) +{ + if (type <= m_pendingSheetType) + return; + m_pendingSheetType = type; + + if (m_pendingSheetType == NonBlocking) + return; + document()->addPendingSheet(); +} + +void HTMLLinkElement::removePendingSheet() +{ + PendingSheetType type = m_pendingSheetType; + m_pendingSheetType = None; + + if (type == None) + return; + if (type == NonBlocking) { + // Document::removePendingSheet() triggers the style selector recalc for blocking sheets. + document()->styleSelectorChanged(RecalcStyleImmediately); + return; + } + document()->removePendingSheet(); +} + +DOMSettableTokenList* HTMLLinkElement::sizes() const +{ + return m_sizes.get(); +} + +void HTMLLinkElement::setSizes(const String& value) +{ + m_sizes->setValue(value); +} + +#if ENABLE(MICRODATA) +String HTMLLinkElement::itemValueText() const +{ + return getURLAttribute(hrefAttr); +} + +void HTMLLinkElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(hrefAttr, value, ec); +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLLinkElement.h b/Source/WebCore/html/HTMLLinkElement.h new file mode 100644 index 000000000..d67a0774f --- /dev/null +++ b/Source/WebCore/html/HTMLLinkElement.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2011 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 HTMLLinkElement_h +#define HTMLLinkElement_h + +#include "CSSStyleSheet.h" +#include "CachedStyleSheetClient.h" +#include "CachedResourceHandle.h" +#include "DOMSettableTokenList.h" +#include "HTMLElement.h" +#include "IconURL.h" +#include "LinkLoader.h" +#include "LinkLoaderClient.h" +#include "LinkRelAttribute.h" +#include "Timer.h" + +namespace WebCore { + +class KURL; + +class HTMLLinkElement : public HTMLElement, public CachedStyleSheetClient, public LinkLoaderClient { +public: + static PassRefPtr<HTMLLinkElement> create(const QualifiedName&, Document*, bool createdByParser); + virtual ~HTMLLinkElement(); + + KURL href() const; + String rel() const; + + virtual String target() const; + + String type() const; + + CSSStyleSheet* sheet() const { return m_sheet.get(); } + + bool styleSheetIsLoading() const; + + bool isDisabled() const { return m_disabledState == Disabled; } + bool isEnabledViaScript() const { return m_disabledState == EnabledViaScript; } + void setSizes(const String&); + DOMSettableTokenList* sizes() const; + +private: + virtual void parseMappedAttribute(Attribute*); + + virtual bool shouldLoadLink(); + void process(); + static void processCallback(Node*); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + // from CachedResourceClient + virtual void setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet); + virtual bool sheetLoaded(); + virtual void startLoadingDynamicSheet(); + + virtual void linkLoaded(); + virtual void linkLoadingErrored(); + + bool isAlternate() const { return m_disabledState == Unset && m_relAttribute.m_isAlternate; } + + void setDisabledState(bool); + + virtual bool isURLAttribute(Attribute*) const; + +private: + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + virtual void finishParsingChildren(); + + enum PendingSheetType { None, NonBlocking, Blocking }; + void addPendingSheet(PendingSheetType); + void removePendingSheet(); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + +private: + HTMLLinkElement(const QualifiedName&, Document*, bool createdByParser); + + LinkLoader m_linkLoader; + CachedResourceHandle<CachedCSSStyleSheet> m_cachedSheet; + RefPtr<CSSStyleSheet> m_sheet; + enum DisabledState { + Unset, + EnabledViaScript, + Disabled + }; + + KURL m_url; + String m_type; + String m_media; + RefPtr<DOMSettableTokenList> m_sizes; + DisabledState m_disabledState; + LinkRelAttribute m_relAttribute; + bool m_loading; + bool m_createdByParser; + bool m_isInShadowTree; + + PendingSheetType m_pendingSheetType; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLLinkElement.idl b/Source/WebCore/html/HTMLLinkElement.idl new file mode 100644 index 000000000..8d16f25e8 --- /dev/null +++ b/Source/WebCore/html/HTMLLinkElement.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2011 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. + */ + +module html { + + interface HTMLLinkElement : HTMLElement { + attribute [Reflect] boolean disabled; + attribute [Reflect] DOMString charset; + attribute [Reflect, URL] DOMString href; + attribute [Reflect] DOMString hreflang; + attribute [Reflect] DOMString media; + attribute [Reflect] DOMString rel; + attribute [Reflect] DOMString rev; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + attribute [Custom] DOMSettableTokenList sizes; +#endif + attribute [Reflect] DOMString target; + attribute [Reflect] DOMString type; + + // DOM Level 2 Style + readonly attribute StyleSheet sheet; + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extension: + readonly attribute URL absoluteLinkURL; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLMapElement.cpp b/Source/WebCore/html/HTMLMapElement.cpp new file mode 100644 index 000000000..098295b73 --- /dev/null +++ b/Source/WebCore/html/HTMLMapElement.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 "HTMLMapElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "HTMLAreaElement.h" +#include "HTMLCollection.h" +#include "HTMLImageElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "IntSize.h" +#include "RenderObject.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +HTMLMapElement::HTMLMapElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(mapTag)); +} + +PassRefPtr<HTMLMapElement> HTMLMapElement::create(Document* document) +{ + return adoptRef(new HTMLMapElement(mapTag, document)); +} + +PassRefPtr<HTMLMapElement> HTMLMapElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLMapElement(tagName, document)); +} + +HTMLMapElement::~HTMLMapElement() +{ +} + +bool HTMLMapElement::mapMouseEvent(LayoutPoint location, const LayoutSize& size, HitTestResult& result) +{ + HTMLAreaElement* defaultArea = 0; + Node *node = this; + while ((node = node->traverseNextNode(this))) { + if (node->hasTagName(areaTag)) { + HTMLAreaElement* areaElt = static_cast<HTMLAreaElement*>(node); + if (areaElt->isDefault()) { + if (!defaultArea) + defaultArea = areaElt; + } else if (areaElt->mapMouseEvent(location, size, result)) + return true; + } + } + + if (defaultArea) { + result.setInnerNode(defaultArea); + result.setURLElement(defaultArea); + } + return defaultArea; +} + +HTMLImageElement* HTMLMapElement::imageElement() +{ + RefPtr<HTMLCollection> coll = document()->images(); + for (Node* curr = coll->firstItem(); curr; curr = coll->nextItem()) { + if (!curr->hasTagName(imgTag)) + continue; + + // The HTMLImageElement's useMap() value includes the '#' symbol at the beginning, + // which has to be stripped off. + HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(curr); + String useMapName = imageElement->getAttribute(usemapAttr).string().substring(1); + if (equalIgnoringCase(useMapName, m_name)) + return imageElement; + } + + return 0; +} + +void HTMLMapElement::parseMappedAttribute(Attribute* attribute) +{ + // FIXME: This logic seems wrong for XML documents. + // Either the id or name will be used depending on the order the attributes are parsed. + + const QualifiedName& attrName = attribute->name(); + if (isIdAttributeName(attrName) || attrName == nameAttr) { + if (isIdAttributeName(attrName)) { + // Call base class so that hasID bit gets set. + HTMLElement::parseMappedAttribute(attribute); + if (document()->isHTMLDocument()) + return; + } + if (inDocument()) + treeScope()->removeImageMap(this); + String mapName = attribute->value(); + if (mapName[0] == '#') + mapName = mapName.substring(1); + m_name = document()->isHTMLDocument() ? mapName.lower() : mapName; + if (inDocument()) + treeScope()->addImageMap(this); + + return; + } + + HTMLElement::parseMappedAttribute(attribute); +} + +PassRefPtr<HTMLCollection> HTMLMapElement::areas() +{ + return ensureCachedHTMLCollection(MapAreas); +} + +void HTMLMapElement::insertedIntoDocument() +{ + treeScope()->addImageMap(this); + HTMLElement::insertedIntoDocument(); +} + +void HTMLMapElement::removedFromDocument() +{ + treeScope()->removeImageMap(this); + HTMLElement::removedFromDocument(); +} + +} diff --git a/Source/WebCore/html/HTMLMapElement.h b/Source/WebCore/html/HTMLMapElement.h new file mode 100644 index 000000000..300676695 --- /dev/null +++ b/Source/WebCore/html/HTMLMapElement.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2010 Apple 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 HTMLMapElement_h +#define HTMLMapElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HitTestResult; +class HTMLImageElement; + +class HTMLMapElement : public HTMLElement { +public: + static PassRefPtr<HTMLMapElement> create(Document*); + static PassRefPtr<HTMLMapElement> create(const QualifiedName&, Document*); + virtual ~HTMLMapElement(); + + const AtomicString& getName() const { return m_name; } + + bool mapMouseEvent(LayoutPoint location, const LayoutSize&, HitTestResult&); + + HTMLImageElement* imageElement(); + PassRefPtr<HTMLCollection> areas(); + +private: + HTMLMapElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + + AtomicString m_name; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLMapElement.idl b/Source/WebCore/html/HTMLMapElement.idl new file mode 100644 index 000000000..7811c9a9f --- /dev/null +++ b/Source/WebCore/html/HTMLMapElement.idl @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLMapElement : HTMLElement { + readonly attribute HTMLCollection areas; + attribute [Reflect] DOMString name; + }; + +} diff --git a/Source/WebCore/html/HTMLMarqueeElement.cpp b/Source/WebCore/html/HTMLMarqueeElement.cpp new file mode 100644 index 000000000..053ffa892 --- /dev/null +++ b/Source/WebCore/html/HTMLMarqueeElement.cpp @@ -0,0 +1,200 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2007, 2010 Apple 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 "HTMLMarqueeElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" +#include "RenderLayer.h" +#include "RenderMarquee.h" + +namespace WebCore { + +using namespace HTMLNames; + +// WinIE uses 60ms as the minimum delay by default. +const int defaultMinimumDelay = 60; + +inline HTMLMarqueeElement::HTMLMarqueeElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , ActiveDOMObject(document, this) + , m_minimumDelay(defaultMinimumDelay) +{ + ASSERT(hasTagName(marqueeTag)); +} + +PassRefPtr<HTMLMarqueeElement> HTMLMarqueeElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLMarqueeElement(tagName, document)); +} + +bool HTMLMarqueeElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr || + attrName == heightAttr || + attrName == bgcolorAttr || + attrName == vspaceAttr || + attrName == hspaceAttr || + attrName == scrollamountAttr || + attrName == scrolldelayAttr || + attrName == loopAttr || + attrName == behaviorAttr || + attrName == directionAttr) { + result = eUniversal; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLMarqueeElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == widthAttr) { + if (!attr->value().isEmpty()) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + } else if (attr->name() == heightAttr) { + if (!attr->value().isEmpty()) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + } else if (attr->name() == bgcolorAttr) { + if (!attr->value().isEmpty()) + addCSSColor(attr, CSSPropertyBackgroundColor, attr->value()); + } else if (attr->name() == vspaceAttr) { + if (!attr->value().isEmpty()) { + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + } + } else if (attr->name() == hspaceAttr) { + if (!attr->value().isEmpty()) { + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + } + } else if (attr->name() == scrollamountAttr) { + if (!attr->value().isEmpty()) + addCSSLength(attr, CSSPropertyWebkitMarqueeIncrement, attr->value()); + } else if (attr->name() == scrolldelayAttr) { + if (!attr->value().isEmpty()) + addCSSLength(attr, CSSPropertyWebkitMarqueeSpeed, attr->value()); + } else if (attr->name() == loopAttr) { + if (!attr->value().isEmpty()) { + if (attr->value() == "-1" || equalIgnoringCase(attr->value(), "infinite")) + addCSSProperty(attr, CSSPropertyWebkitMarqueeRepetition, CSSValueInfinite); + else + addCSSLength(attr, CSSPropertyWebkitMarqueeRepetition, attr->value()); + } + } else if (attr->name() == behaviorAttr) { + if (!attr->value().isEmpty()) + addCSSProperty(attr, CSSPropertyWebkitMarqueeStyle, attr->value()); + } else if (attr->name() == directionAttr) { + if (!attr->value().isEmpty()) + addCSSProperty(attr, CSSPropertyWebkitMarqueeDirection, attr->value()); + } else if (attr->name() == truespeedAttr) + m_minimumDelay = !attr->isEmpty() ? 0 : defaultMinimumDelay; + else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLMarqueeElement::start() +{ + if (RenderMarquee* marqueeRenderer = renderMarquee()) + marqueeRenderer->start(); +} + +void HTMLMarqueeElement::stop() +{ + if (RenderMarquee* marqueeRenderer = renderMarquee()) + marqueeRenderer->stop(); +} + +int HTMLMarqueeElement::scrollAmount() const +{ + bool ok; + int scrollAmount = fastGetAttribute(scrollamountAttr).toInt(&ok); + return ok && scrollAmount >= 0 ? scrollAmount : RenderStyle::initialMarqueeIncrement().value(); +} + +void HTMLMarqueeElement::setScrollAmount(int scrollAmount, ExceptionCode& ec) +{ + if (scrollAmount < 0) + ec = INDEX_SIZE_ERR; + else + setIntegralAttribute(scrollamountAttr, scrollAmount); +} + +int HTMLMarqueeElement::scrollDelay() const +{ + bool ok; + int scrollDelay = fastGetAttribute(scrolldelayAttr).toInt(&ok); + return ok && scrollDelay >= 0 ? scrollDelay : RenderStyle::initialMarqueeSpeed(); +} + +void HTMLMarqueeElement::setScrollDelay(int scrollDelay, ExceptionCode& ec) +{ + if (scrollDelay < 0) + ec = INDEX_SIZE_ERR; + else + setIntegralAttribute(scrolldelayAttr, scrollDelay); +} + +int HTMLMarqueeElement::loop() const +{ + bool ok; + int loopValue = fastGetAttribute(loopAttr).toInt(&ok); + return ok && loopValue > 0 ? loopValue : -1; +} + +void HTMLMarqueeElement::setLoop(int loop, ExceptionCode& ec) +{ + if (loop <= 0 && loop != -1) + ec = INDEX_SIZE_ERR; + else + setIntegralAttribute(loopAttr, loop); +} + +bool HTMLMarqueeElement::canSuspend() const +{ + return true; +} + +void HTMLMarqueeElement::suspend(ReasonForSuspension) +{ + if (RenderMarquee* marqueeRenderer = renderMarquee()) + marqueeRenderer->suspend(); +} + +void HTMLMarqueeElement::resume() +{ + if (RenderMarquee* marqueeRenderer = renderMarquee()) + marqueeRenderer->updateMarqueePosition(); +} + +RenderMarquee* HTMLMarqueeElement::renderMarquee() const +{ + if (renderer() && renderer()->hasLayer()) + return renderBoxModelObject()->layer()->marquee(); + return 0; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLMarqueeElement.h b/Source/WebCore/html/HTMLMarqueeElement.h new file mode 100644 index 000000000..586aa6fde --- /dev/null +++ b/Source/WebCore/html/HTMLMarqueeElement.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2007, 2010 Apple 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 HTMLMarqueeElement_h +#define HTMLMarqueeElement_h + +#include "ActiveDOMObject.h" +#include "HTMLElement.h" + +namespace WebCore { + +class RenderMarquee; + +class HTMLMarqueeElement : public HTMLElement, private ActiveDOMObject { +public: + static PassRefPtr<HTMLMarqueeElement> create(const QualifiedName&, Document*); + + int minimumDelay() const { return m_minimumDelay; } + + // DOM Functions + + void start(); + void stop(); + + int scrollAmount() const; + void setScrollAmount(int, ExceptionCode&); + + int scrollDelay() const; + void setScrollDelay(int, ExceptionCode&); + + int loop() const; + void setLoop(int, ExceptionCode&); + +private: + HTMLMarqueeElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + // ActiveDOMObject + virtual bool canSuspend() const; + virtual void suspend(ReasonForSuspension); + virtual void resume(); + + RenderMarquee* renderMarquee() const; + + int m_minimumDelay; +}; + +} // namespace WebCore + +#endif // HTMLMarqueeElement_h diff --git a/Source/WebCore/html/HTMLMarqueeElement.idl b/Source/WebCore/html/HTMLMarqueeElement.idl new file mode 100644 index 000000000..3174facbc --- /dev/null +++ b/Source/WebCore/html/HTMLMarqueeElement.idl @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2007 Apple 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. + */ + +module html { + + interface HTMLMarqueeElement : HTMLElement { + void start(); + void stop(); + + attribute [Reflect] DOMString behavior; + attribute [Reflect] DOMString bgColor; + attribute [Reflect] DOMString direction; + attribute [Reflect] DOMString height; + attribute [Reflect] unsigned long hspace; + attribute long loop setter raises(DOMException); + attribute long scrollAmount setter raises(DOMException); + attribute long scrollDelay setter raises(DOMException); + attribute [Reflect] boolean trueSpeed; + attribute [Reflect] unsigned long vspace; + attribute [Reflect] DOMString width; + + // FIXME: Implement the following event handler attributes + // https://bugs.webkit.org/show_bug.cgi?id=49788 + // attribute EventListener onbounce; + // attribute EventListener onfinish; + // attribute EventListener onstart; + }; +} diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp new file mode 100644 index 000000000..0b693b2b9 --- /dev/null +++ b/Source/WebCore/html/HTMLMediaElement.cpp @@ -0,0 +1,3727 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "HTMLMediaElement.h" + +#include "ApplicationCacheHost.h" +#include "ApplicationCacheResource.h" +#include "Attribute.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "ClientRect.h" +#include "ClientRectList.h" +#include "ContentSecurityPolicy.h" +#include "ContentType.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "DocumentLoader.h" +#include "Event.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameView.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLSourceElement.h" +#include "HTMLVideoElement.h" +#include "Logging.h" +#include "MediaController.h" +#include "MediaControls.h" +#include "MediaDocument.h" +#include "MediaError.h" +#include "MediaFragmentURIParser.h" +#include "MediaList.h" +#include "MediaPlayer.h" +#include "MediaQueryEvaluator.h" +#include "MouseEvent.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "RenderVideo.h" +#include "RenderView.h" +#include "ScriptController.h" +#include "ScriptEventListener.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "ShadowRoot.h" +#include "TimeRanges.h" +#include "UUID.h" +#include <limits> +#include <wtf/CurrentTime.h> +#include <wtf/MathExtras.h> +#include <wtf/Uint8Array.h> +#include <wtf/text/CString.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderView.h" +#include "RenderLayerCompositor.h" +#endif + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "RenderEmbeddedObject.h" +#include "Widget.h" +#endif + +#if ENABLE(VIDEO_TRACK) +#include "HTMLTrackElement.h" +#include "RuntimeEnabledFeatures.h" +#include "TextTrackCueList.h" +#include "TextTrackList.h" +#endif + +#if ENABLE(WEB_AUDIO) +#include "AudioSourceProvider.h" +#include "MediaElementAudioSourceNode.h" +#endif + +#if PLATFORM(MAC) +#include "DisplaySleepDisabler.h" +#endif + +using namespace std; + +namespace WebCore { + +#if !LOG_DISABLED +static String urlForLogging(const KURL& url) +{ + static const unsigned maximumURLLengthForLogging = 128; + + if (url.string().length() < maximumURLLengthForLogging) + return url.string(); + return url.string().substring(0, maximumURLLengthForLogging) + "..."; +} + +static const char* boolString(bool val) +{ + return val ? "true" : "false"; +} +#endif + +#ifndef LOG_MEDIA_EVENTS +// Default to not logging events because so many are generated they can overwhelm the rest of +// the logging. +#define LOG_MEDIA_EVENTS 0 +#endif + +#ifndef LOG_CACHED_TIME_WARNINGS +// Default to not logging warnings about excessive drift in the cached media time because it adds a +// fair amount of overhead and logging. +#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"; +#endif + +using namespace HTMLNames; +using namespace std; + +typedef HashMap<Document*, HashSet<HTMLMediaElement*> > DocumentElementSetMap; +static DocumentElementSetMap& documentToElementSetMap() +{ + DEFINE_STATIC_LOCAL(DocumentElementSetMap, map, ()); + return map; +} + +static void addElementToDocumentMap(HTMLMediaElement* element, Document* document) +{ + DocumentElementSetMap& map = documentToElementSetMap(); + HashSet<HTMLMediaElement*> set = map.take(document); + set.add(element); + map.add(document, set); +} + +static void removeElementFromDocumentMap(HTMLMediaElement* element, Document* document) +{ + DocumentElementSetMap& map = documentToElementSetMap(); + HashSet<HTMLMediaElement*> set = map.take(document); + set.remove(element); + if (!set.isEmpty()) + map.add(document, set); +} + +HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document, bool createdByParser) + : HTMLElement(tagName, document) + , ActiveDOMObject(document, this) + , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) + , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired) + , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) + , m_playbackProgressTimer(this, &HTMLMediaElement::playbackProgressTimerFired) + , m_playedTimeRanges() + , m_playbackRate(1.0f) + , m_defaultPlaybackRate(1.0f) + , m_webkitPreservesPitch(true) + , m_networkState(NETWORK_EMPTY) + , m_readyState(HAVE_NOTHING) + , m_readyStateMaximum(HAVE_NOTHING) + , m_volume(1.0f) + , m_lastSeekTime(0) + , m_previousProgress(0) + , m_previousProgressTime(numeric_limits<double>::max()) + , m_lastTimeUpdateEventWallTime(0) + , m_lastTimeUpdateEventMovieTime(numeric_limits<float>::max()) + , m_loadState(WaitingForSource) + , m_currentSourceNode(0) + , m_nextChildNodeToConsider(0) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + , m_proxyWidget(0) +#endif + , m_restrictions(RequireUserGestureForFullscreenRestriction | RequirePageConsentToLoadMediaRestriction) + , m_preload(MediaPlayer::Auto) + , m_displayMode(Unknown) + , m_processingMediaPlayerCallback(0) +#if ENABLE(MEDIA_SOURCE) + , m_sourceState(SOURCE_CLOSED) +#endif + , m_cachedTime(invalidMediaTime) + , m_cachedTimeWallClockUpdateTime(0) + , m_minimumWallClockTimeToCacheMediaTime(0) + , m_fragmentStartTime(invalidMediaTime) + , m_fragmentEndTime(invalidMediaTime) + , m_pendingLoadFlags(0) + , m_playing(false) + , m_isWaitingUntilMediaCanStart(false) + , m_shouldDelayLoadEvent(false) + , m_haveFiredLoadedData(false) + , m_inActiveDocument(true) + , m_autoplaying(true) + , m_muted(false) + , m_paused(true) + , m_seeking(false) + , m_sentStalledEvent(false) + , m_sentEndEvent(false) + , m_pausedInternal(false) + , m_sendProgressEvents(true) + , m_isFullscreen(false) + , m_closedCaptionsVisible(false) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + , m_needWidgetUpdate(false) +#endif + , m_dispatchingCanPlayEvent(false) + , m_loadInitiatedByUserGesture(false) + , m_completelyLoaded(false) + , m_havePreparedToPlay(false) + , m_parsingInProgress(createdByParser) +#if ENABLE(VIDEO_TRACK) + , m_tracksAreReady(true) + , m_haveVisibleTextTrack(false) + , m_textTracks(0) +#endif +#if ENABLE(WEB_AUDIO) + , m_audioSourceNode(0) +#endif +{ + LOG(Media, "HTMLMediaElement::HTMLMediaElement"); + document->registerForMediaVolumeCallbacks(this); + document->registerForPrivateBrowsingStateChangedCallbacks(this); + + if (document->settings() && document->settings()->mediaPlaybackRequiresUserGesture()) + addBehaviorRestriction(RequireUserGestureForRateChangeRestriction); + +#if ENABLE(MEDIA_SOURCE) + m_mediaSourceURL.setProtocol(mediaSourceURLProtocol); + m_mediaSourceURL.setPath(createCanonicalUUIDString()); +#endif + + setHasCustomWillOrDidRecalcStyle(); + addElementToDocumentMap(this, document); +} + +HTMLMediaElement::~HTMLMediaElement() +{ + LOG(Media, "HTMLMediaElement::~HTMLMediaElement"); + if (m_isWaitingUntilMediaCanStart) + document()->removeMediaCanStartListener(this); + setShouldDelayLoadEvent(false); + document()->unregisterForMediaVolumeCallbacks(this); + document()->unregisterForPrivateBrowsingStateChangedCallbacks(this); +#if ENABLE(VIDEO_TRACK) + if (m_textTracks) + m_textTracks->clearOwner(); + if (m_textTracks) { + for (unsigned i = 0; i < m_textTracks->length(); ++i) + m_textTracks->item(i)->clearClient(); + } +#endif + + if (m_mediaController) + m_mediaController->removeMediaElement(this); + + removeElementFromDocumentMap(this, document()); +} + +void HTMLMediaElement::didMoveToNewDocument(Document* oldDocument) +{ + if (m_isWaitingUntilMediaCanStart) { + if (oldDocument) + oldDocument->removeMediaCanStartListener(this); + document()->addMediaCanStartListener(this); + } + + if (m_shouldDelayLoadEvent) { + if (oldDocument) + oldDocument->decrementLoadEventDelayCount(); + document()->incrementLoadEventDelayCount(); + } + + if (oldDocument) { + oldDocument->unregisterForMediaVolumeCallbacks(this); + removeElementFromDocumentMap(this, oldDocument); + } + + document()->registerForMediaVolumeCallbacks(this); + addElementToDocumentMap(this, document()); + + HTMLElement::didMoveToNewDocument(oldDocument); +} + +bool HTMLMediaElement::supportsFocus() const +{ + if (ownerDocument()->isMediaDocument()) + return false; + + // If no controls specified, we should still be able to focus the element if it has tabIndex. + return controls() || HTMLElement::supportsFocus(); +} + +void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + HTMLElement::attributeChanged(attr, preserveDecls); + + const QualifiedName& attrName = attr->name(); + if (attrName == srcAttr) { + // Trigger a reload, as long as the 'src' attribute is present. + if (!getAttribute(srcAttr).isEmpty()) + scheduleLoad(MediaResource); + } else if (attrName == controlsAttr) + configureMediaControls(); +} + +void HTMLMediaElement::parseMappedAttribute(Attribute* attr) +{ + const QualifiedName& attrName = attr->name(); + + if (attrName == preloadAttr) { + String value = attr->value(); + + if (equalIgnoringCase(value, "none")) + m_preload = MediaPlayer::None; + else if (equalIgnoringCase(value, "metadata")) + m_preload = MediaPlayer::MetaData; + else { + // The spec does not define an "invalid value default" but "auto" is suggested as the + // "missing value default", so use it for everything except "none" and "metadata" + m_preload = MediaPlayer::Auto; + } + + // The attribute must be ignored if the autoplay attribute is present + if (!autoplay() && m_player) + m_player->setPreload(m_preload); + + } else if (attrName == mediagroupAttr) + setMediaGroup(attr->value()); + else if (attrName == onabortAttr) + setAttributeEventListener(eventNames().abortEvent, createAttributeEventListener(this, attr)); + else if (attrName == onbeforeloadAttr) + setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); + else if (attrName == oncanplayAttr) + setAttributeEventListener(eventNames().canplayEvent, createAttributeEventListener(this, attr)); + else if (attrName == oncanplaythroughAttr) + setAttributeEventListener(eventNames().canplaythroughEvent, createAttributeEventListener(this, attr)); + else if (attrName == ondurationchangeAttr) + setAttributeEventListener(eventNames().durationchangeEvent, createAttributeEventListener(this, attr)); + else if (attrName == onemptiedAttr) + setAttributeEventListener(eventNames().emptiedEvent, createAttributeEventListener(this, attr)); + else if (attrName == onendedAttr) + setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr)); + else if (attrName == onerrorAttr) + setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); + else if (attrName == onloadeddataAttr) + setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr)); + else if (attrName == onloadedmetadataAttr) + setAttributeEventListener(eventNames().loadedmetadataEvent, createAttributeEventListener(this, attr)); + else if (attrName == onloadstartAttr) + setAttributeEventListener(eventNames().loadstartEvent, createAttributeEventListener(this, attr)); + else if (attrName == onpauseAttr) + setAttributeEventListener(eventNames().pauseEvent, createAttributeEventListener(this, attr)); + else if (attrName == onplayAttr) + setAttributeEventListener(eventNames().playEvent, createAttributeEventListener(this, attr)); + else if (attrName == onplayingAttr) + setAttributeEventListener(eventNames().playingEvent, createAttributeEventListener(this, attr)); + else if (attrName == onprogressAttr) + setAttributeEventListener(eventNames().progressEvent, createAttributeEventListener(this, attr)); + else if (attrName == onratechangeAttr) + setAttributeEventListener(eventNames().ratechangeEvent, createAttributeEventListener(this, attr)); + else if (attrName == onseekedAttr) + setAttributeEventListener(eventNames().seekedEvent, createAttributeEventListener(this, attr)); + else if (attrName == onseekingAttr) + setAttributeEventListener(eventNames().seekingEvent, createAttributeEventListener(this, attr)); + else if (attrName == onstalledAttr) + setAttributeEventListener(eventNames().stalledEvent, createAttributeEventListener(this, attr)); + else if (attrName == onsuspendAttr) + setAttributeEventListener(eventNames().suspendEvent, createAttributeEventListener(this, attr)); + else if (attrName == ontimeupdateAttr) + setAttributeEventListener(eventNames().timeupdateEvent, createAttributeEventListener(this, attr)); + else if (attrName == onvolumechangeAttr) + setAttributeEventListener(eventNames().volumechangeEvent, createAttributeEventListener(this, attr)); + else if (attrName == onwaitingAttr) + setAttributeEventListener(eventNames().waitingEvent, createAttributeEventListener(this, attr)); + else if (attrName == onwebkitbeginfullscreenAttr) + setAttributeEventListener(eventNames().webkitbeginfullscreenEvent, createAttributeEventListener(this, attr)); + else if (attrName == onwebkitendfullscreenAttr) + setAttributeEventListener(eventNames().webkitendfullscreenEvent, createAttributeEventListener(this, attr)); + else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLMediaElement::finishParsingChildren() +{ + HTMLElement::finishParsingChildren(); + m_parsingInProgress = false; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + document()->updateStyleIfNeeded(); + createMediaPlayerProxy(); +#endif + +#if ENABLE(VIDEO_TRACK) + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return; + + for (Node* node = firstChild(); node; node = node->nextSibling()) { + if (node->hasTagName(trackTag)) { + scheduleLoad(TextTrackResource); + break; + } + } +#endif +} + +bool HTMLMediaElement::rendererIsNeeded(const NodeRenderingContext& context) +{ +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + UNUSED_PARAM(context); + Frame* frame = document()->frame(); + if (!frame) + return false; + + return true; +#else + return controls() ? HTMLElement::rendererIsNeeded(context) : false; +#endif +} + +RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*) +{ +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + // Setup the renderer if we already have a proxy widget. + RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this); + if (m_proxyWidget) { + mediaRenderer->setWidget(m_proxyWidget); + + if (Frame* frame = document()->frame()) + frame->loader()->client()->showMediaPlayerProxyPlugin(m_proxyWidget.get()); + } + return mediaRenderer; +#else + return new (arena) RenderMedia(this); +#endif +} + +void HTMLMediaElement::insertedIntoDocument() +{ + LOG(Media, "HTMLMediaElement::insertedIntoDocument"); + HTMLElement::insertedIntoDocument(); + if (!getAttribute(srcAttr).isEmpty() && m_networkState == NETWORK_EMPTY) + scheduleLoad(MediaResource); +} + +void HTMLMediaElement::removedFromDocument() +{ + LOG(Media, "HTMLMediaElement::removedFromDocument"); + if (m_networkState > NETWORK_EMPTY) + pause(); + if (m_isFullscreen) + exitFullscreen(); + HTMLElement::removedFromDocument(); +} + +void HTMLMediaElement::attach() +{ + ASSERT(!attached()); + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + m_needWidgetUpdate = true; +#endif + + HTMLElement::attach(); + + if (renderer()) + renderer()->updateFromElement(); +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + else if (m_proxyWidget) { + if (Frame* frame = document()->frame()) + frame->loader()->client()->hideMediaPlayerProxyPlugin(m_proxyWidget.get()); + } +#endif +} + +void HTMLMediaElement::didRecalcStyle(StyleChange) +{ + if (renderer()) + renderer()->updateFromElement(); +} + +void HTMLMediaElement::scheduleLoad(LoadType loadType) +{ + LOG(Media, "HTMLMediaElement::scheduleLoad"); + + if ((loadType & MediaResource) && !(m_pendingLoadFlags & MediaResource)) { +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + createMediaPlayerProxy(); +#endif + + prepareForLoad(); + m_pendingLoadFlags |= MediaResource; + } + +#if ENABLE(VIDEO_TRACK) + if (loadType & TextTrackResource) + m_pendingLoadFlags |= TextTrackResource; +#endif + + if (!m_loadTimer.isActive()) + m_loadTimer.startOneShot(0); +} + +void HTMLMediaElement::scheduleNextSourceChild() +{ + // Schedule the timer to try the next <source> element WITHOUT resetting state ala prepareForLoad. + m_pendingLoadFlags |= MediaResource; + m_loadTimer.startOneShot(0); +} + +void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) +{ +#if LOG_MEDIA_EVENTS + LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data()); +#endif + m_pendingEvents.append(Event::create(eventName, false, true)); + if (!m_asyncEventTimer.isActive()) + m_asyncEventTimer.startOneShot(0); +} + +void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*) +{ + Vector<RefPtr<Event> > pendingEvents; + ExceptionCode ec = 0; + + m_pendingEvents.swap(pendingEvents); + unsigned count = pendingEvents.size(); + for (unsigned ndx = 0; ndx < count; ++ndx) { +#if LOG_MEDIA_EVENTS + LOG(Media, "HTMLMediaElement::asyncEventTimerFired - dispatching '%s'", pendingEvents[ndx]->type().string().ascii().data()); +#endif + if (pendingEvents[ndx]->type() == eventNames().canplayEvent) { + m_dispatchingCanPlayEvent = true; + dispatchEvent(pendingEvents[ndx].release(), ec); + m_dispatchingCanPlayEvent = false; + } else + dispatchEvent(pendingEvents[ndx].release(), ec); + } +} + +void HTMLMediaElement::loadTimerFired(Timer<HTMLMediaElement>*) +{ + if (m_pendingLoadFlags & MediaResource) { + if (m_loadState == LoadingFromSourceElement) + loadNextSourceChild(); + else + loadInternal(); + } + +#if ENABLE(VIDEO_TRACK) + if (m_pendingLoadFlags & TextTrackResource) + configureTextTracks(); +#endif + + m_pendingLoadFlags = 0; +} + +PassRefPtr<MediaError> HTMLMediaElement::error() const +{ + return m_error; +} + +void HTMLMediaElement::setSrc(const String& url) +{ + setAttribute(srcAttr, url); +} + +HTMLMediaElement::NetworkState HTMLMediaElement::networkState() const +{ + return m_networkState; +} + +String HTMLMediaElement::canPlayType(const String& mimeType) const +{ + MediaPlayer::SupportsType support = MediaPlayer::supportsType(ContentType(mimeType)); + String canPlay; + + // 4.8.10.3 + switch (support) + { + case MediaPlayer::IsNotSupported: + canPlay = ""; + break; + case MediaPlayer::MayBeSupported: + canPlay = "maybe"; + break; + case MediaPlayer::IsSupported: + canPlay = "probably"; + break; + } + + LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data()); + + return canPlay; +} + +void HTMLMediaElement::load(ExceptionCode& ec) +{ + LOG(Media, "HTMLMediaElement::load()"); + + if (userGestureRequiredForLoad() && !ScriptController::processingUserGesture()) + ec = INVALID_STATE_ERR; + else { + m_loadInitiatedByUserGesture = ScriptController::processingUserGesture(); + prepareForLoad(); + loadInternal(); + } + prepareToPlay(); +} + +void HTMLMediaElement::prepareForLoad() +{ + LOG(Media, "HTMLMediaElement::prepareForLoad"); + + // Perform the cleanup required for the resource load algorithm to run. + stopPeriodicTimers(); + m_loadTimer.stop(); + m_sentEndEvent = false; + m_sentStalledEvent = false; + m_haveFiredLoadedData = false; + m_completelyLoaded = false; + m_havePreparedToPlay = false; + m_displayMode = Unknown; + + // 1 - Abort any already-running instance of the resource selection algorithm for this element. + m_loadState = WaitingForSource; + m_currentSourceNode = 0; + + // 2 - If there are any tasks from the media element's media element event task source in + // one of the task queues, then remove those tasks. + cancelPendingEventsAndCallbacks(); + + // 3 - If the media element's networkState is set to NETWORK_LOADING or NETWORK_IDLE, queue + // a task to fire a simple event named abort at the media element. + if (m_networkState == NETWORK_LOADING || m_networkState == NETWORK_IDLE) + scheduleEvent(eventNames().abortEvent); + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + createMediaPlayer(); +#else + if (m_player) + m_player->cancelLoad(); + else + createMediaPlayerProxy(); +#endif + +#if ENABLE(MEDIA_SOURCE) + if (m_sourceState != SOURCE_CLOSED) + setSourceState(SOURCE_CLOSED); +#endif + + // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps + if (m_networkState != NETWORK_EMPTY) { + m_networkState = NETWORK_EMPTY; + m_readyState = HAVE_NOTHING; + m_readyStateMaximum = HAVE_NOTHING; + refreshCachedTime(); + m_paused = true; + m_seeking = false; + invalidateCachedTime(); + scheduleEvent(eventNames().emptiedEvent); + updateMediaController(); + } + + // 5 - Set the playbackRate attribute to the value of the defaultPlaybackRate attribute. + setPlaybackRate(defaultPlaybackRate()); + + // 6 - Set the error attribute to null and the autoplaying flag to true. + m_error = 0; + m_autoplaying = true; + + // 7 - Invoke the media element's resource selection algorithm. + + // 8 - Note: Playback of any previously playing media resource for this element stops. + + // The resource selection algorithm + // 1 - Set the networkState to NETWORK_NO_SOURCE + m_networkState = NETWORK_NO_SOURCE; + + // 2 - Asynchronously await a stable state. + + m_playedTimeRanges = TimeRanges::create(); + m_lastSeekTime = 0; + m_closedCaptionsVisible = false; + + // The spec doesn't say to block the load event until we actually run the asynchronous section + // algorithm, but do it now because we won't start that until after the timer fires and the + // event may have already fired by then. + setShouldDelayLoadEvent(true); + +#if ENABLE(VIDEO_TRACK) + // HTMLMediaElement::textTracksAreReady will need "... the text tracks whose mode was not in the + // disabled state when the element's resource selection algorithm last started". + m_textTracksWhenResourceSelectionBegan.clear(); + if (m_textTracks) { + for (unsigned i = 0; i < m_textTracks->length(); ++i) { + TextTrack* track = m_textTracks->item(i); + if (track->mode() != TextTrack::DISABLED) + m_textTracksWhenResourceSelectionBegan.append(track); + } + } +#endif + + configureMediaControls(); +} + +void HTMLMediaElement::loadInternal() +{ + // If we can't start a load right away, start it later. + Page* page = document()->page(); + if (pageConsentRequiredForLoad() && page && !page->canStartMedia()) { + if (m_isWaitingUntilMediaCanStart) + return; + document()->addMediaCanStartListener(this); + m_isWaitingUntilMediaCanStart = true; + return; + } + + // Once the page has allowed an element to load media, it is free to load at will. This allows a + // playlist that starts in a foreground tab to continue automatically if the tab is subsequently + // put in the the background. + removeBehaviorRestriction(RequirePageConsentToLoadMediaRestriction); + + selectMediaResource(); +} + +void HTMLMediaElement::selectMediaResource() +{ + LOG(Media, "HTMLMediaElement::selectMediaResource"); + + enum Mode { attribute, children }; + + // 3 - If the media element has a src attribute, then let mode be attribute. + Mode mode = attribute; + if (!fastHasAttribute(srcAttr)) { + Node* node; + for (node = firstChild(); node; node = node->nextSibling()) { + if (node->hasTagName(sourceTag)) + break; + } + + // Otherwise, if the media element does not have a src attribute but has a source + // element child, then let mode be children and let candidate be the first such + // source element child in tree order. + if (node) { + mode = children; + m_nextChildNodeToConsider = 0; + m_currentSourceNode = 0; + } else { + // Otherwise the media element has neither a src attribute nor a source element + // child: set the networkState to NETWORK_EMPTY, and abort these steps; the + // synchronous section ends. + m_loadState = WaitingForSource; + setShouldDelayLoadEvent(false); + m_networkState = NETWORK_EMPTY; + + LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load"); + return; + } + } + + // 4 - Set the media element's delaying-the-load-event flag to true (this delays the load event), + // and set its networkState to NETWORK_LOADING. + setShouldDelayLoadEvent(true); + m_networkState = NETWORK_LOADING; + + // 5 - Queue a task to fire a simple event named loadstart at the media element. + scheduleEvent(eventNames().loadstartEvent); + + // 6 - If mode is attribute, then run these substeps + if (mode == attribute) { + m_loadState = LoadingFromSrcAttr; + + // If the src attribute's value is the empty string ... jump down to the failed step below + KURL mediaURL = getNonEmptyURLAttribute(srcAttr); + if (mediaURL.isEmpty()) { + mediaLoadingFailed(MediaPlayer::FormatError); + LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'"); + return; + } + + if (!isSafeToLoadURL(mediaURL, Complain) || !dispatchBeforeLoadEvent(mediaURL.string())) { + mediaLoadingFailed(MediaPlayer::FormatError); + return; + } + + // No type information is available when the url comes from the 'src' attribute so MediaPlayer + // will have to pick a media engine based on the file extension. + ContentType contentType(""); + loadResource(mediaURL, contentType); + LOG(Media, "HTMLMediaElement::selectMediaResource, using 'src' attribute url"); + return; + } + + // Otherwise, the source elements will be used + loadNextSourceChild(); +} + +void HTMLMediaElement::loadNextSourceChild() +{ + ContentType contentType(""); + KURL mediaURL = selectNextSourceChild(&contentType, Complain); + if (!mediaURL.isValid()) { + waitForSourceChange(); + return; + } + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + // Recreate the media player for the new url + createMediaPlayer(); +#endif + + m_loadState = LoadingFromSourceElement; + loadResource(mediaURL, contentType); +} + +#if !PLATFORM(CHROMIUM) +static KURL createFileURLForApplicationCacheResource(const String& path) +{ + // KURL should have a function to create a url from a path, but it does not. This function + // is not suitable because KURL::setPath uses encodeWithURLEscapeSequences, which it notes + // does not correctly escape '#' and '?'. This function works for our purposes because + // app cache media files are always created with encodeForFileName(createCanonicalUUIDString()). + +#if USE(CF) && PLATFORM(WIN) + RetainPtr<CFStringRef> cfPath(AdoptCF, path.createCFString()); + RetainPtr<CFURLRef> cfURL(AdoptCF, CFURLCreateWithFileSystemPath(0, cfPath.get(), kCFURLWindowsPathStyle, false)); + KURL url(cfURL.get()); +#else + KURL url; + + url.setProtocol("file"); + url.setPath(path); +#endif + return url; +} +#endif + +void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& contentType) +{ + ASSERT(isSafeToLoadURL(initialURL, Complain)); + + LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL).utf8().data(), contentType.raw().utf8().data()); + + Frame* frame = document()->frame(); + if (!frame) { + mediaLoadingFailed(MediaPlayer::FormatError); + return; + } + + KURL url = initialURL; + if (!frame->loader()->willLoadMediaElementURL(url)) { + mediaLoadingFailed(MediaPlayer::FormatError); + return; + } + +#if ENABLE(MEDIA_SOURCE) + // If this is a media source URL, make sure it is the one for this media element. + if (url.protocolIs(mediaSourceURLProtocol) && url != m_mediaSourceURL) { + mediaLoadingFailed(MediaPlayer::FormatError); + return; + } +#endif + + // The resource fetch algorithm + m_networkState = NETWORK_LOADING; + +#if !PLATFORM(CHROMIUM) + // If the url should be loaded from the application cache, pass the url of the cached file + // to the media engine. + ApplicationCacheHost* cacheHost = frame->loader()->documentLoader()->applicationCacheHost(); + ApplicationCacheResource* resource = 0; + if (cacheHost && cacheHost->shouldLoadResourceFromApplicationCache(ResourceRequest(url), resource)) { + // Resources that are not present in the manifest will always fail to load (at least, after the + // cache has been primed the first time), making the testing of offline applications simpler. + if (!resource || resource->path().isEmpty()) { + mediaLoadingFailed(MediaPlayer::NetworkError); + return; + } + } +#endif + + // Set m_currentSrc *before* changing to the cache url, the fact that we are loading from the app + // cache is an internal detail not exposed through the media element API. + m_currentSrc = url; + +#if !PLATFORM(CHROMIUM) + if (resource) { + url = createFileURLForApplicationCacheResource(resource->path()); + LOG(Media, "HTMLMediaElement::loadResource - will load from app cache -> %s", urlForLogging(url).utf8().data()); + } +#endif + + LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data()); + + if (m_sendProgressEvents) + startProgressEventTimer(); + + Settings* settings = document()->settings(); + bool privateMode = !settings || settings->privateBrowsingEnabled(); + m_player->setPrivateBrowsingMode(privateMode); + + // Reset display mode to force a recalculation of what to show because we are resetting the player. + setDisplayMode(Unknown); + + if (!autoplay()) + m_player->setPreload(m_preload); + m_player->setPreservesPitch(m_webkitPreservesPitch); + + if (fastHasAttribute(mutedAttr)) + m_muted = true; + updateVolume(); + + if (!m_player->load(url, contentType)) + mediaLoadingFailed(MediaPlayer::FormatError); + + // If there is no poster to display, allow the media engine to render video frames as soon as + // they are available. + updateDisplayState(); + + if (renderer()) + renderer()->updateFromElement(); +} + +#if ENABLE(VIDEO_TRACK) +void HTMLMediaElement::updateActiveTextTrackCues(float movieTime) +{ + CueList previouslyActiveCues = m_currentlyActiveCues; + bool activeSetChanged = false; + + m_currentlyActiveCues = m_cueTree.allOverlaps(m_cueTree.createInterval(movieTime, movieTime)); + + // FIXME(72171): Events need to be sorted and filtered before dispatching. + + for (size_t i = 0; i < previouslyActiveCues.size(); ++i) { + if (!m_currentlyActiveCues.contains(previouslyActiveCues[i])) { + previouslyActiveCues[i].data()->setIsActive(false); + activeSetChanged = true; + } + } + for (size_t i = 0; i < m_currentlyActiveCues.size(); ++i) { + if (!previouslyActiveCues.contains(m_currentlyActiveCues[i])) { + m_currentlyActiveCues[i].data()->setIsActive(true); + activeSetChanged = true; + } + } + + // FIXME(72173): Pause the media element for cues going past their endTime + // during a monotonic time increase. + + if (activeSetChanged && hasMediaControls()) + mediaControls()->updateTextTrackDisplay(); +} + +bool HTMLMediaElement::textTracksAreReady() const +{ + // The text tracks of a media element are ready if all the text tracks whose mode was not + // in the disabled state when the element's resource selection algorithm last started now + // have a text track readiness state of loaded or failed to load. + for (unsigned i = 0; i < m_textTracksWhenResourceSelectionBegan.size(); ++i) { + if (m_textTracksWhenResourceSelectionBegan[i]->readinessState() == TextTrack::Loading) + return false; + } + + return true; +} + +void HTMLMediaElement::textTrackReadyStateChanged(TextTrack* track) +{ + if (m_player && m_textTracksWhenResourceSelectionBegan.contains(track)) { + if (track->readinessState() != TextTrack::Loading) + setReadyState(m_player->readyState()); + } +} + +void HTMLMediaElement::textTrackModeChanged(TextTrack* track) +{ + if (track->trackType() == TextTrack::TrackElement) { + // 4.8.10.12.3 Sourcing out-of-band text tracks + // ... when a text track corresponding to a track element is created with text track + // mode set to disabled and subsequently changes its text track mode to hidden, showing, + // or showing by default for the first time, the user agent must immediately and synchronously + // run the following algorithm ... + + for (Node* node = firstChild(); node; node = node->nextSibling()) { + if (!node->hasTagName(trackTag)) + continue; + HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node); + if (trackElement->track() != track) + continue; + + // Mark this track as "configured" so configureTextTrack won't change the mode again. + trackElement->setHasBeenConfigured(true); + if (track->mode() != TextTrack::DISABLED && trackElement->readyState() == HTMLTrackElement::NONE) + trackElement->scheduleLoad(); + break; + } + } + + configureTextTrackDisplay(); +} + +void HTMLMediaElement::textTrackKindChanged(TextTrack*) +{ + // FIXME(62885): Implement. +} + +void HTMLMediaElement::textTrackAddCues(TextTrack*, const TextTrackCueList* cues) +{ + for (size_t i = 0; i < cues->length(); ++i) + textTrackAddCue(cues->item(i)->track(), cues->item(i)); +} + +void HTMLMediaElement::textTrackRemoveCues(TextTrack*, const TextTrackCueList* cues) +{ + for (size_t i = 0; i < cues->length(); ++i) + textTrackRemoveCue(cues->item(i)->track(), cues->item(i)); +} + +void HTMLMediaElement::textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue> cue) +{ + m_cueTree.add(m_cueTree.createInterval(cue->startTime(), cue->endTime(), cue.get())); +} + +void HTMLMediaElement::textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue> cue) +{ + m_cueTree.remove(m_cueTree.createInterval(cue->startTime(), cue->endTime(), cue.get())); +} + +#endif + +bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidURLAction actionIfInvalid) +{ + if (!url.isValid()) { + LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url).utf8().data()); + return false; + } + + Frame* frame = document()->frame(); + if (!frame || !document()->securityOrigin()->canDisplay(url)) { + if (actionIfInvalid == Complain) + FrameLoader::reportLocalLoadFailed(frame, url.string()); + LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url).utf8().data()); + return false; + } + + if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) { + LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> rejected by Content Security Policy", urlForLogging(url).utf8().data()); + return false; + } + + return true; +} + +void HTMLMediaElement::startProgressEventTimer() +{ + if (m_progressEventTimer.isActive()) + return; + + m_previousProgressTime = WTF::currentTime(); + m_previousProgress = 0; + // 350ms is not magic, it is in the spec! + m_progressEventTimer.startRepeating(0.350); +} + +void HTMLMediaElement::waitForSourceChange() +{ + LOG(Media, "HTMLMediaElement::waitForSourceChange"); + + stopPeriodicTimers(); + m_loadState = WaitingForSource; + + // 6.17 - Waiting: Set the element's networkState attribute to the NETWORK_NO_SOURCE value + m_networkState = NETWORK_NO_SOURCE; + + // 6.18 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + setShouldDelayLoadEvent(false); + + updateDisplayState(); + + if (renderer()) + renderer()->updateFromElement(); +} + +void HTMLMediaElement::noneSupported() +{ + LOG(Media, "HTMLMediaElement::noneSupported"); + + stopPeriodicTimers(); + m_loadState = WaitingForSource; + m_currentSourceNode = 0; + + // 4.8.10.5 + // 6 - Reaching this step indicates that the media resource failed to load or that the given + // URL could not be resolved. In one atomic operation, run the following steps: + + // 6.1 - Set the error attribute to a new MediaError object whose code attribute is set to + // MEDIA_ERR_SRC_NOT_SUPPORTED. + m_error = MediaError::create(MediaError::MEDIA_ERR_SRC_NOT_SUPPORTED); + + // 6.2 - Forget the media element's media-resource-specific text tracks. + + // 6.3 - Set the element's networkState attribute to the NETWORK_NO_SOURCE value. + m_networkState = NETWORK_NO_SOURCE; + + // 7 - Queue a task to fire a simple event named error at the media element. + scheduleEvent(eventNames().errorEvent); + + // 8 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + setShouldDelayLoadEvent(false); + + // 9 - Abort these steps. Until the load() method is invoked or the src attribute is changed, + // the element won't attempt to load another resource. + + updateDisplayState(); + + if (renderer()) + renderer()->updateFromElement(); +} + +void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) +{ + LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code())); + + // 1 - The user agent should cancel the fetching process. + stopPeriodicTimers(); + m_loadState = WaitingForSource; + + // 2 - Set the error attribute to a new MediaError object whose code attribute is + // set to MEDIA_ERR_NETWORK/MEDIA_ERR_DECODE. + m_error = err; + + // 3 - Queue a task to fire a simple event named error at the media element. + scheduleEvent(eventNames().errorEvent); + +#if ENABLE(MEDIA_SOURCE) + if (m_sourceState != SOURCE_CLOSED) + setSourceState(SOURCE_CLOSED); +#endif + + // 4 - Set the element's networkState attribute to the NETWORK_EMPTY value and queue a + // task to fire a simple event called emptied at the element. + m_networkState = NETWORK_EMPTY; + scheduleEvent(eventNames().emptiedEvent); + + // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + setShouldDelayLoadEvent(false); + + // 6 - Abort the overall resource selection algorithm. + m_currentSourceNode = 0; +} + +void HTMLMediaElement::cancelPendingEventsAndCallbacks() +{ + LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks"); + + m_pendingEvents.clear(); + + for (Node* node = firstChild(); node; node = node->nextSibling()) { + if (node->hasTagName(sourceTag)) + static_cast<HTMLSourceElement*>(node)->cancelPendingErrorEvent(); + } +} + +Document* HTMLMediaElement::mediaPlayerOwningDocument() +{ + Document* d = document(); + + if (!d) + d = ownerDocument(); + + return d; +} + +void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); + setNetworkState(m_player->networkState()); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaLoadingFailed(MediaPlayer::NetworkState error) +{ + stopPeriodicTimers(); + + // If we failed while trying to load a <source> element, the movie was never parsed, and there are more + // <source> children, schedule the next one + if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) { + + if (m_currentSourceNode) + m_currentSourceNode->scheduleErrorEvent(); + else + LOG(Media, "HTMLMediaElement::setNetworkState - error event not sent, <source> was removed"); + + if (havePotentialSourceChild()) { + LOG(Media, "HTMLMediaElement::setNetworkState - scheduling next <source>"); + scheduleNextSourceChild(); + } else { + LOG(Media, "HTMLMediaElement::setNetworkState - no more <source> elements, waiting"); + waitForSourceChange(); + } + + return; + } + + if (error == MediaPlayer::NetworkError && m_readyState >= HAVE_METADATA) + mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_NETWORK)); + else if (error == MediaPlayer::DecodeError) + mediaEngineError(MediaError::create(MediaError::MEDIA_ERR_DECODE)); + else if ((error == MediaPlayer::FormatError || error == MediaPlayer::NetworkError) && m_loadState == LoadingFromSrcAttr) + noneSupported(); + + updateDisplayState(); + if (hasMediaControls()) { + mediaControls()->reset(); + mediaControls()->reportedError(); + } +} + +void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) +{ + LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState)); + + if (state == MediaPlayer::Empty) { + // Just update the cached state and leave, we can't do anything. + m_networkState = NETWORK_EMPTY; + return; + } + + if (state == MediaPlayer::FormatError || state == MediaPlayer::NetworkError || state == MediaPlayer::DecodeError) { + mediaLoadingFailed(state); + return; + } + + if (state == MediaPlayer::Idle) { + if (m_networkState > NETWORK_IDLE) { + m_progressEventTimer.stop(); + scheduleEvent(eventNames().suspendEvent); + setShouldDelayLoadEvent(false); + } + m_networkState = NETWORK_IDLE; + } + + if (state == MediaPlayer::Loading) { + if (m_networkState < NETWORK_LOADING || m_networkState == NETWORK_NO_SOURCE) + startProgressEventTimer(); + m_networkState = NETWORK_LOADING; + } + + if (state == MediaPlayer::Loaded) { + if (m_networkState != NETWORK_IDLE) { + m_progressEventTimer.stop(); + + // Schedule one last progress event so we guarantee that at least one is fired + // for files that load very quickly. + scheduleEvent(eventNames().progressEvent); + } + m_networkState = NETWORK_IDLE; + m_completelyLoaded = true; + } + + if (hasMediaControls()) + mediaControls()->updateStatusDisplay(); +} + +void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); + + setReadyState(m_player->readyState()); + + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) +{ + LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState)); + + // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it + bool wasPotentiallyPlaying = potentiallyPlaying(); + + ReadyState oldState = m_readyState; + m_readyState = static_cast<ReadyState>(state); + +#if ENABLE(VIDEO_TRACK) + bool tracksAreReady = textTracksAreReady(); + + if (m_readyState == oldState && m_tracksAreReady == tracksAreReady) + return; + + m_tracksAreReady = tracksAreReady; +#else + if (m_readyState == oldState) + return; + bool tracksAreReady = true; +#endif + + if (oldState > m_readyStateMaximum) + m_readyStateMaximum = oldState; + + if (m_networkState == NETWORK_EMPTY) + return; + + if (m_seeking) { + // 4.8.10.9, step 11 + if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) + scheduleEvent(eventNames().waitingEvent); + + // 4.8.10.10 step 14 & 15. + if (m_readyState >= HAVE_CURRENT_DATA) + finishSeek(); + } else { + if (wasPotentiallyPlaying && m_readyState < HAVE_FUTURE_DATA) { + // 4.8.10.8 + scheduleTimeupdateEvent(false); + scheduleEvent(eventNames().waitingEvent); + } + } + + if (m_readyState >= HAVE_METADATA && oldState < HAVE_METADATA && tracksAreReady) { + prepareMediaFragmentURI(); + scheduleEvent(eventNames().durationchangeEvent); + scheduleEvent(eventNames().loadedmetadataEvent); + if (hasMediaControls()) + mediaControls()->loadedMetadata(); + if (renderer()) + renderer()->updateFromElement(); + } + + bool shouldUpdateDisplayState = false; + + if (m_readyState >= HAVE_CURRENT_DATA && oldState < HAVE_CURRENT_DATA && !m_haveFiredLoadedData && tracksAreReady) { + m_haveFiredLoadedData = true; + shouldUpdateDisplayState = true; + scheduleEvent(eventNames().loadeddataEvent); + setShouldDelayLoadEvent(false); + applyMediaFragmentURI(); + } + + bool isPotentiallyPlaying = potentiallyPlaying(); + if (m_readyState == HAVE_FUTURE_DATA && oldState <= HAVE_CURRENT_DATA && tracksAreReady) { + scheduleEvent(eventNames().canplayEvent); + if (isPotentiallyPlaying) + scheduleEvent(eventNames().playingEvent); + shouldUpdateDisplayState = true; + } + + if (m_readyState == HAVE_ENOUGH_DATA && oldState < HAVE_ENOUGH_DATA && tracksAreReady) { + if (oldState <= HAVE_CURRENT_DATA) + scheduleEvent(eventNames().canplayEvent); + + scheduleEvent(eventNames().canplaythroughEvent); + + if (isPotentiallyPlaying && oldState <= HAVE_CURRENT_DATA) + scheduleEvent(eventNames().playingEvent); + + if (m_autoplaying && m_paused && autoplay()) { + m_paused = false; + invalidateCachedTime(); + scheduleEvent(eventNames().playEvent); + scheduleEvent(eventNames().playingEvent); + } + + shouldUpdateDisplayState = true; + } + + if (shouldUpdateDisplayState) { + updateDisplayState(); + if (hasMediaControls()) + mediaControls()->updateStatusDisplay(); + } + + updatePlayState(); + updateMediaController(); +} + +#if ENABLE(MEDIA_SOURCE) +void HTMLMediaElement::mediaPlayerSourceOpened() +{ + beginProcessingMediaPlayerCallback(); + + setSourceState(SOURCE_OPEN); + + endProcessingMediaPlayerCallback(); +} + +String HTMLMediaElement::mediaPlayerSourceURL() const +{ + return m_mediaSourceURL.string(); +} +#endif + +void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) +{ + ASSERT(m_player); + if (m_networkState != NETWORK_LOADING) + return; + + unsigned progress = m_player->bytesLoaded(); + double time = WTF::currentTime(); + double timedelta = time - m_previousProgressTime; + + if (progress == m_previousProgress) { + if (timedelta > 3.0 && !m_sentStalledEvent) { + scheduleEvent(eventNames().stalledEvent); + m_sentStalledEvent = true; + setShouldDelayLoadEvent(false); + } + } else { + scheduleEvent(eventNames().progressEvent); + m_previousProgress = progress; + m_previousProgressTime = time; + m_sentStalledEvent = false; + if (renderer()) + renderer()->updateFromElement(); + } +} + +void HTMLMediaElement::rewind(float timeDelta) +{ + LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta); + + ExceptionCode e; + setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e); +} + +void HTMLMediaElement::returnToRealtime() +{ + LOG(Media, "HTMLMediaElement::returnToRealtime"); + ExceptionCode e; + setCurrentTime(maxTimeSeekable(), e); +} + +void HTMLMediaElement::addPlayedRange(float start, float end) +{ + LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end); + if (!m_playedTimeRanges) + m_playedTimeRanges = TimeRanges::create(); + m_playedTimeRanges->add(start, end); +} + +bool HTMLMediaElement::supportsSave() const +{ + return m_player ? m_player->supportsSave() : false; +} + +bool HTMLMediaElement::supportsScanning() const +{ + return m_player ? m_player->supportsScanning() : false; +} + +void HTMLMediaElement::prepareToPlay() +{ + LOG(Media, "HTMLMediaElement::prepareToPlay(%p)", this); + if (m_havePreparedToPlay) + return; + m_havePreparedToPlay = true; + m_player->prepareToPlay(); +} + +void HTMLMediaElement::seek(float time, ExceptionCode& ec) +{ + LOG(Media, "HTMLMediaElement::seek(%f)", time); + + // 4.8.9.9 Seeking + + // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception. + if (m_readyState == HAVE_NOTHING || !m_player) { + ec = INVALID_STATE_ERR; + return; + } + + // If the media engine has been told to postpone loading data, let it go ahead now. + if (m_preload < MediaPlayer::Auto && m_readyState < HAVE_FUTURE_DATA) + prepareToPlay(); + + // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set. + refreshCachedTime(); + float now = currentTime(); + + // 2 - If the element's seeking IDL attribute is true, then another instance of this algorithm is + // already running. Abort that other instance of the algorithm without waiting for the step that + // it is running to complete. + // Nothing specific to be done here. + + // 3 - Set the seeking IDL attribute to true. + // The flag will be cleared when the engine tells us the time has actually changed. + m_seeking = true; + + // 5 - If the new playback position is later than the end of the media resource, then let it be the end + // of the media resource instead. + time = min(time, duration()); + + // 6 - If the new playback position is less than the earliest possible position, let it be that position instead. + float earliestTime = m_player->startTime(); + time = max(time, earliestTime); + + // Ask the media engine for the time value in the movie's time scale before comparing with current time. This + // is necessary because if the seek time is not equal to currentTime but the delta is less than the movie's + // time scale, we will ask the media engine to "seek" to the current movie time, which may be a noop and + // not generate a timechanged callback. This means m_seeking will never be cleared and we will never + // fire a 'seeked' event. +#if !LOG_DISABLED + float mediaTime = m_player->mediaTimeForTimeValue(time); + if (time != mediaTime) + LOG(Media, "HTMLMediaElement::seek(%f) - media timeline equivalent is %f", time, mediaTime); +#endif + time = m_player->mediaTimeForTimeValue(time); + + // 7 - If the (possibly now changed) new playback position is not in one of the ranges given in the + // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute + // that is the nearest to the new playback position. ... If there are no ranges given in the seekable + // attribute then set the seeking IDL attribute to false and abort these steps. + RefPtr<TimeRanges> seekableRanges = seekable(); + + // Short circuit seeking to the current time by just firing the events if no seek is required. + // Don't skip calling the media engine if we are in poster mode because a seek should always + // cancel poster display. + bool noSeekRequired = !seekableRanges->length() || (time == now && displayMode() != Poster); + +#if ENABLE(MEDIA_SOURCE) + // Always notify the media engine of a seek if the source is not closed. This ensures that the source is + // always in a flushed state when the 'seeking' event fires. + if (m_sourceState != SOURCE_CLOSED) + noSeekRequired = false; +#endif + + if (noSeekRequired) { + if (time == now) { + scheduleEvent(eventNames().seekingEvent); + scheduleTimeupdateEvent(false); + scheduleEvent(eventNames().seekedEvent); + } + m_seeking = false; + return; + } + time = seekableRanges->nearest(time); + + if (m_playing) { + if (m_lastSeekTime < now) + addPlayedRange(m_lastSeekTime, now); + } + m_lastSeekTime = time; + m_sentEndEvent = false; + +#if ENABLE(MEDIA_SOURCE) + if (m_sourceState == SOURCE_ENDED) + setSourceState(SOURCE_OPEN); +#endif + + // 8 - Set the current playback position to the given new playback position + m_player->seek(time); + + // 9 - Queue a task to fire a simple event named seeking at the element. + scheduleEvent(eventNames().seekingEvent); + + // 10 - Queue a task to fire a simple event named timeupdate at the element. + scheduleTimeupdateEvent(false); + + // 11-15 are handled, if necessary, when the engine signals a readystate change. +} + +void HTMLMediaElement::finishSeek() +{ + LOG(Media, "HTMLMediaElement::finishSeek"); + + // 4.8.10.9 Seeking step 14 + m_seeking = false; + + // 4.8.10.9 Seeking step 15 + scheduleEvent(eventNames().seekedEvent); + + setDisplayMode(Video); +} + +HTMLMediaElement::ReadyState HTMLMediaElement::readyState() const +{ + return m_readyState; +} + +MediaPlayer::MovieLoadType HTMLMediaElement::movieLoadType() const +{ + return m_player ? m_player->movieLoadType() : MediaPlayer::Unknown; +} + +bool HTMLMediaElement::hasAudio() const +{ + return m_player ? m_player->hasAudio() : false; +} + +bool HTMLMediaElement::seeking() const +{ + return m_seeking; +} + +void HTMLMediaElement::refreshCachedTime() const +{ + m_cachedTime = m_player->currentTime(); + m_cachedTimeWallClockUpdateTime = WTF::currentTime(); +} + +void HTMLMediaElement::invalidateCachedTime() +{ + LOG(Media, "HTMLMediaElement::invalidateCachedTime"); + + // Don't try to cache movie time when playback first starts as the time reported by the engine + // sometimes fluctuates for a short amount of time, so the cached time will be off if we take it + // too early. + static const double minimumTimePlayingBeforeCacheSnapshot = 0.5; + + m_minimumWallClockTimeToCacheMediaTime = WTF::currentTime() + minimumTimePlayingBeforeCacheSnapshot; + m_cachedTime = invalidMediaTime; +} + +// playback state +float HTMLMediaElement::currentTime() const +{ +#if LOG_CACHED_TIME_WARNINGS + static const double minCachedDeltaForWarning = 0.01; +#endif + + if (!m_player) + return 0; + + if (m_seeking) { + LOG(Media, "HTMLMediaElement::currentTime - seeking, returning %f", m_lastSeekTime); + return m_lastSeekTime; + } + + if (m_cachedTime != invalidMediaTime && m_paused) { +#if LOG_CACHED_TIME_WARNINGS + float delta = m_cachedTime - m_player->currentTime(); + if (delta > minCachedDeltaForWarning) + LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when paused", delta); +#endif + return m_cachedTime; + } + + // Is it too soon use a cached time? + double now = WTF::currentTime(); + double maximumDurationToCacheMediaTime = m_player->maximumDurationToCacheMediaTime(); + + if (maximumDurationToCacheMediaTime && m_cachedTime != invalidMediaTime && !m_paused && now > m_minimumWallClockTimeToCacheMediaTime) { + double wallClockDelta = now - m_cachedTimeWallClockUpdateTime; + + // Not too soon, use the cached time only if it hasn't expired. + if (wallClockDelta < maximumDurationToCacheMediaTime) { + float adjustedCacheTime = static_cast<float>(m_cachedTime + (m_playbackRate * wallClockDelta)); + +#if LOG_CACHED_TIME_WARNINGS + float delta = adjustedCacheTime - m_player->currentTime(); + if (delta > minCachedDeltaForWarning) + LOG(Media, "HTMLMediaElement::currentTime - WARNING, cached time is %f seconds off of media time when playing", delta); +#endif + return adjustedCacheTime; + } + } + +#if LOG_CACHED_TIME_WARNINGS + if (maximumDurationToCacheMediaTime && now > m_minimumWallClockTimeToCacheMediaTime && m_cachedTime != invalidMediaTime) { + 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); + } +#endif + + refreshCachedTime(); + + return m_cachedTime; +} + +void HTMLMediaElement::setCurrentTime(float time, ExceptionCode& ec) +{ + if (m_mediaController) { + ec = INVALID_STATE_ERR; + return; + } + seek(time, ec); +} + +float HTMLMediaElement::startTime() const +{ + if (!m_player) + return 0; + return m_player->startTime(); +} + +double HTMLMediaElement::initialTime() const +{ + if (m_fragmentStartTime != invalidMediaTime) + return m_fragmentStartTime; + + if (!m_player) + return 0; + + return m_player->initialTime(); +} + +float HTMLMediaElement::duration() const +{ + if (m_player && m_readyState >= HAVE_METADATA) + return m_player->duration(); + + return numeric_limits<float>::quiet_NaN(); +} + +bool HTMLMediaElement::paused() const +{ + return m_paused; +} + +float HTMLMediaElement::defaultPlaybackRate() const +{ + return m_defaultPlaybackRate; +} + +void HTMLMediaElement::setDefaultPlaybackRate(float rate) +{ + if (m_defaultPlaybackRate != rate) { + m_defaultPlaybackRate = rate; + scheduleEvent(eventNames().ratechangeEvent); + } +} + +float HTMLMediaElement::playbackRate() const +{ + return m_playbackRate; +} + +void HTMLMediaElement::setPlaybackRate(float rate) +{ + LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate); + + if (m_playbackRate != rate) { + m_playbackRate = rate; + invalidateCachedTime(); + scheduleEvent(eventNames().ratechangeEvent); + } + + if (m_player && potentiallyPlaying() && m_player->rate() != rate && !m_mediaController) + m_player->setRate(rate); +} + +void HTMLMediaElement::updatePlaybackRate() +{ + float effectiveRate = m_mediaController ? m_mediaController->playbackRate() : m_playbackRate; + if (m_player && potentiallyPlaying() && m_player->rate() != effectiveRate && !m_mediaController) + m_player->setRate(effectiveRate); +} + +bool HTMLMediaElement::webkitPreservesPitch() const +{ + return m_webkitPreservesPitch; +} + +void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) +{ + LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch)); + + m_webkitPreservesPitch = preservesPitch; + + if (!m_player) + return; + + m_player->setPreservesPitch(preservesPitch); +} + +bool HTMLMediaElement::ended() const +{ + // 4.8.10.8 Playing the media resource + // The ended attribute must return true if the media element has ended + // playback and the direction of playback is forwards, and false otherwise. + return endedPlayback() && m_playbackRate > 0; +} + +bool HTMLMediaElement::autoplay() const +{ + return fastHasAttribute(autoplayAttr); +} + +void HTMLMediaElement::setAutoplay(bool b) +{ + LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b)); + setBooleanAttribute(autoplayAttr, b); +} + +String HTMLMediaElement::preload() const +{ + switch (m_preload) { + case MediaPlayer::None: + return "none"; + break; + case MediaPlayer::MetaData: + return "metadata"; + break; + case MediaPlayer::Auto: + return "auto"; + break; + } + + ASSERT_NOT_REACHED(); + return String(); +} + +void HTMLMediaElement::setPreload(const String& preload) +{ + LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data()); + setAttribute(preloadAttr, preload); +} + +void HTMLMediaElement::play() +{ + LOG(Media, "HTMLMediaElement::play()"); + + if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture()) + return; + + Settings* settings = document()->settings(); + if (settings && settings->needsSiteSpecificQuirks() && m_dispatchingCanPlayEvent && !m_loadInitiatedByUserGesture) { + // It should be impossible to be processing the canplay event while handling a user gesture + // since it is dispatched asynchronously. + ASSERT(!ScriptController::processingUserGesture()); + String host = document()->baseURL().host(); + if (host.endsWith(".npr.org", false) || equalIgnoringCase(host, "npr.org")) + return; + } + + playInternal(); +} + +void HTMLMediaElement::playInternal() +{ + LOG(Media, "HTMLMediaElement::playInternal"); + + // 4.8.10.9. Playing the media resource + if (!m_player || m_networkState == NETWORK_EMPTY) + scheduleLoad(MediaResource); + + if (endedPlayback()) { + ExceptionCode unused; + seek(0, unused); + } + + if (m_mediaController) + m_mediaController->bringElementUpToSpeed(this); + + if (m_paused) { + m_paused = false; + invalidateCachedTime(); + scheduleEvent(eventNames().playEvent); + + if (m_readyState <= HAVE_CURRENT_DATA) + scheduleEvent(eventNames().waitingEvent); + else if (m_readyState >= HAVE_FUTURE_DATA) + scheduleEvent(eventNames().playingEvent); + } + m_autoplaying = false; + + updatePlayState(); + updateMediaController(); +} + +void HTMLMediaElement::pause() +{ + LOG(Media, "HTMLMediaElement::pause()"); + + if (userGestureRequiredForRateChange() && !ScriptController::processingUserGesture()) + return; + + pauseInternal(); +} + + +void HTMLMediaElement::pauseInternal() +{ + LOG(Media, "HTMLMediaElement::pauseInternal"); + + // 4.8.10.9. Playing the media resource + if (!m_player || m_networkState == NETWORK_EMPTY) + scheduleLoad(MediaResource); + + m_autoplaying = false; + + if (!m_paused) { + m_paused = true; + scheduleTimeupdateEvent(false); + scheduleEvent(eventNames().pauseEvent); + } + + updatePlayState(); +} + +#if ENABLE(MEDIA_SOURCE) +void HTMLMediaElement::webkitSourceAppend(PassRefPtr<Uint8Array> data, ExceptionCode& ec) +{ + if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) { + ec = INVALID_STATE_ERR; + return; + } + + if (!data.get() || !m_player->sourceAppend(data->data(), data->length())) { + ec = SYNTAX_ERR; + return; + } +} + +void HTMLMediaElement::webkitSourceEndOfStream(unsigned short status, ExceptionCode& ec) +{ + if (!m_player || m_currentSrc != m_mediaSourceURL || m_sourceState != SOURCE_OPEN) { + ec = INVALID_STATE_ERR; + return; + } + + MediaPlayer::EndOfStreamStatus eosStatus = MediaPlayer::EosNoError; + + switch (status) { + case EOS_NO_ERROR: + eosStatus = MediaPlayer::EosNoError; + break; + case EOS_NETWORK_ERR: + eosStatus = MediaPlayer::EosNetworkError; + break; + case EOS_DECODE_ERR: + eosStatus = MediaPlayer::EosDecodeError; + break; + default: + ec = SYNTAX_ERR; + return; + } + + setSourceState(SOURCE_ENDED); + m_player->sourceEndOfStream(eosStatus); +} + +HTMLMediaElement::SourceState HTMLMediaElement::webkitSourceState() const +{ + return m_sourceState; +} + +void HTMLMediaElement::setSourceState(SourceState state) +{ + SourceState oldState = m_sourceState; + m_sourceState = static_cast<SourceState>(state); + + if (m_sourceState == oldState) + return; + + if (m_sourceState == SOURCE_CLOSED) { + scheduleEvent(eventNames().webkitsourcecloseEvent); + return; + } + + if (oldState == SOURCE_OPEN && m_sourceState == SOURCE_ENDED) { + scheduleEvent(eventNames().webkitsourceendedEvent); + return; + } + + if (m_sourceState == SOURCE_OPEN) { + scheduleEvent(eventNames().webkitsourceopenEvent); + return; + } +} +#endif + +bool HTMLMediaElement::loop() const +{ + return fastHasAttribute(loopAttr); +} + +void HTMLMediaElement::setLoop(bool b) +{ + LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b)); + setBooleanAttribute(loopAttr, b); +} + +bool HTMLMediaElement::controls() const +{ + Frame* frame = document()->frame(); + + // always show controls when scripting is disabled + if (frame && !frame->script()->canExecuteScripts(NotAboutToExecuteScript)) + return true; + + // always show controls for video when fullscreen playback is required. + if (isVideo() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback()) + return true; + + // Always show controls when in full screen mode. + if (isFullscreen()) + return true; + + return fastHasAttribute(controlsAttr); +} + +void HTMLMediaElement::setControls(bool b) +{ + LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b)); + setBooleanAttribute(controlsAttr, b); +} + +float HTMLMediaElement::volume() const +{ + return m_volume; +} + +void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) +{ + LOG(Media, "HTMLMediaElement::setVolume(%f)", vol); + + if (vol < 0.0f || vol > 1.0f) { + ec = INDEX_SIZE_ERR; + return; + } + + if (m_volume != vol) { + m_volume = vol; + updateVolume(); + scheduleEvent(eventNames().volumechangeEvent); + } +} + +bool HTMLMediaElement::muted() const +{ + return m_muted; +} + +void HTMLMediaElement::setMuted(bool muted) +{ + LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted)); + + if (m_muted != muted) { + m_muted = muted; + // Avoid recursion when the player reports volume changes. + if (!processingMediaPlayerCallback()) { + if (m_player) { + m_player->setMuted(m_muted); + if (hasMediaControls()) + mediaControls()->changedMute(); + } + } + scheduleEvent(eventNames().volumechangeEvent); + } +} + +void HTMLMediaElement::togglePlayState() +{ + LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay())); + + // We can safely call the internal play/pause methods, which don't check restrictions, because + // this method is only called from the built-in media controller + if (canPlay()) { + updatePlaybackRate(); + playInternal(); + } else + pauseInternal(); +} + +void HTMLMediaElement::beginScrubbing() +{ + LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused())); + + if (!paused()) { + if (ended()) { + // Because a media element stays in non-paused state when it reaches end, playback resumes + // when the slider is dragged from the end to another position unless we pause first. Do + // a "hard pause" so an event is generated, since we want to stay paused after scrubbing finishes. + pause(); + } else { + // Not at the end but we still want to pause playback so the media engine doesn't try to + // continue playing during scrubbing. Pause without generating an event as we will + // unpause after scrubbing finishes. + setPausedInternal(true); + } + } +} + +void HTMLMediaElement::endScrubbing() +{ + LOG(Media, "HTMLMediaElement::endScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal)); + + if (m_pausedInternal) + setPausedInternal(false); +} + +// The spec says to fire periodic timeupdate events (those sent while playing) every +// "15 to 250ms", we choose the slowest frequency +static const double maxTimeupdateEventFrequency = 0.25; + +static const double timeWithoutMouseMovementBeforeHidingControls = 3; + +void HTMLMediaElement::startPlaybackProgressTimer() +{ + if (m_playbackProgressTimer.isActive()) + return; + + m_previousProgressTime = WTF::currentTime(); + m_previousProgress = 0; + m_playbackProgressTimer.startRepeating(maxTimeupdateEventFrequency); +} + +void HTMLMediaElement::playbackProgressTimerFired(Timer<HTMLMediaElement>*) +{ + ASSERT(m_player); + + if (m_fragmentEndTime != invalidMediaTime && currentTime() >= m_fragmentEndTime && m_playbackRate > 0) { + m_fragmentEndTime = invalidMediaTime; + if (!m_mediaController && !m_paused) { + // changes paused to true and fires a simple event named pause at the media element. + pauseInternal(); + } + } + + scheduleTimeupdateEvent(true); + + if (!m_playbackRate) + return; + + if (!m_paused && hasMediaControls()) + mediaControls()->playbackProgressed(); + +#if ENABLE(VIDEO_TRACK) + updateActiveTextTrackCues(currentTime()); +#endif +} + +void HTMLMediaElement::scheduleTimeupdateEvent(bool periodicEvent) +{ + double now = WTF::currentTime(); + double timedelta = now - m_lastTimeUpdateEventWallTime; + + // throttle the periodic events + if (periodicEvent && timedelta < maxTimeupdateEventFrequency) + return; + + // Some media engines make multiple "time changed" callbacks at the same time, but we only want one + // event at a given time so filter here + float movieTime = currentTime(); + if (movieTime != m_lastTimeUpdateEventMovieTime) { + scheduleEvent(eventNames().timeupdateEvent); + m_lastTimeUpdateEventWallTime = now; + m_lastTimeUpdateEventMovieTime = movieTime; + } +} + +bool HTMLMediaElement::canPlay() const +{ + return paused() || ended() || m_readyState < HAVE_METADATA; +} + +float HTMLMediaElement::percentLoaded() const +{ + if (!m_player) + return 0; + float duration = m_player->duration(); + + if (!duration || isinf(duration)) + return 0; + + float buffered = 0; + RefPtr<TimeRanges> timeRanges = m_player->buffered(); + for (unsigned i = 0; i < timeRanges->length(); ++i) { + ExceptionCode ignoredException; + float start = timeRanges->start(i, ignoredException); + float end = timeRanges->end(i, ignoredException); + buffered += end - start; + } + return buffered / duration; +} + +#if ENABLE(VIDEO_TRACK) +PassRefPtr<TextTrack> HTMLMediaElement::addTrack(const String& kind, const String& label, const String& language, ExceptionCode& ec) +{ + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return 0; + + // 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; + return 0; + } + + // 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. + + // 6. Add the new text track to the media element's list of text tracks. + textTracks()->append(textTrack); + + return textTrack.release(); +} + +TextTrackList* HTMLMediaElement::textTracks() +{ + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return 0; + + if (!m_textTracks) + m_textTracks = TextTrackList::create(this, ActiveDOMObject::scriptExecutionContext()); + + return m_textTracks.get(); +} + +HTMLTrackElement* HTMLMediaElement::showingTrackWithSameKind(HTMLTrackElement* trackElement) const +{ + HTMLTrackElement* showingTrack = 0; + + for (Node* node = firstChild(); node; node = node->nextSibling()) { + if (trackElement == node) + continue; + if (!node->hasTagName(trackTag)) + continue; + + showingTrack = static_cast<HTMLTrackElement*>(node); + if (showingTrack->kind() == trackElement->kind() && showingTrack->track()->mode() == TextTrack::SHOWING) + return showingTrack; + } + + return 0; +} + +void HTMLMediaElement::trackWasAdded(HTMLTrackElement* trackElement) +{ + ASSERT(trackElement->hasTagName(trackTag)); + + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return; + + // 4.8.10.12.3 Sourcing out-of-band text tracks + // When a track element's parent element changes and the new parent is a media element, + // then the user agent must add the track element's corresponding text track to the + // media element's list of text tracks ... [continues in TextTrackList::append] + RefPtr<TextTrack> textTrack = trackElement->track(); + if (!textTrack) + return; + + textTracks()->append(textTrack); + + // Do not schedule the track loading until parsing finishes so we don't start before all tracks + // in the markup have been added. + if (!m_parsingInProgress) + scheduleLoad(TextTrackResource); +} + +void HTMLMediaElement::trackWillBeRemoved(HTMLTrackElement* trackElement) +{ + ASSERT(trackElement->hasTagName(trackTag)); + + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return; + +#if !LOG_DISABLED + if (trackElement->hasTagName(trackTag)) { + KURL url = trackElement->getNonEmptyURLAttribute(srcAttr); + LOG(Media, "HTMLMediaElement::trackWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data()); + } +#endif + + trackElement->setHasBeenConfigured(false); + + RefPtr<TextTrack> textTrack = trackElement->track(); + if (!textTrack) + return; + + // 4.8.10.12.3 Sourcing out-of-band text tracks + // When a track element's parent element changes and the old parent was a media element, + // then the user agent must remove the track element's corresponding text track from the + // media element's list of text tracks. + m_textTracks->remove(textTrack.get()); + size_t index = m_textTracksWhenResourceSelectionBegan.find(textTrack.get()); + if (index != notFound) + m_textTracksWhenResourceSelectionBegan.remove(index); +} + +bool HTMLMediaElement::userIsInterestedInThisLanguage(const String&) const +{ + // FIXME: check the user's language preference - bugs.webkit.org/show_bug.cgi?id=74121 + return true; +} + +bool HTMLMediaElement::userIsInterestedInThisTrack(HTMLTrackElement* trackElement) const +{ + RefPtr<TextTrack> textTrack = trackElement->track(); + if (!textTrack) + return false; + + String kind = textTrack->kind(); + if (!TextTrack::isValidKindKeyword(kind)) + return false; + + // If ... the user has indicated an interest in having a track with this text track kind, text track language, ... + Settings* settings = document()->settings(); + if (!settings) + return false; + + if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) { + if (kind == TextTrack::subtitlesKeyword() && !settings->shouldDisplaySubtitles()) + return false; + if (kind == TextTrack::captionsKeyword() && !settings->shouldDisplayCaptions()) + return false; + return userIsInterestedInThisLanguage(trackElement->srclang()); + } + + if (kind == TextTrack::descriptionsKeyword()) { + if (!settings->shouldDisplayTextDescriptions()) + return false; + return userIsInterestedInThisLanguage(trackElement->srclang()); + } + + return false; +} + +void HTMLMediaElement::configureTextTrack(HTMLTrackElement* trackElement) +{ +#if !LOG_DISABLED + if (trackElement->hasTagName(trackTag)) { + KURL url = trackElement->getNonEmptyURLAttribute(srcAttr); + LOG(Media, "HTMLMediaElement::configureTextTrack - 'src' is %s", urlForLogging(url).utf8().data()); + } +#endif + + // 4.8.10.12.3 Sourcing out-of-band text tracks + + // When a text track corresponding to a track element is added to a media element's list of text tracks, + // the user agent must set the text track mode appropriately, as determined by the following conditions: + RefPtr<TextTrack> textTrack = trackElement->track(); + if (!textTrack) + return; + + TextTrack::Mode mode = TextTrack::HIDDEN; + HTMLTrackElement* trackElementCurrentlyShowing = showingTrackWithSameKind(trackElement); + String kind = textTrack->kind(); + bool hideDefaultTrack = false; + + if (userIsInterestedInThisTrack(trackElement)) { + if (kind == TextTrack::subtitlesKeyword() || kind == TextTrack::captionsKeyword()) { + // * If the text track kind is subtitles or captions and the user has indicated an interest in having a + // track with this text track kind, text track language, and text track label enabled, and there is no + // other text track in the media element's list of text tracks with a text track kind of either subtitles + // or captions whose text track mode is showing + hideDefaultTrack = trackElementCurrentlyShowing && trackElementCurrentlyShowing->track()->showingByDefault(); + if (!trackElementCurrentlyShowing || hideDefaultTrack) { + // Let the text track mode be showing. + // If there is a text track in the media element's list of text tracks whose text track mode is showing + // by default, the user agent must furthermore change that text track's text track mode to hidden. + mode = TextTrack::SHOWING; + } + } else if (kind == TextTrack::descriptionsKeyword()) { + // * If the text track kind is descriptions and the user has indicated an interest in having text + // descriptions with this text track language and text track label enabled, and there is no other text + // track in the media element's list of text tracks with a text track kind of descriptions whose text + // track mode is showing + hideDefaultTrack = trackElementCurrentlyShowing && trackElementCurrentlyShowing->track()->showingByDefault(); + if (!trackElementCurrentlyShowing || hideDefaultTrack) { + // Let the text track mode be showing. + // If there is a text track in the media element's list of text tracks whose text track mode is showing + // by default, the user agent must furthermore change that text track's text track mode to hidden. + mode = TextTrack::SHOWING; + } + } else if (kind == TextTrack::chaptersKeyword()) { + // * If the text track kind is chapters and the text track language is one that the user agent has reason + // to believe is appropriate for the user, and there is no other text track in the media element's list of + // text tracks with a text track kind of chapters whose text track mode is showing + // Let the text track mode be showing. + if (!trackElementCurrentlyShowing) + mode = TextTrack::SHOWING; + } + } else if (!trackElementCurrentlyShowing && trackElement->isDefault()) { + // * If the track element has a default attribute specified, and there is no other text track in the media + // element's list of text tracks whose text track mode is showing or showing by default + // Let the text track mode be showing by default. + mode = TextTrack::SHOWING; + textTrack->setShowingByDefault(false); + } else { + // Otherwise + // Let the text track mode be disabled. + mode = TextTrack::DISABLED; + } + + ExceptionCode unusedException; + if (hideDefaultTrack) { + trackElementCurrentlyShowing->track()->setMode(TextTrack::HIDDEN, unusedException); + trackElementCurrentlyShowing->track()->setShowingByDefault(false); + } + + textTrack->setMode(mode, unusedException); +} + +void HTMLMediaElement::configureTextTracks() +{ + for (Node* node = firstChild(); node; node = node->nextSibling()) { + if (!node->hasTagName(trackTag)) + continue; + HTMLTrackElement* trackElement = static_cast<HTMLTrackElement*>(node); + + // Only call configureTextTrack once per track so that adding another track after + // the initial configuration doesn't reconfigure every track, only those that should + // be changed by the new addition. For example all metadata tracks are disabled by + // default, and we don't want a track that has been enabled by script to be disabled + // automatically when a new track element is added later. + if (!trackElement->hasBeenConfigured()) + configureTextTrack(trackElement); + } +} + +#endif + +bool HTMLMediaElement::havePotentialSourceChild() +{ + // Stash the current <source> node and next nodes so we can restore them after checking + // to see there is another potential. + HTMLSourceElement* currentSourceNode = m_currentSourceNode; + Node* nextNode = m_nextChildNodeToConsider; + + KURL nextURL = selectNextSourceChild(0, DoNothing); + + m_currentSourceNode = currentSourceNode; + m_nextChildNodeToConsider = nextNode; + + return nextURL.isValid(); +} + +KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidURLAction actionIfInvalid) +{ +#if !LOG_DISABLED + // Don't log if this was just called to find out if there are any valid <source> elements. + bool shouldLog = actionIfInvalid != DoNothing; + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild"); +#endif + + if (m_nextChildNodeToConsider == sourceChildEndOfListValue()) { +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild -> 0x0000, \"\""); +#endif + return KURL(); + } + + KURL mediaURL; + Node* node; + HTMLSourceElement* source = 0; + bool lookingForStartNode = m_nextChildNodeToConsider; + bool canUse = false; + + for (node = firstChild(); !canUse && node; node = node->nextSibling()) { + if (lookingForStartNode && m_nextChildNodeToConsider != node) + continue; + lookingForStartNode = false; + + if (!node->hasTagName(sourceTag)) + continue; + + source = static_cast<HTMLSourceElement*>(node); + + // If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below + mediaURL = source->getNonEmptyURLAttribute(srcAttr); +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data()); +#endif + if (mediaURL.isEmpty()) + goto check_again; + + if (source->fastHasAttribute(mediaAttr)) { + MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); + RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data()); +#endif + if (!screenEval.eval(media.get())) + goto check_again; + } + + if (source->fastHasAttribute(typeAttr)) { +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data()); +#endif + if (!MediaPlayer::supportsType(ContentType(source->type()))) + goto check_again; + } + + // Is it safe to load this url? + if (!isSafeToLoadURL(mediaURL, actionIfInvalid) || !dispatchBeforeLoadEvent(mediaURL.string())) + goto check_again; + + // Making it this far means the <source> looks reasonable. + canUse = true; + +check_again: + if (!canUse && actionIfInvalid == Complain) + source->scheduleErrorEvent(); + } + + if (canUse) { + if (contentType) + *contentType = ContentType(source->type()); + m_currentSourceNode = source; + m_nextChildNodeToConsider = source->nextSibling(); + if (!m_nextChildNodeToConsider) + m_nextChildNodeToConsider = sourceChildEndOfListValue(); + } else { + m_currentSourceNode = 0; + m_nextChildNodeToConsider = sourceChildEndOfListValue(); + } + +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %p, %s", m_currentSourceNode, canUse ? urlForLogging(mediaURL).utf8().data() : ""); +#endif + return canUse ? mediaURL : KURL(); +} + +void HTMLMediaElement::sourceWasAdded(HTMLSourceElement* source) +{ + LOG(Media, "HTMLMediaElement::sourceWasAdded(%p)", source); + +#if !LOG_DISABLED + if (source->hasTagName(sourceTag)) { + KURL url = source->getNonEmptyURLAttribute(srcAttr); + LOG(Media, "HTMLMediaElement::sourceWasAdded - 'src' is %s", urlForLogging(url).utf8().data()); + } +#endif + + // We should only consider a <source> element when there is not src attribute at all. + if (fastHasAttribute(srcAttr)) + return; + + // 4.8.8 - If a source element is inserted as a child of a media element that has no src + // attribute and whose networkState has the value NETWORK_EMPTY, the user agent must invoke + // the media element's resource selection algorithm. + if (networkState() == HTMLMediaElement::NETWORK_EMPTY) { + scheduleLoad(MediaResource); + return; + } + + if (m_currentSourceNode && source == m_currentSourceNode->nextSibling()) { + LOG(Media, "HTMLMediaElement::sourceWasAdded - <source> inserted immediately after current source"); + m_nextChildNodeToConsider = source; + return; + } + + if (m_nextChildNodeToConsider != sourceChildEndOfListValue()) + return; + + // 4.8.9.5, resource selection algorithm, source elements section: + // 20 - Wait until the node after pointer is a node other than the end of the list. (This step might wait forever.) + // 21 - Asynchronously await a stable state... + // 22 - Set the element's delaying-the-load-event flag back to true (this delays the load event again, in case + // it hasn't been fired yet). + setShouldDelayLoadEvent(true); + + // 23 - Set the networkState back to NETWORK_LOADING. + m_networkState = NETWORK_LOADING; + + // 24 - Jump back to the find next candidate step above. + m_nextChildNodeToConsider = source; + scheduleNextSourceChild(); +} + +void HTMLMediaElement::sourceWillBeRemoved(HTMLSourceElement* source) +{ + LOG(Media, "HTMLMediaElement::sourceWillBeRemoved(%p)", source); + +#if !LOG_DISABLED + if (source->hasTagName(sourceTag)) { + KURL url = source->getNonEmptyURLAttribute(srcAttr); + LOG(Media, "HTMLMediaElement::sourceWillBeRemoved - 'src' is %s", urlForLogging(url).utf8().data()); + } +#endif + + if (source != m_currentSourceNode && source != m_nextChildNodeToConsider) + return; + + if (source == m_nextChildNodeToConsider) { + m_nextChildNodeToConsider = m_nextChildNodeToConsider->nextSibling(); + if (!m_nextChildNodeToConsider) + m_nextChildNodeToConsider = sourceChildEndOfListValue(); + LOG(Media, "HTMLMediaElement::sourceRemoved - m_nextChildNodeToConsider set to %p", m_nextChildNodeToConsider); + } else if (source == m_currentSourceNode) { + // Clear the current source node pointer, but don't change the movie as the spec says: + // 4.8.8 - Dynamically modifying a source element and its attribute when the element is already + // inserted in a video or audio element will have no effect. + m_currentSourceNode = 0; + LOG(Media, "HTMLMediaElement::sourceRemoved - m_currentSourceNode set to 0"); + } +} + +void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged"); + + beginProcessingMediaPlayerCallback(); + + invalidateCachedTime(); + + // 4.8.10.9 step 14 & 15. Needed if no ReadyState change is associated with the seek. + if (m_seeking && m_readyState >= HAVE_CURRENT_DATA) + finishSeek(); + + // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, + // it will only queue a 'timeupdate' event if we haven't already posted one at the current + // movie time. + scheduleTimeupdateEvent(false); + + float now = currentTime(); + float dur = duration(); + + // When the current playback position reaches the end of the media resource when the direction of + // playback is forwards, then the user agent must follow these steps: + if (!isnan(dur) && dur && now >= dur && m_playbackRate > 0) { + // If the media element has a loop attribute specified and does not have a current media controller, + if (loop() && !m_mediaController) { + ExceptionCode ignoredException; + m_sentEndEvent = false; + // then seek to the earliest possible position of the media resource and abort these steps. + seek(startTime(), ignoredException); + } else { + // If the media element does not have a current media controller, and the media element + // has still ended playback, and the direction of playback is still forwards, and paused + // is false, + if (!m_mediaController && !m_paused) { + // changes paused to true and fires a simple event named pause at the media element. + m_paused = true; + scheduleEvent(eventNames().pauseEvent); + } + // Queue a task to fire a simple event named ended at the media element. + if (!m_sentEndEvent) { + m_sentEndEvent = true; + scheduleEvent(eventNames().endedEvent); + } + // If the media element has a current media controller, then report the controller state + // for the media element's current media controller. + updateMediaController(); + } + } + else + m_sentEndEvent = false; + + updatePlayState(); +#if ENABLE(VIDEO_TRACK) + updateActiveTextTrackCues(now); +#endif + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged"); + + beginProcessingMediaPlayerCallback(); + if (m_player) { + float vol = m_player->volume(); + if (vol != m_volume) { + m_volume = vol; + updateVolume(); + scheduleEvent(eventNames().volumechangeEvent); + } + } + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged"); + + beginProcessingMediaPlayerCallback(); + if (m_player) + setMuted(m_player->muted()); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer* player) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged"); + + beginProcessingMediaPlayerCallback(); + scheduleEvent(eventNames().durationchangeEvent); + mediaPlayerCharacteristicChanged(player); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged"); + + beginProcessingMediaPlayerCallback(); + + // Stash the rate in case the one we tried to set isn't what the engine is + // using (eg. it can't handle the rate we set) + m_playbackRate = m_player->rate(); + if (m_playing) + invalidateCachedTime(); + +#if PLATFORM(MAC) + if (m_player->paused() && m_sleepDisabler) + m_sleepDisabler = nullptr; + else if (!m_player->paused() && !m_sleepDisabler) + m_sleepDisabler = DisplaySleepDisabler::create("com.apple.WebCore: HTMLMediaElement playback"); +#endif + + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged"); + + if (!m_player || m_pausedInternal) + return; + + beginProcessingMediaPlayerCallback(); + if (m_player->paused()) + pauseInternal(); + else + playInternal(); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks"); + + // The MediaPlayer came across content it cannot completely handle. + // This is normally acceptable except when we are in a standalone + // MediaDocument. If so, tell the document what has happened. + if (ownerDocument()->isMediaDocument()) { + MediaDocument* mediaDocument = static_cast<MediaDocument*>(ownerDocument()); + mediaDocument->mediaElementSawUnsupportedTracks(); + } +} + +// MediaPlayerPresentation methods +void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) +{ + beginProcessingMediaPlayerCallback(); + updateDisplayState(); + if (renderer()) + renderer()->repaint(); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged"); + + beginProcessingMediaPlayerCallback(); + if (renderer()) + renderer()->updateFromElement(); + endProcessingMediaPlayerCallback(); +} + +#if USE(ACCELERATED_COMPOSITING) +bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) +{ + if (renderer() && renderer()->isVideo()) { + ASSERT(renderer()->view()); + return renderer()->view()->compositor()->canAccelerateVideoRendering(toRenderVideo(renderer())); + } + return false; +} + +void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged"); + + // Kick off a fake recalcStyle that will update the compositing tree. + setNeedsStyleRecalc(SyntheticStyleChange); +} +#endif + +void HTMLMediaElement::mediaPlayerEngineUpdated(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerEngineUpdated"); + beginProcessingMediaPlayerCallback(); + if (renderer()) + renderer()->updateFromElement(); + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerFirstVideoFrameAvailable"); + beginProcessingMediaPlayerCallback(); + if (displayMode() == PosterWaitingForVideo) { + setDisplayMode(Video); +#if USE(ACCELERATED_COMPOSITING) + mediaPlayerRenderingModeChanged(m_player.get()); +#endif + } + endProcessingMediaPlayerCallback(); +} + +void HTMLMediaElement::mediaPlayerCharacteristicChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerCharacteristicChanged"); + + beginProcessingMediaPlayerCallback(); + if (hasMediaControls()) + mediaControls()->reset(); + if (renderer()) + renderer()->updateFromElement(); + endProcessingMediaPlayerCallback(); +} + +PassRefPtr<TimeRanges> HTMLMediaElement::buffered() const +{ + if (!m_player) + return TimeRanges::create(); + return m_player->buffered(); +} + +PassRefPtr<TimeRanges> HTMLMediaElement::played() +{ + if (m_playing) { + float time = currentTime(); + if (time > m_lastSeekTime) + addPlayedRange(m_lastSeekTime, time); + } + + if (!m_playedTimeRanges) + m_playedTimeRanges = TimeRanges::create(); + + return m_playedTimeRanges->copy(); +} + +PassRefPtr<TimeRanges> HTMLMediaElement::seekable() const +{ + return m_player ? m_player->seekable() : TimeRanges::create(); +} + +bool HTMLMediaElement::potentiallyPlaying() const +{ + // "pausedToBuffer" means the media engine's rate is 0, but only because it had to stop playing + // when it ran out of buffered data. A movie is this state is "potentially playing", modulo the + // checks in couldPlayIfEnoughData(). + bool pausedToBuffer = m_readyStateMaximum >= HAVE_FUTURE_DATA && m_readyState < HAVE_FUTURE_DATA; + return (pausedToBuffer || m_readyState >= HAVE_FUTURE_DATA) && couldPlayIfEnoughData() && !isBlockedOnMediaController(); +} + +bool HTMLMediaElement::couldPlayIfEnoughData() const +{ + return !paused() && !endedPlayback() && !stoppedDueToErrors() && !pausedForUserInteraction(); +} + +bool HTMLMediaElement::endedPlayback() const +{ + float dur = duration(); + if (!m_player || isnan(dur)) + return false; + + // 4.8.10.8 Playing the media resource + + // A media element is said to have ended playback when the element's + // readyState attribute is HAVE_METADATA or greater, + if (m_readyState < HAVE_METADATA) + return false; + + // and the current playback position is the end of the media resource and the direction + // of playback is forwards, Either the media element does not have a loop attribute specified, + // or the media element has a current media controller. + float now = currentTime(); + if (m_playbackRate > 0) + return dur > 0 && now >= dur && (!loop() || m_mediaController); + + // or the current playback position is the earliest possible position and the direction + // of playback is backwards + if (m_playbackRate < 0) + return now <= 0; + + return false; +} + +bool HTMLMediaElement::stoppedDueToErrors() const +{ + if (m_readyState >= HAVE_METADATA && m_error) { + RefPtr<TimeRanges> seekableRanges = seekable(); + if (!seekableRanges->contain(currentTime())) + return true; + } + + return false; +} + +bool HTMLMediaElement::pausedForUserInteraction() const +{ +// return !paused() && m_readyState >= HAVE_FUTURE_DATA && [UA requires a decitions from the user] + return false; +} + +float HTMLMediaElement::minTimeSeekable() const +{ + return 0; +} + +float HTMLMediaElement::maxTimeSeekable() const +{ + return m_player ? m_player->maxTimeSeekable() : 0; +} + +void HTMLMediaElement::updateVolume() +{ + if (!m_player) + return; + + // Avoid recursion when the player reports volume changes. + if (!processingMediaPlayerCallback()) { + Page* page = document()->page(); + float volumeMultiplier = page ? page->mediaVolume() : 1; + bool shouldMute = m_muted; + + if (m_mediaController) { + volumeMultiplier *= m_mediaController->volume(); + shouldMute = m_mediaController->muted(); + } + + m_player->setMuted(shouldMute); + m_player->setVolume(m_volume * volumeMultiplier); + } + + if (hasMediaControls()) + mediaControls()->changedVolume(); +} + +void HTMLMediaElement::updatePlayState() +{ + if (!m_player) + return; + + if (m_pausedInternal) { + if (!m_player->paused()) + m_player->pause(); + refreshCachedTime(); + m_playbackProgressTimer.stop(); + if (hasMediaControls()) + mediaControls()->playbackStopped(); + return; + } + + bool shouldBePlaying = potentiallyPlaying(); + bool playerPaused = m_player->paused(); + + LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s", + boolString(shouldBePlaying), boolString(playerPaused)); + + if (shouldBePlaying) { + setDisplayMode(Video); + invalidateCachedTime(); + + if (playerPaused) { + if (!m_isFullscreen && isVideo() && document() && document()->page() && document()->page()->chrome()->requiresFullscreenForVideoPlayback()) + enterFullscreen(); + + // Set rate, muted before calling play in case they were set before the media engine was setup. + // The media engine should just stash the rate and muted values since it isn't already playing. + m_player->setRate(m_playbackRate); + m_player->setMuted(m_muted); + + m_player->play(); + } + + if (hasMediaControls()) + mediaControls()->playbackStarted(); + startPlaybackProgressTimer(); + m_playing = true; + + } else { // Should not be playing right now + if (!playerPaused) + m_player->pause(); + refreshCachedTime(); + + m_playbackProgressTimer.stop(); + m_playing = false; + float time = currentTime(); + if (time > m_lastSeekTime) + addPlayedRange(m_lastSeekTime, time); + + if (couldPlayIfEnoughData()) + prepareToPlay(); + + if (hasMediaControls()) + mediaControls()->playbackStopped(); + } + + updateMediaController(); + + if (renderer()) + renderer()->updateFromElement(); +} + +void HTMLMediaElement::setPausedInternal(bool b) +{ + m_pausedInternal = b; + updatePlayState(); +} + +void HTMLMediaElement::stopPeriodicTimers() +{ + m_progressEventTimer.stop(); + m_playbackProgressTimer.stop(); +} + +void HTMLMediaElement::userCancelledLoad() +{ + LOG(Media, "HTMLMediaElement::userCancelledLoad"); + + if (m_networkState == NETWORK_EMPTY || m_completelyLoaded) + return; + + // If the media data fetching process is aborted by the user: + + // 1 - The user agent should cancel the fetching process. +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + m_player.clear(); +#endif + stopPeriodicTimers(); + m_loadTimer.stop(); + m_loadState = WaitingForSource; + + // 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); + + // 3 - Queue a task to fire a simple event named error at the media element. + scheduleEvent(eventNames().abortEvent); + +#if ENABLE(MEDIA_SOURCE) + if (m_sourceState != SOURCE_CLOSED) + setSourceState(SOURCE_CLOSED); +#endif + + // 4 - If the media element's readyState attribute has a value equal to HAVE_NOTHING, set the + // element's networkState attribute to the NETWORK_EMPTY value and queue a task to fire a + // simple event named emptied at the element. Otherwise, set the element's networkState + // attribute to the NETWORK_IDLE value. + if (m_readyState == HAVE_NOTHING) { + m_networkState = NETWORK_EMPTY; + scheduleEvent(eventNames().emptiedEvent); + } + else + m_networkState = NETWORK_IDLE; + + // 5 - Set the element's delaying-the-load-event flag to false. This stops delaying the load event. + setShouldDelayLoadEvent(false); + + // 6 - Abort the overall resource selection algorithm. + m_currentSourceNode = 0; + + // Reset m_readyState since m_player is gone. + m_readyState = HAVE_NOTHING; + updateMediaController(); +} + +bool HTMLMediaElement::canSuspend() const +{ + return true; +} + +void HTMLMediaElement::stop() +{ + LOG(Media, "HTMLMediaElement::stop"); + if (m_isFullscreen) + exitFullscreen(); + + m_inActiveDocument = false; + userCancelledLoad(); + + // Stop the playback without generating events + setPausedInternal(true); + + if (renderer()) + renderer()->updateFromElement(); + + stopPeriodicTimers(); + cancelPendingEventsAndCallbacks(); +} + +void HTMLMediaElement::suspend(ReasonForSuspension why) +{ + LOG(Media, "HTMLMediaElement::suspend"); + + switch (why) + { + case DocumentWillBecomeInactive: + stop(); + break; + case JavaScriptDebuggerPaused: + case WillShowDialog: + // Do nothing, we don't pause media playback in these cases. + break; + } +} + +void HTMLMediaElement::resume() +{ + LOG(Media, "HTMLMediaElement::resume"); + + m_inActiveDocument = true; + setPausedInternal(false); + + if (m_error && m_error->code() == MediaError::MEDIA_ERR_ABORTED) { + // Restart the load if it was aborted in the middle by moving the document to the page cache. + // m_error is only left at MEDIA_ERR_ABORTED when the document becomes inactive (it is set to + // MEDIA_ERR_ABORTED while the abortEvent is being sent, but cleared immediately afterwards). + // This behavior is not specified but it seems like a sensible thing to do. + ExceptionCode ec; + load(ec); + } + + if (renderer()) + renderer()->updateFromElement(); +} + +bool HTMLMediaElement::hasPendingActivity() const +{ + // Return true when we have pending events so we can't fire events after the JS + // object gets collected. + bool pending = m_pendingEvents.size(); + LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending)); + return pending; +} + +void HTMLMediaElement::mediaVolumeDidChange() +{ + LOG(Media, "HTMLMediaElement::mediaVolumeDidChange"); + updateVolume(); +} + +void HTMLMediaElement::defaultEventHandler(Event* event) +{ +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + RenderObject* r = renderer(); + if (!r || !r->isWidget()) + return; + + Widget* widget = toRenderWidget(r)->widget(); + if (widget) + widget->handleEvent(event); +#else + HTMLElement::defaultEventHandler(event); +#endif +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + +void HTMLMediaElement::ensureMediaPlayer() +{ + if (!m_player) + createMediaPlayer(); +} + +void HTMLMediaElement::deliverNotification(MediaPlayerProxyNotificationType notification) +{ + if (notification == MediaPlayerNotificationPlayPauseButtonPressed) { + togglePlayState(); + return; + } + + if (m_player) + m_player->deliverNotification(notification); +} + +void HTMLMediaElement::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) +{ + ensureMediaPlayer(); + m_player->setMediaPlayerProxy(proxy); +} + +void HTMLMediaElement::getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values) +{ + Frame* frame = document()->frame(); + + if (isVideo()) { + KURL posterURL = getNonEmptyURLAttribute(posterAttr); + if (!posterURL.isEmpty() && frame && frame->loader()->willLoadMediaElementURL(posterURL)) { + names.append("_media_element_poster_"); + values.append(posterURL.string()); + } + } + + if (controls()) { + names.append("_media_element_controls_"); + values.append("true"); + } + + url = src(); + if (!isSafeToLoadURL(url, Complain)) + url = selectNextSourceChild(0, DoNothing); + + m_currentSrc = url; + if (url.isValid() && frame && frame->loader()->willLoadMediaElementURL(url)) { + names.append("_media_element_src_"); + values.append(m_currentSrc.string()); + } +} + +void HTMLMediaElement::createMediaPlayerProxy() +{ + ensureMediaPlayer(); + + if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate)) + return; + + Frame* frame = document()->frame(); + if (!frame) + return; + + LOG(Media, "HTMLMediaElement::createMediaPlayerProxy"); + + KURL url; + Vector<String> paramNames; + Vector<String> paramValues; + + getPluginProxyParams(url, paramNames, paramValues); + + // Hang onto the proxy widget so it won't be destroyed if the plug-in is set to + // display:none + m_proxyWidget = frame->loader()->subframeLoader()->loadMediaPlayerProxyPlugin(this, url, paramNames, paramValues); + if (m_proxyWidget) + m_needWidgetUpdate = false; +} + +void HTMLMediaElement::updateWidget(PluginCreationOption) +{ + mediaElement->setNeedWidgetUpdate(false); + + Vector<String> paramNames; + Vector<String> paramValues; + // FIXME: Rename kurl to something more sensible. + KURL kurl; + + mediaElement->getPluginProxyParams(kurl, paramNames, paramValues); + // FIXME: What if document()->frame() is 0? + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues); +} + +#endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) + +bool HTMLMediaElement::isFullscreen() const +{ + if (m_isFullscreen) + return true; + +#if ENABLE(FULLSCREEN_API) + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) + return true; +#endif + + return false; +} + +void HTMLMediaElement::enterFullscreen() +{ + LOG(Media, "HTMLMediaElement::enterFullscreen"); + +#if ENABLE(FULLSCREEN_API) + if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { + document()->requestFullScreenForElement(this, 0, Document::ExemptIFrameAllowFulScreenRequirement); + return; + } +#endif + ASSERT(!m_isFullscreen); + m_isFullscreen = true; + if (hasMediaControls()) + mediaControls()->enteredFullscreen(); + if (document() && document()->page()) { + document()->page()->chrome()->client()->enterFullscreenForNode(this); + scheduleEvent(eventNames().webkitbeginfullscreenEvent); + } +} + +void HTMLMediaElement::exitFullscreen() +{ + LOG(Media, "HTMLMediaElement::exitFullscreen"); + +#if ENABLE(FULLSCREEN_API) + if (document() && document()->settings() && document()->settings()->fullScreenEnabled()) { + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == this) + document()->webkitCancelFullScreen(); + return; + } +#endif + ASSERT(m_isFullscreen); + m_isFullscreen = false; + if (hasMediaControls()) + mediaControls()->exitedFullscreen(); + if (document() && document()->page()) { + if (document()->page()->chrome()->requiresFullscreenForVideoPlayback()) + pauseInternal(); + document()->page()->chrome()->client()->exitFullscreenForNode(this); + scheduleEvent(eventNames().webkitendfullscreenEvent); + } +} + +void HTMLMediaElement::didBecomeFullscreenElement() +{ + if (hasMediaControls()) + mediaControls()->enteredFullscreen(); +} + +void HTMLMediaElement::willStopBeingFullscreenElement() +{ + if (hasMediaControls()) + mediaControls()->exitedFullscreen(); +} + +PlatformMedia HTMLMediaElement::platformMedia() const +{ + return m_player ? m_player->platformMedia() : NoPlatformMedia; +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* HTMLMediaElement::platformLayer() const +{ + return m_player ? m_player->platformLayer() : 0; +} +#endif + +bool HTMLMediaElement::hasClosedCaptions() const +{ + return m_player && m_player->hasClosedCaptions(); +} + +bool HTMLMediaElement::closedCaptionsVisible() const +{ + return m_closedCaptionsVisible; +} + +void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) +{ + LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible)); + + if (!m_player ||!hasClosedCaptions()) + return; + + m_closedCaptionsVisible = closedCaptionVisible; + m_player->setClosedCaptionsVisible(closedCaptionVisible); + if (hasMediaControls()) + mediaControls()->changedClosedCaptionsVisibility(); +} + +void HTMLMediaElement::setWebkitClosedCaptionsVisible(bool visible) +{ + setClosedCaptionsVisible(visible); +} + +bool HTMLMediaElement::webkitClosedCaptionsVisible() const +{ + return closedCaptionsVisible(); +} + + +bool HTMLMediaElement::webkitHasClosedCaptions() const +{ + return hasClosedCaptions(); +} + +#if ENABLE(MEDIA_STATISTICS) +unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const +{ + if (!m_player) + return 0; + return m_player->audioDecodedByteCount(); +} + +unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const +{ + if (!m_player) + return 0; + return m_player->videoDecodedByteCount(); +} +#endif + +void HTMLMediaElement::mediaCanStart() +{ + LOG(Media, "HTMLMediaElement::mediaCanStart"); + + ASSERT(m_isWaitingUntilMediaCanStart); + m_isWaitingUntilMediaCanStart = false; + loadInternal(); +} + +bool HTMLMediaElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == srcAttr || HTMLElement::isURLAttribute(attribute); +} + +void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay) +{ + if (m_shouldDelayLoadEvent == shouldDelay) + return; + + LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay)); + + m_shouldDelayLoadEvent = shouldDelay; + if (shouldDelay) + document()->incrementLoadEventDelayCount(); + else + document()->decrementLoadEventDelayCount(); +} + + +void HTMLMediaElement::getSitesInMediaCache(Vector<String>& sites) +{ + MediaPlayer::getSitesInMediaCache(sites); +} + +void HTMLMediaElement::clearMediaCache() +{ + MediaPlayer::clearMediaCache(); +} + +void HTMLMediaElement::clearMediaCacheForSite(const String& site) +{ + MediaPlayer::clearMediaCacheForSite(site); +} + +void HTMLMediaElement::privateBrowsingStateDidChange() +{ + if (!m_player) + return; + + Settings* settings = document()->settings(); + bool privateMode = !settings || settings->privateBrowsingEnabled(); + LOG(Media, "HTMLMediaElement::privateBrowsingStateDidChange(%s)", boolString(privateMode)); + m_player->setPrivateBrowsingMode(privateMode); +} + +MediaControls* HTMLMediaElement::mediaControls() +{ + return toMediaControls(shadowRoot()->firstChild()); +} + +bool HTMLMediaElement::hasMediaControls() +{ + if (!shadowRoot()) + return false; + + Node* node = shadowRoot()->firstChild(); + return node && node->isMediaControls(); +} + +bool HTMLMediaElement::createMediaControls() +{ + if (hasMediaControls()) + return true; + + ExceptionCode ec; + RefPtr<MediaControls> controls = MediaControls::create(document()); + if (!controls) + return false; + + controls->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this)); + controls->reset(); + + ensureShadowRoot()->appendChild(controls, ec); + return true; +} + +void HTMLMediaElement::configureMediaControls() +{ +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (!controls()) { + if (hasMediaControls()) + mediaControls()->hide(); + return; + } + + if (!hasMediaControls() && !createMediaControls()) + return; + + mediaControls()->show(); +#else + if (m_player) + m_player->setControls(controls()); +#endif +} + +#if ENABLE(VIDEO_TRACK) +void HTMLMediaElement::configureTextTrackDisplay() +{ + ASSERT(m_textTracks); + + bool haveVisibleTextTrack = false; + for (unsigned i = 0; i < m_textTracks->length(); ++i) { + if (m_textTracks->item(i)->mode() == TextTrack::SHOWING) { + haveVisibleTextTrack = true; + break; + } + } + + if (m_haveVisibleTextTrack == haveVisibleTextTrack) + return; + m_haveVisibleTextTrack = haveVisibleTextTrack; + + if (!m_haveVisibleTextTrack && !hasMediaControls()) + return; + if (!hasMediaControls() && !createMediaControls()) + return; + mediaControls()->updateTextTrackDisplay(); +} +#endif + +void* HTMLMediaElement::preDispatchEventHandler(Event* event) +{ + if (event && event->type() == eventNames().webkitfullscreenchangeEvent) + configureMediaControls(); + + return 0; +} + +void HTMLMediaElement::createMediaPlayer() +{ +#if ENABLE(WEB_AUDIO) + if (m_audioSourceNode) + m_audioSourceNode->lock(); +#endif + + m_player = MediaPlayer::create(this); + +#if ENABLE(WEB_AUDIO) + if (m_audioSourceNode) { + // When creating the player, make sure its AudioSourceProvider knows about the MediaElementAudioSourceNode. + if (audioSourceProvider()) + audioSourceProvider()->setClient(m_audioSourceNode); + + m_audioSourceNode->unlock(); + } +#endif +} + +#if ENABLE(WEB_AUDIO) +void HTMLMediaElement::setAudioSourceNode(MediaElementAudioSourceNode* sourceNode) +{ + m_audioSourceNode = sourceNode; + + if (audioSourceProvider()) + audioSourceProvider()->setClient(m_audioSourceNode); +} + +AudioSourceProvider* HTMLMediaElement::audioSourceProvider() +{ + if (m_player) + return m_player->audioSourceProvider(); + + return 0; +} +#endif + +#if ENABLE(MICRODATA) +String HTMLMediaElement::itemValueText() const +{ + return getURLAttribute(srcAttr); +} + +void HTMLMediaElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(srcAttr, value, ec); +} +#endif + +const String& HTMLMediaElement::mediaGroup() const +{ + return m_mediaGroup; +} + +void HTMLMediaElement::setMediaGroup(const String& group) +{ + if (m_mediaGroup == group) + return; + m_mediaGroup = group; + + // When a media element is created with a mediagroup attribute, and when a media element's mediagroup + // attribute is set, changed, or removed, the user agent must run the following steps: + // 1. Let m [this] be the media element in question. + // 2. Let m have no current media controller, if it currently has one. + setController(0); + + // 3. If m's mediagroup attribute is being removed, then abort these steps. + if (group.isNull() || group.isEmpty()) + return; + + // 4. If there is another media element whose Document is the same as m's Document (even if one or both + // of these elements are not actually in the Document), + HashSet<HTMLMediaElement*> elements = documentToElementSetMap().get(document()); + for (HashSet<HTMLMediaElement*>::iterator i = elements.begin(); i != elements.end(); ++i) { + if (*i == this) + continue; + + // and which also has a mediagroup attribute, and whose mediagroup attribute has the same value as + // the new value of m's mediagroup attribute, + if ((*i)->mediaGroup() == group) { + // then let controller be that media element's current media controller. + setController((*i)->controller()); + return; + } + } + + // Otherwise, let controller be a newly created MediaController. + setController(MediaController::create(Node::scriptExecutionContext())); +} + +MediaController* HTMLMediaElement::controller() const +{ + return m_mediaController.get(); +} + +void HTMLMediaElement::setController(PassRefPtr<MediaController> controller) +{ + if (m_mediaController) + m_mediaController->removeMediaElement(this); + + m_mediaController = controller; + + if (m_mediaController) + m_mediaController->addMediaElement(this); + + if (hasMediaControls()) + mediaControls()->setMediaController(m_mediaController ? m_mediaController.get() : static_cast<MediaControllerInterface*>(this)); +} + +void HTMLMediaElement::updateMediaController() +{ + if (m_mediaController) + m_mediaController->reportControllerState(); +} + +bool HTMLMediaElement::isBlocked() const +{ + // A media element is a blocked media element if its readyState attribute is in the + // HAVE_NOTHING state, the HAVE_METADATA state, or the HAVE_CURRENT_DATA state, + if (m_readyState <= HAVE_CURRENT_DATA) + return true; + + // or if the element has paused for user interaction. + return pausedForUserInteraction(); +} + +bool HTMLMediaElement::isBlockedOnMediaController() const +{ + if (!m_mediaController) + return false; + + // A media element is blocked on its media controller if the MediaController is a blocked + // media controller, + if (m_mediaController->isBlocked()) + return true; + + // or if its media controller position is either before the media resource's earliest possible + // position relative to the MediaController's timeline or after the end of the media resource + // relative to the MediaController's timeline. + float mediaControllerPosition = m_mediaController->currentTime(); + if (mediaControllerPosition < startTime() || mediaControllerPosition > startTime() + duration()) + return true; + + return false; +} + +void HTMLMediaElement::prepareMediaFragmentURI() +{ + MediaFragmentURIParser fragmentParser(m_currentSrc); + float dur = duration(); + + double start = fragmentParser.startTime(); + if (start != MediaFragmentURIParser::invalidTimeValue() && start > 0) { + m_fragmentStartTime = start; + if (m_fragmentStartTime > dur) + m_fragmentStartTime = dur; + } else + m_fragmentStartTime = invalidMediaTime; + + double end = fragmentParser.endTime(); + if (end != MediaFragmentURIParser::invalidTimeValue() && end > 0 && end > m_fragmentStartTime) { + m_fragmentEndTime = end; + if (m_fragmentEndTime > dur) + m_fragmentEndTime = dur; + } else + m_fragmentEndTime = invalidMediaTime; + + if (m_fragmentStartTime != invalidMediaTime && m_readyState < HAVE_FUTURE_DATA) + prepareToPlay(); +} + +void HTMLMediaElement::applyMediaFragmentURI() +{ + if (m_fragmentStartTime != invalidMediaTime) { + ExceptionCode ignoredException; + m_sentEndEvent = false; + seek(m_fragmentStartTime, ignoredException); + } +} + +} + +#endif diff --git a/Source/WebCore/html/HTMLMediaElement.h b/Source/WebCore/html/HTMLMediaElement.h new file mode 100644 index 000000000..2623a65f0 --- /dev/null +++ b/Source/WebCore/html/HTMLMediaElement.h @@ -0,0 +1,611 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLMediaElement_h +#define HTMLMediaElement_h + +#if ENABLE(VIDEO) + +#include "HTMLElement.h" +#include "ActiveDOMObject.h" +#include "MediaCanStartListener.h" +#include "MediaControllerInterface.h" +#include "MediaPlayer.h" + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "MediaPlayerProxy.h" +#endif + +#if ENABLE(VIDEO_TRACK) +#include "PODIntervalTree.h" +#include "TextTrack.h" +#include "TextTrackCue.h" +#endif + +namespace WebCore { + +#if ENABLE(WEB_AUDIO) +class AudioSourceProvider; +class MediaElementAudioSourceNode; +#endif +class Event; +class HTMLSourceElement; +class HTMLTrackElement; +class MediaController; +class MediaControls; +class MediaError; +class KURL; +class TextTrackList; +class TimeRanges; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +class Widget; +#endif +#if PLATFORM(MAC) +class DisplaySleepDisabler; +#endif + +#if ENABLE(VIDEO_TRACK) +typedef PODIntervalTree<double, TextTrackCue*> CueIntervalTree; +typedef Vector<CueIntervalTree::IntervalType> CueList; +#endif + +// FIXME: The inheritance from MediaPlayerClient here should be private inheritance. +// But it can't be until the Chromium WebMediaPlayerClientImpl class is fixed so it +// no longer depends on typecasting a MediaPlayerClient to an HTMLMediaElement. + +class HTMLMediaElement : public HTMLElement, public MediaPlayerClient, private MediaCanStartListener, public ActiveDOMObject, public MediaControllerInterface +#if ENABLE(VIDEO_TRACK) + , private TextTrackClient +#endif +{ +public: + MediaPlayer* player() const { return m_player.get(); } + + virtual bool isVideo() const = 0; + virtual bool hasVideo() const { return false; } + virtual bool hasAudio() const; + + void rewind(float timeDelta); + void returnToRealtime(); + + // Eventually overloaded in HTMLVideoElement + virtual bool supportsFullscreen() const { return false; }; + + virtual bool supportsSave() const; + virtual bool supportsScanning() const; + + PlatformMedia platformMedia() const; +#if USE(ACCELERATED_COMPOSITING) + PlatformLayer* platformLayer() const; +#endif + + enum LoadType { + MediaResource = 1 << 0, + TextTrackResource = 1 << 1 + }; + void scheduleLoad(LoadType); + + MediaPlayer::MovieLoadType movieLoadType() const; + + bool inActiveDocument() const { return m_inActiveDocument; } + +// DOM API +// error state + PassRefPtr<MediaError> error() const; + +// network state + void setSrc(const String&); + const KURL& currentSrc() const { return m_currentSrc; } + + enum NetworkState { NETWORK_EMPTY, NETWORK_IDLE, NETWORK_LOADING, NETWORK_NO_SOURCE }; + NetworkState networkState() const; + + String preload() const; + void setPreload(const String&); + + PassRefPtr<TimeRanges> buffered() const; + void load(ExceptionCode&); + String canPlayType(const String& mimeType) const; + +// ready state + ReadyState readyState() const; + bool seeking() const; + +// playback state + float currentTime() const; + void setCurrentTime(float, ExceptionCode&); + double initialTime() const; + float startTime() const; + float duration() const; + bool paused() const; + float defaultPlaybackRate() const; + void setDefaultPlaybackRate(float); + float playbackRate() const; + void setPlaybackRate(float); + void updatePlaybackRate(); + bool webkitPreservesPitch() const; + void setWebkitPreservesPitch(bool); + PassRefPtr<TimeRanges> played(); + PassRefPtr<TimeRanges> seekable() const; + bool ended() const; + bool autoplay() const; + void setAutoplay(bool b); + bool loop() const; + void setLoop(bool b); + void play(); + void pause(); + +// captions + bool webkitHasClosedCaptions() const; + bool webkitClosedCaptionsVisible() const; + void setWebkitClosedCaptionsVisible(bool); + +#if ENABLE(MEDIA_STATISTICS) +// Statistics + unsigned webkitAudioDecodedByteCount() const; + unsigned webkitVideoDecodedByteCount() const; +#endif + +#if ENABLE(MEDIA_SOURCE) +// Media Source. + const KURL& webkitMediaSourceURL() const { return m_mediaSourceURL; } + void webkitSourceAppend(PassRefPtr<Uint8Array> data, ExceptionCode&); + enum EndOfStreamStatus { EOS_NO_ERROR, EOS_NETWORK_ERR, EOS_DECODE_ERR }; + void webkitSourceEndOfStream(unsigned short, ExceptionCode&); + enum SourceState { SOURCE_CLOSED, SOURCE_OPEN, SOURCE_ENDED }; + SourceState webkitSourceState() const; + void setSourceState(SourceState); +#endif + +// controls + bool controls() const; + void setControls(bool); + float volume() const; + void setVolume(float, ExceptionCode&); + bool muted() const; + void setMuted(bool); + + void togglePlayState(); + void beginScrubbing(); + void endScrubbing(); + + bool canPlay() const; + + float percentLoaded() const; + +#if ENABLE(VIDEO_TRACK) + PassRefPtr<TextTrack> addTrack(const String& kind, const String& label, const String& language, ExceptionCode&); + PassRefPtr<TextTrack> addTrack(const String& kind, const String& label, ExceptionCode& ec) { return addTrack(kind, label, emptyString(), ec); } + PassRefPtr<TextTrack> addTrack(const String& kind, ExceptionCode& ec) { return addTrack(kind, emptyString(), emptyString(), ec); } + + TextTrackList* textTracks(); + CueList currentlyActiveCues() const { return m_currentlyActiveCues; } + + virtual void trackWasAdded(HTMLTrackElement*); + virtual void trackWillBeRemoved(HTMLTrackElement*); + + void configureTextTrack(HTMLTrackElement*); + void configureTextTracks(); + bool textTracksAreReady() const; + void configureTextTrackDisplay(); + + // TextTrackClient + virtual void textTrackReadyStateChanged(TextTrack*); + virtual void textTrackKindChanged(TextTrack*); + virtual void textTrackModeChanged(TextTrack*); + virtual void textTrackAddCues(TextTrack*, const TextTrackCueList*); + virtual void textTrackRemoveCues(TextTrack*, const TextTrackCueList*); + virtual void textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue>); + virtual void textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue>); +#endif + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + void allocateMediaPlayerIfNecessary(); + void setNeedWidgetUpdate(bool needWidgetUpdate) { m_needWidgetUpdate = needWidgetUpdate; } + void deliverNotification(MediaPlayerProxyNotificationType notification); + void setMediaPlayerProxy(WebMediaPlayerProxy* proxy); + void getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values); + void createMediaPlayerProxy(); + void updateWidget(PluginCreationOption); +#endif + + bool hasSingleSecurityOrigin() const { return !m_player || m_player->hasSingleSecurityOrigin(); } + + bool isFullscreen() const; + void enterFullscreen(); + void exitFullscreen(); + + bool hasClosedCaptions() const; + bool closedCaptionsVisible() const; + void setClosedCaptionsVisible(bool); + + MediaControls* mediaControls(); + + void sourceWillBeRemoved(HTMLSourceElement*); + void sourceWasAdded(HTMLSourceElement*); + + void privateBrowsingStateDidChange(); + + // Media cache management. + static void getSitesInMediaCache(Vector<String>&); + static void clearMediaCache(); + static void clearMediaCacheForSite(const String&); + + bool isPlaying() const { return m_playing; } + + virtual bool hasPendingActivity() const; + +#if ENABLE(WEB_AUDIO) + MediaElementAudioSourceNode* audioSourceNode() { return m_audioSourceNode; } + void setAudioSourceNode(MediaElementAudioSourceNode*); + + AudioSourceProvider* audioSourceProvider(); +#endif + + enum InvalidURLAction { DoNothing, Complain }; + bool isSafeToLoadURL(const KURL&, InvalidURLAction); + + const String& mediaGroup() const; + void setMediaGroup(const String&); + + MediaController* controller() const; + void setController(PassRefPtr<MediaController>); + +protected: + HTMLMediaElement(const QualifiedName&, Document*, bool); + virtual ~HTMLMediaElement(); + + virtual void parseMappedAttribute(Attribute*); + virtual void finishParsingChildren(); + virtual bool isURLAttribute(Attribute*) const; + virtual void attach(); + + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + enum DisplayMode { Unknown, None, Poster, PosterWaitingForVideo, Video }; + DisplayMode displayMode() const { return m_displayMode; } + virtual void setDisplayMode(DisplayMode mode) { m_displayMode = mode; } + + virtual bool isMediaElement() const { return true; } + + // Restrictions to change default behaviors. + enum BehaviorRestrictionFlags { + NoRestrictions = 0, + RequireUserGestureForLoadRestriction = 1 << 0, + RequireUserGestureForRateChangeRestriction = 1 << 1, + RequireUserGestureForFullscreenRestriction = 1 << 2, + RequirePageConsentToLoadMediaRestriction = 1 << 3, + }; + typedef unsigned BehaviorRestrictions; + + bool userGestureRequiredForLoad() const { return m_restrictions & RequireUserGestureForLoadRestriction; } + bool userGestureRequiredForRateChange() const { return m_restrictions & RequireUserGestureForRateChangeRestriction; } + bool userGestureRequiredForFullscreen() const { return m_restrictions & RequireUserGestureForFullscreenRestriction; } + bool pageConsentRequiredForLoad() const { return m_restrictions & RequirePageConsentToLoadMediaRestriction; } + + void addBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions |= restriction; } + void removeBehaviorRestriction(BehaviorRestrictions restriction) { m_restrictions &= ~restriction; } + +private: + void createMediaPlayer(); + + virtual bool supportsFocus() const; + virtual void attributeChanged(Attribute*, bool preserveDecls); + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void didRecalcStyle(StyleChange); + + virtual void defaultEventHandler(Event*); + + virtual void didBecomeFullscreenElement(); + virtual void willStopBeingFullscreenElement(); + + // ActiveDOMObject functions. + virtual bool canSuspend() const; + virtual void suspend(ReasonForSuspension); + virtual void resume(); + virtual void stop(); + + virtual void mediaVolumeDidChange(); + + virtual void updateDisplayState() { } + + void setReadyState(MediaPlayer::ReadyState); + void setNetworkState(MediaPlayer::NetworkState); + + virtual Document* mediaPlayerOwningDocument(); + virtual void mediaPlayerNetworkStateChanged(MediaPlayer*); + virtual void mediaPlayerReadyStateChanged(MediaPlayer*); + virtual void mediaPlayerTimeChanged(MediaPlayer*); + virtual void mediaPlayerVolumeChanged(MediaPlayer*); + virtual void mediaPlayerMuteChanged(MediaPlayer*); + virtual void mediaPlayerDurationChanged(MediaPlayer*); + virtual void mediaPlayerRateChanged(MediaPlayer*); + virtual void mediaPlayerPlaybackStateChanged(MediaPlayer*); + virtual void mediaPlayerSawUnsupportedTracks(MediaPlayer*); + virtual void mediaPlayerRepaint(MediaPlayer*); + virtual void mediaPlayerSizeChanged(MediaPlayer*); +#if USE(ACCELERATED_COMPOSITING) + virtual bool mediaPlayerRenderingCanBeAccelerated(MediaPlayer*); + virtual void mediaPlayerRenderingModeChanged(MediaPlayer*); +#endif + virtual void mediaPlayerEngineUpdated(MediaPlayer*); + + virtual void mediaPlayerFirstVideoFrameAvailable(MediaPlayer*); + virtual void mediaPlayerCharacteristicChanged(MediaPlayer*); + +#if ENABLE(MEDIA_SOURCE) + virtual void mediaPlayerSourceOpened(); + virtual String mediaPlayerSourceURL() const; +#endif + + void loadTimerFired(Timer<HTMLMediaElement>*); + void asyncEventTimerFired(Timer<HTMLMediaElement>*); + void progressEventTimerFired(Timer<HTMLMediaElement>*); + void playbackProgressTimerFired(Timer<HTMLMediaElement>*); + void startPlaybackProgressTimer(); + void startProgressEventTimer(); + void stopPeriodicTimers(); + + void seek(float time, ExceptionCode&); + void finishSeek(); + void checkIfSeekNeeded(); + void addPlayedRange(float start, float end); + + void scheduleTimeupdateEvent(bool periodicEvent); + void scheduleEvent(const AtomicString& eventName); + + // loading + void selectMediaResource(); + void loadResource(const KURL&, ContentType&); + void scheduleNextSourceChild(); + void loadNextSourceChild(); + void userCancelledLoad(); + bool havePotentialSourceChild(); + void noneSupported(); + void mediaEngineError(PassRefPtr<MediaError> err); + void cancelPendingEventsAndCallbacks(); + void waitForSourceChange(); + void prepareToPlay(); + + KURL selectNextSourceChild(ContentType*, InvalidURLAction); + void mediaLoadingFailed(MediaPlayer::NetworkState); + +#if ENABLE(VIDEO_TRACK) + void updateActiveTextTrackCues(float); + bool userIsInterestedInThisLanguage(const String&) const; + bool userIsInterestedInThisTrack(HTMLTrackElement*) const; + HTMLTrackElement* showingTrackWithSameKind(HTMLTrackElement*) const; +#endif + + // These "internal" functions do not check user gesture restrictions. + void loadInternal(); + void playInternal(); + void pauseInternal(); + + void prepareForLoad(); + void allowVideoRendering(); + + bool processingMediaPlayerCallback() const { return m_processingMediaPlayerCallback > 0; } + void beginProcessingMediaPlayerCallback() { ++m_processingMediaPlayerCallback; } + void endProcessingMediaPlayerCallback() { ASSERT(m_processingMediaPlayerCallback); --m_processingMediaPlayerCallback; } + + void updateVolume(); + void updatePlayState(); + bool potentiallyPlaying() const; + bool endedPlayback() const; + bool stoppedDueToErrors() const; + bool pausedForUserInteraction() const; + bool couldPlayIfEnoughData() const; + + float minTimeSeekable() const; + float maxTimeSeekable() const; + + // Pauses playback without changing any states or generating events + void setPausedInternal(bool); + + void setPlaybackRateInternal(float); + + virtual void mediaCanStart(); + + void setShouldDelayLoadEvent(bool); + void invalidateCachedTime(); + void refreshCachedTime() const; + + bool hasMediaControls(); + bool createMediaControls(); + void configureMediaControls(); + + void prepareMediaFragmentURI(); + void applyMediaFragmentURI(); + + virtual void* preDispatchEventHandler(Event*); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const; + virtual void setItemValueText(const String&, ExceptionCode&); +#endif + + void updateMediaController(); + bool isBlocked() const; + bool isBlockedOnMediaController() const; + bool hasCurrentSrc() const { return !m_currentSrc.isEmpty(); } + bool isLiveStream() const { return movieLoadType() == MediaPlayer::LiveStream; } + bool isAutoplaying() const { return m_autoplaying; } + + Timer<HTMLMediaElement> m_loadTimer; + Timer<HTMLMediaElement> m_asyncEventTimer; + Timer<HTMLMediaElement> m_progressEventTimer; + Timer<HTMLMediaElement> m_playbackProgressTimer; + Vector<RefPtr<Event> > m_pendingEvents; + RefPtr<TimeRanges> m_playedTimeRanges; + + float m_playbackRate; + float m_defaultPlaybackRate; + bool m_webkitPreservesPitch; + NetworkState m_networkState; + ReadyState m_readyState; + ReadyState m_readyStateMaximum; + KURL m_currentSrc; + + RefPtr<MediaError> m_error; + + float m_volume; + float m_lastSeekTime; + + unsigned m_previousProgress; + double m_previousProgressTime; + + // The last time a timeupdate event was sent (wall clock). + double m_lastTimeUpdateEventWallTime; + + // The last time a timeupdate event was sent in movie time. + float m_lastTimeUpdateEventMovieTime; + + // Loading state. + enum LoadState { WaitingForSource, LoadingFromSrcAttr, LoadingFromSourceElement }; + LoadState m_loadState; + HTMLSourceElement* m_currentSourceNode; + Node* m_nextChildNodeToConsider; + Node* sourceChildEndOfListValue() { return static_cast<Node*>(this); } + + OwnPtr<MediaPlayer> m_player; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + RefPtr<Widget> m_proxyWidget; +#endif + + BehaviorRestrictions m_restrictions; + + MediaPlayer::Preload m_preload; + + DisplayMode m_displayMode; + + // Counter incremented while processing a callback from the media player, so we can avoid + // calling the media engine recursively. + int m_processingMediaPlayerCallback; + +#if ENABLE(MEDIA_SOURCE) + KURL m_mediaSourceURL; + SourceState m_sourceState; +#endif + + mutable float m_cachedTime; + mutable double m_cachedTimeWallClockUpdateTime; + mutable double m_minimumWallClockTimeToCacheMediaTime; + + double m_fragmentStartTime; + double m_fragmentEndTime; + + typedef unsigned PendingLoadFlags; + PendingLoadFlags m_pendingLoadFlags; + + bool m_playing : 1; + bool m_isWaitingUntilMediaCanStart : 1; + bool m_shouldDelayLoadEvent : 1; + bool m_haveFiredLoadedData : 1; + bool m_inActiveDocument : 1; + bool m_autoplaying : 1; + bool m_muted : 1; + bool m_paused : 1; + bool m_seeking : 1; + + // data has not been loaded since sending a "stalled" event + bool m_sentStalledEvent : 1; + + // time has not changed since sending an "ended" event + bool m_sentEndEvent : 1; + + bool m_pausedInternal : 1; + + // Not all media engines provide enough information about a file to be able to + // support progress events so setting m_sendProgressEvents disables them + bool m_sendProgressEvents : 1; + + bool m_isFullscreen : 1; + bool m_closedCaptionsVisible : 1; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + bool m_needWidgetUpdate : 1; +#endif + + bool m_dispatchingCanPlayEvent : 1; + bool m_loadInitiatedByUserGesture : 1; + bool m_completelyLoaded : 1; + bool m_havePreparedToPlay : 1; + bool m_parsingInProgress : 1; + +#if ENABLE(VIDEO_TRACK) + bool m_tracksAreReady : 1; + bool m_haveVisibleTextTrack : 1; + + RefPtr<TextTrackList> m_textTracks; + Vector<RefPtr<TextTrack> > m_textTracksWhenResourceSelectionBegan; + CueIntervalTree m_cueTree; + CueList m_currentlyActiveCues; +#endif + +#if ENABLE(WEB_AUDIO) + // This is a weak reference, since m_audioSourceNode holds a reference to us. + // The value is set just after the MediaElementAudioSourceNode is created. + // The value is cleared in MediaElementAudioSourceNode::~MediaElementAudioSourceNode(). + MediaElementAudioSourceNode* m_audioSourceNode; +#endif + + String m_mediaGroup; + friend class MediaController; + RefPtr<MediaController> m_mediaController; + +#if PLATFORM(MAC) + OwnPtr<DisplaySleepDisabler> m_sleepDisabler; +#endif +}; + +#if ENABLE(VIDEO_TRACK) +#ifndef NDEBUG +// Template specializations required by PodIntervalTree in debug mode. +template <> +struct ValueToString<double> { + static String string(const double value) + { + return String::number(value); + } +}; + +template <> +struct ValueToString<TextTrackCue*> { + static String string(TextTrackCue* const& cue) + { + return String::format("%p id=%s interval=%f-->%f cue=%s)", cue, cue->id().utf8().data(), cue->startTime(), cue->endTime(), cue->getCueAsSource().utf8().data()); + } +}; +#endif +#endif + +} //namespace + +#endif +#endif diff --git a/Source/WebCore/html/HTMLMediaElement.idl b/Source/WebCore/html/HTMLMediaElement.idl new file mode 100644 index 000000000..2dd55d562 --- /dev/null +++ b/Source/WebCore/html/HTMLMediaElement.idl @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2007, 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=VIDEO, + GenerateNativeConverter + ] HTMLMediaElement : HTMLElement { + + // error state + readonly attribute MediaError error; + + // network state + attribute [Reflect, URL] DOMString src; + readonly attribute [URL] DOMString currentSrc; + + const unsigned short NETWORK_EMPTY = 0; + const unsigned short NETWORK_IDLE = 1; + const unsigned short NETWORK_LOADING = 2; + const unsigned short NETWORK_NO_SOURCE = 3; + readonly attribute unsigned short networkState; + attribute DOMString preload; + + readonly attribute TimeRanges buffered; + void load() + raises (DOMException); + DOMString canPlayType(in [Optional=CallWithDefaultValue] DOMString type); + + // ready state + const unsigned short HAVE_NOTHING = 0; + const unsigned short HAVE_METADATA = 1; + const unsigned short HAVE_CURRENT_DATA = 2; + const unsigned short HAVE_FUTURE_DATA = 3; + const unsigned short HAVE_ENOUGH_DATA = 4; + readonly attribute unsigned short readyState; + readonly attribute boolean seeking; + + // playback state + attribute float currentTime + setter raises (DOMException); + readonly attribute double initialTime; + readonly attribute float startTime; + readonly attribute float duration; + readonly attribute boolean paused; + attribute float defaultPlaybackRate; + attribute float playbackRate; + readonly attribute TimeRanges played; + readonly attribute TimeRanges seekable; + readonly attribute boolean ended; + attribute [Reflect] boolean autoplay; + attribute [Reflect] boolean loop; + void play(); + void pause(); + + // controls + attribute boolean controls; + attribute float volume + setter raises (DOMException); + attribute boolean muted; + attribute [Reflect=muted] boolean defaultMuted; + + // WebKit extensions + attribute boolean webkitPreservesPitch; + + readonly attribute boolean webkitHasClosedCaptions; + attribute boolean webkitClosedCaptionsVisible; + + // The number of bytes consumed by the media decoder. + readonly attribute [Conditional=MEDIA_STATISTICS] unsigned long webkitAudioDecodedByteCount; + readonly attribute [Conditional=MEDIA_STATISTICS] unsigned long webkitVideoDecodedByteCount; + +#if defined(ENABLE_MEDIA_SOURCE) && ENABLE_MEDIA_SOURCE + // URL passed to src attribute to enable the media source logic. + readonly attribute [EnabledAtRuntime=webkitMediaSource, URL] DOMString webkitMediaSourceURL; + + // Appends media to to the source. + [EnabledAtRuntime=webkitMediaSource] void webkitSourceAppend(in Uint8Array data) raises (DOMException); + + // Signals the end of stream. + const [EnabledAtRuntime=webkitMediaSource] unsigned short EOS_NO_ERROR = 0; // End of stream reached w/o error. + const [EnabledAtRuntime=webkitMediaSource] unsigned short EOS_NETWORK_ERR = 1; // A network error triggered end of stream. + const [EnabledAtRuntime=webkitMediaSource] unsigned short EOS_DECODE_ERR = 2; // A decode error triggered end of stream. + [EnabledAtRuntime=webkitMediaSource] void webkitSourceEndOfStream(in unsigned short status) raises (DOMException); + + // Indicates the current state of the media source. + const [EnabledAtRuntime=webkitMediaSource] unsigned short SOURCE_CLOSED = 0; + const [EnabledAtRuntime=webkitMediaSource] unsigned short SOURCE_OPEN = 1; + const [EnabledAtRuntime=webkitMediaSource] unsigned short SOURCE_ENDED = 2; + readonly attribute [EnabledAtRuntime=webkitMediaSource] unsigned short webkitSourceState; +#endif + +#if defined(ENABLE_VIDEO_TRACK) && ENABLE_VIDEO_TRACK + [EnabledAtRuntime=webkitVideoTrack] TextTrack addTrack(in DOMString kind, in [Optional] DOMString label, in [Optional] DOMString language) + raises (DOMException); + readonly attribute [EnabledAtRuntime=webkitVideoTrack] TextTrackList textTracks; +#endif + + attribute [Reflect, ConvertNullToNullString, ConvertNullStringToNull] DOMString mediaGroup; + attribute [CustomSetter] MediaController controller; +}; +} diff --git a/Source/WebCore/html/HTMLMenuElement.cpp b/Source/WebCore/html/HTMLMenuElement.cpp new file mode 100644 index 000000000..9f706ef54 --- /dev/null +++ b/Source/WebCore/html/HTMLMenuElement.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 "HTMLMenuElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLMenuElement::HTMLMenuElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(menuTag)); +} + +PassRefPtr<HTMLMenuElement> HTMLMenuElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLMenuElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLMenuElement.h b/Source/WebCore/html/HTMLMenuElement.h new file mode 100644 index 000000000..6b588ecb1 --- /dev/null +++ b/Source/WebCore/html/HTMLMenuElement.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLMenuElement_h +#define HTMLMenuElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLMenuElement : public HTMLElement { +public: + static PassRefPtr<HTMLMenuElement> create(const QualifiedName&, Document*); + +private: + HTMLMenuElement(const QualifiedName&, Document*); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLMenuElement.idl b/Source/WebCore/html/HTMLMenuElement.idl new file mode 100644 index 000000000..ff14754c0 --- /dev/null +++ b/Source/WebCore/html/HTMLMenuElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLMenuElement : HTMLElement { + attribute [Reflect] boolean compact; + }; + +} diff --git a/Source/WebCore/html/HTMLMetaElement.cpp b/Source/WebCore/html/HTMLMetaElement.cpp new file mode 100644 index 000000000..c27b9ba23 --- /dev/null +++ b/Source/WebCore/html/HTMLMetaElement.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLMetaElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLMetaElement::HTMLMetaElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(metaTag)); +} + +PassRefPtr<HTMLMetaElement> HTMLMetaElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLMetaElement(tagName, document)); +} + +void HTMLMetaElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == http_equivAttr) + process(); + else if (attr->name() == contentAttr) + process(); + else if (attr->name() == nameAttr) { + // Do nothing + } else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLMetaElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + process(); +} + +void HTMLMetaElement::process() +{ + if (!inDocument()) + return; + + const AtomicString& contentValue = fastGetAttribute(contentAttr); + if (contentValue.isNull()) + return; + + if (equalIgnoringCase(name(), "viewport")) + document()->processViewport(contentValue); + + if (equalIgnoringCase(name(), "referrer")) + document()->processReferrerPolicy(contentValue); + + // Get the document to process the tag, but only if we're actually part of DOM tree (changing a meta tag while + // it's not in the tree shouldn't have any effect on the document) + const AtomicString& httpEquivValue = fastGetAttribute(http_equivAttr); + if (!httpEquivValue.isNull()) + document()->processHttpEquiv(httpEquivValue, contentValue); +} + +String HTMLMetaElement::content() const +{ + return getAttribute(contentAttr); +} + +String HTMLMetaElement::httpEquiv() const +{ + return getAttribute(http_equivAttr); +} + +String HTMLMetaElement::name() const +{ + return getAttribute(nameAttr); +} + +#if ENABLE(MICRODATA) +String HTMLMetaElement::itemValueText() const +{ + return getAttribute(contentAttr); +} + +void HTMLMetaElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(contentAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLMetaElement.h b/Source/WebCore/html/HTMLMetaElement.h new file mode 100644 index 000000000..df4d7e1f9 --- /dev/null +++ b/Source/WebCore/html/HTMLMetaElement.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 HTMLMetaElement_h +#define HTMLMetaElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLMetaElement : public HTMLElement { +public: + static PassRefPtr<HTMLMetaElement> create(const QualifiedName&, Document*); + + String content() const; + String httpEquiv() const; + String name() const; + +private: + HTMLMetaElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + virtual void insertedIntoDocument(); + + void process(); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLMetaElement.idl b/Source/WebCore/html/HTMLMetaElement.idl new file mode 100644 index 000000000..f4ffb2d42 --- /dev/null +++ b/Source/WebCore/html/HTMLMetaElement.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLMetaElement : HTMLElement { + attribute [Reflect] DOMString content; + attribute [Reflect=http_equiv] DOMString httpEquiv; + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString scheme; + }; + +} diff --git a/Source/WebCore/html/HTMLMeterElement.cpp b/Source/WebCore/html/HTMLMeterElement.cpp new file mode 100644 index 000000000..011a669e0 --- /dev/null +++ b/Source/WebCore/html/HTMLMeterElement.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * 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" +#if ENABLE(METER_TAG) +#include "HTMLMeterElement.h" + +#include "Attribute.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FormDataList.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "MeterShadowElement.h" +#include "RenderMeter.h" +#include "ShadowRoot.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +HTMLMeterElement::HTMLMeterElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) +{ + ASSERT(hasTagName(meterTag)); +} + +HTMLMeterElement::~HTMLMeterElement() +{ +} + +PassRefPtr<HTMLMeterElement> HTMLMeterElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + RefPtr<HTMLMeterElement> meter = adoptRef(new HTMLMeterElement(tagName, document, form)); + meter->createShadowSubtree(); + return meter; +} + +RenderObject* HTMLMeterElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderMeter(this); +} + +const AtomicString& HTMLMeterElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, meter, ("meter")); + return meter; +} + +bool HTMLMeterElement::supportsFocus() const +{ + return Node::supportsFocus() && !disabled(); +} + +void HTMLMeterElement::parseMappedAttribute(Attribute* attribute) +{ + if (attribute->name() == valueAttr || attribute->name() == minAttr || attribute->name() == maxAttr || attribute->name() == lowAttr || attribute->name() == highAttr || attribute->name() == optimumAttr) + didElementStateChange(); + else + HTMLFormControlElement::parseMappedAttribute(attribute); +} + +void HTMLMeterElement::attach() +{ + HTMLFormControlElement::attach(); + didElementStateChange(); +} + +double HTMLMeterElement::min() const +{ + double min = 0; + parseToDoubleForNumberType(getAttribute(minAttr), &min); + return min; +} + +void HTMLMeterElement::setMin(double min, ExceptionCode& ec) +{ + if (!isfinite(min)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(minAttr, String::number(min)); +} + +double HTMLMeterElement::max() const +{ + double max = std::max(1.0, min()); + parseToDoubleForNumberType(getAttribute(maxAttr), &max); + return std::max(max, min()); +} + +void HTMLMeterElement::setMax(double max, ExceptionCode& ec) +{ + if (!isfinite(max)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(maxAttr, String::number(max)); +} + +double HTMLMeterElement::value() const +{ + double value = 0; + parseToDoubleForNumberType(getAttribute(valueAttr), &value); + return std::min(std::max(value, min()), max()); +} + +void HTMLMeterElement::setValue(double value, ExceptionCode& ec) +{ + if (!isfinite(value)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(valueAttr, String::number(value)); +} + +double HTMLMeterElement::low() const +{ + double low = min(); + parseToDoubleForNumberType(getAttribute(lowAttr), &low); + return std::min(std::max(low, min()), max()); +} + +void HTMLMeterElement::setLow(double low, ExceptionCode& ec) +{ + if (!isfinite(low)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(lowAttr, String::number(low)); +} + +double HTMLMeterElement::high() const +{ + double high = max(); + parseToDoubleForNumberType(getAttribute(highAttr), &high); + return std::min(std::max(high, low()), max()); +} + +void HTMLMeterElement::setHigh(double high, ExceptionCode& ec) +{ + if (!isfinite(high)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(highAttr, String::number(high)); +} + +double HTMLMeterElement::optimum() const +{ + double optimum = (max() + min()) / 2; + parseToDoubleForNumberType(getAttribute(optimumAttr), &optimum); + return std::min(std::max(optimum, min()), max()); +} + +void HTMLMeterElement::setOptimum(double optimum, ExceptionCode& ec) +{ + if (!isfinite(optimum)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(optimumAttr, String::number(optimum)); +} + +HTMLMeterElement::GaugeRegion HTMLMeterElement::gaugeRegion() const +{ + double lowValue = low(); + double highValue = high(); + double theValue = value(); + double optimumValue = optimum(); + + if (optimumValue < lowValue) { + // The optimum range stays under low + if (theValue <= lowValue) + return GaugeRegionOptimum; + if (theValue <= highValue) + return GaugeRegionSuboptimal; + return GaugeRegionEvenLessGood; + } + + if (highValue < optimumValue) { + // The optimum range stays over high + if (highValue <= theValue) + return GaugeRegionOptimum; + if (lowValue <= theValue) + return GaugeRegionSuboptimal; + return GaugeRegionEvenLessGood; + } + + // The optimum range stays between high and low. + // According to the standard, <meter> never show GaugeRegionEvenLessGood in this case + // because the value is never less or greater than min or max. + if (lowValue <= theValue && theValue <= highValue) + return GaugeRegionOptimum; + return GaugeRegionSuboptimal; +} + +double HTMLMeterElement::valueRatio() const +{ + double min = this->min(); + double max = this->max(); + double value = this->value(); + + if (max <= min) + return 0; + return (value - min) / (max - min); +} + +void HTMLMeterElement::didElementStateChange() +{ + m_value->setWidthPercentage(valueRatio()*100); + if (RenderObject* render = renderer()) + render->updateFromElement(); +} + +void HTMLMeterElement::createShadowSubtree() +{ + RefPtr<MeterBarElement> bar = MeterBarElement::create(document()); + m_value = MeterValueElement::create(document()); + ExceptionCode ec = 0; + bar->appendChild(m_value, ec); + ensureShadowRoot()->appendChild(bar, ec); +} + +} // namespace +#endif diff --git a/Source/WebCore/html/HTMLMeterElement.h b/Source/WebCore/html/HTMLMeterElement.h new file mode 100644 index 000000000..c1c02cd49 --- /dev/null +++ b/Source/WebCore/html/HTMLMeterElement.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * 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 HTMLMeterElement_h +#define HTMLMeterElement_h + +#if ENABLE(METER_TAG) +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class MeterValueElement; + +class HTMLMeterElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLMeterElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + enum GaugeRegion { + GaugeRegionOptimum, + GaugeRegionSuboptimal, + GaugeRegionEvenLessGood + }; + + double min() const; + void setMin(double, ExceptionCode&); + + double max() const; + void setMax(double, ExceptionCode&); + + double value() const; + void setValue(double, ExceptionCode&); + + double low() const; + void setLow(double, ExceptionCode&); + + double high() const; + void setHigh(double, ExceptionCode&); + + double optimum() const; + void setOptimum(double, ExceptionCode&); + + double valueRatio() const; + GaugeRegion gaugeRegion() const; + + bool canContainRangeEndPoint() const { return false; } + +private: + HTMLMeterElement(const QualifiedName&, Document*, HTMLFormElement*); + virtual ~HTMLMeterElement(); + + virtual bool supportsFocus() const; + + virtual bool recalcWillValidate() const { return false; } + virtual const AtomicString& formControlType() const; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void parseMappedAttribute(Attribute*); + virtual void attach(); + + void didElementStateChange(); + void createShadowSubtree(); + + RefPtr<MeterValueElement> m_value; +}; + +} // namespace + +#endif +#endif diff --git a/Source/WebCore/html/HTMLMeterElement.idl b/Source/WebCore/html/HTMLMeterElement.idl new file mode 100644 index 000000000..339868889 --- /dev/null +++ b/Source/WebCore/html/HTMLMeterElement.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * 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. + */ + +module html { + interface [ + Conditional=METER_TAG + ] HTMLMeterElement : HTMLElement { + attribute double value + setter raises(DOMException); + attribute double min + setter raises(DOMException); + attribute double max + setter raises(DOMException); + attribute double low + setter raises(DOMException); + attribute double high + setter raises(DOMException); + attribute double optimum + setter raises(DOMException); + readonly attribute HTMLFormElement form; + readonly attribute NodeList labels; + }; +} + diff --git a/Source/WebCore/html/HTMLModElement.cpp b/Source/WebCore/html/HTMLModElement.cpp new file mode 100644 index 000000000..9d0961c11 --- /dev/null +++ b/Source/WebCore/html/HTMLModElement.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2003, 2006, 2010 Apple 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 "HTMLModElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLModElement::HTMLModElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ +} + +PassRefPtr<HTMLModElement> HTMLModElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLModElement(tagName, document)); +} + +bool HTMLModElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == citeAttr || HTMLElement::isURLAttribute(attribute); +} + +} diff --git a/Source/WebCore/html/HTMLModElement.h b/Source/WebCore/html/HTMLModElement.h new file mode 100644 index 000000000..cdb6dceaa --- /dev/null +++ b/Source/WebCore/html/HTMLModElement.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2010 Apple 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 HTMLModElement_h +#define HTMLModElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLModElement : public HTMLElement { +public: + static PassRefPtr<HTMLModElement> create(const QualifiedName&, Document*); + +private: + HTMLModElement(const QualifiedName&, Document*); + + virtual bool isURLAttribute(Attribute*) const; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLModElement.idl b/Source/WebCore/html/HTMLModElement.idl new file mode 100644 index 000000000..ad8281c45 --- /dev/null +++ b/Source/WebCore/html/HTMLModElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLModElement : HTMLElement { + attribute [Reflect, URL] DOMString cite; + attribute [Reflect] DOMString dateTime; + }; + +} diff --git a/Source/WebCore/html/HTMLNameCollection.cpp b/Source/WebCore/html/HTMLNameCollection.cpp new file mode 100644 index 000000000..ad73d3201 --- /dev/null +++ b/Source/WebCore/html/HTMLNameCollection.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2011, 2012 Apple 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 "HTMLNameCollection.h" + +#include "Element.h" +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLNameCollection::HTMLNameCollection(Document* document, CollectionType type, const AtomicString& name) + : HTMLCollection(document, type) + , m_name(name) +{ +} + +Element* HTMLNameCollection::itemAfter(Element* previous) const +{ + if (!base()) + return 0; + + ASSERT(previous != base()); + + Node* current; + if (!previous) + current = base()->firstChild(); + else + current = previous->traverseNextNode(base()); + + for (; current; current = current->traverseNextNode(base())) { + if (!current->isElementNode()) + continue; + Element* e = static_cast<Element*>(current); + switch (type()) { + case WindowNamedItems: + // find only images, forms, applets, embeds and objects by name, + // but anything by id + if (e->hasTagName(imgTag) || + e->hasTagName(formTag) || + e->hasTagName(appletTag) || + e->hasTagName(embedTag) || + e->hasTagName(objectTag)) + if (e->getAttribute(nameAttr) == m_name) + return e; + if (e->getIdAttribute() == m_name) + return e; + break; + case DocumentNamedItems: + // find images, forms, applets, embeds, objects and iframes by name, + // applets and object by id, and images by id but only if they have + // a name attribute (this very strange rule matches IE) + if (e->hasTagName(formTag) || e->hasTagName(embedTag) || e->hasTagName(iframeTag)) { + if (e->getAttribute(nameAttr) == m_name) + return e; + } else if (e->hasTagName(appletTag)) { + if (e->getAttribute(nameAttr) == m_name || e->getIdAttribute() == m_name) + return e; + } else if (e->hasTagName(objectTag)) { + if ((e->getAttribute(nameAttr) == m_name || e->getIdAttribute() == m_name) + && static_cast<HTMLObjectElement*>(e)->isDocNamedItem()) + return e; + } else if (e->hasTagName(imgTag)) { + if (e->getAttribute(nameAttr) == m_name || (e->getIdAttribute() == m_name && e->hasAttribute(nameAttr))) + return e; + } + break; + default: + ASSERT_NOT_REACHED(); + } + } + + return 0; +} + +} diff --git a/Source/WebCore/html/HTMLNameCollection.h b/Source/WebCore/html/HTMLNameCollection.h new file mode 100644 index 000000000..c4087cbd4 --- /dev/null +++ b/Source/WebCore/html/HTMLNameCollection.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple 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 HTMLNameCollection_h +#define HTMLNameCollection_h + +#include "HTMLCollection.h" + +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class Document; + +class HTMLNameCollection : public HTMLCollection { +public: + static PassRefPtr<HTMLNameCollection> create(Document* document, CollectionType type, const AtomicString& name) + { + return adoptRef(new HTMLNameCollection(document, type, name)); + } + +private: + HTMLNameCollection(Document*, CollectionType, const AtomicString& name); + + virtual Element* itemAfter(Element*) const OVERRIDE; + + AtomicString m_name; +}; + +} + +#endif diff --git a/Source/WebCore/html/HTMLOListElement.cpp b/Source/WebCore/html/HTMLOListElement.cpp new file mode 100644 index 000000000..d90a2f772 --- /dev/null +++ b/Source/WebCore/html/HTMLOListElement.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 "HTMLOListElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" +#include "RenderListItem.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLOListElement::HTMLOListElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_start(0xBADBEEF) + , m_itemCount(0) + , m_hasExplicitStart(false) + , m_isReversed(false) + , m_shouldRecalculateItemCount(false) +{ + ASSERT(hasTagName(olTag)); +} + +PassRefPtr<HTMLOListElement> HTMLOListElement::create(Document* document) +{ + return adoptRef(new HTMLOListElement(olTag, document)); +} + +PassRefPtr<HTMLOListElement> HTMLOListElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLOListElement(tagName, document)); +} + +bool HTMLOListElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == typeAttr) { + result = eListItem; // Share with <li> + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLOListElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == typeAttr) { + if (attr->value() == "a") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueLowerAlpha); + else if (attr->value() == "A") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueUpperAlpha); + else if (attr->value() == "i") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueLowerRoman); + else if (attr->value() == "I") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueUpperRoman); + else if (attr->value() == "1") + addCSSProperty(attr, CSSPropertyListStyleType, CSSValueDecimal); + } else if (attr->name() == startAttr) { + int oldStart = start(); + bool canParse; + int parsedStart = attr->value().toInt(&canParse); + m_hasExplicitStart = canParse; + m_start = canParse ? parsedStart : 0xBADBEEF; + if (oldStart == start()) + return; + updateItemValues(); + } else if (attr->name() == reversedAttr) { + bool reversed = !attr->isNull(); + if (reversed == m_isReversed) + return; + m_isReversed = reversed; + updateItemValues(); + } else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLOListElement::setStart(int start) +{ + setAttribute(startAttr, String::number(start)); +} + +void HTMLOListElement::updateItemValues() +{ + for (RenderListItem* listItem = RenderListItem::nextListItem(renderer()); listItem; listItem = RenderListItem::nextListItem(renderer(), listItem)) + listItem->updateValue(); +} + +void HTMLOListElement::recalculateItemCount() +{ + m_itemCount = 0; + + for (RenderListItem* listItem = RenderListItem::nextListItem(renderer()); listItem; listItem = RenderListItem::nextListItem(renderer(), listItem)) + m_itemCount++; + + m_shouldRecalculateItemCount = false; +} + +} diff --git a/Source/WebCore/html/HTMLOListElement.h b/Source/WebCore/html/HTMLOListElement.h new file mode 100644 index 000000000..8000b28bc --- /dev/null +++ b/Source/WebCore/html/HTMLOListElement.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLOListElement_h +#define HTMLOListElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLOListElement : public HTMLElement { +public: + static PassRefPtr<HTMLOListElement> create(Document*); + static PassRefPtr<HTMLOListElement> create(const QualifiedName&, Document*); + + int start() const { return m_hasExplicitStart ? m_start : (m_isReversed ? itemCount() : 1); } + void setStart(int); + + bool isReversed() const { return m_isReversed; } + + void itemCountChanged() { m_shouldRecalculateItemCount = true; } + +private: + HTMLOListElement(const QualifiedName&, Document*); + + void updateItemValues(); + + unsigned itemCount() const + { + if (m_shouldRecalculateItemCount) + const_cast<HTMLOListElement*>(this)->recalculateItemCount(); + return m_itemCount; + } + + void recalculateItemCount(); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + int m_start; + unsigned m_itemCount; + + bool m_hasExplicitStart : 1; + bool m_isReversed : 1; + bool m_shouldRecalculateItemCount : 1; +}; + + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLOListElement.idl b/Source/WebCore/html/HTMLOListElement.idl new file mode 100644 index 000000000..8d1d3a46f --- /dev/null +++ b/Source/WebCore/html/HTMLOListElement.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLOListElement : HTMLElement { + attribute [Reflect] boolean compact; + attribute long start; + attribute [Reflect] boolean reversed; + attribute [Reflect] DOMString type; + }; + +} diff --git a/Source/WebCore/html/HTMLObjectElement.cpp b/Source/WebCore/html/HTMLObjectElement.cpp new file mode 100644 index 000000000..e9bc36c24 --- /dev/null +++ b/Source/WebCore/html/HTMLObjectElement.cpp @@ -0,0 +1,535 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "HTMLObjectElement.h" + +#include "Attribute.h" +#include "CSSValueKeywords.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLDocument.h" +#include "HTMLFormElement.h" +#include "HTMLImageLoader.h" +#include "HTMLMetaElement.h" +#include "HTMLNames.h" +#include "HTMLParamElement.h" +#include "HTMLParserIdioms.h" +#include "MIMETypeRegistry.h" +#include "NodeList.h" +#include "Page.h" +#include "PluginViewBase.h" +#include "RenderEmbeddedObject.h" +#include "RenderImage.h" +#include "RenderWidget.h" +#include "ScriptEventListener.h" +#include "Settings.h" +#include "Text.h" +#include "Widget.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLObjectElement::HTMLObjectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser) + : HTMLPlugInImageElement(tagName, document, createdByParser, ShouldNotPreferPlugInsForImages) + , FormAssociatedElement(form) + , m_docNamedItem(true) + , m_useFallbackContent(false) +{ + ASSERT(hasTagName(objectTag)); + if (!this->form()) + setForm(findFormAncestor()); + if (this->form()) + this->form()->registerFormElement(this); +} + +inline HTMLObjectElement::~HTMLObjectElement() +{ + if (form()) + form()->removeFormElement(this); +} + +PassRefPtr<HTMLObjectElement> HTMLObjectElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form, bool createdByParser) +{ + return adoptRef(new HTMLObjectElement(tagName, document, form, createdByParser)); +} + +RenderWidget* HTMLObjectElement::renderWidgetForJSBindings() +{ + document()->updateLayoutIgnorePendingStylesheets(); + return renderPart(); // This will return 0 if the renderer is not a RenderPart. +} + +void HTMLObjectElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == formAttr) + formAttributeChanged(); + else if (attr->name() == typeAttr) { + m_serviceType = attr->value().lower(); + size_t pos = m_serviceType.find(";"); + if (pos != notFound) + m_serviceType = m_serviceType.left(pos); + if (renderer()) + setNeedsWidgetUpdate(true); + if (!isImageType() && m_imageLoader) + m_imageLoader.clear(); + } else if (attr->name() == dataAttr) { + m_url = stripLeadingAndTrailingHTMLSpaces(attr->value()); + if (renderer()) { + setNeedsWidgetUpdate(true); + if (isImageType()) { + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(this)); + m_imageLoader->updateFromElementIgnoringPreviousError(); + } + } + } else if (attr->name() == classidAttr) { + m_classId = attr->value(); + if (renderer()) + setNeedsWidgetUpdate(true); + } else if (attr->name() == onloadAttr) + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == onbeforeloadAttr) + setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == borderAttr) + applyBorderAttribute(attr); + else + HTMLPlugInImageElement::parseMappedAttribute(attr); +} + +static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) +{ + // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP + // require "src" attribute). + int srcIndex = -1, dataIndex = -1; + for (unsigned int i = 0; i < paramNames->size(); ++i) { + if (equalIgnoringCase((*paramNames)[i], "src")) + srcIndex = i; + else if (equalIgnoringCase((*paramNames)[i], "data")) + dataIndex = i; + } + + if (srcIndex == -1 && dataIndex != -1) { + paramNames->append("src"); + paramValues->append((*paramValues)[dataIndex]); + } +} + +// FIXME: This function should not deal with url or serviceType! +void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) +{ + HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; + String urlParameter; + + // Scan the PARAM children and store their name/value pairs. + // Get the URL and type from the params if we don't already have them. + for (Node* child = firstChild(); child; child = child->nextSibling()) { + if (!child->hasTagName(paramTag)) + continue; + + HTMLParamElement* p = static_cast<HTMLParamElement*>(child); + String name = p->name(); + if (name.isEmpty()) + continue; + + uniqueParamNames.add(name.impl()); + paramNames.append(p->name()); + paramValues.append(p->value()); + + // FIXME: url adjustment does not belong in this function. + if (url.isEmpty() && urlParameter.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) + urlParameter = stripLeadingAndTrailingHTMLSpaces(p->value()); + // FIXME: serviceType calculation does not belong in this function. + if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { + serviceType = p->value(); + size_t pos = serviceType.find(";"); + if (pos != notFound) + serviceType = serviceType.left(pos); + } + } + + // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag + // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is + // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means + // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, + // else our Java plugin will misinterpret it. [4004531] + String codebase; + if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { + codebase = "codebase"; + uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already + } + + // Turn the attributes of the <object> element into arrays, but don't override <param> values. + NamedNodeMap* attributes = this->attributes(true); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + const AtomicString& name = it->name().localName(); + if (!uniqueParamNames.contains(name.impl())) { + paramNames.append(name.string()); + paramValues.append(it->value().string()); + } + } + } + + mapDataParamToSrc(¶mNames, ¶mValues); + + // HTML5 says that an object resource's URL is specified by the object's data + // attribute, not by a param element. However, for compatibility, allow the + // resource's URL to be given by a param named "src", "movie", "code" or "url" + // if we know that resource points to a plug-in. + if (url.isEmpty() && !urlParameter.isEmpty()) { + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + if (loader->resourceWillUsePlugin(urlParameter, serviceType, shouldPreferPlugInsForImages())) + url = urlParameter; + } +} + + +bool HTMLObjectElement::hasFallbackContent() const +{ + for (Node* child = firstChild(); child; child = child->nextSibling()) { + // Ignore whitespace-only text, and <param> tags, any other content is fallback content. + if (child->isTextNode()) { + if (!static_cast<Text*>(child)->containsOnlyWhitespace()) + return true; + } else if (!child->hasTagName(paramTag)) + return true; + } + return false; +} + +bool HTMLObjectElement::shouldAllowQuickTimeClassIdQuirk() +{ + // This site-specific hack maintains compatibility with Mac OS X Wiki Server, + // which embeds QuickTime movies using an object tag containing QuickTime's + // ActiveX classid. Treat this classid as valid only if OS X Server's unique + // 'generator' meta tag is present. Only apply this quirk if there is no + // fallback content, which ensures the quirk will disable itself if Wiki + // Server is updated to generate an alternate embed tag as fallback content. + if (!document()->page() + || !document()->page()->settings()->needsSiteSpecificQuirks() + || hasFallbackContent() + || !equalIgnoringCase(classId(), "clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B")) + return false; + + RefPtr<NodeList> metaElements = document()->getElementsByTagName(HTMLNames::metaTag.localName()); + unsigned length = metaElements->length(); + for (unsigned i = 0; i < length; ++i) { + ASSERT(metaElements->item(i)->isHTMLElement()); + HTMLMetaElement* metaElement = static_cast<HTMLMetaElement*>(metaElements->item(i)); + if (equalIgnoringCase(metaElement->name(), "generator") && metaElement->content().startsWith("Mac OS X Server Web Services Server", false)) + return true; + } + + return false; +} + +bool HTMLObjectElement::hasValidClassId() +{ +#if PLATFORM(QT) + if (equalIgnoringCase(serviceType(), "application/x-qt-plugin") || equalIgnoringCase(serviceType(), "application/x-qt-styled-widget")) + return true; +#endif + + if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType()) && classId().startsWith("java:", false)) + return true; + + if (shouldAllowQuickTimeClassIdQuirk()) + return true; + + // HTML5 says that fallback content should be rendered if a non-empty + // classid is specified for which the UA can't find a suitable plug-in. + return classId().isEmpty(); +} + +// FIXME: This should be unified with HTMLEmbedElement::updateWidget and +// moved down into HTMLPluginImageElement.cpp +void HTMLObjectElement::updateWidget(PluginCreationOption pluginCreationOption) +{ + ASSERT(!renderEmbeddedObject()->pluginCrashedOrWasMissing()); + ASSERT(needsWidgetUpdate()); + setNeedsWidgetUpdate(false); + // FIXME: This should ASSERT isFinishedParsingChildren() instead. + if (!isFinishedParsingChildren()) + return; + + String url = this->url(); + String serviceType = this->serviceType(); + + // FIXME: These should be joined into a PluginParameters class. + Vector<String> paramNames; + Vector<String> paramValues; + parametersForPlugin(paramNames, paramValues, url, serviceType); + + // Note: url is modified above by parametersForPlugin. + if (!allowedToLoadFrameURL(url)) + return; + + bool fallbackContent = hasFallbackContent(); + renderEmbeddedObject()->setHasFallbackContent(fallbackContent); + + if (pluginCreationOption == CreateOnlyNonNetscapePlugins && wouldLoadAsNetscapePlugin(url, serviceType)) + return; + + ASSERT(!m_inBeforeLoadEventHandler); + m_inBeforeLoadEventHandler = true; + bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(url); + m_inBeforeLoadEventHandler = false; + + // beforeload events can modify the DOM, potentially causing + // RenderWidget::destroy() to be called. Ensure we haven't been + // destroyed before continuing. + // FIXME: Should this render fallback content? + if (!renderer()) + return; + + RefPtr<HTMLObjectElement> protect(this); // Loading the plugin might remove us from the document. + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + bool success = beforeLoadAllowedLoad && hasValidClassId() && loader->requestObject(this, url, getAttribute(nameAttr), serviceType, paramNames, paramValues); + + if (!success && fallbackContent) + renderFallbackContent(); +} + +bool HTMLObjectElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + // FIXME: This check should not be needed, detached documents never render! + Frame* frame = document()->frame(); + if (!frame) + return false; + + return HTMLPlugInImageElement::rendererIsNeeded(context); +} + +void HTMLObjectElement::insertedIntoDocument() +{ + HTMLPlugInImageElement::insertedIntoDocument(); + FormAssociatedElement::insertedIntoDocument(); +} + +void HTMLObjectElement::removedFromDocument() +{ + HTMLPlugInImageElement::removedFromDocument(); + FormAssociatedElement::removedFromDocument(); +} + +void HTMLObjectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + updateDocNamedItem(); + if (inDocument() && !useFallbackContent()) { + setNeedsWidgetUpdate(true); + setNeedsStyleRecalc(); + } + HTMLPlugInImageElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +bool HTMLObjectElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == dataAttr || (attr->name() == usemapAttr && attr->value().string()[0] != '#') || HTMLPlugInImageElement::isURLAttribute(attr); +} + +const QualifiedName& HTMLObjectElement::imageSourceAttributeName() const +{ + return dataAttr; +} + +void HTMLObjectElement::renderFallbackContent() +{ + if (useFallbackContent()) + return; + + if (!inDocument()) + return; + + // Before we give up and use fallback content, check to see if this is a MIME type issue. + if (m_imageLoader && m_imageLoader->image() && m_imageLoader->image()->status() != CachedResource::LoadError) { + m_serviceType = m_imageLoader->image()->response().mimeType(); + if (!isImageType()) { + // If we don't think we have an image type anymore, then clear the image from the loader. + m_imageLoader->setImage(0); + reattach(); + return; + } + } + + m_useFallbackContent = true; + + // FIXME: Style gets recalculated which is suboptimal. + detach(); + attach(); +} + +// FIXME: This should be removed, all callers are almost certainly wrong. +static bool isRecognizedTagName(const QualifiedName& tagName) +{ + DEFINE_STATIC_LOCAL(HashSet<AtomicStringImpl*>, tagList, ()); + if (tagList.isEmpty()) { + size_t tagCount = 0; + QualifiedName** tags = HTMLNames::getHTMLTags(&tagCount); + for (size_t i = 0; i < tagCount; i++) { + if (*tags[i] == bgsoundTag + || *tags[i] == commandTag + || *tags[i] == detailsTag + || *tags[i] == figcaptionTag + || *tags[i] == figureTag + || *tags[i] == summaryTag + || *tags[i] == trackTag) { + // Even though we have atoms for these tags, we don't want to + // treat them as "recognized tags" for the purpose of parsing + // because that changes how we parse documents. + continue; + } + tagList.add(tags[i]->localName().impl()); + } + } + return tagList.contains(tagName.localName().impl()); +} + +void HTMLObjectElement::updateDocNamedItem() +{ + // The rule is "<object> elements with no children other than + // <param> elements, unknown elements and whitespace can be + // found by name in a document, and other <object> elements cannot." + bool wasNamedItem = m_docNamedItem; + bool isNamedItem = true; + Node* child = firstChild(); + while (child && isNamedItem) { + if (child->isElementNode()) { + Element* element = static_cast<Element*>(child); + // FIXME: Use of isRecognizedTagName is almost certainly wrong here. + if (isRecognizedTagName(element->tagQName()) && !element->hasTagName(paramTag)) + isNamedItem = false; + } else if (child->isTextNode()) { + if (!static_cast<Text*>(child)->containsOnlyWhitespace()) + isNamedItem = false; + } else + isNamedItem = false; + child = child->nextSibling(); + } + if (isNamedItem != wasNamedItem && document()->isHTMLDocument()) { + HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); + if (isNamedItem) { + document->addNamedItem(fastGetAttribute(nameAttr)); + document->addExtraNamedItem(getIdAttribute()); + } else { + document->removeNamedItem(fastGetAttribute(nameAttr)); + document->removeExtraNamedItem(getIdAttribute()); + } + } + m_docNamedItem = isNamedItem; +} + +bool HTMLObjectElement::containsJavaApplet() const +{ + if (MIMETypeRegistry::isJavaAppletMIMEType(getAttribute(typeAttr))) + return true; + + for (Element* child = firstElementChild(); child; child = child->nextElementSibling()) { + if (child->hasTagName(paramTag) + && equalIgnoringCase(child->getAttribute(nameAttr), "type") + && MIMETypeRegistry::isJavaAppletMIMEType(child->getAttribute(valueAttr).string())) + return true; + if (child->hasTagName(objectTag) + && static_cast<HTMLObjectElement*>(child)->containsJavaApplet()) + return true; + if (child->hasTagName(appletTag)) + return true; + } + + return false; +} + +void HTMLObjectElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLPlugInImageElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, document()->completeURL(getAttribute(dataAttr))); + + // FIXME: Passing a string that starts with "#" to the completeURL function does + // not seem like it would work. The image element has similar but not identical code. + const AtomicString& useMap = getAttribute(usemapAttr); + if (useMap.startsWith("#")) + addSubresourceURL(urls, document()->completeURL(useMap)); +} + +void HTMLObjectElement::didMoveToNewDocument(Document* oldDocument) +{ + FormAssociatedElement::didMoveToNewDocument(oldDocument); + HTMLPlugInImageElement::didMoveToNewDocument(oldDocument); +} + +void HTMLObjectElement::insertedIntoTree(bool deep) +{ + FormAssociatedElement::insertedIntoTree(); + HTMLPlugInImageElement::insertedIntoTree(deep); +} + +void HTMLObjectElement::removedFromTree(bool deep) +{ + FormAssociatedElement::removedFromTree(); + HTMLPlugInImageElement::removedFromTree(deep); +} + +bool HTMLObjectElement::appendFormData(FormDataList& encoding, bool) +{ + if (name().isEmpty()) + return false; + + Widget* widget = pluginWidget(); + if (!widget || !widget->isPluginViewBase()) + return false; + String value; + if (!static_cast<PluginViewBase*>(widget)->getFormValue(value)) + return false; + encoding.appendData(name(), value); + return true; +} + +const AtomicString& HTMLObjectElement::formControlName() const +{ + const AtomicString& name = fastGetAttribute(nameAttr); + return name.isNull() ? emptyAtom : name; +} + +HTMLFormElement* HTMLObjectElement::virtualForm() const +{ + return FormAssociatedElement::form(); +} + +#if ENABLE(MICRODATA) +String HTMLObjectElement::itemValueText() const +{ + return getURLAttribute(dataAttr); +} + +void HTMLObjectElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(dataAttr, value, ec); +} +#endif + +} diff --git a/Source/WebCore/html/HTMLObjectElement.h b/Source/WebCore/html/HTMLObjectElement.h new file mode 100644 index 000000000..2878039fe --- /dev/null +++ b/Source/WebCore/html/HTMLObjectElement.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2007, 2008, 2009 Apple 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 HTMLObjectElement_h +#define HTMLObjectElement_h + +#include "FormAssociatedElement.h" +#include "HTMLPlugInImageElement.h" + +namespace WebCore { + +class HTMLFormElement; + +class HTMLObjectElement : public HTMLPlugInImageElement, public FormAssociatedElement { +public: + static PassRefPtr<HTMLObjectElement> create(const QualifiedName&, Document*, HTMLFormElement*, bool createdByParser); + virtual ~HTMLObjectElement(); + + bool isDocNamedItem() const { return m_docNamedItem; } + + const String& classId() const { return m_classId; } + + bool containsJavaApplet() const; + + virtual bool useFallbackContent() const { return m_useFallbackContent; } + void renderFallbackContent(); + + // Implementations of FormAssociatedElement + HTMLFormElement* form() const { return FormAssociatedElement::form(); } + + virtual bool isFormControlElement() const { return false; } + + virtual bool isEnumeratable() const { return true; } + virtual bool appendFormData(FormDataList&, bool); + + // Implementations of constraint validation API. + // Note that the object elements are always barred from constraint validation. + String validationMessage() { return String(); } + bool checkValidity() { return true; } + void setCustomValidity(const String&) { } + + using TreeShared<ContainerNode>::ref; + using TreeShared<ContainerNode>::deref; + + virtual bool canContainRangeEndPoint() const { return useFallbackContent(); } + +private: + HTMLObjectElement(const QualifiedName&, Document*, HTMLFormElement*, bool createdByParser); + + virtual void parseMappedAttribute(Attribute*); + virtual void insertedIntoTree(bool deep); + virtual void removedFromTree(bool deep); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual bool isURLAttribute(Attribute*) const; + virtual const QualifiedName& imageSourceAttributeName() const; + + virtual RenderWidget* renderWidgetForJSBindings(); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + virtual void updateWidget(PluginCreationOption); + void updateDocNamedItem(); + + bool hasFallbackContent() const; + + // FIXME: This function should not deal with url or serviceType + // so that we can better share code between <object> and <embed>. + void parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType); + + bool shouldAllowQuickTimeClassIdQuirk(); + bool hasValidClassId(); + + virtual void refFormAssociatedElement() { ref(); } + 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; +#endif + + virtual bool shouldRegisterAsNamedItem() const OVERRIDE { return isDocNamedItem(); } + virtual bool shouldRegisterAsExtraNamedItem() const OVERRIDE { return isDocNamedItem(); } + + String m_classId; + bool m_docNamedItem : 1; + bool m_useFallbackContent : 1; +}; + +} + +#endif diff --git a/Source/WebCore/html/HTMLObjectElement.idl b/Source/WebCore/html/HTMLObjectElement.idl new file mode 100644 index 000000000..c1fa8c003 --- /dev/null +++ b/Source/WebCore/html/HTMLObjectElement.idl @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006, 2007, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + DelegatingPutFunction, + DelegatingGetOwnPropertySlot, + CustomCall + ] HTMLObjectElement : HTMLElement { + readonly attribute HTMLFormElement form; + attribute [Reflect] DOMString code; + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString archive; + attribute [Reflect] DOMString border; + attribute [Reflect] DOMString codeBase; + attribute [Reflect] DOMString codeType; + attribute [Reflect, URL] DOMString data; + attribute [Reflect] boolean declare; + attribute [Reflect] DOMString height; + attribute [Reflect] long hspace; + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString standby; + attribute [Reflect] DOMString type; + attribute [Reflect] DOMString useMap; + attribute [Reflect] long vspace; + attribute [Reflect] DOMString width; + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + + // Introduced in DOM Level 2: + readonly attribute [CheckFrameSecurity] Document contentDocument; + +#if defined(ENABLE_SVG) && ENABLE_SVG +#if !defined(LANGUAGE_OBJECTIVE_C) || !LANGUAGE_OBJECTIVE_C || defined(ENABLE_SVG_DOM_OBJC_BINDINGS) && ENABLE_SVG_DOM_OBJC_BINDINGS + [SVGCheckSecurityDocument] SVGDocument getSVGDocument() raises(DOMException); +#endif +#endif + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + // Objective-C extension: + readonly attribute URL absoluteImageURL; +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLOptGroupElement.cpp b/Source/WebCore/html/HTMLOptGroupElement.cpp new file mode 100644 index 000000000..dfc6d8d19 --- /dev/null +++ b/Source/WebCore/html/HTMLOptGroupElement.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLOptGroupElement.h" + +#include "CSSStyleSelector.h" +#include "Document.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.h" +#include "RenderMenuList.h" +#include "NodeRenderStyle.h" +#include "NodeRenderingContext.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLOptGroupElement::HTMLOptGroupElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) +{ + ASSERT(hasTagName(optgroupTag)); +} + +PassRefPtr<HTMLOptGroupElement> HTMLOptGroupElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLOptGroupElement(tagName, document, form)); +} + +bool HTMLOptGroupElement::supportsFocus() const +{ + return HTMLElement::supportsFocus(); +} + +bool HTMLOptGroupElement::isFocusable() const +{ + // Optgroup elements do not have a renderer so we check the renderStyle instead. + return supportsFocus() && renderStyle() && renderStyle()->display() != NONE; +} + +const AtomicString& HTMLOptGroupElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, optgroup, ("optgroup")); + return optgroup; +} + +void HTMLOptGroupElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + recalcSelectOptions(); + HTMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +void HTMLOptGroupElement::parseMappedAttribute(Attribute* attr) +{ + HTMLFormControlElement::parseMappedAttribute(attr); + recalcSelectOptions(); +} + +void HTMLOptGroupElement::recalcSelectOptions() +{ + ContainerNode* select = parentNode(); + while (select && !select->hasTagName(selectTag)) + select = select->parentNode(); + if (select) + toHTMLSelectElement(select)->setRecalcListItems(); +} + +void HTMLOptGroupElement::attach() +{ + if (parentNode()->renderStyle()) + setRenderStyle(styleForRenderer()); + HTMLFormControlElement::attach(); +} + +void HTMLOptGroupElement::detach() +{ + m_style.clear(); + HTMLFormControlElement::detach(); +} + +void HTMLOptGroupElement::setRenderStyle(PassRefPtr<RenderStyle> newStyle) +{ + m_style = newStyle; +} + +RenderStyle* HTMLOptGroupElement::nonRendererRenderStyle() const +{ + return m_style.get(); +} + +String HTMLOptGroupElement::groupLabelText() const +{ + String itemText = document()->displayStringModifiedByEncoding(getAttribute(labelAttr)); + + // In WinIE, leading and trailing whitespace is ignored in options and optgroups. We match this behavior. + itemText = itemText.stripWhiteSpace(); + // We want to collapse our whitespace too. This will match other browsers. + itemText = itemText.simplifyWhiteSpace(); + + return itemText; +} + +HTMLSelectElement* HTMLOptGroupElement::ownerSelectElement() const +{ + ContainerNode* select = parentNode(); + while (select && !select->hasTagName(selectTag)) + select = select->parentNode(); + + if (!select) + return 0; + + return toHTMLSelectElement(select); +} + +void HTMLOptGroupElement::accessKeyAction(bool) +{ + HTMLSelectElement* select = ownerSelectElement(); + // send to the parent to bring focus to the list box + if (select && !select->focused()) + select->accessKeyAction(false); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLOptGroupElement.h b/Source/WebCore/html/HTMLOptGroupElement.h new file mode 100644 index 000000000..3d8dd1753 --- /dev/null +++ b/Source/WebCore/html/HTMLOptGroupElement.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 HTMLOptGroupElement_h +#define HTMLOptGroupElement_h + +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLSelectElement; + +class HTMLOptGroupElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLOptGroupElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + HTMLSelectElement* ownerSelectElement() const; + + String groupLabelText() const; + +private: + HTMLOptGroupElement(const QualifiedName&, Document*, HTMLFormElement*); + + virtual const AtomicString& formControlType() const; + virtual bool supportsFocus() const; + virtual bool isFocusable() const; + virtual void parseMappedAttribute(Attribute*); + virtual bool rendererIsNeeded(const NodeRenderingContext&) { return false; } + virtual void attach(); + virtual void detach(); + virtual void setRenderStyle(PassRefPtr<RenderStyle>); + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual void accessKeyAction(bool sendMouseEvents); + + virtual RenderStyle* nonRendererRenderStyle() const; + + void recalcSelectOptions(); + + RefPtr<RenderStyle> m_style; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLOptGroupElement.idl b/Source/WebCore/html/HTMLOptGroupElement.idl new file mode 100644 index 000000000..75cead0fb --- /dev/null +++ b/Source/WebCore/html/HTMLOptGroupElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLOptGroupElement : HTMLElement { + attribute [Reflect] boolean disabled; + attribute [Reflect] DOMString label; + }; + +} diff --git a/Source/WebCore/html/HTMLOptionElement.cpp b/Source/WebCore/html/HTMLOptionElement.cpp new file mode 100644 index 000000000..9654d674b --- /dev/null +++ b/Source/WebCore/html/HTMLOptionElement.cpp @@ -0,0 +1,350 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2004, 2005, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Motorola Mobility, 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 "HTMLOptionElement.h" + +#include "Attribute.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLSelectElement.h" +#include "NodeRenderStyle.h" +#include "NodeRenderingContext.h" +#include "RenderMenuList.h" +#include "ScriptElement.h" +#include "Text.h" +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +using namespace HTMLNames; + +HTMLOptionElement::HTMLOptionElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) + , m_isSelected(false) +{ + ASSERT(hasTagName(optionTag)); +} + +PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLOptionElement(optionTag, document, form)); +} + +PassRefPtr<HTMLOptionElement> HTMLOptionElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLOptionElement(tagName, document, form)); +} + +PassRefPtr<HTMLOptionElement> HTMLOptionElement::createForJSConstructor(Document* document, const String& data, const String& value, + bool defaultSelected, bool selected, ExceptionCode& ec) +{ + RefPtr<HTMLOptionElement> element = adoptRef(new HTMLOptionElement(optionTag, document)); + + RefPtr<Text> text = Text::create(document, data.isNull() ? "" : data); + + ec = 0; + element->appendChild(text.release(), ec); + if (ec) + return 0; + + if (!value.isNull()) + element->setValue(value); + if (defaultSelected) + element->setAttribute(selectedAttr, emptyAtom); + element->setSelected(selected); + + return element.release(); +} + +void HTMLOptionElement::attach() +{ + if (parentNode()->renderStyle()) + setRenderStyle(styleForRenderer()); + HTMLFormControlElement::attach(); +} + +void HTMLOptionElement::detach() +{ + m_style.clear(); + HTMLFormControlElement::detach(); +} + +bool HTMLOptionElement::supportsFocus() const +{ + return HTMLElement::supportsFocus(); +} + +bool HTMLOptionElement::isFocusable() const +{ + // Option elements do not have a renderer so we check the renderStyle instead. + return supportsFocus() && renderStyle() && renderStyle()->display() != NONE; +} + +const AtomicString& HTMLOptionElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, option, ("option")); + return option; +} + +String HTMLOptionElement::text() const +{ + Document* document = this->document(); + String text; + + // WinIE does not use the label attribute, so as a quirk, we ignore it. + if (!document->inQuirksMode()) + text = fastGetAttribute(labelAttr); + + // FIXME: The following treats an element with the label attribute set to + // the empty string the same as an element with no label attribute at all. + // Is that correct? If it is, then should the label function work the same way? + if (text.isEmpty()) + text = collectOptionInnerText(); + + // FIXME: Is displayStringModifiedByEncoding helpful here? + // If it's correct here, then isn't it needed in the value and label functions too? + return document->displayStringModifiedByEncoding(text).stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace); +} + +void HTMLOptionElement::setText(const String &text, ExceptionCode& ec) +{ + // Changing the text causes a recalc of a select's items, which will reset the selected + // index to the first item if the select is single selection with a menu list. We attempt to + // preserve the selected item. + HTMLSelectElement* select = ownerSelectElement(); + bool selectIsMenuList = select && select->usesMenuList(); + int oldSelectedIndex = selectIsMenuList ? select->selectedIndex() : -1; + + // Handle the common special case where there's exactly 1 child node, and it's a text node. + Node* child = firstChild(); + if (child && child->isTextNode() && !child->nextSibling()) + static_cast<Text *>(child)->setData(text, ec); + else { + removeChildren(); + appendChild(Text::create(document(), text), ec); + } + + if (selectIsMenuList && select->selectedIndex() != oldSelectedIndex) + select->setSelectedIndex(oldSelectedIndex); +} + +void HTMLOptionElement::accessKeyAction(bool) +{ + HTMLSelectElement* select = ownerSelectElement(); + if (select) + select->accessKeySetSelectedIndex(index()); +} + +int HTMLOptionElement::index() const +{ + // It would be faster to cache the index, but harder to get it right in all cases. + + HTMLSelectElement* selectElement = ownerSelectElement(); + if (!selectElement) + return 0; + + int optionIndex = 0; + + const Vector<HTMLElement*>& items = selectElement->listItems(); + size_t length = items.size(); + for (size_t i = 0; i < length; ++i) { + if (!items[i]->hasTagName(optionTag)) + continue; + if (items[i] == this) + return optionIndex; + ++optionIndex; + } + + return 0; +} + +void HTMLOptionElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == selectedAttr) { + // FIXME: This doesn't match what the HTML specification says. + // The specification implies that removing the selected attribute or + // changing the value of a selected attribute that is already present + // has no effect on whether the element is selected. Further, it seems + // that we need to do more than just set m_isSelected to select in that + // case; we'd need to do the other work from the setSelected function. + m_isSelected = !attr->isNull(); + } else + HTMLFormControlElement::parseMappedAttribute(attr); +} + +String HTMLOptionElement::value() const +{ + const AtomicString& value = fastGetAttribute(valueAttr); + if (!value.isNull()) + return value; + return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace); +} + +void HTMLOptionElement::setValue(const String& value) +{ + setAttribute(valueAttr, value); +} + +bool HTMLOptionElement::selected() +{ + if (HTMLSelectElement* select = ownerSelectElement()) + select->updateListItemSelectedStates(); + return m_isSelected; +} + +void HTMLOptionElement::setSelected(bool selected) +{ + if (m_isSelected == selected) + return; + + setSelectedState(selected); + + if (HTMLSelectElement* select = ownerSelectElement()) + select->optionSelectionStateChanged(this, selected); +} + +void HTMLOptionElement::setSelectedState(bool selected) +{ + if (m_isSelected == selected) + return; + + m_isSelected = selected; + setNeedsStyleRecalc(); +} + +void HTMLOptionElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + if (HTMLSelectElement* select = ownerSelectElement()) + select->optionElementChildrenChanged(); + HTMLFormControlElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +HTMLSelectElement* HTMLOptionElement::ownerSelectElement() const +{ + ContainerNode* select = parentNode(); + while (select && !select->hasTagName(selectTag)) + select = select->parentNode(); + + if (!select) + return 0; + + return toHTMLSelectElement(select); +} + +String HTMLOptionElement::label() const +{ + const AtomicString& label = fastGetAttribute(labelAttr); + if (!label.isNull()) + return label; + return collectOptionInnerText().stripWhiteSpace(isHTMLSpace).simplifyWhiteSpace(isHTMLSpace); +} + +void HTMLOptionElement::setLabel(const String& label) +{ + setAttribute(labelAttr, label); +} + +void HTMLOptionElement::setRenderStyle(PassRefPtr<RenderStyle> newStyle) +{ + m_style = newStyle; + if (HTMLSelectElement* select = ownerSelectElement()) { + if (RenderObject* renderer = select->renderer()) + renderer->repaint(); + } +} + +RenderStyle* HTMLOptionElement::nonRendererRenderStyle() const +{ + return m_style.get(); +} + +String HTMLOptionElement::textIndentedToRespectGroupLabel() const +{ + ContainerNode* parent = parentNode(); + if (parent && parent->hasTagName(optgroupTag)) + return " " + text(); + return text(); +} + +bool HTMLOptionElement::disabled() const +{ + return ownElementDisabled() || (parentNode() && static_cast<HTMLFormControlElement*>(parentNode())->disabled()); +} + +void HTMLOptionElement::insertedIntoTree(bool deep) +{ + if (HTMLSelectElement* select = ownerSelectElement()) { + select->setRecalcListItems(); + // Do not call selected() since calling updateListItemSelectedStates() + // at this time won't do the right thing. (Why, exactly?) + // FIXME: Might be better to call this unconditionally, always passing m_isSelected, + // rather than only calling it if we are selected. + if (m_isSelected) + select->optionSelectionStateChanged(this, true); + select->scrollToSelection(); + } + + HTMLFormControlElement::insertedIntoTree(deep); +} + +String HTMLOptionElement::collectOptionInnerText() const +{ + StringBuilder text; + for (Node* node = firstChild(); node; ) { + if (node->isTextNode()) + text.append(node->nodeValue()); + // Text nodes inside script elements are not part of the option text. + if (node->isElementNode() && toScriptElement(toElement(node))) + node = node->traverseNextSibling(this); + else + node = node->traverseNextNode(this); + } + return text.toString(); +} + +#ifndef NDEBUG + +HTMLOptionElement* toHTMLOptionElement(Node* node) +{ + ASSERT(!node || node->hasTagName(optionTag)); + return static_cast<HTMLOptionElement*>(node); +} + +const HTMLOptionElement* toHTMLOptionElement(const Node* node) +{ + ASSERT(!node || node->hasTagName(optionTag)); + return static_cast<const HTMLOptionElement*>(node); +} + +#endif + +} // namespace diff --git a/Source/WebCore/html/HTMLOptionElement.h b/Source/WebCore/html/HTMLOptionElement.h new file mode 100644 index 000000000..19a8abd76 --- /dev/null +++ b/Source/WebCore/html/HTMLOptionElement.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 HTMLOptionElement_h +#define HTMLOptionElement_h + +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class HTMLSelectElement; + +class HTMLOptionElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLOptionElement> create(Document*, HTMLFormElement*); + static PassRefPtr<HTMLOptionElement> create(const QualifiedName&, Document*, HTMLFormElement*); + static PassRefPtr<HTMLOptionElement> createForJSConstructor(Document*, const String& data, const String& value, + bool defaultSelected, bool selected, ExceptionCode&); + + virtual String text() const; + void setText(const String&, ExceptionCode&); + + int index() const; + + String value() const; + void setValue(const String&); + + bool selected(); + void setSelected(bool); + + HTMLSelectElement* ownerSelectElement() const; + + String label() const; + void setLabel(const String&); + + bool ownElementDisabled() const { return HTMLFormControlElement::disabled(); } + + virtual bool disabled() const; + + String textIndentedToRespectGroupLabel() const; + + void setSelectedState(bool); + +private: + HTMLOptionElement(const QualifiedName&, Document*, HTMLFormElement* = 0); + + virtual bool supportsFocus() const; + virtual bool isFocusable() const; + virtual bool rendererIsNeeded(const NodeRenderingContext&) { return false; } + virtual void attach(); + virtual void detach(); + virtual void setRenderStyle(PassRefPtr<RenderStyle>); + + virtual const AtomicString& formControlType() const; + + virtual void parseMappedAttribute(Attribute*); + + virtual void insertedIntoTree(bool); + virtual void accessKeyAction(bool); + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual RenderStyle* nonRendererRenderStyle() const; + + String collectOptionInnerText() const; + + String m_value; + String m_label; + bool m_isSelected; + RefPtr<RenderStyle> m_style; +}; + +HTMLOptionElement* toHTMLOptionElement(Node*); +const HTMLOptionElement* toHTMLOptionElement(const Node*); +void toHTMLOptionElement(const HTMLOptionElement*); // This overload will catch anyone doing an unnecessary cast. + +#ifdef NDEBUG + +// The debug versions of these, with assertions, are not inlined. + +inline HTMLOptionElement* toHTMLOptionElement(Node* node) +{ + return static_cast<HTMLOptionElement*>(node); +} + +inline const HTMLOptionElement* toHTMLOptionElement(const Node* node) +{ + return static_cast<const HTMLOptionElement*>(node); +} + +#endif + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLOptionElement.idl b/Source/WebCore/html/HTMLOptionElement.idl new file mode 100644 index 000000000..fb84a8ec8 --- /dev/null +++ b/Source/WebCore/html/HTMLOptionElement.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple, Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + GenerateNativeConverter, + NamedConstructor=Option(in [Optional=CallWithNullValue] DOMString data, in [Optional=CallWithNullValue] DOMString value, in [Optional=CallWithDefaultValue] boolean defaultSelected, in [Optional=CallWithDefaultValue] boolean selected), + ConstructorRaisesException + ] HTMLOptionElement : HTMLElement { + readonly attribute HTMLFormElement form; + attribute [Reflect=selected] boolean defaultSelected; +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + attribute DOMString text setter raises(DOMException); +#else + readonly attribute DOMString text; +#endif + readonly attribute long index; + attribute [Reflect] boolean disabled; + attribute DOMString label; + attribute boolean selected; + attribute DOMString value; + }; + +} diff --git a/Source/WebCore/html/HTMLOptionsCollection.cpp b/Source/WebCore/html/HTMLOptionsCollection.cpp new file mode 100644 index 000000000..3abb35300 --- /dev/null +++ b/Source/WebCore/html/HTMLOptionsCollection.cpp @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2006, 2011, 2012 Apple Computer, Inc. + * + * 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 "HTMLOptionsCollection.h" + +#include "ExceptionCode.h" +#include "HTMLOptionElement.h" +#include "HTMLSelectElement.h" + +namespace WebCore { + +HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement* select) + : HTMLCollection(select, SelectOptions) +{ +} + +PassRefPtr<HTMLOptionsCollection> HTMLOptionsCollection::create(HTMLSelectElement* select) +{ + return adoptRef(new HTMLOptionsCollection(select)); +} + +void HTMLOptionsCollection::add(PassRefPtr<HTMLOptionElement> element, ExceptionCode &ec) +{ + add(element, length(), ec); +} + +void HTMLOptionsCollection::add(PassRefPtr<HTMLOptionElement> element, int index, ExceptionCode &ec) +{ + HTMLOptionElement* newOption = element.get(); + + if (!newOption) { + ec = TYPE_MISMATCH_ERR; + return; + } + + if (index < -1) { + ec = INDEX_SIZE_ERR; + return; + } + + ec = 0; + HTMLSelectElement* select = toHTMLSelectElement(base()); + + if (!select) + return; + + if (index == -1 || unsigned(index) >= length()) + select->add(newOption, 0, ec); + else + select->add(newOption, static_cast<HTMLOptionElement*>(item(index)), ec); + + ASSERT(ec == 0); +} + +void HTMLOptionsCollection::remove(int index) +{ + if (!base()) + return; + toHTMLSelectElement(base())->remove(index); +} + +int HTMLOptionsCollection::selectedIndex() const +{ + if (!base()) + return -1; + return toHTMLSelectElement(base())->selectedIndex(); +} + +void HTMLOptionsCollection::setSelectedIndex(int index) +{ + if (!base()) + return; + toHTMLSelectElement(base())->setSelectedIndex(index); +} + +void HTMLOptionsCollection::setLength(unsigned length, ExceptionCode& ec) +{ + if (!base()) + return; + toHTMLSelectElement(base())->setLength(length, ec); +} + +} //namespace diff --git a/Source/WebCore/html/HTMLOptionsCollection.h b/Source/WebCore/html/HTMLOptionsCollection.h new file mode 100644 index 000000000..333334b5d --- /dev/null +++ b/Source/WebCore/html/HTMLOptionsCollection.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple 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 HTMLOptionsCollection_h +#define HTMLOptionsCollection_h + +#include "HTMLCollection.h" + +namespace WebCore { + +class HTMLOptionElement; +class HTMLSelectElement; + +typedef int ExceptionCode; + +class HTMLOptionsCollection : public HTMLCollection { +public: + static PassRefPtr<HTMLOptionsCollection> create(HTMLSelectElement*); + + void add(PassRefPtr<HTMLOptionElement>, ExceptionCode&); + void add(PassRefPtr<HTMLOptionElement>, int index, ExceptionCode&); + void remove(int index); + + int selectedIndex() const; + void setSelectedIndex(int); + + void setLength(unsigned, ExceptionCode&); + + using HTMLCollection::invalidateCacheIfNeeded; + +private: + HTMLOptionsCollection(HTMLSelectElement*); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLOptionsCollection.idl b/Source/WebCore/html/HTMLOptionsCollection.idl new file mode 100644 index 000000000..7d1f27458 --- /dev/null +++ b/Source/WebCore/html/HTMLOptionsCollection.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + GenerateNativeConverter, + HasCustomIndexSetter + ] HTMLOptionsCollection : HTMLCollection { + attribute long selectedIndex; + attribute [Custom] unsigned long length + setter raises (DOMException); + + [Custom] void add(in [Optional=CallWithDefaultValue] HTMLOptionElement option, + in [Optional] unsigned long index) + raises (DOMException); + [Custom] void remove(in [Optional=CallWithDefaultValue] unsigned long index); + +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + Node item(in unsigned long index); + Node namedItem(in DOMString name); +#endif + }; + +} diff --git a/Source/WebCore/html/HTMLOutputElement.cpp b/Source/WebCore/html/HTMLOutputElement.cpp new file mode 100644 index 000000000..ee3c0d852 --- /dev/null +++ b/Source/WebCore/html/HTMLOutputElement.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLOutputElement.h" + +#include "HTMLFormElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +inline HTMLOutputElement::HTMLOutputElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) + , m_isDefaultValueMode(true) + , m_isSetTextContentInProgress(false) + , m_defaultValue("") + , m_tokens(DOMSettableTokenList::create()) +{ +} + +PassRefPtr<HTMLOutputElement> HTMLOutputElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + return adoptRef(new HTMLOutputElement(tagName, document, form)); +} + +const AtomicString& HTMLOutputElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, output, ("output")); + return output; +} + +bool HTMLOutputElement::supportsFocus() const +{ + return Node::supportsFocus() && !disabled(); +} + +bool HTMLOutputElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == HTMLNames::dirAttr) { + result = eBDI; + return true; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLOutputElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == HTMLNames::forAttr) + setFor(attr->value()); + else + HTMLFormControlElement::parseMappedAttribute(attr); +} + +DOMSettableTokenList* HTMLOutputElement::htmlFor() const +{ + return m_tokens.get(); +} + +void HTMLOutputElement::setFor(const String& value) +{ + m_tokens->setValue(value); +} + +void HTMLOutputElement::childrenChanged(bool createdByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + if (createdByParser || m_isSetTextContentInProgress) { + m_isSetTextContentInProgress = false; + return; + } + + if (m_isDefaultValueMode) + m_defaultValue = textContent(); + HTMLFormControlElement::childrenChanged(createdByParser, beforeChange, afterChange, childCountDelta); +} + +void HTMLOutputElement::reset() +{ + // The reset algorithm for output elements is to set the element's + // value mode flag to "default" and then to set the element's textContent + // attribute to the default value. + m_isDefaultValueMode = true; + if (m_defaultValue == value()) + return; + setTextContentInternal(m_defaultValue); +} + +String HTMLOutputElement::value() const +{ + return textContent(); +} + +void HTMLOutputElement::setValue(const String& value) +{ + // The value mode flag set to "value" when the value attribute is set. + m_isDefaultValueMode = false; + if (value == this->value()) + return; + setTextContentInternal(value); +} + +String HTMLOutputElement::defaultValue() const +{ + return m_defaultValue; +} + +void HTMLOutputElement::setDefaultValue(const String& value) +{ + if (m_defaultValue == value) + return; + m_defaultValue = value; + // The spec requires the value attribute set to the default value + // when the element's value mode flag to "default". + if (m_isDefaultValueMode) + setTextContentInternal(value); +} + +void HTMLOutputElement::setTextContentInternal(const String& value) +{ + ASSERT(!m_isSetTextContentInProgress); + ExceptionCode ec; + m_isSetTextContentInProgress = true; + setTextContent(value, ec); +} + +} // namespace diff --git a/Source/WebCore/html/HTMLOutputElement.h b/Source/WebCore/html/HTMLOutputElement.h new file mode 100644 index 000000000..83df7fae7 --- /dev/null +++ b/Source/WebCore/html/HTMLOutputElement.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLOutputElement_h +#define HTMLOutputElement_h + +#include "DOMSettableTokenList.h" +#include "HTMLFormControlElement.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class HTMLOutputElement : public HTMLFormControlElement { +public: + static PassRefPtr<HTMLOutputElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + virtual bool willValidate() const { return false; } + + String value() const; + void setValue(const String&); + String defaultValue() const; + void setDefaultValue(const String&); + void setFor(const String&); + DOMSettableTokenList* htmlFor() const; + + virtual bool canContainRangeEndPoint() const { return false; } + +private: + HTMLOutputElement(const QualifiedName&, Document*, HTMLFormElement*); + + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const OVERRIDE; + virtual void parseMappedAttribute(Attribute*); + virtual const AtomicString& formControlType() const; + virtual bool isEnumeratable() const { return true; } + virtual bool supportsFocus() const; + virtual void childrenChanged(bool createdByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + virtual void reset(); + + void setTextContentInternal(const String&); + + bool m_isDefaultValueMode; + bool m_isSetTextContentInProgress; + String m_defaultValue; + RefPtr<DOMSettableTokenList> m_tokens; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLOutputElement.idl b/Source/WebCore/html/HTMLOutputElement.idl new file mode 100644 index 000000000..4e6cbfbd7 --- /dev/null +++ b/Source/WebCore/html/HTMLOutputElement.idl @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface HTMLOutputElement : HTMLElement { + attribute [Custom] DOMSettableTokenList htmlFor; + readonly attribute HTMLFormElement form; + attribute [Reflect] DOMString name; + + readonly attribute DOMString type; + attribute [ConvertNullToNullString] DOMString defaultValue; + attribute [ConvertNullToNullString] DOMString value; + + readonly attribute boolean willValidate; + readonly attribute ValidityState validity; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + + readonly attribute NodeList labels; + }; +} diff --git a/Source/WebCore/html/HTMLParagraphElement.cpp b/Source/WebCore/html/HTMLParagraphElement.cpp new file mode 100644 index 000000000..e8a5aa70f --- /dev/null +++ b/Source/WebCore/html/HTMLParagraphElement.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLParagraphElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "Document.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLParagraphElement::HTMLParagraphElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(pTag)); +} + +PassRefPtr<HTMLParagraphElement> HTMLParagraphElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLParagraphElement(tagName, document)); +} + +bool HTMLParagraphElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == alignAttr) { + result = eBlock; // We can share with DIV here. + return false; + } + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLParagraphElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == alignAttr) { + if (equalIgnoringCase(attr->value(), "middle") || equalIgnoringCase(attr->value(), "center")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitCenter); + else if (equalIgnoringCase(attr->value(), "left")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitLeft); + else if (equalIgnoringCase(attr->value(), "right")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitRight); + else + addCSSProperty(attr, CSSPropertyTextAlign, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLParagraphElement.h b/Source/WebCore/html/HTMLParagraphElement.h new file mode 100644 index 000000000..6dbf07110 --- /dev/null +++ b/Source/WebCore/html/HTMLParagraphElement.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLParagraphElement_h +#define HTMLParagraphElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLParagraphElement : public HTMLElement { +public: + static PassRefPtr<HTMLParagraphElement> create(const QualifiedName&, Document*); + +private: + HTMLParagraphElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace WebCore + +#endif // HTMLParagraphElement_h diff --git a/Source/WebCore/html/HTMLParagraphElement.idl b/Source/WebCore/html/HTMLParagraphElement.idl new file mode 100644 index 000000000..246e9e99e --- /dev/null +++ b/Source/WebCore/html/HTMLParagraphElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLParagraphElement : HTMLElement { + attribute [Reflect] DOMString align; + }; + +} diff --git a/Source/WebCore/html/HTMLParamElement.cpp b/Source/WebCore/html/HTMLParamElement.cpp new file mode 100644 index 000000000..d1f53a14d --- /dev/null +++ b/Source/WebCore/html/HTMLParamElement.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008, 2010 Apple 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 "HTMLParamElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLParamElement::HTMLParamElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(paramTag)); +} + +PassRefPtr<HTMLParamElement> HTMLParamElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLParamElement(tagName, document)); +} + +bool HTMLParamElement::isURLParameter(const String& name) +{ + return equalIgnoringCase(name, "data") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "src"); +} + +void HTMLParamElement::parseMappedAttribute(Attribute* attr) +{ + if (isIdAttributeName(attr->name())) { + // Must call base class so that hasID bit gets set. + HTMLElement::parseMappedAttribute(attr); + if (document()->isHTMLDocument()) + return; + m_name = attr->value(); + } else if (attr->name() == nameAttr) { + m_name = attr->value(); + } else if (attr->name() == valueAttr) { + m_value = attr->value(); + } else + HTMLElement::parseMappedAttribute(attr); +} + +bool HTMLParamElement::isURLAttribute(Attribute* attr) const +{ + if (attr->name() == valueAttr) { + Attribute* attr = attributes()->getAttributeItem(nameAttr); + if (attr) { + const AtomicString& value = attr->value(); + if (isURLParameter(value)) + return true; + } + } + return HTMLElement::isURLAttribute(attr); +} + +void HTMLParamElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + if (!isURLParameter(name())) + return; + + addSubresourceURL(urls, document()->completeURL(value())); +} + +} diff --git a/Source/WebCore/html/HTMLParamElement.h b/Source/WebCore/html/HTMLParamElement.h new file mode 100644 index 000000000..f448547cb --- /dev/null +++ b/Source/WebCore/html/HTMLParamElement.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2010 Apple 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 HTMLParamElement_h +#define HTMLParamElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLParamElement : public HTMLElement { +public: + static PassRefPtr<HTMLParamElement> create(const QualifiedName&, Document*); + + String name() const { return m_name; } + String value() const { return m_value; } + + static bool isURLParameter(const String&); + +private: + HTMLParamElement(const QualifiedName&, Document*); + + virtual void parseMappedAttribute(Attribute*); + + virtual bool isURLAttribute(Attribute*) const; + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + // FIXME: These don't need to be stored as members and instead + // name() value() could use getAttribute(nameAttr/valueAttr). + AtomicString m_name; + AtomicString m_value; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/HTMLParamElement.idl b/Source/WebCore/html/HTMLParamElement.idl new file mode 100644 index 000000000..1f0c0ded6 --- /dev/null +++ b/Source/WebCore/html/HTMLParamElement.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLParamElement : HTMLElement { + attribute [Reflect] DOMString name; + attribute [Reflect] DOMString type; + attribute [Reflect] DOMString value; + attribute [Reflect] DOMString valueType; + }; + +} diff --git a/Source/WebCore/html/HTMLParserErrorCodes.cpp b/Source/WebCore/html/HTMLParserErrorCodes.cpp new file mode 100644 index 000000000..e1861a781 --- /dev/null +++ b/Source/WebCore/html/HTMLParserErrorCodes.cpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +// FIXME: Delete this file. diff --git a/Source/WebCore/html/HTMLParserErrorCodes.h b/Source/WebCore/html/HTMLParserErrorCodes.h new file mode 100644 index 000000000..1d88bc670 --- /dev/null +++ b/Source/WebCore/html/HTMLParserErrorCodes.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2007 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// FIXME: Delete this file. diff --git a/Source/WebCore/html/HTMLParserQuirks.h b/Source/WebCore/html/HTMLParserQuirks.h new file mode 100644 index 000000000..4ac6db8ab --- /dev/null +++ b/Source/WebCore/html/HTMLParserQuirks.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2009 Apple 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. + */ + +// FIXME: Delete this file. diff --git a/Source/WebCore/html/HTMLPlugInElement.cpp b/Source/WebCore/html/HTMLPlugInElement.cpp new file mode 100644 index 000000000..cc6f3dea6 --- /dev/null +++ b/Source/WebCore/html/HTMLPlugInElement.cpp @@ -0,0 +1,201 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "HTMLPlugInElement.h" + +#include "Attribute.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "CSSPropertyNames.h" +#include "Document.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameTree.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderEmbeddedObject.h" +#include "RenderWidget.h" +#include "Settings.h" +#include "Widget.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) +#include "npruntime_impl.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +HTMLPlugInElement::HTMLPlugInElement(const QualifiedName& tagName, Document* doc) + : HTMLFrameOwnerElement(tagName, doc) + , m_inBeforeLoadEventHandler(false) +#if ENABLE(NETSCAPE_PLUGIN_API) + , m_NPObject(0) +#endif + , m_isCapturingMouseEvents(false) +{ +} + +HTMLPlugInElement::~HTMLPlugInElement() +{ + ASSERT(!m_instance); // cleared in detach() + +#if ENABLE(NETSCAPE_PLUGIN_API) + if (m_NPObject) { + _NPN_ReleaseObject(m_NPObject); + m_NPObject = 0; + } +#endif +} + +void HTMLPlugInElement::detach() +{ + m_instance.clear(); + + if (m_isCapturingMouseEvents) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_isCapturingMouseEvents = false; + } + + HTMLFrameOwnerElement::detach(); +} + +void HTMLPlugInElement::removedFromDocument() +{ +#if ENABLE(NETSCAPE_PLUGIN_API) + if (m_NPObject) { + _NPN_ReleaseObject(m_NPObject); + m_NPObject = 0; + } +#endif + + HTMLFrameOwnerElement::removedFromDocument(); +} + +PassScriptInstance HTMLPlugInElement::getInstance() +{ + Frame* frame = document()->frame(); + if (!frame) + return 0; + + // If the host dynamically turns off JavaScript (or Java) we will still return + // the cached allocated Bindings::Instance. Not supporting this edge-case is OK. + if (m_instance) + return m_instance; + + if (Widget* widget = pluginWidget()) + m_instance = frame->script()->createScriptInstanceForWidget(widget); + + return m_instance; +} + +Widget* HTMLPlugInElement::pluginWidget() +{ + if (m_inBeforeLoadEventHandler) { + // The plug-in hasn't loaded yet, and it makes no sense to try to load if beforeload handler happened to touch the plug-in element. + // That would recursively call beforeload for the same element. + return 0; + } + + RenderWidget* renderWidget = renderWidgetForJSBindings(); + if (!renderWidget) + return 0; + + return renderWidget->widget(); +} + +bool HTMLPlugInElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr || + attrName == heightAttr || + attrName == vspaceAttr || + attrName == hspaceAttr) { + result = eUniversal; + return false; + } + + if (attrName == alignAttr) { + result = eReplaced; // Share with <img> since the alignment behavior is the same. + return false; + } + + return HTMLFrameOwnerElement::mapToEntry(attrName, result); +} + +void HTMLPlugInElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == widthAttr) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + else if (attr->name() == heightAttr) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + else if (attr->name() == vspaceAttr) { + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + } else if (attr->name() == hspaceAttr) { + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + } else if (attr->name() == alignAttr) + addHTMLAlignment(attr); + else + HTMLFrameOwnerElement::parseMappedAttribute(attr); +} + +void HTMLPlugInElement::defaultEventHandler(Event* event) +{ + // Firefox seems to use a fake event listener to dispatch events to plug-in (tested with mouse events only). + // This is observable via different order of events - in Firefox, event listeners specified in HTML attributes fires first, then an event + // gets dispatched to plug-in, and only then other event listeners fire. Hopefully, this difference does not matter in practice. + + // FIXME: Mouse down and scroll events are passed down to plug-in via custom code in EventHandler; these code paths should be united. + + RenderObject* r = renderer(); + if (r && r->isEmbeddedObject() && toRenderEmbeddedObject(r)->showsMissingPluginIndicator()) { + toRenderEmbeddedObject(r)->handleMissingPluginIndicatorEvent(event); + return; + } + + if (!r || !r->isWidget()) + return; + RefPtr<Widget> widget = toRenderWidget(r)->widget(); + if (!widget) + return; + widget->handleEvent(event); + if (event->defaultHandled()) + return; + HTMLFrameOwnerElement::defaultEventHandler(event); +} + +#if ENABLE(NETSCAPE_PLUGIN_API) + +NPObject* HTMLPlugInElement::getNPObject() +{ + ASSERT(document()->frame()); + if (!m_NPObject) + m_NPObject = document()->frame()->script()->createScriptObjectForPluginElement(this); + return m_NPObject; +} + +#endif /* ENABLE(NETSCAPE_PLUGIN_API) */ + +} diff --git a/Source/WebCore/html/HTMLPlugInElement.h b/Source/WebCore/html/HTMLPlugInElement.h new file mode 100644 index 000000000..7dadfda9f --- /dev/null +++ b/Source/WebCore/html/HTMLPlugInElement.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2006, 2007, 2008, 2009 Apple 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 HTMLPlugInElement_h +#define HTMLPlugInElement_h + +#include "HTMLFrameOwnerElement.h" +#include "ScriptInstance.h" + +#if ENABLE(NETSCAPE_PLUGIN_API) +struct NPObject; +#endif + +namespace WebCore { + +class RenderEmbeddedObject; +class RenderWidget; +class Widget; + +class HTMLPlugInElement : public HTMLFrameOwnerElement { +public: + virtual ~HTMLPlugInElement(); + + PassScriptInstance getInstance(); + + Widget* pluginWidget(); + +#if ENABLE(NETSCAPE_PLUGIN_API) + NPObject* getNPObject(); +#endif + + bool isCapturingMouseEvents() const { return m_isCapturingMouseEvents; } + void setIsCapturingMouseEvents(bool capturing) { m_isCapturingMouseEvents = capturing; } + + bool canContainRangeEndPoint() const { return false; } + +protected: + HTMLPlugInElement(const QualifiedName& tagName, Document*); + + virtual void detach(); + virtual void removedFromDocument(); + virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; + virtual void parseMappedAttribute(Attribute*); + + bool m_inBeforeLoadEventHandler; + +private: + virtual void defaultEventHandler(Event*); + + virtual RenderWidget* renderWidgetForJSBindings() = 0; + +private: + mutable ScriptInstance m_instance; +#if ENABLE(NETSCAPE_PLUGIN_API) + NPObject* m_NPObject; +#endif + bool m_isCapturingMouseEvents; +}; + +} // namespace WebCore + +#endif // HTMLPlugInElement_h diff --git a/Source/WebCore/html/HTMLPlugInImageElement.cpp b/Source/WebCore/html/HTMLPlugInImageElement.cpp new file mode 100644 index 000000000..ce9bb7431 --- /dev/null +++ b/Source/WebCore/html/HTMLPlugInImageElement.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2008, 2011 Apple 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 "HTMLPlugInImageElement.h" + +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "HTMLImageLoader.h" +#include "HTMLNames.h" +#include "Image.h" +#include "NodeRenderStyle.h" +#include "Page.h" +#include "RenderEmbeddedObject.h" +#include "RenderImage.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +HTMLPlugInImageElement::HTMLPlugInImageElement(const QualifiedName& tagName, Document* document, bool createdByParser, PreferPlugInsForImagesOption preferPlugInsForImagesOption) + : HTMLPlugInElement(tagName, document) + // m_needsWidgetUpdate(!createdByParser) allows HTMLObjectElement to delay + // widget updates until after all children are parsed. For HTMLEmbedElement + // this delay is unnecessary, but it is simpler to make both classes share + // the same codepath in this class. + , m_needsWidgetUpdate(!createdByParser) + , m_shouldPreferPlugInsForImages(preferPlugInsForImagesOption == ShouldPreferPlugInsForImages) + , m_needsDocumentActivationCallbacks(false) +{ + setHasCustomWillOrDidRecalcStyle(); +} + +HTMLPlugInImageElement::~HTMLPlugInImageElement() +{ + if (m_needsDocumentActivationCallbacks) + document()->unregisterForPageCacheSuspensionCallbacks(this); +} + +RenderEmbeddedObject* HTMLPlugInImageElement::renderEmbeddedObject() const +{ + // HTMLObjectElement and HTMLEmbedElement may return arbitrary renderers + // when using fallback content. + if (!renderer() || !renderer()->isEmbeddedObject()) + return 0; + return toRenderEmbeddedObject(renderer()); +} + +bool HTMLPlugInImageElement::isImageType() +{ + if (m_serviceType.isEmpty() && protocolIs(m_url, "data")) + m_serviceType = mimeTypeFromDataURL(m_url); + + if (Frame* frame = document()->frame()) { + KURL completedURL = document()->completeURL(m_url); + return frame->loader()->client()->objectContentType(completedURL, m_serviceType, shouldPreferPlugInsForImages()) == ObjectContentImage; + } + + return Image::supportsType(m_serviceType); +} + +// We don't use m_url, as it may not be the final URL that the object loads, +// depending on <param> values. +bool HTMLPlugInImageElement::allowedToLoadFrameURL(const String& url) +{ + ASSERT(document()); + ASSERT(document()->frame()); + if (document()->frame()->page()->frameCount() >= Page::maxNumberOfFrames) + return false; + + KURL completeURL = document()->completeURL(url); + + if (contentFrame() && protocolIsJavaScript(completeURL) + && !document()->securityOrigin()->canAccess(contentDocument()->securityOrigin())) + return false; + + // We allow one level of self-reference because some sites depend on that. + // But we don't allow more than one. + bool foundSelfReference = false; + for (Frame* frame = document()->frame(); frame; frame = frame->tree()->parent()) { + if (equalIgnoringFragmentIdentifier(frame->document()->url(), completeURL)) { + if (foundSelfReference) + return false; + foundSelfReference = true; + } + } + return true; +} + +// We don't use m_url, or m_serviceType as they may not be the final values +// that <object> uses depending on <param> values. +bool HTMLPlugInImageElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType) +{ + ASSERT(document()); + ASSERT(document()->frame()); + KURL completedURL; + if (!url.isEmpty()) + completedURL = document()->completeURL(url); + + FrameLoader* frameLoader = document()->frame()->loader(); + ASSERT(frameLoader); + if (frameLoader->client()->objectContentType(completedURL, serviceType, shouldPreferPlugInsForImages()) == ObjectContentNetscapePlugin) + return true; + return false; +} + +RenderObject* HTMLPlugInImageElement::createRenderer(RenderArena* arena, RenderStyle* style) +{ + // Once a PlugIn Element creates its renderer, it needs to be told when the Document goes + // inactive or reactivates so it can clear the renderer before going into the page cache. + if (!m_needsDocumentActivationCallbacks) { + m_needsDocumentActivationCallbacks = true; + document()->registerForPageCacheSuspensionCallbacks(this); + } + + // Fallback content breaks the DOM->Renderer class relationship of this + // class and all superclasses because createObject won't necessarily + // return a RenderEmbeddedObject, RenderPart or even RenderWidget. + if (useFallbackContent()) + return RenderObject::createObject(this, style); + if (isImageType()) { + RenderImage* image = new (arena) RenderImage(this); + image->setImageResource(RenderImageResource::create()); + return image; + } + return new (arena) RenderEmbeddedObject(this); +} + +bool HTMLPlugInImageElement::willRecalcStyle(StyleChange) +{ + // FIXME: Why is this necessary? Manual re-attach is almost always wrong. + if (!useFallbackContent() && needsWidgetUpdate() && renderer() && !isImageType()) + reattach(); + return true; +} + +void HTMLPlugInImageElement::attach() +{ + bool isImage = isImageType(); + + if (!isImage) + queuePostAttachCallback(&HTMLPlugInImageElement::updateWidgetCallback, this); + + HTMLPlugInElement::attach(); + + if (isImage && renderer() && !useFallbackContent()) { + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(this)); + m_imageLoader->updateFromElement(); + } +} + +void HTMLPlugInImageElement::detach() +{ + // FIXME: Because of the insanity that is HTMLPlugInImageElement::recalcStyle, + // we can end up detaching during an attach() call, before we even have a + // renderer. In that case, don't mark the widget for update. + if (attached() && renderer() && !useFallbackContent()) + // Update the widget the next time we attach (detaching destroys the plugin). + setNeedsWidgetUpdate(true); + HTMLPlugInElement::detach(); +} + +void HTMLPlugInImageElement::updateWidgetIfNecessary() +{ + document()->updateStyleIfNeeded(); + + if (!needsWidgetUpdate() || useFallbackContent() || isImageType()) + return; + + if (!renderEmbeddedObject() || renderEmbeddedObject()->pluginCrashedOrWasMissing()) + return; + + updateWidget(CreateOnlyNonNetscapePlugins); +} + +void HTMLPlugInImageElement::finishParsingChildren() +{ + HTMLPlugInElement::finishParsingChildren(); + if (useFallbackContent()) + return; + + setNeedsWidgetUpdate(true); + if (inDocument()) + setNeedsStyleRecalc(); +} + +void HTMLPlugInImageElement::didMoveToNewDocument(Document* oldDocument) +{ + if (m_needsDocumentActivationCallbacks) { + if (oldDocument) + oldDocument->unregisterForPageCacheSuspensionCallbacks(this); + document()->registerForPageCacheSuspensionCallbacks(this); + } + + if (m_imageLoader) + m_imageLoader->elementDidMoveToNewDocument(); + HTMLPlugInElement::didMoveToNewDocument(oldDocument); +} + +void HTMLPlugInImageElement::documentWillSuspendForPageCache() +{ + if (RenderStyle* rs = renderStyle()) { + m_customStyleForPageCache = RenderStyle::clone(rs); + m_customStyleForPageCache->setDisplay(NONE); + } + + setHasCustomStyleForRenderer(); + + if (m_customStyleForPageCache) + recalcStyle(Force); + + HTMLPlugInElement::documentWillSuspendForPageCache(); +} + +void HTMLPlugInImageElement::documentDidResumeFromPageCache() +{ + clearHasCustomStyleForRenderer(); + + if (m_customStyleForPageCache) { + m_customStyleForPageCache = 0; + recalcStyle(Force); + } + + HTMLPlugInElement::documentDidResumeFromPageCache(); +} + +PassRefPtr<RenderStyle> HTMLPlugInImageElement::customStyleForRenderer() +{ + if (!m_customStyleForPageCache) + return renderStyle(); + + return m_customStyleForPageCache; +} + +void HTMLPlugInImageElement::updateWidgetCallback(Node* n, unsigned) +{ + static_cast<HTMLPlugInImageElement*>(n)->updateWidgetIfNecessary(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLPlugInImageElement.h b/Source/WebCore/html/HTMLPlugInImageElement.h new file mode 100644 index 000000000..168ed4634 --- /dev/null +++ b/Source/WebCore/html/HTMLPlugInImageElement.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2008, 2009, 2011 Apple 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 HTMLPlugInImageElement_h +#define HTMLPlugInImageElement_h + +#include "HTMLPlugInElement.h" + +#include "RenderStyle.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class HTMLImageLoader; +class FrameLoader; + +enum PluginCreationOption { + CreateAnyWidgetType, + CreateOnlyNonNetscapePlugins, +}; + +enum PreferPlugInsForImagesOption { + ShouldPreferPlugInsForImages, + ShouldNotPreferPlugInsForImages +}; + +// Base class for HTMLObjectElement and HTMLEmbedElement +class HTMLPlugInImageElement : public HTMLPlugInElement { +public: + virtual ~HTMLPlugInImageElement(); + + RenderEmbeddedObject* renderEmbeddedObject() const; + + virtual void updateWidget(PluginCreationOption) = 0; + + const String& serviceType() const { return m_serviceType; } + const String& url() const { return m_url; } + bool shouldPreferPlugInsForImages() const { return m_shouldPreferPlugInsForImages; } + + // Public for FrameView::addWidgetToUpdate() + bool needsWidgetUpdate() const { return m_needsWidgetUpdate; } + void setNeedsWidgetUpdate(bool needsWidgetUpdate) { m_needsWidgetUpdate = needsWidgetUpdate; } + +protected: + HTMLPlugInImageElement(const QualifiedName& tagName, Document*, bool createdByParser, PreferPlugInsForImagesOption); + + bool isImageType(); + + OwnPtr<HTMLImageLoader> m_imageLoader; + String m_serviceType; + String m_url; + + static void updateWidgetCallback(Node*, unsigned = 0); + virtual void attach(); + virtual void detach(); + + bool allowedToLoadFrameURL(const String& url); + bool wouldLoadAsNetscapePlugin(const String& url, const String& serviceType); + + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + virtual void documentWillSuspendForPageCache() OVERRIDE; + virtual void documentDidResumeFromPageCache() OVERRIDE; + + virtual PassRefPtr<RenderStyle> customStyleForRenderer() OVERRIDE; + +private: + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual bool willRecalcStyle(StyleChange); + + virtual void finishParsingChildren(); + + void updateWidgetIfNecessary(); + virtual bool useFallbackContent() const { return false; } + + bool m_needsWidgetUpdate; + bool m_shouldPreferPlugInsForImages; + bool m_needsDocumentActivationCallbacks; + RefPtr<RenderStyle> m_customStyleForPageCache; +}; + +} // namespace WebCore + +#endif // HTMLPlugInImageElement_h diff --git a/Source/WebCore/html/HTMLPreElement.cpp b/Source/WebCore/html/HTMLPreElement.cpp new file mode 100644 index 000000000..fc67aca7f --- /dev/null +++ b/Source/WebCore/html/HTMLPreElement.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLPreElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLPreElement::HTMLPreElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ +} + +PassRefPtr<HTMLPreElement> HTMLPreElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLPreElement(tagName, document)); +} + +bool HTMLPreElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr || attrName == wrapAttr) { + result = ePre; + return false; + } + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLPreElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == widthAttr) { + // FIXME: Implement this some day. Width on a <pre> is the # of characters that + // we should size the pre to. We basically need to take the width of a space, + // multiply by the value of the attribute and then set that as the width CSS + // property. + } else if (attr->name() == wrapAttr) { + if (!attr->value().isNull()) + addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePreWrap); + } else + return HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLPreElement.h b/Source/WebCore/html/HTMLPreElement.h new file mode 100644 index 000000000..3ccdb8603 --- /dev/null +++ b/Source/WebCore/html/HTMLPreElement.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLPreElement_h +#define HTMLPreElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLPreElement : public HTMLElement { +public: + static PassRefPtr<HTMLPreElement> create(const QualifiedName&, Document*); + +private: + HTMLPreElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace WebCore + +#endif // HTMLPreElement_h diff --git a/Source/WebCore/html/HTMLPreElement.idl b/Source/WebCore/html/HTMLPreElement.idl new file mode 100644 index 000000000..ae137f024 --- /dev/null +++ b/Source/WebCore/html/HTMLPreElement.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All right reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLPreElement : HTMLElement { + // FIXME: DOM spec says that width should be of type DOMString + // see http://bugs.webkit.org/show_bug.cgi?id=8992 + attribute [Reflect] long width; + + // Extensions + attribute [Reflect] boolean wrap; + }; + +} diff --git a/Source/WebCore/html/HTMLProgressElement.cpp b/Source/WebCore/html/HTMLProgressElement.cpp new file mode 100644 index 000000000..960f15782 --- /dev/null +++ b/Source/WebCore/html/HTMLProgressElement.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * 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" +#if ENABLE(PROGRESS_TAG) +#include "HTMLProgressElement.h" + +#include "Attribute.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FormDataList.h" +#include "HTMLDivElement.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "ProgressShadowElement.h" +#include "RenderProgress.h" +#include "ShadowRoot.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +const double HTMLProgressElement::IndeterminatePosition = -1; +const double HTMLProgressElement::InvalidPosition = -2; + +HTMLProgressElement::HTMLProgressElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElement(tagName, document, form) +{ + ASSERT(hasTagName(progressTag)); +} + +HTMLProgressElement::~HTMLProgressElement() +{ +} + +PassRefPtr<HTMLProgressElement> HTMLProgressElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + RefPtr<HTMLProgressElement> progress = adoptRef(new HTMLProgressElement(tagName, document, form)); + progress->createShadowSubtree(); + return progress; +} + +RenderObject* HTMLProgressElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderProgress(this); +} + +bool HTMLProgressElement::supportsFocus() const +{ + return Node::supportsFocus() && !disabled(); +} + +const AtomicString& HTMLProgressElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, progress, ("progress")); + return progress; +} + +void HTMLProgressElement::parseMappedAttribute(Attribute* attribute) +{ + if (attribute->name() == valueAttr) + didElementStateChange(); + else if (attribute->name() == maxAttr) + didElementStateChange(); + else + HTMLFormControlElement::parseMappedAttribute(attribute); +} + +void HTMLProgressElement::attach() +{ + HTMLFormControlElement::attach(); + didElementStateChange(); +} + +double HTMLProgressElement::value() const +{ + double value; + bool ok = parseToDoubleForNumberType(fastGetAttribute(valueAttr), &value); + if (!ok || value < 0) + return 0; + return (value > max()) ? max() : value; +} + +void HTMLProgressElement::setValue(double value, ExceptionCode& ec) +{ + if (!isfinite(value)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(valueAttr, String::number(value >= 0 ? value : 0)); +} + +double HTMLProgressElement::max() const +{ + double max; + bool ok = parseToDoubleForNumberType(getAttribute(maxAttr), &max); + if (!ok || max <= 0) + return 1; + return max; +} + +void HTMLProgressElement::setMax(double max, ExceptionCode& ec) +{ + if (!isfinite(max)) { + ec = NOT_SUPPORTED_ERR; + return; + } + setAttribute(maxAttr, String::number(max > 0 ? max : 1)); +} + +double HTMLProgressElement::position() const +{ + if (!isDeterminate()) + return HTMLProgressElement::IndeterminatePosition; + return value() / max(); +} + +bool HTMLProgressElement::isDeterminate() const +{ + return fastHasAttribute(valueAttr); +} + +void HTMLProgressElement::didElementStateChange() +{ + m_value->setWidthPercentage(position() * 100); + if (renderer()) { + RenderProgress* render = toRenderProgress(renderer()); + bool wasDeterminate = render->isDeterminate(); + renderer()->updateFromElement(); + if (wasDeterminate != isDeterminate()) + setNeedsStyleRecalc(); + } +} + +void HTMLProgressElement::createShadowSubtree() +{ + RefPtr<ProgressBarElement> bar = ProgressBarElement::create(document()); + m_value = ProgressValueElement::create(document()); + ExceptionCode ec = 0; + bar->appendChild(m_value, ec); + ensureShadowRoot()->appendChild(bar, ec); +} + +} // namespace +#endif diff --git a/Source/WebCore/html/HTMLProgressElement.h b/Source/WebCore/html/HTMLProgressElement.h new file mode 100644 index 000000000..2ee4c6ba8 --- /dev/null +++ b/Source/WebCore/html/HTMLProgressElement.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * 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 HTMLProgressElement_h +#define HTMLProgressElement_h + +#if ENABLE(PROGRESS_TAG) +#include "HTMLFormControlElement.h" + +namespace WebCore { + +class ProgressValueElement; + +class HTMLProgressElement : public HTMLFormControlElement { +public: + static const double IndeterminatePosition; + static const double InvalidPosition; + + static PassRefPtr<HTMLProgressElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + double value() const; + void setValue(double, ExceptionCode&); + + double max() const; + void setMax(double, ExceptionCode&); + + double position() const; + + bool isDeterminate() const; + + virtual bool canContainRangeEndPoint() const { return false; } + +private: + HTMLProgressElement(const QualifiedName&, Document*, HTMLFormElement*); + virtual ~HTMLProgressElement(); + + virtual bool supportsFocus() const; + + virtual bool recalcWillValidate() const { return false; } + + virtual const AtomicString& formControlType() const; + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + virtual void parseMappedAttribute(Attribute*); + + virtual void attach(); + + void didElementStateChange(); + void createShadowSubtree(); + + RefPtr<ProgressValueElement> m_value; +}; + +} // namespace + +#endif +#endif diff --git a/Source/WebCore/html/HTMLProgressElement.idl b/Source/WebCore/html/HTMLProgressElement.idl new file mode 100644 index 000000000..b49252cad --- /dev/null +++ b/Source/WebCore/html/HTMLProgressElement.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * + * 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. + */ + +module html { + interface [ + Conditional=PROGRESS_TAG + ] HTMLProgressElement : HTMLElement { + attribute double value + setter raises(DOMException); + attribute double max + setter raises(DOMException); + readonly attribute double position; + readonly attribute HTMLFormElement form; + readonly attribute NodeList labels; + }; + +} diff --git a/Source/WebCore/html/HTMLPropertiesCollection.cpp b/Source/WebCore/html/HTMLPropertiesCollection.cpp new file mode 100644 index 000000000..439bae68c --- /dev/null +++ b/Source/WebCore/html/HTMLPropertiesCollection.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(MICRODATA) + +#include "HTMLPropertiesCollection.h" + +#include "DOMSettableTokenList.h" +#include "DOMStringList.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "Node.h" + +namespace WebCore { + +using namespace HTMLNames; + +static inline bool compareTreeOrder(Node* node1, Node* node2) +{ + return (node2->compareDocumentPosition(node1) & (Node::DOCUMENT_POSITION_PRECEDING | Node::DOCUMENT_POSITION_DISCONNECTED)) == Node::DOCUMENT_POSITION_PRECEDING; +} + +PassRefPtr<HTMLPropertiesCollection> HTMLPropertiesCollection::create(PassRefPtr<Node> itemNode) +{ + return adoptRef(new HTMLPropertiesCollection(itemNode)); +} + +HTMLPropertiesCollection::HTMLPropertiesCollection(PassRefPtr<Node> itemNode) + : HTMLCollection(itemNode, ItemProperties) + , m_propertyNames(DOMStringList::create()) +{ +} + +HTMLPropertiesCollection::~HTMLPropertiesCollection() +{ +} + +void HTMLPropertiesCollection::findPropetiesOfAnItem(Node* root) const +{ + // 5.2.5 Associating names with items. + Vector<Node*> memory; + + memory.append(root); + + Vector<Node*> pending; + // Add the child elements of root, if any, to pending. + for (Node* child = root->firstChild(); child; child = child->nextSibling()) + if (child->isHTMLElement()) + pending.append(child); + + // If root has an itemref attribute, split the value of that itemref attribute on spaces. + // For each resulting token ID, if there is an element in the home subtree of root with the ID ID, + // then add the first such element to pending. + if (toHTMLElement(root)->fastHasAttribute(itemrefAttr)) { + DOMSettableTokenList* itemRef = root->itemRef(); + + for (size_t i = 0; i < itemRef->length(); ++i) { + AtomicString id = itemRef->item(i); + + Element* element = root->document()->getElementById(id); + if (element && element->isHTMLElement()) + pending.append(element); + } + } + + // Loop till we have processed all pending elements + while (!pending.isEmpty()) { + + // Remove first element from pending and let current be that element. + Node* current = pending[0]; + pending.remove(0); + + // If current is already in memory, there is a microdata error; + if (memory.contains(current)) { + // microdata error; + continue; + } + + memory.append(current); + + // If current does not have an itemscope attribute, then: add all the child elements of current to pending. + HTMLElement* element = toHTMLElement(current); + if (!element->fastHasAttribute(itemscopeAttr)) { + for (Node* child = current->firstChild(); child; child = child->nextSibling()) + if (child->isHTMLElement()) + pending.append(child); + } + + // If current has an itemprop attribute specified, add it to results. + if (element->fastHasAttribute(itempropAttr)) + m_properties.append(current); + } +} + +unsigned HTMLPropertiesCollection::length() const +{ + if (!base()) + return 0; + + if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr)) + return 0; + + m_properties.clear(); + findPropetiesOfAnItem(base()); + return m_properties.size(); +} + +Node* HTMLPropertiesCollection::item(unsigned index) const +{ + if (!base()) + return 0; + + if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr)) + return 0; + + m_properties.clear(); + findPropetiesOfAnItem(base()); + + if (m_properties.size() <= index) + return 0; + + std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder); + return m_properties[index]; +} + +PassRefPtr<DOMStringList> HTMLPropertiesCollection::names() const +{ + m_properties.clear(); + m_propertyNames->clear(); + + if (!base()) + return 0; + + if (!base()->isHTMLElement() || !toHTMLElement(base())->fastHasAttribute(itemscopeAttr)) + return m_propertyNames; + + findPropetiesOfAnItem(base()); + + std::sort(m_properties.begin(), m_properties.end(), compareTreeOrder); + + for (size_t i = 0; i < m_properties.size(); ++i) { + // For each item properties, split the value of that itemprop attribute on spaces. + // Add all tokens to property names, with the order preserved but with duplicates removed. + DOMSettableTokenList* itemProperty = m_properties[i]->itemProp(); + for (size_t i = 0; i < itemProperty->length(); ++i) { + AtomicString propertyName = itemProperty->item(i); + if (m_propertyNames->isEmpty() || !m_propertyNames->contains(propertyName)) + m_propertyNames->append(propertyName); + } + } + + return m_propertyNames; +} + +} // namespace WebCore + +#endif // ENABLE(MICRODATA) diff --git a/Source/WebCore/html/HTMLPropertiesCollection.h b/Source/WebCore/html/HTMLPropertiesCollection.h new file mode 100644 index 000000000..ca822f160 --- /dev/null +++ b/Source/WebCore/html/HTMLPropertiesCollection.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLPropertiesCollection_h +#define HTMLPropertiesCollection_h + +#if ENABLE(MICRODATA) + +#include "HTMLCollection.h" + +namespace WebCore { + +class DOMStringList; + +class HTMLPropertiesCollection : public HTMLCollection { +public: + static PassRefPtr<HTMLPropertiesCollection> create(PassRefPtr<Node>); + virtual ~HTMLPropertiesCollection(); + + unsigned length() const OVERRIDE; + + virtual Node* item(unsigned) const OVERRIDE; + + PassRefPtr<DOMStringList> names() const; + +private: + HTMLPropertiesCollection(PassRefPtr<Node>); + + void findPropetiesOfAnItem(Node* current) const; + + mutable Vector<Node*> m_properties; + mutable RefPtr<DOMStringList> m_propertyNames; +}; + +} // namespace WebCore + +#endif // ENABLE(MICRODATA) + +#endif // HTMLPropertiesCollection_h diff --git a/Source/WebCore/html/HTMLPropertiesCollection.idl b/Source/WebCore/html/HTMLPropertiesCollection.idl new file mode 100644 index 000000000..1697ea904 --- /dev/null +++ b/Source/WebCore/html/HTMLPropertiesCollection.idl @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=MICRODATA, + HasIndexGetter + ] HTMLPropertiesCollection : HTMLCollection { + readonly attribute unsigned long length; + Node item(in unsigned long index); + + readonly attribute DOMStringList names; + + // FIXME: override inherited namedItem() + }; +} diff --git a/Source/WebCore/html/HTMLQuoteElement.cpp b/Source/WebCore/html/HTMLQuoteElement.cpp new file mode 100644 index 000000000..dfc14cbe5 --- /dev/null +++ b/Source/WebCore/html/HTMLQuoteElement.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2003, 2006, 2010 Apple 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 "HTMLQuoteElement.h" + +#include "Document.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLQuoteElement::HTMLQuoteElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(qTag) || hasTagName(blockquoteTag)); +} + +PassRefPtr<HTMLQuoteElement> HTMLQuoteElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLQuoteElement(tagName, document)); +} + +void HTMLQuoteElement::insertedIntoDocument() +{ + if (hasTagName(qTag)) + document()->setUsesBeforeAfterRules(true); + + HTMLElement::insertedIntoDocument(); +} + +bool HTMLQuoteElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == citeAttr || HTMLElement::isURLAttribute(attribute); +} + +} diff --git a/Source/WebCore/html/HTMLQuoteElement.h b/Source/WebCore/html/HTMLQuoteElement.h new file mode 100644 index 000000000..d378de472 --- /dev/null +++ b/Source/WebCore/html/HTMLQuoteElement.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2010 Apple 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 HTMLQuoteElement_h +#define HTMLQuoteElement_h + +#include <wtf/Forward.h> +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLQuoteElement : public HTMLElement { +public: + static PassRefPtr<HTMLQuoteElement> create(const QualifiedName&, Document*); + +private: + HTMLQuoteElement(const QualifiedName&, Document*); + + virtual void insertedIntoDocument(); + virtual bool isURLAttribute(Attribute*) const; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLQuoteElement.idl b/Source/WebCore/html/HTMLQuoteElement.idl new file mode 100644 index 000000000..fa1bcdb57 --- /dev/null +++ b/Source/WebCore/html/HTMLQuoteElement.idl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLQuoteElement : HTMLElement { + attribute [Reflect, URL] DOMString cite; + }; +} diff --git a/Source/WebCore/html/HTMLScriptElement.cpp b/Source/WebCore/html/HTMLScriptElement.cpp new file mode 100644 index 000000000..9b18ec5d3 --- /dev/null +++ b/Source/WebCore/html/HTMLScriptElement.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple 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 "HTMLScriptElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "HTMLNames.h" +#include "ScriptEventListener.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLScriptElement::HTMLScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted) + : HTMLElement(tagName, document) + , ScriptElement(this, wasInsertedByParser, alreadyStarted) +{ + ASSERT(hasTagName(scriptTag)); +} + +PassRefPtr<HTMLScriptElement> HTMLScriptElement::create(const QualifiedName& tagName, Document* document, bool wasInsertedByParser) +{ + return adoptRef(new HTMLScriptElement(tagName, document, wasInsertedByParser, false)); +} + +bool HTMLScriptElement::isURLAttribute(Attribute* attr) const +{ + return attr->name() == srcAttr || HTMLElement::isURLAttribute(attr); +} + +void HTMLScriptElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + ScriptElement::childrenChanged(); + HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +void HTMLScriptElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + if (attr->name() == asyncAttr) + handleAsyncAttribute(); + HTMLElement::attributeChanged(attr, preserveDecls); +} + +void HTMLScriptElement::parseMappedAttribute(Attribute* attr) +{ + const QualifiedName& attrName = attr->name(); + + if (attrName == srcAttr) + handleSourceAttribute(attr->value()); + else if (attrName == onloadAttr) + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); + else if (attrName == onbeforeloadAttr) + setAttributeEventListener(eventNames().beforeloadEvent, createAttributeEventListener(this, attr)); + else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLScriptElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + ScriptElement::insertedIntoDocument(); +} + +void HTMLScriptElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + ScriptElement::removedFromDocument(); +} + +void HTMLScriptElement::setText(const String &value) +{ + ExceptionCode ec = 0; + int numChildren = childNodeCount(); + + if (numChildren == 1 && firstChild()->isTextNode()) { + static_cast<Text*>(firstChild())->setData(value, ec); + return; + } + + if (numChildren > 0) + removeChildren(); + + appendChild(document()->createTextNode(value.impl()), ec); +} + +void HTMLScriptElement::setAsync(bool async) +{ + setBooleanAttribute(asyncAttr, async); + handleAsyncAttribute(); +} + +bool HTMLScriptElement::async() const +{ + return fastHasAttribute(asyncAttr) || forceAsync(); +} + +KURL HTMLScriptElement::src() const +{ + return document()->completeURL(sourceAttributeValue()); +} + +void HTMLScriptElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, src()); +} + +String HTMLScriptElement::sourceAttributeValue() const +{ + return getAttribute(srcAttr).string(); +} + +String HTMLScriptElement::charsetAttributeValue() const +{ + return getAttribute(charsetAttr).string(); +} + +String HTMLScriptElement::typeAttributeValue() const +{ + return getAttribute(typeAttr).string(); +} + +String HTMLScriptElement::languageAttributeValue() const +{ + return getAttribute(languageAttr).string(); +} + +String HTMLScriptElement::forAttributeValue() const +{ + return getAttribute(forAttr).string(); +} + +String HTMLScriptElement::eventAttributeValue() const +{ + return getAttribute(eventAttr).string(); +} + +bool HTMLScriptElement::asyncAttributeValue() const +{ + return fastHasAttribute(asyncAttr); +} + +bool HTMLScriptElement::deferAttributeValue() const +{ + return fastHasAttribute(deferAttr); +} + +bool HTMLScriptElement::hasSourceAttribute() const +{ + return fastHasAttribute(srcAttr); +} + +void HTMLScriptElement::dispatchLoadEvent() +{ + ASSERT(!haveFiredLoadEvent()); + setHaveFiredLoadEvent(true); + + dispatchEvent(Event::create(eventNames().loadEvent, false, false)); +} + +PassRefPtr<Element> HTMLScriptElement::cloneElementWithoutAttributesAndChildren() +{ + return adoptRef(new HTMLScriptElement(tagQName(), document(), false, alreadyStarted())); +} + +} diff --git a/Source/WebCore/html/HTMLScriptElement.h b/Source/WebCore/html/HTMLScriptElement.h new file mode 100644 index 000000000..ee6f7b560 --- /dev/null +++ b/Source/WebCore/html/HTMLScriptElement.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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 HTMLScriptElement_h +#define HTMLScriptElement_h + +#include "ScriptElement.h" +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLScriptElement : public HTMLElement, public ScriptElement { +public: + static PassRefPtr<HTMLScriptElement> create(const QualifiedName&, Document*, bool wasInsertedByParser); + + String text() const { return scriptContent(); } + void setText(const String&); + + KURL src() const; + + void setAsync(bool); + bool async() const; + +private: + HTMLScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool alreadyStarted); + + virtual void parseMappedAttribute(Attribute*); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + virtual void attributeChanged(Attribute*, bool preserveDecls = false); + + virtual bool isURLAttribute(Attribute*) const; + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + virtual String sourceAttributeValue() const; + virtual String charsetAttributeValue() const; + virtual String typeAttributeValue() const; + virtual String languageAttributeValue() const; + virtual String forAttributeValue() const; + virtual String eventAttributeValue() const; + virtual bool asyncAttributeValue() const; + virtual bool deferAttributeValue() const; + virtual bool hasSourceAttribute() const; + + virtual void dispatchLoadEvent(); + + virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren(); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLScriptElement.idl b/Source/WebCore/html/HTMLScriptElement.idl new file mode 100644 index 000000000..fa799cb2c --- /dev/null +++ b/Source/WebCore/html/HTMLScriptElement.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLScriptElement : HTMLElement { + attribute [ConvertNullToNullString] DOMString text; + attribute [Reflect=for] DOMString htmlFor; + attribute [Reflect] DOMString event; + attribute [Reflect] DOMString charset; + attribute boolean async; + attribute [Reflect] boolean defer; + attribute [Reflect, URL] DOMString src; + attribute [Reflect] DOMString type; + }; +} diff --git a/Source/WebCore/html/HTMLSelectElement.cpp b/Source/WebCore/html/HTMLSelectElement.cpp new file mode 100644 index 000000000..95421f30b --- /dev/null +++ b/Source/WebCore/html/HTMLSelectElement.cpp @@ -0,0 +1,1521 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * + * 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 "HTMLSelectElement.h" + +#include "AXObjectCache.h" +#include "Attribute.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "EventNames.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLFormElement.h" +#include "HTMLNames.h" +#include "HTMLOptGroupElement.h" +#include "HTMLOptionElement.h" +#include "HTMLOptionsCollection.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "Page.h" +#include "RenderListBox.h" +#include "RenderMenuList.h" +#include "RenderTheme.h" +#include "ScriptEventListener.h" +#include "SpatialNavigation.h" +#include <wtf/text/StringBuilder.h> +#include <wtf/unicode/Unicode.h> + +using namespace std; +using namespace WTF::Unicode; + +namespace WebCore { + +using namespace HTMLNames; + +// Upper limit agreed upon with representatives of Opera and Mozilla. +static const unsigned maxSelectItems = 10000; + +// Configure platform-specific behavior when focused pop-up receives arrow/space/return keystroke. +// (PLATFORM(MAC) and PLATFORM(GTK) are always false in Chromium, hence the extra tests.) +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) +#define ARROW_KEYS_POP_MENU 1 +#define SPACE_OR_RETURN_POP_MENU 0 +#elif PLATFORM(GTK) || (PLATFORM(CHROMIUM) && OS(UNIX)) +#define ARROW_KEYS_POP_MENU 0 +#define SPACE_OR_RETURN_POP_MENU 1 +#else +#define ARROW_KEYS_POP_MENU 0 +#define SPACE_OR_RETURN_POP_MENU 0 +#endif + +static const DOMTimeStamp typeAheadTimeout = 1000; + +HTMLSelectElement::HTMLSelectElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLFormControlElementWithState(tagName, document, form) + , m_lastCharTime(0) + , m_size(0) + , m_lastOnChangeIndex(-1) + , m_activeSelectionAnchorIndex(-1) + , m_activeSelectionEndIndex(-1) + , m_repeatingChar(0) + , m_isProcessingUserDrivenChange(false) + , m_multiple(false) + , m_activeSelectionState(false) + , m_shouldRecalcListItems(false) +{ + ASSERT(hasTagName(selectTag)); +} + +HTMLSelectElement::~HTMLSelectElement() +{ + if (m_optionsCollection) + m_optionsCollection->detachFromNode(); +} + +PassRefPtr<HTMLSelectElement> HTMLSelectElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + ASSERT(tagName.matches(selectTag)); + return adoptRef(new HTMLSelectElement(tagName, document, form)); +} + +const AtomicString& HTMLSelectElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, selectMultiple, ("select-multiple")); + DEFINE_STATIC_LOCAL(const AtomicString, selectOne, ("select-one")); + return m_multiple ? selectMultiple : selectOne; +} + +void HTMLSelectElement::deselectItems(HTMLOptionElement* excludeElement) +{ + deselectItemsWithoutValidation(excludeElement); + setNeedsValidityCheck(); +} + +void HTMLSelectElement::optionSelectedByUser(int optionIndex, bool fireOnChangeNow, bool allowMultipleSelection) +{ + // User interaction such as mousedown events can cause list box select elements to send change events. + // This produces that same behavior for changes triggered by other code running on behalf of the user. + if (!usesMenuList()) { + updateSelectedState(optionIndex, allowMultipleSelection, false); + setNeedsValidityCheck(); + if (fireOnChangeNow) + listBoxOnChange(); + return; + } + + // Bail out if this index is already the selected one, to avoid running unnecessary JavaScript that can mess up + // autofill when there is no actual change (see https://bugs.webkit.org/show_bug.cgi?id=35256 and <rdar://7467917>). + // The selectOption function does not behave this way, possibly because other callers need a change event even + // in cases where the selected option is not change. + if (optionIndex == selectedIndex()) + return; + + selectOption(optionIndex, DeselectOtherOptions | (fireOnChangeNow ? DispatchChangeEvent : 0) | UserDriven); +} + +bool HTMLSelectElement::hasPlaceholderLabelOption() const +{ + // The select element has no placeholder label option if it has an attribute "multiple" specified or a display size of non-1. + // + // The condition "size() > 1" is not compliant with the HTML5 spec as of Dec 3, 2010. "size() != 1" is correct. + // Using "size() > 1" here because size() may be 0 in WebKit. + // See the discussion at https://bugs.webkit.org/show_bug.cgi?id=43887 + // + // "0 size()" happens when an attribute "size" is absent or an invalid size attribute is specified. + // In this case, the display size should be assumed as the default. + // The default display size is 1 for non-multiple select elements, and 4 for multiple select elements. + // + // Finally, if size() == 0 and non-multiple, the display size can be assumed as 1. + if (multiple() || size() > 1) + return false; + + int listIndex = optionToListIndex(0); + ASSERT(listIndex >= 0); + if (listIndex < 0) + return false; + HTMLOptionElement* option = static_cast<HTMLOptionElement*>(listItems()[listIndex]); + return !listIndex && option->value().isEmpty(); +} + +bool HTMLSelectElement::valueMissing() const +{ + if (!isRequiredFormControl()) + return false; + + int firstSelectionIndex = selectedIndex(); + + // If a non-placeholer label option is selected (firstSelectionIndex > 0), it's not value-missing. + return firstSelectionIndex < 0 || (!firstSelectionIndex && hasPlaceholderLabelOption()); +} + +void HTMLSelectElement::listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow) +{ + if (!multiple()) + optionSelectedByUser(listToOptionIndex(listIndex), fireOnChangeNow, false); + else { + updateSelectedState(listIndex, allowMultiplySelections, shift); + setNeedsValidityCheck(); + if (fireOnChangeNow) + listBoxOnChange(); + } +} + +bool HTMLSelectElement::usesMenuList() const +{ + const Page* page = document()->page(); + RefPtr<RenderTheme> renderTheme = page ? page->theme() : RenderTheme::defaultTheme(); + if (renderTheme->delegatesMenuListRendering()) + return true; + + return !m_multiple && m_size <= 1; +} + +int HTMLSelectElement::activeSelectionStartListIndex() const +{ + if (m_activeSelectionAnchorIndex >= 0) + return m_activeSelectionAnchorIndex; + return optionToListIndex(selectedIndex()); +} + +int HTMLSelectElement::activeSelectionEndListIndex() const +{ + if (m_activeSelectionEndIndex >= 0) + return m_activeSelectionEndIndex; + return lastSelectedListIndex(); +} + +void HTMLSelectElement::add(HTMLElement* element, HTMLElement* before, ExceptionCode& ec) +{ + // Make sure the element is ref'd and deref'd so we don't leak it. + RefPtr<HTMLElement> protectNewChild(element); + + if (!element || !(element->hasLocalName(optionTag) || element->hasLocalName(hrTag))) + return; + + insertBefore(element, before, ec); + setNeedsValidityCheck(); +} + +void HTMLSelectElement::remove(int optionIndex) +{ + int listIndex = optionToListIndex(optionIndex); + if (listIndex < 0) + return; + + ExceptionCode ec; + listItems()[listIndex]->remove(ec); +} + +void HTMLSelectElement::remove(HTMLOptionElement* option) +{ + if (option->ownerSelectElement() != this) + return; + + ExceptionCode ec; + option->remove(ec); +} + +String HTMLSelectElement::value() const +{ + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); i++) { + if (items[i]->hasLocalName(optionTag) && static_cast<HTMLOptionElement*>(items[i])->selected()) + return static_cast<HTMLOptionElement*>(items[i])->value(); + } + return ""; +} + +void HTMLSelectElement::setValue(const String &value) +{ + if (value.isNull()) + return; + // find the option with value() matching the given parameter + // and make it the current selection. + const Vector<HTMLElement*>& items = listItems(); + unsigned optionIndex = 0; + for (unsigned i = 0; i < items.size(); i++) { + if (items[i]->hasLocalName(optionTag)) { + if (static_cast<HTMLOptionElement*>(items[i])->value() == value) { + setSelectedIndex(optionIndex); + return; + } + optionIndex++; + } + } +} + +void HTMLSelectElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == sizeAttr) { + int oldSize = m_size; + // Set the attribute value to a number. + // This is important since the style rules for this attribute can determine the appearance property. + int size = attr->value().toInt(); + String attrSize = String::number(size); + if (attrSize != attr->value()) + attr->setValue(attrSize); + size = max(size, 1); + + // Ensure that we've determined selectedness of the items at least once prior to changing the size. + if (oldSize != size) + updateListItemSelectedStates(); + + m_size = size; + setNeedsValidityCheck(); + if (m_size != oldSize && attached()) { + reattach(); + setRecalcListItems(); + } + } else if (attr->name() == multipleAttr) + parseMultipleAttribute(attr); + else if (attr->name() == accesskeyAttr) { + // FIXME: ignore for the moment. + } else if (attr->name() == alignAttr) { + // Don't map 'align' attribute. This matches what Firefox, Opera and IE do. + // See http://bugs.webkit.org/show_bug.cgi?id=12072 + } else if (attr->name() == onchangeAttr) + setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr)); + else + HTMLFormControlElementWithState::parseMappedAttribute(attr); +} + +bool HTMLSelectElement::isKeyboardFocusable(KeyboardEvent* event) const +{ + if (renderer()) + return isFocusable(); + return HTMLFormControlElementWithState::isKeyboardFocusable(event); +} + +bool HTMLSelectElement::isMouseFocusable() const +{ + if (renderer()) + return isFocusable(); + return HTMLFormControlElementWithState::isMouseFocusable(); +} + +bool HTMLSelectElement::canSelectAll() const +{ + return !usesMenuList(); +} + +RenderObject* HTMLSelectElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + if (usesMenuList()) + return new (arena) RenderMenuList(this); + return new (arena) RenderListBox(this); +} + +PassRefPtr<HTMLOptionsCollection> HTMLSelectElement::options() +{ + if (!m_optionsCollection) + m_optionsCollection = HTMLOptionsCollection::create(this); + return m_optionsCollection; +} + +void HTMLSelectElement::updateListItemSelectedStates() +{ + if (m_shouldRecalcListItems) + recalcListItems(); +} + +void HTMLSelectElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + setRecalcListItems(); + setNeedsValidityCheck(); + + HTMLFormControlElementWithState::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); + + if (AXObjectCache::accessibilityEnabled() && renderer()) + renderer()->document()->axObjectCache()->childrenChanged(renderer()); +} + +void HTMLSelectElement::optionElementChildrenChanged() +{ + setRecalcListItems(); + setNeedsValidityCheck(); + + if (AXObjectCache::accessibilityEnabled() && renderer()) + renderer()->document()->axObjectCache()->childrenChanged(renderer()); +} + +void HTMLSelectElement::accessKeyAction(bool sendMouseEvents) +{ + focus(); + dispatchSimulatedClick(0, sendMouseEvents); +} + +void HTMLSelectElement::setMultiple(bool multiple) +{ + bool oldMultiple = this->multiple(); + int oldSelectedIndex = selectedIndex(); + setAttribute(multipleAttr, multiple ? "" : 0); + + // Restore selectedIndex after changing the multiple flag to preserve + // selection as single-line and multi-line has different defaults. + if (oldMultiple != this->multiple()) + setSelectedIndex(oldSelectedIndex); +} + +void HTMLSelectElement::setSize(int size) +{ + setAttribute(sizeAttr, String::number(size)); +} + +Node* HTMLSelectElement::namedItem(const AtomicString& name) +{ + return options()->namedItem(name); +} + +Node* HTMLSelectElement::item(unsigned index) +{ + return options()->item(index); +} + +void HTMLSelectElement::setOption(unsigned index, HTMLOptionElement* option, ExceptionCode& ec) +{ + ec = 0; + if (index > maxSelectItems - 1) + index = maxSelectItems - 1; + int diff = index - length(); + HTMLElement* before = 0; + // Out of array bounds? First insert empty dummies. + if (diff > 0) { + setLength(index, ec); + // Replace an existing entry? + } else if (diff < 0) { + before = toHTMLElement(options()->item(index+1)); + remove(index); + } + // Finally add the new element. + if (!ec) { + add(option, before, ec); + if (diff >= 0 && option->selected()) + optionSelectionStateChanged(option, true); + } +} + +void HTMLSelectElement::setLength(unsigned newLen, ExceptionCode& ec) +{ + ec = 0; + if (newLen > maxSelectItems) + newLen = maxSelectItems; + int diff = length() - newLen; + + if (diff < 0) { // Add dummy elements. + do { + RefPtr<Element> option = document()->createElement(optionTag, false); + ASSERT(option); + add(toHTMLElement(option.get()), 0, ec); + if (ec) + break; + } while (++diff); + } else { + const Vector<HTMLElement*>& items = listItems(); + + // Removing children fires mutation events, which might mutate the DOM further, so we first copy out a list + // of elements that we intend to remove then attempt to remove them one at a time. + Vector<RefPtr<Element> > itemsToRemove; + size_t optionIndex = 0; + for (size_t i = 0; i < items.size(); ++i) { + Element* item = items[i]; + if (item->hasLocalName(optionTag) && optionIndex++ >= newLen) { + ASSERT(item->parentNode()); + itemsToRemove.append(item); + } + } + + for (size_t i = 0; i < itemsToRemove.size(); ++i) { + Element* item = itemsToRemove[i].get(); + if (item->parentNode()) + item->parentNode()->removeChild(item, ec); + } + } + setNeedsValidityCheck(); +} + +bool HTMLSelectElement::isRequiredFormControl() const +{ + return required(); +} + +// Returns the 1st valid item |skip| items from |listIndex| in direction |direction| if there is one. +// Otherwise, it returns the valid item closest to that boundary which is past |listIndex| if there is one. +// Otherwise, it returns |listIndex|. +// Valid means that it is enabled and an option element. +int HTMLSelectElement::nextValidIndex(int listIndex, SkipDirection direction, int skip) const +{ + ASSERT(direction == -1 || direction == 1); + const Vector<HTMLElement*>& listItems = this->listItems(); + int lastGoodIndex = listIndex; + int size = listItems.size(); + for (listIndex += direction; listIndex >= 0 && listIndex < size; listIndex += direction) { + --skip; + if (!listItems[listIndex]->disabled() && listItems[listIndex]->hasTagName(optionTag)) { + lastGoodIndex = listIndex; + if (skip <= 0) + break; + } + } + return lastGoodIndex; +} + +int HTMLSelectElement::nextSelectableListIndex(int startIndex) const +{ + return nextValidIndex(startIndex, SkipForwards, 1); +} + +int HTMLSelectElement::previousSelectableListIndex(int startIndex) const +{ + if (startIndex == -1) + startIndex = listItems().size(); + return nextValidIndex(startIndex, SkipBackwards, 1); +} + +int HTMLSelectElement::firstSelectableListIndex() const +{ + const Vector<HTMLElement*>& items = listItems(); + int index = nextValidIndex(items.size(), SkipBackwards, INT_MAX); + if (static_cast<size_t>(index) == items.size()) + return -1; + return index; +} + +int HTMLSelectElement::lastSelectableListIndex() const +{ + return nextValidIndex(-1, SkipForwards, INT_MAX); +} + +// Returns the index of the next valid item one page away from |startIndex| in direction |direction|. +int HTMLSelectElement::nextSelectableListIndexPageAway(int startIndex, SkipDirection direction) const +{ + const Vector<HTMLElement*>& items = listItems(); + // Can't use m_size because renderer forces a minimum size. + int pageSize = 0; + if (renderer()->isListBox()) + pageSize = toRenderListBox(renderer())->size() - 1; // -1 so we still show context. + + // One page away, but not outside valid bounds. + // If there is a valid option item one page away, the index is chosen. + // If there is no exact one page away valid option, returns startIndex or the most far index. + int edgeIndex = (direction == SkipForwards) ? 0 : (items.size() - 1); + int skipAmount = pageSize + ((direction == SkipForwards) ? startIndex : (edgeIndex - startIndex)); + return nextValidIndex(edgeIndex, direction, skipAmount); +} + +void HTMLSelectElement::selectAll() +{ + ASSERT(!usesMenuList()); + if (!renderer() || !m_multiple) + return; + + // Save the selection so it can be compared to the new selectAll selection + // when dispatching change events. + saveLastSelection(); + + m_activeSelectionState = true; + setActiveSelectionAnchorIndex(nextSelectableListIndex(-1)); + setActiveSelectionEndIndex(previousSelectableListIndex(-1)); + + updateListBoxSelection(false); + listBoxOnChange(); + setNeedsValidityCheck(); +} + +void HTMLSelectElement::saveLastSelection() +{ + if (usesMenuList()) { + m_lastOnChangeIndex = selectedIndex(); + return; + } + + m_lastOnChangeSelection.clear(); + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + m_lastOnChangeSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected()); + } +} + +void HTMLSelectElement::setActiveSelectionAnchorIndex(int index) +{ + m_activeSelectionAnchorIndex = index; + + // Cache the selection state so we can restore the old selection as the new + // selection pivots around this anchor index. + m_cachedStateForActiveSelection.clear(); + + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + m_cachedStateForActiveSelection.append(element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected()); + } +} + +void HTMLSelectElement::setActiveSelectionEndIndex(int index) +{ + m_activeSelectionEndIndex = index; +} + +void HTMLSelectElement::updateListBoxSelection(bool deselectOtherOptions) +{ + ASSERT(renderer() && (renderer()->isListBox() || m_multiple)); + ASSERT(!listItems().size() || m_activeSelectionAnchorIndex >= 0); + + unsigned start = min(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); + unsigned end = max(m_activeSelectionAnchorIndex, m_activeSelectionEndIndex); + + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->disabled()) + continue; + + if (i >= start && i <= end) + toHTMLOptionElement(element)->setSelectedState(m_activeSelectionState); + else if (deselectOtherOptions || i >= m_cachedStateForActiveSelection.size()) + toHTMLOptionElement(element)->setSelectedState(false); + else + toHTMLOptionElement(element)->setSelectedState(m_cachedStateForActiveSelection[i]); + } + + scrollToSelection(); + setNeedsValidityCheck(); +} + +void HTMLSelectElement::listBoxOnChange() +{ + ASSERT(!usesMenuList() || m_multiple); + + const Vector<HTMLElement*>& items = listItems(); + + // If the cached selection list is empty, or the size has changed, then fire + // dispatchFormControlChangeEvent, and return early. + if (m_lastOnChangeSelection.isEmpty() || m_lastOnChangeSelection.size() != items.size()) { + dispatchFormControlChangeEvent(); + return; + } + + // Update m_lastOnChangeSelection and fire dispatchFormControlChangeEvent. + bool fireOnChange = false; + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected(); + if (selected != m_lastOnChangeSelection[i]) + fireOnChange = true; + m_lastOnChangeSelection[i] = selected; + } + + if (fireOnChange) + dispatchFormControlChangeEvent(); +} + +void HTMLSelectElement::dispatchChangeEventForMenuList() +{ + ASSERT(usesMenuList()); + + int selected = selectedIndex(); + if (m_lastOnChangeIndex != selected && m_isProcessingUserDrivenChange) { + m_lastOnChangeIndex = selected; + m_isProcessingUserDrivenChange = false; + dispatchFormControlChangeEvent(); + } +} + +void HTMLSelectElement::scrollToSelection() +{ + if (usesMenuList()) + return; + + if (RenderObject* renderer = this->renderer()) + toRenderListBox(renderer)->selectionChanged(); +} + +void HTMLSelectElement::setOptionsChangedOnRenderer() +{ + if (RenderObject* renderer = this->renderer()) { + if (usesMenuList()) + toRenderMenuList(renderer)->setOptionsChanged(true); + else + toRenderListBox(renderer)->setOptionsChanged(true); + } +} + +const Vector<HTMLElement*>& HTMLSelectElement::listItems() const +{ + if (m_shouldRecalcListItems) + recalcListItems(); + else { +#if !ASSERT_DISABLED + Vector<HTMLElement*> items = m_listItems; + recalcListItems(false); + ASSERT(items == m_listItems); +#endif + } + + return m_listItems; +} + +void HTMLSelectElement::setRecalcListItems() +{ + m_shouldRecalcListItems = true; + // Manual selection anchor is reset when manipulating the select programmatically. + m_activeSelectionAnchorIndex = -1; + setOptionsChangedOnRenderer(); + setNeedsStyleRecalc(); + if (!inDocument() && m_optionsCollection) + m_optionsCollection->invalidateCacheIfNeeded(); +} + +void HTMLSelectElement::recalcListItems(bool updateSelectedStates) const +{ + m_listItems.clear(); + + m_shouldRecalcListItems = false; + + HTMLOptionElement* foundSelected = 0; + HTMLOptionElement* firstOption = 0; + for (Node* currentNode = this->firstChild(); currentNode;) { + if (!currentNode->isHTMLElement()) { + currentNode = currentNode->traverseNextSibling(this); + continue; + } + + HTMLElement* current = toHTMLElement(currentNode); + + // optgroup tags may not nest. However, both FireFox and IE will + // flatten the tree automatically, so we follow suit. + // (http://www.w3.org/TR/html401/interact/forms.html#h-17.6) + if (current->hasTagName(optgroupTag)) { + m_listItems.append(current); + if (current->firstChild()) { + currentNode = current->firstChild(); + continue; + } + } + + if (current->hasTagName(optionTag)) { + m_listItems.append(current); + + if (updateSelectedStates && !m_multiple) { + HTMLOptionElement* option = toHTMLOptionElement(current); + if (!firstOption) + firstOption = option; + if (option->selected()) { + if (foundSelected) + foundSelected->setSelectedState(false); + foundSelected = option; + } else if (m_size <= 1 && !foundSelected && !option->disabled()) { + foundSelected = option; + foundSelected->setSelectedState(true); + } + } + } + + if (current->hasTagName(hrTag)) + m_listItems.append(current); + + // In conforming HTML code, only <optgroup> and <option> will be found + // within a <select>. We call traverseNextSibling so that we only step + // into those tags that we choose to. For web-compat, we should cope + // with the case where odd tags like a <div> have been added but we + // handle this because such tags have already been removed from the + // <select>'s subtree at this point. + currentNode = currentNode->traverseNextSibling(this); + } + + if (!foundSelected && m_size <= 1 && firstOption && !firstOption->selected()) + firstOption->setSelectedState(true); +} + +int HTMLSelectElement::selectedIndex() const +{ + unsigned index = 0; + + // Return the number of the first option selected. + const Vector<HTMLElement*>& items = listItems(); + for (size_t i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + if (element->hasTagName(optionTag)) { + if (toHTMLOptionElement(element)->selected()) + return index; + ++index; + } + } + + return -1; +} + +void HTMLSelectElement::setSelectedIndex(int index) +{ + selectOption(index, DeselectOtherOptions); +} + +void HTMLSelectElement::optionSelectionStateChanged(HTMLOptionElement* option, bool optionIsSelected) +{ + ASSERT(option->ownerSelectElement() == this); + if (optionIsSelected) + selectOption(option->index()); + else + selectOption(m_multiple ? -1 : nextSelectableListIndex(-1)); +} + +void HTMLSelectElement::selectOption(int optionIndex, SelectOptionFlags flags) +{ + bool shouldDeselect = !m_multiple || (flags & DeselectOtherOptions); + + const Vector<HTMLElement*>& items = listItems(); + int listIndex = optionToListIndex(optionIndex); + + HTMLElement* element = 0; + if (listIndex >= 0) { + element = items[listIndex]; + if (element->hasTagName(optionTag)) { + if (m_activeSelectionAnchorIndex < 0 || shouldDeselect) + setActiveSelectionAnchorIndex(listIndex); + if (m_activeSelectionEndIndex < 0 || shouldDeselect) + setActiveSelectionEndIndex(listIndex); + toHTMLOptionElement(element)->setSelectedState(true); + } + } + + if (shouldDeselect) + deselectItemsWithoutValidation(element); + + // For the menu list case, this is what makes the selected element appear. + if (RenderObject* renderer = this->renderer()) + renderer->updateFromElement(); + + scrollToSelection(); + + if (usesMenuList()) { + m_isProcessingUserDrivenChange = flags & UserDriven; + if (flags & DispatchChangeEvent) + dispatchChangeEventForMenuList(); + if (RenderObject* renderer = this->renderer()) { + if (usesMenuList()) + toRenderMenuList(renderer)->didSetSelectedIndex(listIndex); + else if (renderer->isListBox()) + toRenderListBox(renderer)->selectionChanged(); + } + } + + setNeedsValidityCheck(); + if (Frame* frame = document()->frame()) + frame->page()->chrome()->client()->formStateDidChange(this); +} + +int HTMLSelectElement::optionToListIndex(int optionIndex) const +{ + const Vector<HTMLElement*>& items = listItems(); + int listSize = static_cast<int>(items.size()); + if (optionIndex < 0 || optionIndex >= listSize) + return -1; + + int optionIndex2 = -1; + for (int listIndex = 0; listIndex < listSize; ++listIndex) { + if (items[listIndex]->hasTagName(optionTag)) { + ++optionIndex2; + if (optionIndex2 == optionIndex) + return listIndex; + } + } + + return -1; +} + +int HTMLSelectElement::listToOptionIndex(int listIndex) const +{ + const Vector<HTMLElement*>& items = listItems(); + if (listIndex < 0 || listIndex >= static_cast<int>(items.size()) || !items[listIndex]->hasTagName(optionTag)) + return -1; + + // Actual index of option not counting OPTGROUP entries that may be in list. + int optionIndex = 0; + for (int i = 0; i < listIndex; ++i) { + if (items[i]->hasTagName(optionTag)) + ++optionIndex; + } + + return optionIndex; +} + +void HTMLSelectElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode) +{ + // Save the selection so it can be compared to the new selection when + // dispatching change events during blur event dispatch. + if (usesMenuList()) + saveLastSelection(); + HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode); +} + +void HTMLSelectElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode) +{ + // We only need to fire change events here for menu lists, because we fire + // change events for list boxes whenever the selection change is actually made. + // This matches other browsers' behavior. + if (usesMenuList()) + dispatchChangeEventForMenuList(); + HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode); +} + +void HTMLSelectElement::deselectItemsWithoutValidation(HTMLElement* excludeElement) +{ + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + if (element != excludeElement && element->hasTagName(optionTag)) + toHTMLOptionElement(element)->setSelectedState(false); + } +} + +bool HTMLSelectElement::saveFormControlState(String& value) const +{ + const Vector<HTMLElement*>& items = listItems(); + size_t length = items.size(); + StringBuilder builder; + builder.reserveCapacity(length); + for (unsigned i = 0; i < length; ++i) { + HTMLElement* element = items[i]; + bool selected = element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected(); + builder.append(selected ? 'X' : '.'); + } + value = builder.toString(); + return true; +} + +void HTMLSelectElement::restoreFormControlState(const String& state) +{ + recalcListItems(); + + const Vector<HTMLElement*>& items = listItems(); + size_t length = items.size(); + + for (size_t i = 0; i < length; ++i) { + HTMLElement* element = items[i]; + if (element->hasTagName(optionTag)) + toHTMLOptionElement(element)->setSelectedState(state[i] == 'X'); + } + + setOptionsChangedOnRenderer(); + setNeedsValidityCheck(); +} + +void HTMLSelectElement::parseMultipleAttribute(const Attribute* attribute) +{ + bool oldUsesMenuList = usesMenuList(); + m_multiple = !attribute->isNull(); + setNeedsValidityCheck(); + if (oldUsesMenuList != usesMenuList()) + reattachIfAttached(); +} + +bool HTMLSelectElement::appendFormData(FormDataList& list, bool) +{ + const AtomicString& name = formControlName(); + if (name.isEmpty()) + return false; + + bool successful = false; + const Vector<HTMLElement*>& items = listItems(); + + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected() && !toHTMLOptionElement(element)->disabled()) { + list.appendData(name, toHTMLOptionElement(element)->value()); + successful = true; + } + } + + // It's possible that this is a menulist with multiple options and nothing + // will be submitted (!successful). We won't send a unselected non-disabled + // option as fallback. This behavior matches to other browsers. + return successful; +} + +void HTMLSelectElement::reset() +{ + HTMLOptionElement* firstOption = 0; + HTMLOptionElement* selectedOption = 0; + + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); ++i) { + HTMLElement* element = items[i]; + if (!element->hasTagName(optionTag)) + continue; + + if (items[i]->fastHasAttribute(selectedAttr)) { + if (selectedOption && !m_multiple) + selectedOption->setSelectedState(false); + toHTMLOptionElement(element)->setSelectedState(true); + selectedOption = toHTMLOptionElement(element); + } else + toHTMLOptionElement(element)->setSelectedState(false); + + if (!firstOption) + firstOption = toHTMLOptionElement(element); + } + + if (!selectedOption && firstOption && !m_multiple && m_size <= 1) + firstOption->setSelectedState(true); + + setOptionsChangedOnRenderer(); + setNeedsStyleRecalc(); + setNeedsValidityCheck(); +} + +#if !PLATFORM(WIN) || OS(WINCE) +bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event) +{ +#if ARROW_KEYS_POP_MENU + if (!isSpatialNavigationEnabled(document()->frame())) { + if (event->keyIdentifier() == "Down" || event->keyIdentifier() == "Up") { + focus(); + // Calling focus() may cause us to lose our renderer. Return true so + // that our caller doesn't process the event further, but don't set + // the event as handled. + if (!renderer()) + return true; + + // Save the selection so it can be compared to the new selection + // when dispatching change events during selectOption, which + // gets called from RenderMenuList::valueChanged, which gets called + // after the user makes a selection from the menu. + saveLastSelection(); + if (RenderMenuList* menuList = toRenderMenuList(renderer())) + menuList->showPopup(); + event->setDefaultHandled(); + } + return true; + } +#else + UNUSED_PARAM(event); +#endif + return false; +} +#endif + +void HTMLSelectElement::menuListDefaultEventHandler(Event* event) +{ + if (event->type() == eventNames().keydownEvent) { + if (!renderer() || !event->isKeyboardEvent()) + return; + + if (platformHandleKeydownEvent(static_cast<KeyboardEvent*>(event))) + return; + + // When using spatial navigation, we want to be able to navigate away + // from the select element when the user hits any of the arrow keys, + // instead of changing the selection. + if (isSpatialNavigationEnabled(document()->frame())) { + if (!m_activeSelectionState) + return; + } + + const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier(); + bool handled = true; + const Vector<HTMLElement*>& listItems = this->listItems(); + int listIndex = optionToListIndex(selectedIndex()); + + if (keyIdentifier == "Down" || keyIdentifier == "Right") + listIndex = nextValidIndex(listIndex, SkipForwards, 1); + else if (keyIdentifier == "Up" || keyIdentifier == "Left") + listIndex = nextValidIndex(listIndex, SkipBackwards, 1); + else if (keyIdentifier == "PageDown") + listIndex = nextValidIndex(listIndex, SkipForwards, 3); + else if (keyIdentifier == "PageUp") + listIndex = nextValidIndex(listIndex, SkipBackwards, 3); + else if (keyIdentifier == "Home") + listIndex = nextValidIndex(-1, SkipForwards, 1); + else if (keyIdentifier == "End") + listIndex = nextValidIndex(listItems.size(), SkipBackwards, 1); + else + handled = false; + + if (handled && static_cast<size_t>(listIndex) < listItems.size()) + selectOption(listToOptionIndex(listIndex), DeselectOtherOptions | DispatchChangeEvent | UserDriven); + + if (handled) + event->setDefaultHandled(); + } + + // Use key press event here since sending simulated mouse events + // on key down blocks the proper sending of the key press event. + if (event->type() == eventNames().keypressEvent) { + if (!renderer() || !event->isKeyboardEvent()) + return; + + int keyCode = static_cast<KeyboardEvent*>(event)->keyCode(); + bool handled = false; + + if (keyCode == ' ' && isSpatialNavigationEnabled(document()->frame())) { + // Use space to toggle arrow key handling for selection change or spatial navigation. + m_activeSelectionState = !m_activeSelectionState; + event->setDefaultHandled(); + return; + } + +#if SPACE_OR_RETURN_POP_MENU + if (keyCode == ' ' || keyCode == '\r') { + focus(); + + // Calling focus() may cause us to lose our renderer, in which case + // do not want to handle the event. + if (!renderer()) + return; + + // Save the selection so it can be compared to the new selection + // when dispatching change events during selectOption, which + // gets called from RenderMenuList::valueChanged, which gets called + // after the user makes a selection from the menu. + saveLastSelection(); + if (RenderMenuList* menuList = toRenderMenuList(renderer())) + menuList->showPopup(); + handled = true; + } +#elif ARROW_KEYS_POP_MENU + if (keyCode == ' ') { + focus(); + + // Calling focus() may cause us to lose our renderer, in which case + // do not want to handle the event. + if (!renderer()) + return; + + // Save the selection so it can be compared to the new selection + // when dispatching change events during selectOption, which + // gets called from RenderMenuList::valueChanged, which gets called + // after the user makes a selection from the menu. + saveLastSelection(); + if (RenderMenuList* menuList = toRenderMenuList(renderer())) + menuList->showPopup(); + handled = true; + } else if (keyCode == '\r') { + if (form()) + form()->submitImplicitly(event, false); + dispatchChangeEventForMenuList(); + handled = true; + } +#endif + if (handled) + event->setDefaultHandled(); + } + + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + focus(); + if (renderer() && renderer()->isMenuList()) { + if (RenderMenuList* menuList = toRenderMenuList(renderer())) { + if (menuList->popupIsVisible()) + menuList->hidePopup(); + else { + // Save the selection so it can be compared to the new + // selection when we call onChange during selectOption, + // which gets called from RenderMenuList::valueChanged, + // which gets called after the user makes a selection from + // the menu. + saveLastSelection(); + menuList->showPopup(); + } + } + } + event->setDefaultHandled(); + } +} + +void HTMLSelectElement::updateSelectedState(int listIndex, bool multi, bool shift) +{ + ASSERT(listIndex >= 0); + + // Save the selection so it can be compared to the new selection when + // dispatching change events during mouseup, or after autoscroll finishes. + saveLastSelection(); + + m_activeSelectionState = true; + + bool shiftSelect = m_multiple && shift; + bool multiSelect = m_multiple && multi && !shift; + + HTMLElement* clickedElement = listItems()[listIndex]; + if (clickedElement->hasTagName(optionTag)) { + // Keep track of whether an active selection (like during drag + // selection), should select or deselect. + if (toHTMLOptionElement(clickedElement)->selected() && multi) + m_activeSelectionState = false; + if (!m_activeSelectionState) + toHTMLOptionElement(clickedElement)->setSelectedState(false); + } + + // If we're not in any special multiple selection mode, then deselect all + // other items, excluding the clicked option. If no option was clicked, then + // this will deselect all items in the list. + if (!shiftSelect && !multiSelect) + deselectItemsWithoutValidation(clickedElement); + + // If the anchor hasn't been set, and we're doing a single selection or a + // shift selection, then initialize the anchor to the first selected index. + if (m_activeSelectionAnchorIndex < 0 && !multiSelect) + setActiveSelectionAnchorIndex(selectedIndex()); + + // Set the selection state of the clicked option. + if (clickedElement->hasTagName(optionTag) && !toHTMLOptionElement(clickedElement)->disabled()) + toHTMLOptionElement(clickedElement)->setSelectedState(true); + + // If there was no selectedIndex() for the previous initialization, or If + // we're doing a single selection, or a multiple selection (using cmd or + // ctrl), then initialize the anchor index to the listIndex that just got + // clicked. + if (m_activeSelectionAnchorIndex < 0 || !shiftSelect) + setActiveSelectionAnchorIndex(listIndex); + + setActiveSelectionEndIndex(listIndex); + updateListBoxSelection(!multiSelect); +} + +void HTMLSelectElement::listBoxDefaultEventHandler(Event* event) +{ + const Vector<HTMLElement*>& listItems = this->listItems(); + + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + focus(); + // Calling focus() may cause us to lose our renderer, in which case do not want to handle the event. + if (!renderer()) + return; + + // Convert to coords relative to the list box if needed. + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toSize(localOffset)); + if (listIndex >= 0) { +#if PLATFORM(MAC) || (PLATFORM(CHROMIUM) && OS(DARWIN)) + updateSelectedState(listIndex, mouseEvent->metaKey(), mouseEvent->shiftKey()); +#else + updateSelectedState(listIndex, mouseEvent->ctrlKey(), mouseEvent->shiftKey()); +#endif + if (Frame* frame = document()->frame()) + frame->eventHandler()->setMouseDownMayStartAutoscroll(); + + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().mousemoveEvent && event->isMouseEvent() && !toRenderBox(renderer())->canBeScrolledAndHasScrollableArea()) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + if (mouseEvent->button() != LeftButton || !mouseEvent->buttonDown()) + return; + + IntPoint localOffset = roundedIntPoint(renderer()->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + int listIndex = toRenderListBox(renderer())->listIndexAtOffset(toSize(localOffset)); + if (listIndex >= 0) { + if (m_multiple) { + setActiveSelectionEndIndex(listIndex); + updateListBoxSelection(false); + } else + updateSelectedState(listIndex, false, false); + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton && document()->frame()->eventHandler()->autoscrollRenderer() != renderer()) { + // This makes sure we fire dispatchFormControlChangeEvent for a single + // click. For drag selection, onChange will fire when the autoscroll + // timer stops. + listBoxOnChange(); + } else if (event->type() == eventNames().keydownEvent) { + if (!event->isKeyboardEvent()) + return; + const String& keyIdentifier = static_cast<KeyboardEvent*>(event)->keyIdentifier(); + + bool handled = false; + int endIndex = 0; + if (m_activeSelectionEndIndex < 0) { + // Initialize the end index + if (keyIdentifier == "Down" || keyIdentifier == "PageDown") { + int startIndex = lastSelectedListIndex(); + handled = true; + if (keyIdentifier == "Down") + endIndex = nextSelectableListIndex(startIndex); + else + endIndex = nextSelectableListIndexPageAway(startIndex, SkipForwards); + } else if (keyIdentifier == "Up" || keyIdentifier == "PageUp") { + int startIndex = optionToListIndex(selectedIndex()); + handled = true; + if (keyIdentifier == "Up") + endIndex = previousSelectableListIndex(startIndex); + else + endIndex = nextSelectableListIndexPageAway(startIndex, SkipBackwards); + } + } else { + // Set the end index based on the current end index. + if (keyIdentifier == "Down") { + endIndex = nextSelectableListIndex(m_activeSelectionEndIndex); + handled = true; + } else if (keyIdentifier == "Up") { + endIndex = previousSelectableListIndex(m_activeSelectionEndIndex); + handled = true; + } else if (keyIdentifier == "PageDown") { + endIndex = nextSelectableListIndexPageAway(m_activeSelectionEndIndex, SkipForwards); + handled = true; + } else if (keyIdentifier == "PageUp") { + endIndex = nextSelectableListIndexPageAway(m_activeSelectionEndIndex, SkipBackwards); + handled = true; + } + } + if (keyIdentifier == "Home") { + endIndex = firstSelectableListIndex(); + handled = true; + } else if (keyIdentifier == "End") { + endIndex = lastSelectableListIndex(); + handled = true; + } + + if (isSpatialNavigationEnabled(document()->frame())) + // Check if the selection moves to the boundary. + if (keyIdentifier == "Left" || keyIdentifier == "Right" || ((keyIdentifier == "Down" || keyIdentifier == "Up") && endIndex == m_activeSelectionEndIndex)) + return; + + if (endIndex >= 0 && handled) { + // Save the selection so it can be compared to the new selection + // when dispatching change events immediately after making the new + // selection. + saveLastSelection(); + + ASSERT_UNUSED(listItems, !listItems.size() || static_cast<size_t>(endIndex) < listItems.size()); + setActiveSelectionEndIndex(endIndex); + + bool selectNewItem = !m_multiple || static_cast<KeyboardEvent*>(event)->shiftKey() || !isSpatialNavigationEnabled(document()->frame()); + if (selectNewItem) + m_activeSelectionState = true; + // If the anchor is unitialized, or if we're going to deselect all + // other options, then set the anchor index equal to the end index. + bool deselectOthers = !m_multiple || (!static_cast<KeyboardEvent*>(event)->shiftKey() && selectNewItem); + if (m_activeSelectionAnchorIndex < 0 || deselectOthers) { + if (deselectOthers) + deselectItemsWithoutValidation(); + setActiveSelectionAnchorIndex(m_activeSelectionEndIndex); + } + + toRenderListBox(renderer())->scrollToRevealElementAtListIndex(endIndex); + if (selectNewItem) { + updateListBoxSelection(deselectOthers); + listBoxOnChange(); + } else + scrollToSelection(); + + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().keypressEvent) { + if (!event->isKeyboardEvent()) + return; + int keyCode = static_cast<KeyboardEvent*>(event)->keyCode(); + + if (keyCode == '\r') { + if (form()) + form()->submitImplicitly(event, false); + event->setDefaultHandled(); + } else if (m_multiple && keyCode == ' ' && isSpatialNavigationEnabled(document()->frame())) { + // Use space to toggle selection change. + m_activeSelectionState = !m_activeSelectionState; + updateSelectedState(listToOptionIndex(m_activeSelectionEndIndex), true /*multi*/, false /*shift*/); + listBoxOnChange(); + event->setDefaultHandled(); + } + } +} + +void HTMLSelectElement::defaultEventHandler(Event* event) +{ + if (!renderer()) + return; + + if (usesMenuList()) + menuListDefaultEventHandler(event); + else + listBoxDefaultEventHandler(event); + if (event->defaultHandled()) + return; + + if (event->type() == eventNames().keypressEvent && event->isKeyboardEvent()) { + KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event); + if (!keyboardEvent->ctrlKey() && !keyboardEvent->altKey() && !keyboardEvent->metaKey() && isPrintableChar(keyboardEvent->charCode())) { + typeAheadFind(keyboardEvent); + event->setDefaultHandled(); + return; + } + } + HTMLFormControlElementWithState::defaultEventHandler(event); +} + +int HTMLSelectElement::lastSelectedListIndex() const +{ + const Vector<HTMLElement*>& items = listItems(); + for (size_t i = items.size(); i;) { + HTMLElement* element = items[--i]; + if (element->hasTagName(optionTag) && toHTMLOptionElement(element)->selected()) + return i; + } + return -1; +} + +static String stripLeadingWhiteSpace(const String& string) +{ + int length = string.length(); + + int i; + for (i = 0; i < length; ++i) { + if (string[i] != noBreakSpace && (string[i] <= 0x7F ? !isASCIISpace(string[i]) : (direction(string[i]) != WhiteSpaceNeutral))) + break; + } + + return string.substring(i, length - i); +} + +void HTMLSelectElement::typeAheadFind(KeyboardEvent* event) +{ + if (event->timeStamp() < m_lastCharTime) + return; + + DOMTimeStamp delta = event->timeStamp() - m_lastCharTime; + m_lastCharTime = event->timeStamp(); + + UChar c = event->charCode(); + + String prefix; + int searchStartOffset = 1; + if (delta > typeAheadTimeout) { + prefix = String(&c, 1); + m_typedString = prefix; + m_repeatingChar = c; + } else { + m_typedString.append(c); + + if (c == m_repeatingChar) { + // The user is likely trying to cycle through all the items starting + // with this character, so just search on the character. + prefix = String(&c, 1); + } else { + m_repeatingChar = 0; + prefix = m_typedString; + searchStartOffset = 0; + } + } + + const Vector<HTMLElement*>& items = listItems(); + int itemCount = items.size(); + if (itemCount < 1) + return; + + int selected = selectedIndex(); + int index = (optionToListIndex(selected >= 0 ? selected : 0) + searchStartOffset) % itemCount; + ASSERT(index >= 0); + + // Compute a case-folded copy of the prefix string before beginning the search for + // a matching element. This code uses foldCase to work around the fact that + // String::startWith does not fold non-ASCII characters. This code can be changed + // to use startWith once that is fixed. + String prefixWithCaseFolded(prefix.foldCase()); + for (int i = 0; i < itemCount; ++i, index = (index + 1) % itemCount) { + HTMLElement* element = items[index]; + if (!element->hasTagName(optionTag) || toHTMLOptionElement(element)->disabled()) + continue; + + // Fold the option string and check if its prefix is equal to the folded prefix. + String text = toHTMLOptionElement(element)->textIndentedToRespectGroupLabel(); + if (stripLeadingWhiteSpace(text).foldCase().startsWith(prefixWithCaseFolded)) { + selectOption(listToOptionIndex(index), DeselectOtherOptions | DispatchChangeEvent | UserDriven); + if (!usesMenuList()) + listBoxOnChange(); + + setOptionsChangedOnRenderer(); + setNeedsStyleRecalc(); + return; + } + } +} + +void HTMLSelectElement::insertedIntoTree(bool deep) +{ + // When the element is created during document parsing, it won't have any + // items yet - but for innerHTML and related methods, this method is called + // after the whole subtree is constructed. + recalcListItems(); + HTMLFormControlElementWithState::insertedIntoTree(deep); +} + +void HTMLSelectElement::accessKeySetSelectedIndex(int index) +{ + // First bring into focus the list box. + if (!focused()) + accessKeyAction(false); + + // If this index is already selected, unselect. otherwise update the selected index. + const Vector<HTMLElement*>& items = listItems(); + int listIndex = optionToListIndex(index); + if (listIndex >= 0) { + HTMLElement* element = items[listIndex]; + if (element->hasTagName(optionTag)) { + if (toHTMLOptionElement(element)->selected()) + toHTMLOptionElement(element)->setSelectedState(false); + else + selectOption(index, DispatchChangeEvent | UserDriven); + } + } + + if (usesMenuList()) + dispatchChangeEventForMenuList(); + else + listBoxOnChange(); + + scrollToSelection(); +} + +unsigned HTMLSelectElement::length() const +{ + unsigned options = 0; + + const Vector<HTMLElement*>& items = listItems(); + for (unsigned i = 0; i < items.size(); ++i) { + if (items[i]->hasTagName(optionTag)) + ++options; + } + + return options; +} + +#ifndef NDEBUG + +HTMLSelectElement* toHTMLSelectElement(Node* node) +{ + ASSERT(!node || node->hasTagName(selectTag)); + return static_cast<HTMLSelectElement*>(node); +} + +const HTMLSelectElement* toHTMLSelectElement(const Node* node) +{ + ASSERT(!node || node->hasTagName(selectTag)); + return static_cast<const HTMLSelectElement*>(node); +} + +#endif + +} // namespace diff --git a/Source/WebCore/html/HTMLSelectElement.h b/Source/WebCore/html/HTMLSelectElement.h new file mode 100644 index 000000000..5363cc781 --- /dev/null +++ b/Source/WebCore/html/HTMLSelectElement.h @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 HTMLSelectElement_h +#define HTMLSelectElement_h + +#include "Event.h" +#include "HTMLFormControlElementWithState.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class HTMLOptionElement; +class HTMLOptionsCollection; + +class HTMLSelectElement : public HTMLFormControlElementWithState { +public: + static PassRefPtr<HTMLSelectElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + virtual ~HTMLSelectElement(); + + int selectedIndex() const; + void setSelectedIndex(int); + + void optionSelectedByUser(int index, bool dispatchChangeEvent, bool allowMultipleSelection = false); + + // For ValidityState + bool valueMissing() const; + + unsigned length() const; + + int size() const { return m_size; } + bool multiple() const { return m_multiple; } + + bool usesMenuList() const; + + void add(HTMLElement*, HTMLElement* beforeElement, ExceptionCode&); + void remove(int index); + void remove(HTMLOptionElement*); + + String value() const; + void setValue(const String&); + + PassRefPtr<HTMLOptionsCollection> options(); + + void optionElementChildrenChanged(); + + void setRecalcListItems(); + void updateListItemSelectedStates(); + + const Vector<HTMLElement*>& listItems() const; + + virtual void accessKeyAction(bool sendMouseEvents); + void accessKeySetSelectedIndex(int); + + void setMultiple(bool); + + void setSize(int); + + void setOption(unsigned index, HTMLOptionElement*, ExceptionCode&); + void setLength(unsigned, ExceptionCode&); + + Node* namedItem(const AtomicString& name); + Node* item(unsigned index); + + void scrollToSelection(); + + void listBoxSelectItem(int listIndex, bool allowMultiplySelections, bool shift, bool fireOnChangeNow = true); + + bool canSelectAll() const; + void selectAll(); + int listToOptionIndex(int listIndex) const; + void listBoxOnChange(); + int optionToListIndex(int optionIndex) const; + int activeSelectionStartListIndex() const; + int activeSelectionEndListIndex() const; + void setActiveSelectionAnchorIndex(int); + void setActiveSelectionEndIndex(int); + void updateListBoxSelection(bool deselectOtherOptions); + + // For use in the implementation of HTMLOptionElement. + void optionSelectionStateChanged(HTMLOptionElement*, bool optionIsSelected); + +protected: + HTMLSelectElement(const QualifiedName&, Document*, HTMLFormElement*); + +private: + virtual const AtomicString& formControlType() const; + + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual bool isMouseFocusable() const; + + virtual void dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode); + virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode); + + virtual bool canStartSelection() const { return false; } + + virtual bool isEnumeratable() const { return true; } + + virtual bool saveFormControlState(String& value) const; + virtual void restoreFormControlState(const String&); + + virtual void parseMappedAttribute(Attribute*); + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle *); + virtual bool appendFormData(FormDataList&, bool); + + virtual void reset(); + + virtual void defaultEventHandler(Event*); + + void dispatchChangeEventForMenuList(); + + void recalcListItems(bool updateSelectedStates = true) const; + + void deselectItems(HTMLOptionElement* excludeElement = 0); + void typeAheadFind(KeyboardEvent*); + void saveLastSelection(); + + virtual void insertedIntoTree(bool); + + virtual bool isOptionalFormControl() const { return !isRequiredFormControl(); } + virtual bool isRequiredFormControl() const; + + bool hasPlaceholderLabelOption() const; + + enum SelectOptionFlag { + DeselectOtherOptions = 1 << 0, + DispatchChangeEvent = 1 << 1, + UserDriven = 1 << 2, + }; + typedef unsigned SelectOptionFlags; + void selectOption(int optionIndex, SelectOptionFlags = 0); + void deselectItemsWithoutValidation(HTMLElement* elementToExclude = 0); + void parseMultipleAttribute(const Attribute*); + int lastSelectedListIndex() const; + void updateSelectedState(int listIndex, bool multi, bool shift); + void menuListDefaultEventHandler(Event*); + bool platformHandleKeydownEvent(KeyboardEvent*); + void listBoxDefaultEventHandler(Event*); + void setOptionsChangedOnRenderer(); + + enum SkipDirection { + SkipBackwards = -1, + SkipForwards = 1 + }; + int nextValidIndex(int listIndex, SkipDirection, int skip) const; + int nextSelectableListIndex(int startIndex) const; + int previousSelectableListIndex(int startIndex) const; + int firstSelectableListIndex() const; + int lastSelectableListIndex() const; + int nextSelectableListIndexPageAway(int startIndex, SkipDirection) const; + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + RefPtr<HTMLOptionsCollection> m_optionsCollection; + + // m_listItems contains HTMLOptionElement, HTMLOptGroupElement, and HTMLHRElement objects. + mutable Vector<HTMLElement*> m_listItems; + Vector<bool> m_lastOnChangeSelection; + Vector<bool> m_cachedStateForActiveSelection; + DOMTimeStamp m_lastCharTime; + String m_typedString; + int m_size; + int m_lastOnChangeIndex; + int m_activeSelectionAnchorIndex; + int m_activeSelectionEndIndex; + UChar m_repeatingChar; + bool m_isProcessingUserDrivenChange; + bool m_multiple; + bool m_activeSelectionState; + mutable bool m_shouldRecalcListItems; +}; + +HTMLSelectElement* toHTMLSelectElement(Node*); +const HTMLSelectElement* toHTMLSelectElement(const Node*); +void toHTMLSelectElement(const HTMLSelectElement*); // This overload will catch anyone doing an unnecessary cast. + +#ifdef NDEBUG + +// The debug versions of these, with assertions, are not inlined. + +inline HTMLSelectElement* toHTMLSelectElement(Node* node) +{ + return static_cast<HTMLSelectElement*>(node); +} + +inline const HTMLSelectElement* toHTMLSelectElement(const Node* node) +{ + return static_cast<const HTMLSelectElement*>(node); +} + +#endif + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLSelectElement.idl b/Source/WebCore/html/HTMLSelectElement.idl new file mode 100644 index 000000000..45a345601 --- /dev/null +++ b/Source/WebCore/html/HTMLSelectElement.idl @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + HasIndexGetter, + HasCustomIndexSetter + ] HTMLSelectElement : HTMLElement { + readonly attribute DOMString type; + attribute long selectedIndex; + attribute [ConvertNullToNullString] DOMString value; + + // Modified in DOM Level 2: +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + readonly attribute long length; +#else + attribute unsigned long length setter raises (DOMException); +#endif + + readonly attribute HTMLFormElement form; + readonly attribute ValidityState validity; + readonly attribute boolean willValidate; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + + // Modified in DOM Level 2: + readonly attribute HTMLOptionsCollection options; + + attribute [Reflect] boolean disabled; + attribute [Reflect] boolean autofocus; + attribute boolean multiple; + attribute [ConvertNullToNullString] DOMString name; + attribute [Reflect] boolean required; + attribute long size; + + [OldStyleObjC] void add(in [Optional=CallWithDefaultValue] HTMLElement element, + in [Optional=CallWithDefaultValue] HTMLElement before) raises(DOMException); + +#if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT + // In JavaScript, we support both option index and option object parameters. + // As of this writing this cannot be auto-generated. + [Custom] void remove(/* indexOrOption */); +#else + void remove(in long index); +#endif + + // These methods are not in DOM Level 2 IDL, but are mentioned in the standard: + // "The contained options can be directly accessed through the select element as a collection." + Node item(in [IsIndex,Optional=CallWithDefaultValue] unsigned long index); + Node namedItem(in [Optional=CallWithDefaultValue] DOMString name); + readonly attribute NodeList labels; + }; + +} diff --git a/Source/WebCore/html/HTMLSelectElementWin.cpp b/Source/WebCore/html/HTMLSelectElementWin.cpp new file mode 100644 index 000000000..189aaf0c3 --- /dev/null +++ b/Source/WebCore/html/HTMLSelectElementWin.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLSelectElement.h" + +#include "Element.h" +#include "KeyboardEvent.h" +#include "RenderMenuList.h" + +namespace WebCore { + +bool HTMLSelectElement::platformHandleKeydownEvent(KeyboardEvent* event) +{ + // Allow (Shift) F4 and (Ctrl/Shift) Alt/AltGr + Up/Down arrow to pop the menu, matching Firefox. + bool eventShowsMenu = (!event->altKey() && !event->ctrlKey() && event->keyIdentifier() == "F4") + || ((event->altGraphKey() || event->altKey()) && (event->keyIdentifier() == "Down" || event->keyIdentifier() == "Up")); + if (!eventShowsMenu) + return false; + + // Save the selection so it can be compared to the new selection when dispatching change events during setSelectedIndex, + // which gets called from RenderMenuList::valueChanged, which gets called after the user makes a selection from the menu. + saveLastSelection(); + if (RenderMenuList* menuList = toRenderMenuList(renderer())) + menuList->showPopup(); + + int index = selectedIndex(); + ASSERT(index >= 0); + ASSERT(index < listItems().size()); + setSelectedIndex(index); + event->setDefaultHandled(); + return true; +} + +} diff --git a/Source/WebCore/html/HTMLSourceElement.cpp b/Source/WebCore/html/HTMLSourceElement.cpp new file mode 100644 index 000000000..99f211ad2 --- /dev/null +++ b/Source/WebCore/html/HTMLSourceElement.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "HTMLSourceElement.h" + +#include "Event.h" +#include "EventNames.h" +#include "HTMLDocument.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "Logging.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLSourceElement::HTMLSourceElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_errorEventTimer(this, &HTMLSourceElement::errorEventTimerFired) +{ + LOG(Media, "HTMLSourceElement::HTMLSourceElement - %p", this); + ASSERT(hasTagName(sourceTag)); +} + +PassRefPtr<HTMLSourceElement> HTMLSourceElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLSourceElement(tagName, document)); +} + +void HTMLSourceElement::insertedIntoTree(bool deep) +{ + HTMLElement::insertedIntoTree(deep); + Element* parent = parentElement(); + if (parent && parent->isMediaElement()) + static_cast<HTMLMediaElement*>(parentNode())->sourceWasAdded(this); +} + +void HTMLSourceElement::willRemove() +{ + Element* parent = parentElement(); + if (parent && parent->isMediaElement()) + static_cast<HTMLMediaElement*>(parentNode())->sourceWillBeRemoved(this); + HTMLElement::willRemove(); +} + +void HTMLSourceElement::setSrc(const String& url) +{ + setAttribute(srcAttr, url); +} + +String HTMLSourceElement::media() const +{ + return getAttribute(mediaAttr); +} + +void HTMLSourceElement::setMedia(const String& media) +{ + setAttribute(mediaAttr, media); +} + +String HTMLSourceElement::type() const +{ + return getAttribute(typeAttr); +} + +void HTMLSourceElement::setType(const String& type) +{ + setAttribute(typeAttr, type); +} + +void HTMLSourceElement::scheduleErrorEvent() +{ + LOG(Media, "HTMLSourceElement::scheduleErrorEvent - %p", this); + if (m_errorEventTimer.isActive()) + return; + + m_errorEventTimer.startOneShot(0); +} + +void HTMLSourceElement::cancelPendingErrorEvent() +{ + LOG(Media, "HTMLSourceElement::cancelPendingErrorEvent - %p", this); + m_errorEventTimer.stop(); +} + +void HTMLSourceElement::errorEventTimerFired(Timer<HTMLSourceElement>*) +{ + LOG(Media, "HTMLSourceElement::errorEventTimerFired - %p", this); + dispatchEvent(Event::create(eventNames().errorEvent, false, true)); +} + +bool HTMLSourceElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == srcAttr || HTMLElement::isURLAttribute(attribute); +} + +#if ENABLE(MICRODATA) +String HTMLSourceElement::itemValueText() const +{ + return getURLAttribute(srcAttr); +} + +void HTMLSourceElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(srcAttr, value, ec); +} +#endif + +} + +#endif diff --git a/Source/WebCore/html/HTMLSourceElement.h b/Source/WebCore/html/HTMLSourceElement.h new file mode 100644 index 000000000..431a66c03 --- /dev/null +++ b/Source/WebCore/html/HTMLSourceElement.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLSourceElement_h +#define HTMLSourceElement_h + +#if ENABLE(VIDEO) + +#include "HTMLElement.h" +#include "Timer.h" + +namespace WebCore { + +class HTMLSourceElement : public HTMLElement { +public: + static PassRefPtr<HTMLSourceElement> create(const QualifiedName&, Document*); + + String media() const; + String type() const; + void setSrc(const String&); + void setMedia(const String&); + void setType(const String&); + + void scheduleErrorEvent(); + void cancelPendingErrorEvent(); + +private: + HTMLSourceElement(const QualifiedName&, Document*); + + virtual void insertedIntoTree(bool); + virtual void willRemove(); + virtual bool isURLAttribute(Attribute*) const; + + void errorEventTimerFired(Timer<HTMLSourceElement>*); + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + Timer<HTMLSourceElement> m_errorEventTimer; +}; + +} //namespace + +#endif +#endif diff --git a/Source/WebCore/html/HTMLSourceElement.idl b/Source/WebCore/html/HTMLSourceElement.idl new file mode 100644 index 000000000..dc707140a --- /dev/null +++ b/Source/WebCore/html/HTMLSourceElement.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2007, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=VIDEO, + ] HTMLSourceElement : HTMLElement { + attribute [Reflect, URL] DOMString src; + attribute DOMString type; + attribute DOMString media; +}; +} diff --git a/Source/WebCore/html/HTMLSpanElement.cpp b/Source/WebCore/html/HTMLSpanElement.cpp new file mode 100644 index 000000000..41cb01eb4 --- /dev/null +++ b/Source/WebCore/html/HTMLSpanElement.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLSpanElement.h" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLSpanElement::HTMLSpanElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(spanTag)); +} + +PassRefPtr<HTMLSpanElement> HTMLSpanElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLSpanElement(tagName, document)); +} + +} diff --git a/Source/WebCore/html/HTMLSpanElement.h b/Source/WebCore/html/HTMLSpanElement.h new file mode 100644 index 000000000..59d87b5ee --- /dev/null +++ b/Source/WebCore/html/HTMLSpanElement.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 HTMLSpanElement_h +#define HTMLSpanElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLSpanElement : public HTMLElement { +public: + static PassRefPtr<HTMLSpanElement> create(const QualifiedName&, Document*); + +private: + HTMLSpanElement(const QualifiedName&, Document*); +}; + +} + +#endif diff --git a/Source/WebCore/html/HTMLSpanElement.idl b/Source/WebCore/html/HTMLSpanElement.idl new file mode 100644 index 000000000..d6d4e59eb --- /dev/null +++ b/Source/WebCore/html/HTMLSpanElement.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2011 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 { + + // http://www.whatwg.org/specs/web-apps/current-work/#htmlspanelement + interface HTMLSpanElement : HTMLElement { + }; + +} diff --git a/Source/WebCore/html/HTMLStyleElement.cpp b/Source/WebCore/html/HTMLStyleElement.cpp new file mode 100644 index 000000000..9efe80aa0 --- /dev/null +++ b/Source/WebCore/html/HTMLStyleElement.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2010 Apple Inc. All rights reserved. + * (C) 2007 Rob Buis (buis@kde.org) + * + * 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 "HTMLStyleElement.h" + +#include "Attribute.h" +#include "Document.h" +#include "HTMLNames.h" +#include "ScriptEventListener.h" +#include "ScriptableDocumentParser.h" + + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLStyleElement::HTMLStyleElement(const QualifiedName& tagName, Document* document, bool createdByParser) + : HTMLElement(tagName, document) + , StyleElement(document, createdByParser) +{ + ASSERT(hasTagName(styleTag)); +} + +HTMLStyleElement::~HTMLStyleElement() +{ + StyleElement::clearDocumentData(document(), this); +} + +PassRefPtr<HTMLStyleElement> HTMLStyleElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) +{ + return adoptRef(new HTMLStyleElement(tagName, document, createdByParser)); +} + +void HTMLStyleElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == titleAttr && m_sheet) + m_sheet->setTitle(attr->value()); + else + HTMLElement::parseMappedAttribute(attr); +} + +void HTMLStyleElement::finishParsingChildren() +{ + StyleElement::finishParsingChildren(this); + HTMLElement::finishParsingChildren(); +} + +void HTMLStyleElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + StyleElement::insertedIntoDocument(document(), this); +} + +void HTMLStyleElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + StyleElement::removedFromDocument(document(), this); +} + +void HTMLStyleElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + StyleElement::childrenChanged(this); + HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +const AtomicString& HTMLStyleElement::media() const +{ + return getAttribute(mediaAttr); +} + +const AtomicString& HTMLStyleElement::type() const +{ + return getAttribute(typeAttr); +} + +#if ENABLE(STYLE_SCOPED) +bool HTMLStyleElement::scoped() const +{ + return fastHasAttribute(scopedAttr); +} + +void HTMLStyleElement::setScoped(bool scopedValue) +{ + setBooleanAttribute(scopedAttr, scopedValue); +} + +Element* HTMLStyleElement::scopingElement() const +{ + if (!scoped()) + return 0; + + // FIXME: This probably needs to be refined for scoped stylesheets within shadow DOM. + // As written, such a stylesheet could style the host element, as well as children of the host. + // OTOH, this paves the way for a :bound-element implementation. + ContainerNode* parentOrHost = parentOrHostNode(); + if (!parentOrHost || !parentOrHost->isElementNode()) + return 0; + + return toElement(parentOrHost); +} +#endif // ENABLE(STYLE_SCOPED) + +void HTMLStyleElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + if (CSSStyleSheet* styleSheet = const_cast<HTMLStyleElement*>(this)->sheet()) + styleSheet->addSubresourceStyleURLs(urls); +} + +bool HTMLStyleElement::disabled() const +{ + if (!m_sheet) + return false; + + return m_sheet->disabled(); +} + +void HTMLStyleElement::setDisabled(bool setDisabled) +{ + if (CSSStyleSheet* styleSheet = sheet()) + styleSheet->setDisabled(setDisabled); +} + +} diff --git a/Source/WebCore/html/HTMLStyleElement.h b/Source/WebCore/html/HTMLStyleElement.h new file mode 100644 index 000000000..6baaee3b7 --- /dev/null +++ b/Source/WebCore/html/HTMLStyleElement.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 HTMLStyleElement_h +#define HTMLStyleElement_h + +#include "HTMLElement.h" +#include "StyleElement.h" + +namespace WebCore { + +class StyleSheet; + +class HTMLStyleElement : public HTMLElement, private StyleElement { +public: + static PassRefPtr<HTMLStyleElement> create(const QualifiedName&, Document*, bool createdByParser); + virtual ~HTMLStyleElement(); + + void setType(const AtomicString&); + +#if ENABLE(STYLE_SCOPED) + bool scoped() const; + void setScoped(bool); + Element* scopingElement() const; +#endif + + using StyleElement::sheet; + + bool disabled() const; + void setDisabled(bool); + +private: + HTMLStyleElement(const QualifiedName&, Document*, bool createdByParser); + + // overload from HTMLElement + virtual void parseMappedAttribute(Attribute*); + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + virtual void finishParsingChildren(); + + virtual bool isLoading() const { return StyleElement::isLoading(); } + virtual bool sheetLoaded() { return StyleElement::sheetLoaded(document()); } + virtual void startLoadingDynamicSheet() { StyleElement::startLoadingDynamicSheet(document()); } + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + virtual const AtomicString& media() const; + virtual const AtomicString& type() const; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLStyleElement.idl b/Source/WebCore/html/HTMLStyleElement.idl new file mode 100644 index 000000000..7a1b62295 --- /dev/null +++ b/Source/WebCore/html/HTMLStyleElement.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLStyleElement : HTMLElement { + attribute boolean disabled; + attribute [Conditional=STYLE_SCOPED] boolean scoped; + attribute [Reflect] DOMString media; + attribute [Reflect] DOMString type; + + // DOM Level 2 Style + readonly attribute StyleSheet sheet; + }; + +} diff --git a/Source/WebCore/html/HTMLSummaryElement.cpp b/Source/WebCore/html/HTMLSummaryElement.cpp new file mode 100644 index 000000000..ecf1085ce --- /dev/null +++ b/Source/WebCore/html/HTMLSummaryElement.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 "HTMLSummaryElement.h" + +#if ENABLE(DETAILS) + +#include "DetailsMarkerControl.h" +#include "HTMLDetailsElement.h" +#include "HTMLNames.h" +#include "MouseEvent.h" +#include "PlatformMouseEvent.h" +#include "RenderSummary.h" +#include "ShadowContentElement.h" + +#include "ShadowRoot.h" + +namespace WebCore { + +using namespace HTMLNames; + +class SummaryContentElement : public ShadowContentElement { +public: + static PassRefPtr<SummaryContentElement> create(Document*); + +private: + SummaryContentElement(Document* document) + : ShadowContentElement(HTMLNames::divTag, document) + { + } +}; + +PassRefPtr<SummaryContentElement> SummaryContentElement::create(Document* document) +{ + return adoptRef(new SummaryContentElement(document)); +} + +PassRefPtr<HTMLSummaryElement> HTMLSummaryElement::create(const QualifiedName& tagName, Document* document) +{ + RefPtr<HTMLSummaryElement> result = adoptRef(new HTMLSummaryElement(tagName, document)); + result->createShadowSubtree(); + return result; +} + +HTMLSummaryElement::HTMLSummaryElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(summaryTag)); +} + +RenderObject* HTMLSummaryElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderSummary(this); +} + +void HTMLSummaryElement::createShadowSubtree() +{ + ExceptionCode ec = 0; + ensureShadowRoot()->appendChild(DetailsMarkerControl::create(document()), ec, true); + ensureShadowRoot()->appendChild(SummaryContentElement::create(document()), ec, true); +} + +HTMLDetailsElement* HTMLSummaryElement::detailsElement() const +{ + Node* mayDetails = const_cast<HTMLSummaryElement*>(this)->parentNodeForRenderingAndStyle(); + if (!mayDetails || !mayDetails->hasTagName(detailsTag)) + return 0; + return static_cast<HTMLDetailsElement*>(mayDetails); +} + +bool HTMLSummaryElement::isMainSummary() const +{ + if (HTMLDetailsElement* details = detailsElement()) + return details->mainSummary() == this; + return 0; +} + +static bool isClickableControl(Node* node) +{ + if (!node->isElementNode()) + return false; + Element* element = toElement(node); + if (element->isFormControlElement()) + return true; + Element* host = toElement(element->shadowAncestorNode()); + return host && host->isFormControlElement(); +} + +void HTMLSummaryElement::defaultEventHandler(Event* event) +{ + HTMLElement::defaultEventHandler(event); + if (!isMainSummary() || !renderer() || !renderer()->isSummary() || !event->isMouseEvent() || event->type() != eventNames().clickEvent || event->defaultHandled()) + return; + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + if (mouseEvent->button() != LeftButton) + return; + if (isClickableControl(event->target()->toNode())) + return; + + if (HTMLDetailsElement* details = detailsElement()) + details->toggleOpen(); + event->setDefaultHandled(); +} + +} + +#endif diff --git a/Source/WebCore/html/HTMLSummaryElement.h b/Source/WebCore/html/HTMLSummaryElement.h new file mode 100644 index 000000000..43ab73fad --- /dev/null +++ b/Source/WebCore/html/HTMLSummaryElement.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies) + * + * 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 HTMLSummaryElement_h +#define HTMLSummaryElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLDetailsElement; + +class HTMLSummaryElement : public HTMLElement { +public: + static PassRefPtr<HTMLSummaryElement> create(const QualifiedName&, Document*); + bool isMainSummary() const; + +private: + HTMLSummaryElement(const QualifiedName&, Document*); + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void defaultEventHandler(Event*); + + void createShadowSubtree(); + HTMLDetailsElement* detailsElement() const; +}; + +} + +#endif // HTMLSummaryElement_h diff --git a/Source/WebCore/html/HTMLTableCaptionElement.cpp b/Source/WebCore/html/HTMLTableCaptionElement.cpp new file mode 100644 index 000000000..b726df62c --- /dev/null +++ b/Source/WebCore/html/HTMLTableCaptionElement.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 "HTMLTableCaptionElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLTableCaptionElement::HTMLTableCaptionElement(const QualifiedName& tagName, Document* document) + : HTMLTablePartElement(tagName, document) +{ + ASSERT(hasTagName(captionTag)); +} + +PassRefPtr<HTMLTableCaptionElement> HTMLTableCaptionElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTableCaptionElement(tagName, document)); +} + +bool HTMLTableCaptionElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == alignAttr) { + result = eCaption; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLTableCaptionElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == alignAttr) { + if (!attr->value().isEmpty()) + addCSSProperty(attr, CSSPropertyCaptionSide, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLTableCaptionElement.h b/Source/WebCore/html/HTMLTableCaptionElement.h new file mode 100644 index 000000000..a2dd2faae --- /dev/null +++ b/Source/WebCore/html/HTMLTableCaptionElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 HTMLTableCaptionElement_h +#define HTMLTableCaptionElement_h + +#include "HTMLTablePartElement.h" + +namespace WebCore { + +class HTMLTableCaptionElement : public HTMLTablePartElement { +public: + static PassRefPtr<HTMLTableCaptionElement> create(const QualifiedName&, Document*); + +private: + HTMLTableCaptionElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableCaptionElement.idl b/Source/WebCore/html/HTMLTableCaptionElement.idl new file mode 100644 index 000000000..c56c45600 --- /dev/null +++ b/Source/WebCore/html/HTMLTableCaptionElement.idl @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + GenerateNativeConverter + ] HTMLTableCaptionElement : HTMLElement { + attribute [Reflect] DOMString align; + }; + +} diff --git a/Source/WebCore/html/HTMLTableCellElement.cpp b/Source/WebCore/html/HTMLTableCellElement.cpp new file mode 100644 index 000000000..7e205d9e2 --- /dev/null +++ b/Source/WebCore/html/HTMLTableCellElement.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 "HTMLTableCellElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "RenderTableCell.h" + +using std::max; +using std::min; + +namespace WebCore { + +// Clamp rowspan at 8k to match Firefox. +static const int maxRowspan = 8190; + +using namespace HTMLNames; + +inline HTMLTableCellElement::HTMLTableCellElement(const QualifiedName& tagName, Document* document) + : HTMLTablePartElement(tagName, document) +{ +} + +PassRefPtr<HTMLTableCellElement> HTMLTableCellElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTableCellElement(tagName, document)); +} + +int HTMLTableCellElement::colSpan() const +{ + const AtomicString& colSpanValue = fastGetAttribute(colspanAttr); + return max(1, colSpanValue.toInt()); +} + +int HTMLTableCellElement::rowSpan() const +{ + const AtomicString& rowSpanValue = fastGetAttribute(rowspanAttr); + return max(1, min(rowSpanValue.toInt(), maxRowspan)); +} + +int HTMLTableCellElement::cellIndex() const +{ + int index = 0; + for (const Node * node = previousSibling(); node; node = node->previousSibling()) { + if (node->hasTagName(tdTag) || node->hasTagName(thTag)) + index++; + } + + return index; +} + +bool HTMLTableCellElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == nowrapAttr) { + result = eUniversal; + return false; + } + + if (attrName == widthAttr || + attrName == heightAttr) { + result = eCell; // Because of the quirky behavior of ignoring 0 values, cells are special. + return false; + } + + return HTMLTablePartElement::mapToEntry(attrName, result); +} + +void HTMLTableCellElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == rowspanAttr) { + if (renderer() && renderer()->isTableCell()) + toRenderTableCell(renderer())->colSpanOrRowSpanChanged(); + } else if (attr->name() == colspanAttr) { + if (renderer() && renderer()->isTableCell()) + toRenderTableCell(renderer())->colSpanOrRowSpanChanged(); + } else if (attr->name() == nowrapAttr) { + if (!attr->isNull()) + addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValueWebkitNowrap); + } else if (attr->name() == widthAttr) { + if (!attr->value().isEmpty()) { + int widthInt = attr->value().toInt(); + if (widthInt > 0) // width="0" is ignored for compatibility with WinIE. + addCSSLength(attr, CSSPropertyWidth, attr->value()); + } + } else if (attr->name() == heightAttr) { + if (!attr->value().isEmpty()) { + int heightInt = attr->value().toInt(); + if (heightInt > 0) // height="0" is ignored for compatibility with WinIE. + addCSSLength(attr, CSSPropertyHeight, attr->value()); + } + } else + HTMLTablePartElement::parseMappedAttribute(attr); +} + +// used by table cells to share style decls created by the enclosing table. +void HTMLTableCellElement::additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>& results) +{ + ContainerNode* p = parentNode(); + while (p && !p->hasTagName(tableTag)) + p = p->parentNode(); + if (!p) + return; + static_cast<HTMLTableElement*>(p)->addSharedCellDecls(results); +} + +bool HTMLTableCellElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == backgroundAttr || HTMLTablePartElement::isURLAttribute(attr); +} + +String HTMLTableCellElement::abbr() const +{ + return getAttribute(abbrAttr); +} + +String HTMLTableCellElement::axis() const +{ + return getAttribute(axisAttr); +} + +void HTMLTableCellElement::setColSpan(int n) +{ + setAttribute(colspanAttr, String::number(n)); +} + +String HTMLTableCellElement::headers() const +{ + return getAttribute(headersAttr); +} + +void HTMLTableCellElement::setRowSpan(int n) +{ + setAttribute(rowspanAttr, String::number(n)); +} + +String HTMLTableCellElement::scope() const +{ + return getAttribute(scopeAttr); +} + +void HTMLTableCellElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLTablePartElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, document()->completeURL(getAttribute(backgroundAttr))); +} + +HTMLTableCellElement* HTMLTableCellElement::cellAbove() const +{ + RenderObject* cellRenderer = renderer(); + if (!cellRenderer) + return 0; + if (!cellRenderer->isTableCell()) + return 0; + + RenderTableCell* tableCellRenderer = toRenderTableCell(cellRenderer); + RenderTableCell* cellAboveRenderer = tableCellRenderer->table()->cellAbove(tableCellRenderer); + if (!cellAboveRenderer) + return 0; + + return static_cast<HTMLTableCellElement*>(cellAboveRenderer->node()); +} + +#ifndef NDEBUG + +HTMLTableCellElement* toHTMLTableCellElement(Node* node) +{ + ASSERT(!node || node->hasTagName(HTMLNames::tdTag) || node->hasTagName(HTMLNames::thTag)); + return static_cast<HTMLTableCellElement*>(node); +} + +const HTMLTableCellElement* toHTMLTableCellElement(const Node* node) +{ + ASSERT(!node || node->hasTagName(HTMLNames::tdTag) || node->hasTagName(HTMLNames::thTag)); + return static_cast<const HTMLTableCellElement*>(node); +} + +#endif + +} // namespace WebCore diff --git a/Source/WebCore/html/HTMLTableCellElement.h b/Source/WebCore/html/HTMLTableCellElement.h new file mode 100644 index 000000000..5a472727b --- /dev/null +++ b/Source/WebCore/html/HTMLTableCellElement.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 HTMLTableCellElement_h +#define HTMLTableCellElement_h + +#include "HTMLTablePartElement.h" + +namespace WebCore { + +class HTMLTableCellElement : public HTMLTablePartElement { +public: + static PassRefPtr<HTMLTableCellElement> create(const QualifiedName&, Document*); + + int cellIndex() const; + + int colSpan() const; + int rowSpan() const; + + void setCellIndex(int); + + String abbr() const; + String axis() const; + void setColSpan(int); + String headers() const; + void setRowSpan(int); + String scope() const; + + HTMLTableCellElement* cellAbove() const; + +private: + HTMLTableCellElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + + // used by table cells to share style decls created by the enclosing table. + virtual bool canHaveAdditionalAttributeStyleDecls() const { return true; } + virtual void additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>&); + + virtual bool isURLAttribute(Attribute*) const; + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; +}; + +HTMLTableCellElement* toHTMLTableCellElement(Node* node); +const HTMLTableCellElement* toHTMLTableCellElement(const Node* node); + +// This will catch anyone doing an unnecessary cast. +void toHTMLTableCellElement(const HTMLTableCellElement*); + +#ifdef NDEBUG + +// The debug versions of these, with assertions, are not inlined. + +inline HTMLTableCellElement* toHTMLTableCellElement(Node* node) +{ + return static_cast<HTMLTableCellElement*>(node); +} + +inline const HTMLTableCellElement* toHTMLTableCellElement(const Node* node) +{ + return static_cast<const HTMLTableCellElement*>(node); +} +#endif + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableCellElement.idl b/Source/WebCore/html/HTMLTableCellElement.idl new file mode 100644 index 000000000..ae286f4d6 --- /dev/null +++ b/Source/WebCore/html/HTMLTableCellElement.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLTableCellElement : HTMLElement { + readonly attribute long cellIndex; + attribute [Reflect] DOMString abbr; + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString axis; + attribute [Reflect] DOMString bgColor; + attribute [Reflect=char] DOMString ch; + attribute [Reflect=charoff] DOMString chOff; + attribute long colSpan; + attribute [Reflect] DOMString headers; + attribute [Reflect] DOMString height; + attribute [Reflect] boolean noWrap; + attribute long rowSpan; + attribute [Reflect] DOMString scope; + attribute [Reflect] DOMString vAlign; + attribute [Reflect] DOMString width; + }; + +} diff --git a/Source/WebCore/html/HTMLTableColElement.cpp b/Source/WebCore/html/HTMLTableColElement.cpp new file mode 100644 index 000000000..96af70827 --- /dev/null +++ b/Source/WebCore/html/HTMLTableColElement.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 "HTMLTableColElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "RenderTableCol.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLTableColElement::HTMLTableColElement(const QualifiedName& tagName, Document* document) + : HTMLTablePartElement(tagName, document) + , m_span(1) +{ +} + +PassRefPtr<HTMLTableColElement> HTMLTableColElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTableColElement(tagName, document)); +} + +bool HTMLTableColElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == widthAttr) { + result = eUniversal; + return false; + } + + return HTMLTablePartElement::mapToEntry(attrName, result); +} + +void HTMLTableColElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == spanAttr) { + m_span = !attr->isNull() ? attr->value().toInt() : 1; + if (renderer() && renderer()->isTableCol()) + renderer()->updateFromElement(); + } else if (attr->name() == widthAttr) { + if (!attr->value().isEmpty()) { + addCSSLength(attr, CSSPropertyWidth, attr->value()); + if (renderer() && renderer()->isTableCol()) { + RenderTableCol* col = toRenderTableCol(renderer()); + int newWidth = width().toInt(); + if (newWidth != col->width()) + col->setNeedsLayoutAndPrefWidthsRecalc(); + } + } + } else + HTMLTablePartElement::parseMappedAttribute(attr); +} + +// used by table columns and column groups to share style decls created by the enclosing table. +void HTMLTableColElement::additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>& results) +{ + if (!hasLocalName(colgroupTag)) + return; + ContainerNode* p = parentNode(); + while (p && !p->hasTagName(tableTag)) + p = p->parentNode(); + if (!p) + return; + static_cast<HTMLTableElement*>(p)->addSharedGroupDecls(false, results); +} + +void HTMLTableColElement::setSpan(int n) +{ + setAttribute(spanAttr, String::number(n)); +} + +String HTMLTableColElement::width() const +{ + return getAttribute(widthAttr); +} + +} diff --git a/Source/WebCore/html/HTMLTableColElement.h b/Source/WebCore/html/HTMLTableColElement.h new file mode 100644 index 000000000..c7517d10a --- /dev/null +++ b/Source/WebCore/html/HTMLTableColElement.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 HTMLTableColElement_h +#define HTMLTableColElement_h + +#include "HTMLTablePartElement.h" + +namespace WebCore { + +class HTMLTableColElement : public HTMLTablePartElement { +public: + static PassRefPtr<HTMLTableColElement> create(const QualifiedName& tagName, Document*); + + int span() const { return m_span; } + void setSpan(int); + + String width() const; + +private: + HTMLTableColElement(const QualifiedName& tagName, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + virtual bool canHaveAdditionalAttributeStyleDecls() const { return true; } + virtual void additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>&); + + int m_span; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableColElement.idl b/Source/WebCore/html/HTMLTableColElement.idl new file mode 100644 index 000000000..a6e665401 --- /dev/null +++ b/Source/WebCore/html/HTMLTableColElement.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLTableColElement : HTMLElement { + attribute [Reflect] DOMString align; + attribute [Reflect=char] DOMString ch; + attribute [Reflect=charoff] DOMString chOff; + attribute long span; + attribute [Reflect] DOMString vAlign; + attribute [Reflect] DOMString width; + }; + +} diff --git a/Source/WebCore/html/HTMLTableElement.cpp b/Source/WebCore/html/HTMLTableElement.cpp new file mode 100644 index 000000000..b6a4a5a6d --- /dev/null +++ b/Source/WebCore/html/HTMLTableElement.cpp @@ -0,0 +1,656 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2010, 2011 Apple 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 "HTMLTableElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "ExceptionCode.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLTableCaptionElement.h" +#include "HTMLTableRowElement.h" +#include "HTMLTableRowsCollection.h" +#include "HTMLTableSectionElement.h" +#include "RenderTable.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLTableElement::HTMLTableElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_borderAttr(false) + , m_borderColorAttr(false) + , m_frameAttr(false) + , m_rulesAttr(UnsetRules) + , m_padding(1) +{ + ASSERT(hasTagName(tableTag)); +} + +HTMLTableElement::~HTMLTableElement() +{ + if (m_rowsCollection) + m_rowsCollection->detachFromNode(); +} + +PassRefPtr<HTMLTableElement> HTMLTableElement::create(Document* document) +{ + return adoptRef(new HTMLTableElement(tableTag, document)); +} + +PassRefPtr<HTMLTableElement> HTMLTableElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTableElement(tagName, document)); +} + +HTMLTableCaptionElement* HTMLTableElement::caption() const +{ + for (Node* child = firstChild(); child; child = child->nextSibling()) { + if (child->hasTagName(captionTag)) + return static_cast<HTMLTableCaptionElement*>(child); + } + return 0; +} + +void HTMLTableElement::setCaption(PassRefPtr<HTMLTableCaptionElement> newCaption, ExceptionCode& ec) +{ + deleteCaption(); + insertBefore(newCaption, firstChild(), ec); +} + +HTMLTableSectionElement* HTMLTableElement::tHead() const +{ + for (Node* child = firstChild(); child; child = child->nextSibling()) { + if (child->hasTagName(theadTag)) + return static_cast<HTMLTableSectionElement*>(child); + } + return 0; +} + +void HTMLTableElement::setTHead(PassRefPtr<HTMLTableSectionElement> newHead, ExceptionCode& ec) +{ + deleteTHead(); + + Node* child; + for (child = firstChild(); child; child = child->nextSibling()) + if (child->isElementNode() && !child->hasTagName(captionTag) && !child->hasTagName(colgroupTag)) + break; + + insertBefore(newHead, child, ec); +} + +HTMLTableSectionElement* HTMLTableElement::tFoot() const +{ + for (Node* child = firstChild(); child; child = child->nextSibling()) { + if (child->hasTagName(tfootTag)) + return static_cast<HTMLTableSectionElement*>(child); + } + return 0; +} + +void HTMLTableElement::setTFoot(PassRefPtr<HTMLTableSectionElement> newFoot, ExceptionCode& ec) +{ + deleteTFoot(); + + Node* child; + for (child = firstChild(); child; child = child->nextSibling()) + if (child->isElementNode() && !child->hasTagName(captionTag) && !child->hasTagName(colgroupTag) && !child->hasTagName(theadTag)) + break; + + insertBefore(newFoot, child, ec); +} + +PassRefPtr<HTMLElement> HTMLTableElement::createTHead() +{ + if (HTMLTableSectionElement* existingHead = tHead()) + return existingHead; + RefPtr<HTMLTableSectionElement> head = HTMLTableSectionElement::create(theadTag, document()); + ExceptionCode ec; + setTHead(head, ec); + return head.release(); +} + +void HTMLTableElement::deleteTHead() +{ + ExceptionCode ec; + removeChild(tHead(), ec); +} + +PassRefPtr<HTMLElement> HTMLTableElement::createTFoot() +{ + if (HTMLTableSectionElement* existingFoot = tFoot()) + return existingFoot; + RefPtr<HTMLTableSectionElement> foot = HTMLTableSectionElement::create(tfootTag, document()); + ExceptionCode ec; + setTFoot(foot, ec); + return foot.release(); +} + +void HTMLTableElement::deleteTFoot() +{ + ExceptionCode ec; + removeChild(tFoot(), ec); +} + +PassRefPtr<HTMLElement> HTMLTableElement::createCaption() +{ + if (HTMLTableCaptionElement* existingCaption = caption()) + return existingCaption; + RefPtr<HTMLTableCaptionElement> caption = HTMLTableCaptionElement::create(captionTag, document()); + ExceptionCode ec; + setCaption(caption, ec); + return caption.release(); +} + +void HTMLTableElement::deleteCaption() +{ + ExceptionCode ec; + removeChild(caption(), ec); +} + +HTMLTableSectionElement* HTMLTableElement::lastBody() const +{ + for (Node* child = lastChild(); child; child = child->previousSibling()) { + if (child->hasTagName(tbodyTag)) + return static_cast<HTMLTableSectionElement*>(child); + } + return 0; +} + +PassRefPtr<HTMLElement> HTMLTableElement::insertRow(int index, ExceptionCode& ec) +{ + if (index < -1) { + ec = INDEX_SIZE_ERR; + return 0; + } + + HTMLTableRowElement* lastRow = 0; + HTMLTableRowElement* row = 0; + if (index == -1) + lastRow = HTMLTableRowsCollection::lastRow(this); + else { + for (int i = 0; i <= index; ++i) { + row = HTMLTableRowsCollection::rowAfter(this, lastRow); + if (!row) { + if (i != index) { + ec = INDEX_SIZE_ERR; + return 0; + } + break; + } + lastRow = row; + } + } + + ContainerNode* parent; + if (lastRow) + parent = row ? row->parentNode() : lastRow->parentNode(); + else { + parent = lastBody(); + if (!parent) { + RefPtr<HTMLTableSectionElement> newBody = HTMLTableSectionElement::create(tbodyTag, document()); + RefPtr<HTMLTableRowElement> newRow = HTMLTableRowElement::create(document()); + newBody->appendChild(newRow, ec); + appendChild(newBody.release(), ec); + return newRow.release(); + } + } + + RefPtr<HTMLTableRowElement> newRow = HTMLTableRowElement::create(document()); + parent->insertBefore(newRow, row, ec); + return newRow.release(); +} + +void HTMLTableElement::deleteRow(int index, ExceptionCode& ec) +{ + HTMLTableRowElement* row = 0; + if (index == -1) + row = HTMLTableRowsCollection::lastRow(this); + else { + for (int i = 0; i <= index; ++i) { + row = HTMLTableRowsCollection::rowAfter(this, row); + if (!row) + break; + } + } + if (!row) { + ec = INDEX_SIZE_ERR; + return; + } + row->remove(ec); +} + +bool HTMLTableElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == backgroundAttr) { + result = (MappedAttributeEntry)(eLastEntry + document()->docID()); + return false; + } + + if (attrName == widthAttr || + attrName == heightAttr || + attrName == bgcolorAttr || + attrName == cellspacingAttr || + attrName == vspaceAttr || + attrName == hspaceAttr || + attrName == valignAttr) { + result = eUniversal; + return false; + } + + if (attrName == bordercolorAttr || attrName == frameAttr || attrName == rulesAttr) { + result = eUniversal; + return true; + } + + if (attrName == borderAttr) { + result = eTable; + return true; + } + + if (attrName == alignAttr) { + result = eTable; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +static inline bool isTableCellAncestor(Node* n) +{ + return n->hasTagName(theadTag) || n->hasTagName(tbodyTag) || + n->hasTagName(tfootTag) || n->hasTagName(trTag) || + n->hasTagName(thTag); +} + +static bool setTableCellsChanged(Node* n) +{ + ASSERT(n); + bool cellChanged = false; + + if (n->hasTagName(tdTag)) + cellChanged = true; + else if (isTableCellAncestor(n)) { + for (Node* child = n->firstChild(); child; child = child->nextSibling()) + cellChanged |= setTableCellsChanged(child); + } + + if (cellChanged) + n->setNeedsStyleRecalc(); + + return cellChanged; +} + +void HTMLTableElement::parseMappedAttribute(Attribute* attr) +{ + CellBorders bordersBefore = cellBorders(); + unsigned short oldPadding = m_padding; + + if (attr->name() == widthAttr) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + else if (attr->name() == heightAttr) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + else if (attr->name() == borderAttr) { + m_borderAttr = true; + if (attr->decl()) { + RefPtr<CSSValue> val = attr->decl()->getPropertyCSSValue(CSSPropertyBorderLeftWidth); + if (val && val->isPrimitiveValue()) { + CSSPrimitiveValue* primVal = static_cast<CSSPrimitiveValue*>(val.get()); + m_borderAttr = primVal->getDoubleValue(CSSPrimitiveValue::CSS_NUMBER); + } + } else if (!attr->isNull()) { + int border = 0; + if (attr->isEmpty()) + border = 1; + else + border = attr->value().toInt(); + m_borderAttr = border; + addCSSLength(attr, CSSPropertyBorderWidth, String::number(border)); + } + } else if (attr->name() == bgcolorAttr) + addCSSColor(attr, CSSPropertyBackgroundColor, attr->value()); + else if (attr->name() == bordercolorAttr) { + m_borderColorAttr = attr->decl(); + if (!attr->decl() && !attr->isEmpty()) { + addCSSColor(attr, CSSPropertyBorderColor, attr->value()); + m_borderColorAttr = true; + } + } else if (attr->name() == backgroundAttr) { + String url = stripLeadingAndTrailingHTMLSpaces(attr->value()); + if (!url.isEmpty()) + addCSSImageProperty(attr, CSSPropertyBackgroundImage, document()->completeURL(url).string()); + } else if (attr->name() == frameAttr) { + // Cache the value of "frame" so that the table can examine it later. + m_frameAttr = false; + + // Whether or not to hide the top/right/bottom/left borders. + const int cTop = 0; + const int cRight = 1; + const int cBottom = 2; + const int cLeft = 3; + bool borders[4] = { false, false, false, false }; + + // void, above, below, hsides, vsides, lhs, rhs, box, border + if (equalIgnoringCase(attr->value(), "void")) + m_frameAttr = true; + else if (equalIgnoringCase(attr->value(), "above")) { + m_frameAttr = true; + borders[cTop] = true; + } else if (equalIgnoringCase(attr->value(), "below")) { + m_frameAttr = true; + borders[cBottom] = true; + } else if (equalIgnoringCase(attr->value(), "hsides")) { + m_frameAttr = true; + borders[cTop] = borders[cBottom] = true; + } else if (equalIgnoringCase(attr->value(), "vsides")) { + m_frameAttr = true; + borders[cLeft] = borders[cRight] = true; + } else if (equalIgnoringCase(attr->value(), "lhs")) { + m_frameAttr = true; + borders[cLeft] = true; + } else if (equalIgnoringCase(attr->value(), "rhs")) { + m_frameAttr = true; + borders[cRight] = true; + } else if (equalIgnoringCase(attr->value(), "box") || + equalIgnoringCase(attr->value(), "border")) { + m_frameAttr = true; + borders[cTop] = borders[cBottom] = borders[cLeft] = borders[cRight] = true; + } + + // Now map in the border styles of solid and hidden respectively. + if (m_frameAttr) { + addCSSProperty(attr, CSSPropertyBorderTopWidth, CSSValueThin); + addCSSProperty(attr, CSSPropertyBorderBottomWidth, CSSValueThin); + addCSSProperty(attr, CSSPropertyBorderLeftWidth, CSSValueThin); + addCSSProperty(attr, CSSPropertyBorderRightWidth, CSSValueThin); + addCSSProperty(attr, CSSPropertyBorderTopStyle, borders[cTop] ? CSSValueSolid : CSSValueHidden); + addCSSProperty(attr, CSSPropertyBorderBottomStyle, borders[cBottom] ? CSSValueSolid : CSSValueHidden); + addCSSProperty(attr, CSSPropertyBorderLeftStyle, borders[cLeft] ? CSSValueSolid : CSSValueHidden); + addCSSProperty(attr, CSSPropertyBorderRightStyle, borders[cRight] ? CSSValueSolid : CSSValueHidden); + } + } else if (attr->name() == rulesAttr) { + m_rulesAttr = UnsetRules; + if (equalIgnoringCase(attr->value(), "none")) + m_rulesAttr = NoneRules; + else if (equalIgnoringCase(attr->value(), "groups")) + m_rulesAttr = GroupsRules; + else if (equalIgnoringCase(attr->value(), "rows")) + m_rulesAttr = RowsRules; + if (equalIgnoringCase(attr->value(), "cols")) + m_rulesAttr = ColsRules; + if (equalIgnoringCase(attr->value(), "all")) + m_rulesAttr = AllRules; + + // The presence of a valid rules attribute causes border collapsing to be enabled. + if (m_rulesAttr != UnsetRules) + addCSSProperty(attr, CSSPropertyBorderCollapse, CSSValueCollapse); + } else if (attr->name() == cellspacingAttr) { + if (!attr->value().isEmpty()) + addCSSLength(attr, CSSPropertyBorderSpacing, attr->value()); + } else if (attr->name() == cellpaddingAttr) { + if (!attr->value().isEmpty()) + m_padding = max(0, attr->value().toInt()); + else + m_padding = 1; + } else if (attr->name() == colsAttr) { + // ### + } else if (attr->name() == vspaceAttr) { + addCSSLength(attr, CSSPropertyMarginTop, attr->value()); + addCSSLength(attr, CSSPropertyMarginBottom, attr->value()); + } else if (attr->name() == hspaceAttr) { + addCSSLength(attr, CSSPropertyMarginLeft, attr->value()); + addCSSLength(attr, CSSPropertyMarginRight, attr->value()); + } else if (attr->name() == alignAttr) { + if (!attr->value().isEmpty()) { + if (equalIgnoringCase(attr->value(), "center")) { + addCSSProperty(attr, CSSPropertyWebkitMarginStart, CSSValueAuto); + addCSSProperty(attr, CSSPropertyWebkitMarginEnd, CSSValueAuto); + } else + addCSSProperty(attr, CSSPropertyFloat, attr->value()); + } + } else if (attr->name() == valignAttr) { + if (!attr->value().isEmpty()) + addCSSProperty(attr, CSSPropertyVerticalAlign, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); + + if (bordersBefore != cellBorders() || oldPadding != m_padding) { + if (oldPadding != m_padding) + m_paddingDecl = 0; + bool cellChanged = false; + for (Node* child = firstChild(); child; child = child->nextSibling()) + cellChanged |= setTableCellsChanged(child); + if (cellChanged) + setNeedsStyleRecalc(); + } +} + +void HTMLTableElement::additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>& results) +{ + if ((!m_borderAttr && !m_borderColorAttr) || m_frameAttr) + return; + + AtomicString borderValue = m_borderColorAttr ? "solid" : "outset"; + CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(ePersistent, tableborderAttr, borderValue); + if (!decl) { + decl = CSSMappedAttributeDeclaration::create().leakRef(); // This single ref pins us in the table until the document dies. + ASSERT(!decl->useStrictParsing()); + + int value = m_borderColorAttr ? CSSValueSolid : CSSValueOutset; + decl->setMappedProperty(this, CSSPropertyBorderTopStyle, value); + decl->setMappedProperty(this, CSSPropertyBorderBottomStyle, value); + decl->setMappedProperty(this, CSSPropertyBorderLeftStyle, value); + decl->setMappedProperty(this, CSSPropertyBorderRightStyle, value); + + setMappedAttributeDecl(ePersistent, tableborderAttr, borderValue, decl); + decl->setMappedState(ePersistent, tableborderAttr, borderValue); + } + + results.append(decl); +} + +HTMLTableElement::CellBorders HTMLTableElement::cellBorders() const +{ + switch (m_rulesAttr) { + case NoneRules: + case GroupsRules: + return NoBorders; + case AllRules: + return SolidBorders; + case ColsRules: + return SolidBordersColsOnly; + case RowsRules: + return SolidBordersRowsOnly; + case UnsetRules: + if (!m_borderAttr) + return NoBorders; + if (m_borderColorAttr) + return SolidBorders; + return InsetBorders; + } + ASSERT_NOT_REACHED(); + return NoBorders; +} + +void HTMLTableElement::addSharedCellDecls(Vector<CSSMutableStyleDeclaration*>& results) +{ + addSharedCellBordersDecl(results); + addSharedCellPaddingDecl(results); +} + +void HTMLTableElement::addSharedCellBordersDecl(Vector<CSSMutableStyleDeclaration*>& results) +{ + CellBorders borders = cellBorders(); + + static const AtomicString* cellBorderNames[] = { new AtomicString("none"), new AtomicString("solid"), new AtomicString("inset"), new AtomicString("solid-cols"), new AtomicString("solid-rows") }; + const AtomicString& cellborderValue = *cellBorderNames[borders]; + CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(ePersistent, cellborderAttr, cellborderValue); + if (!decl) { + decl = CSSMappedAttributeDeclaration::create().leakRef(); // This single ref pins us in the table until the document dies. + ASSERT(!decl->useStrictParsing()); + + switch (borders) { + case SolidBordersColsOnly: + decl->setMappedProperty(this, CSSPropertyBorderLeftWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderRightWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderLeftStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderRightStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderColor, "inherit"); + break; + case SolidBordersRowsOnly: + decl->setMappedProperty(this, CSSPropertyBorderTopWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderBottomWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderTopStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderBottomStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderColor, "inherit"); + break; + case SolidBorders: + decl->setMappedProperty(this, CSSPropertyBorderWidth, "1px"); + decl->setMappedProperty(this, CSSPropertyBorderTopStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderBottomStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderLeftStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderRightStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderColor, "inherit"); + break; + case InsetBorders: + decl->setMappedProperty(this, CSSPropertyBorderWidth, "1px"); + decl->setMappedProperty(this, CSSPropertyBorderTopStyle, CSSValueInset); + decl->setMappedProperty(this, CSSPropertyBorderBottomStyle, CSSValueInset); + decl->setMappedProperty(this, CSSPropertyBorderLeftStyle, CSSValueInset); + decl->setMappedProperty(this, CSSPropertyBorderRightStyle, CSSValueInset); + decl->setMappedProperty(this, CSSPropertyBorderColor, "inherit"); + break; + case NoBorders: + decl->setMappedProperty(this, CSSPropertyBorderWidth, "0"); + break; + } + + setMappedAttributeDecl(ePersistent, cellborderAttr, *cellBorderNames[borders], decl); + decl->setMappedState(ePersistent, cellborderAttr, cellborderValue); + } + + results.append(decl); +} + +void HTMLTableElement::addSharedCellPaddingDecl(Vector<CSSMutableStyleDeclaration*>& results) +{ + if (m_padding == 0) + return; + + if (!m_paddingDecl) { + String paddingValue = String::number(m_padding); + m_paddingDecl = getMappedAttributeDecl(eUniversal, cellpaddingAttr, paddingValue); + if (!m_paddingDecl) { + m_paddingDecl = CSSMappedAttributeDeclaration::create(); + ASSERT(!m_paddingDecl->useStrictParsing()); + + m_paddingDecl->setMappedProperty(this, CSSPropertyPaddingTop, paddingValue); + m_paddingDecl->setMappedProperty(this, CSSPropertyPaddingRight, paddingValue); + m_paddingDecl->setMappedProperty(this, CSSPropertyPaddingBottom, paddingValue); + m_paddingDecl->setMappedProperty(this, CSSPropertyPaddingLeft, paddingValue); + } + setMappedAttributeDecl(eUniversal, cellpaddingAttr, paddingValue, m_paddingDecl.get()); + m_paddingDecl->setMappedState(eUniversal, cellpaddingAttr, paddingValue); + } + + results.append(m_paddingDecl.get()); +} + +void HTMLTableElement::addSharedGroupDecls(bool rows, Vector<CSSMutableStyleDeclaration*>& results) +{ + if (m_rulesAttr != GroupsRules) + return; + + AtomicString rulesValue = rows ? "rowgroups" : "colgroups"; + CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(ePersistent, rulesAttr, rulesValue); + if (!decl) { + decl = CSSMappedAttributeDeclaration::create().leakRef(); // This single ref pins us in the table until the document dies. + ASSERT(!decl->useStrictParsing()); + + if (rows) { + decl->setMappedProperty(this, CSSPropertyBorderTopWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderBottomWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderTopStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderBottomStyle, CSSValueSolid); + } else { + decl->setMappedProperty(this, CSSPropertyBorderLeftWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderRightWidth, CSSValueThin); + decl->setMappedProperty(this, CSSPropertyBorderLeftStyle, CSSValueSolid); + decl->setMappedProperty(this, CSSPropertyBorderRightStyle, CSSValueSolid); + } + + setMappedAttributeDecl(ePersistent, rulesAttr, rulesValue, decl); + decl->setMappedState(ePersistent, rulesAttr, rulesValue); + } + + results.append(decl); +} + +void HTMLTableElement::attach() +{ + ASSERT(!attached()); + HTMLElement::attach(); +} + +bool HTMLTableElement::isURLAttribute(Attribute *attr) const +{ + return attr->name() == backgroundAttr || HTMLElement::isURLAttribute(attr); +} + +PassRefPtr<HTMLCollection> HTMLTableElement::rows() +{ + if (!m_rowsCollection) + m_rowsCollection = HTMLTableRowsCollection::create(this); + return m_rowsCollection; +} + +PassRefPtr<HTMLCollection> HTMLTableElement::tBodies() +{ + return ensureCachedHTMLCollection(TableTBodies); +} + +String HTMLTableElement::rules() const +{ + return getAttribute(rulesAttr); +} + +String HTMLTableElement::summary() const +{ + return getAttribute(summaryAttr); +} + +void HTMLTableElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + HTMLElement::addSubresourceAttributeURLs(urls); + + addSubresourceURL(urls, document()->completeURL(getAttribute(backgroundAttr))); +} + +} diff --git a/Source/WebCore/html/HTMLTableElement.h b/Source/WebCore/html/HTMLTableElement.h new file mode 100644 index 000000000..63e26056a --- /dev/null +++ b/Source/WebCore/html/HTMLTableElement.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple 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 HTMLTableElement_h +#define HTMLTableElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLCollection; +class HTMLTableCaptionElement; +class HTMLTableRowsCollection; +class HTMLTableSectionElement; + +class HTMLTableElement : public HTMLElement { +public: + static PassRefPtr<HTMLTableElement> create(Document*); + static PassRefPtr<HTMLTableElement> create(const QualifiedName&, Document*); + + virtual ~HTMLTableElement(); + + HTMLTableCaptionElement* caption() const; + void setCaption(PassRefPtr<HTMLTableCaptionElement>, ExceptionCode&); + + HTMLTableSectionElement* tHead() const; + void setTHead(PassRefPtr<HTMLTableSectionElement>, ExceptionCode&); + + HTMLTableSectionElement* tFoot() const; + void setTFoot(PassRefPtr<HTMLTableSectionElement>, ExceptionCode&); + + PassRefPtr<HTMLElement> createTHead(); + void deleteTHead(); + PassRefPtr<HTMLElement> createTFoot(); + void deleteTFoot(); + PassRefPtr<HTMLElement> createCaption(); + void deleteCaption(); + PassRefPtr<HTMLElement> insertRow(int index, ExceptionCode&); + void deleteRow(int index, ExceptionCode&); + + PassRefPtr<HTMLCollection> rows(); + PassRefPtr<HTMLCollection> tBodies(); + + String rules() const; + String summary() const; + + virtual void attach(); + + void addSharedCellDecls(Vector<CSSMutableStyleDeclaration*>&); + void addSharedGroupDecls(bool rows, Vector<CSSMutableStyleDeclaration*>&); + +private: + HTMLTableElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); + virtual bool isURLAttribute(Attribute*) const; + + // Used to obtain either a solid or outset border decl and to deal with the frame + // and rules attributes. + virtual bool canHaveAdditionalAttributeStyleDecls() const { return true; } + virtual void additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>&); + + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + void addSharedCellBordersDecl(Vector<CSSMutableStyleDeclaration*>&); + void addSharedCellPaddingDecl(Vector<CSSMutableStyleDeclaration*>&); + + enum TableRules { UnsetRules, NoneRules, GroupsRules, RowsRules, ColsRules, AllRules }; + enum CellBorders { NoBorders, SolidBorders, InsetBorders, SolidBordersColsOnly, SolidBordersRowsOnly }; + + CellBorders cellBorders() const; + + HTMLTableSectionElement* lastBody() const; + + bool m_borderAttr; // Sets a precise border width and creates an outset border for the table and for its cells. + bool m_borderColorAttr; // Overrides the outset border and makes it solid for the table and cells instead. + bool m_frameAttr; // Implies a thin border width if no border is set and then a certain set of solid/hidden borders based off the value. + TableRules m_rulesAttr; // Implies a thin border width, a collapsing border model, and all borders on the table becoming set to hidden (if frame/border + // are present, to none otherwise). + + unsigned short m_padding; + RefPtr<CSSMappedAttributeDeclaration> m_paddingDecl; + RefPtr<HTMLTableRowsCollection> m_rowsCollection; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableElement.idl b/Source/WebCore/html/HTMLTableElement.idl new file mode 100644 index 000000000..7c046eb6e --- /dev/null +++ b/Source/WebCore/html/HTMLTableElement.idl @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLTableElement : HTMLElement { + attribute HTMLTableCaptionElement caption setter raises(DOMException); + attribute HTMLTableSectionElement tHead setter raises(DOMException); + attribute HTMLTableSectionElement tFoot setter raises(DOMException); + + readonly attribute HTMLCollection rows; + readonly attribute HTMLCollection tBodies; + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString bgColor; + attribute [Reflect] DOMString border; + attribute [Reflect] DOMString cellPadding; + attribute [Reflect] DOMString cellSpacing; + + attribute [Reflect] DOMString frame; + + attribute [Reflect] DOMString rules; + attribute [Reflect] DOMString summary; + attribute [Reflect] DOMString width; + + HTMLElement createTHead(); + void deleteTHead(); + HTMLElement createTFoot(); + void deleteTFoot(); + HTMLElement createCaption(); + void deleteCaption(); + + HTMLElement insertRow(in [Optional=CallWithDefaultValue] long index) raises(DOMException); + void deleteRow(in [Optional=CallWithDefaultValue] long index) raises(DOMException); + }; + +} diff --git a/Source/WebCore/html/HTMLTablePartElement.cpp b/Source/WebCore/html/HTMLTablePartElement.cpp new file mode 100644 index 000000000..dfaecca9a --- /dev/null +++ b/Source/WebCore/html/HTMLTablePartElement.cpp @@ -0,0 +1,100 @@ +/** + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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 "HTMLTablePartElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" +#include "Document.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" + +namespace WebCore { + +using namespace HTMLNames; + +bool HTMLTablePartElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == backgroundAttr) { + result = (MappedAttributeEntry)(eLastEntry + document()->docID()); + return false; + } + + if (attrName == bgcolorAttr || + attrName == bordercolorAttr || + attrName == valignAttr || + attrName == heightAttr) { + result = eUniversal; + return false; + } + + if (attrName == alignAttr) { + result = eCell; // All table parts will just share in the TD space. + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLTablePartElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == bgcolorAttr) + addCSSColor(attr, CSSPropertyBackgroundColor, attr->value()); + else if (attr->name() == backgroundAttr) { + String url = stripLeadingAndTrailingHTMLSpaces(attr->value()); + if (!url.isEmpty()) + addCSSImageProperty(attr, CSSPropertyBackgroundImage, document()->completeURL(url).string()); + } else if (attr->name() == bordercolorAttr) { + if (!attr->value().isEmpty()) { + addCSSColor(attr, CSSPropertyBorderColor, attr->value()); + addCSSProperty(attr, CSSPropertyBorderTopStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderBottomStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderLeftStyle, CSSValueSolid); + addCSSProperty(attr, CSSPropertyBorderRightStyle, CSSValueSolid); + } + } else if (attr->name() == valignAttr) { + if (!attr->value().isEmpty()) + addCSSProperty(attr, CSSPropertyVerticalAlign, attr->value()); + } else if (attr->name() == alignAttr) { + const AtomicString& v = attr->value(); + if (equalIgnoringCase(v, "middle") || equalIgnoringCase(v, "center")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitCenter); + else if (equalIgnoringCase(v, "absmiddle")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueCenter); + else if (equalIgnoringCase(v, "left")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitLeft); + else if (equalIgnoringCase(v, "right")) + addCSSProperty(attr, CSSPropertyTextAlign, CSSValueWebkitRight); + else + addCSSProperty(attr, CSSPropertyTextAlign, v); + } else if (attr->name() == heightAttr) { + if (!attr->value().isEmpty()) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + } else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLTablePartElement.h b/Source/WebCore/html/HTMLTablePartElement.h new file mode 100644 index 000000000..9df2f919d --- /dev/null +++ b/Source/WebCore/html/HTMLTablePartElement.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 HTMLTablePartElement_h +#define HTMLTablePartElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLTablePartElement : public HTMLElement { +protected: + HTMLTablePartElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + { + } + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableRowElement.cpp b/Source/WebCore/html/HTMLTableRowElement.cpp new file mode 100644 index 000000000..5ec3bce2b --- /dev/null +++ b/Source/WebCore/html/HTMLTableRowElement.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 Apple 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 "HTMLTableRowElement.h" + +#include "ExceptionCode.h" +#include "HTMLCollection.h" +#include "HTMLNames.h" +#include "HTMLTableCellElement.h" +#include "HTMLTableElement.h" +#include "HTMLTableSectionElement.h" +#include "NodeList.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLTableRowElement::HTMLTableRowElement(const QualifiedName& tagName, Document* document) + : HTMLTablePartElement(tagName, document) +{ + ASSERT(hasTagName(trTag)); +} + +PassRefPtr<HTMLTableRowElement> HTMLTableRowElement::create(Document* document) +{ + return adoptRef(new HTMLTableRowElement(trTag, document)); +} + +PassRefPtr<HTMLTableRowElement> HTMLTableRowElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTableRowElement(tagName, document)); +} + +int HTMLTableRowElement::rowIndex() const +{ + ContainerNode* table = parentNode(); + if (!table) + return -1; + table = table->parentNode(); + if (!table || !table->hasTagName(tableTag)) + return -1; + + // To match Firefox, the row indices work like this: + // Rows from the first <thead> are numbered before all <tbody> rows. + // Rows from the first <tfoot> are numbered after all <tbody> rows. + // Rows from other <thead> and <tfoot> elements don't get row indices at all. + + int rIndex = 0; + + if (HTMLTableSectionElement* head = static_cast<HTMLTableElement*>(table)->tHead()) { + for (Node *row = head->firstChild(); row; row = row->nextSibling()) { + if (row == this) + return rIndex; + if (row->hasTagName(trTag)) + ++rIndex; + } + } + + for (Node *node = table->firstChild(); node; node = node->nextSibling()) { + if (node->hasTagName(tbodyTag)) { + HTMLTableSectionElement* section = static_cast<HTMLTableSectionElement*>(node); + for (Node* row = section->firstChild(); row; row = row->nextSibling()) { + if (row == this) + return rIndex; + if (row->hasTagName(trTag)) + ++rIndex; + } + } + } + + if (HTMLTableSectionElement* foot = static_cast<HTMLTableElement*>(table)->tFoot()) { + for (Node *row = foot->firstChild(); row; row = row->nextSibling()) { + if (row == this) + return rIndex; + if (row->hasTagName(trTag)) + ++rIndex; + } + } + + // We get here for rows that are in <thead> or <tfoot> sections other than the main header and footer. + return -1; +} + +int HTMLTableRowElement::sectionRowIndex() const +{ + int rIndex = 0; + const Node *n = this; + do { + n = n->previousSibling(); + if (n && n->hasTagName(trTag)) + rIndex++; + } + while (n); + + return rIndex; +} + +PassRefPtr<HTMLElement> HTMLTableRowElement::insertCell(int index, ExceptionCode& ec) +{ + RefPtr<HTMLCollection> children = cells(); + int numCells = children ? children->length() : 0; + if (index < -1 || index > numCells) { + ec = INDEX_SIZE_ERR; + return 0; + } + + RefPtr<HTMLTableCellElement> cell = HTMLTableCellElement::create(tdTag, document()); + if (index < 0 || index >= numCells) + appendChild(cell, ec); + else { + Node* n; + if (index < 1) + n = firstChild(); + else + n = children->item(index); + insertBefore(cell, n, ec); + } + return cell.release(); +} + +void HTMLTableRowElement::deleteCell(int index, ExceptionCode& ec) +{ + RefPtr<HTMLCollection> children = cells(); + int numCells = children ? children->length() : 0; + if (index == -1) + index = numCells-1; + if (index >= 0 && index < numCells) { + RefPtr<Node> cell = children->item(index); + HTMLElement::removeChild(cell.get(), ec); + } else + ec = INDEX_SIZE_ERR; +} + +PassRefPtr<HTMLCollection> HTMLTableRowElement::cells() +{ + return ensureCachedHTMLCollection(TRCells); +} + +void HTMLTableRowElement::setCells(HTMLCollection*, ExceptionCode& ec) +{ + ec = NO_MODIFICATION_ALLOWED_ERR; +} + +} diff --git a/Source/WebCore/html/HTMLTableRowElement.h b/Source/WebCore/html/HTMLTableRowElement.h new file mode 100644 index 000000000..c433677ac --- /dev/null +++ b/Source/WebCore/html/HTMLTableRowElement.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2010 Apple 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 HTMLTableRowElement_h +#define HTMLTableRowElement_h + +#include "HTMLTablePartElement.h" + +namespace WebCore { + +class HTMLTableRowElement : public HTMLTablePartElement { +public: + static PassRefPtr<HTMLTableRowElement> create(Document*); + static PassRefPtr<HTMLTableRowElement> create(const QualifiedName&, Document*); + + int rowIndex() const; + void setRowIndex(int); + + int sectionRowIndex() const; + void setSectionRowIndex(int); + + PassRefPtr<HTMLElement> insertCell(int index, ExceptionCode&); + void deleteCell(int index, ExceptionCode&); + + PassRefPtr<HTMLCollection> cells(); + void setCells(HTMLCollection *, ExceptionCode&); + +private: + HTMLTableRowElement(const QualifiedName&, Document*); +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableRowElement.idl b/Source/WebCore/html/HTMLTableRowElement.idl new file mode 100644 index 000000000..482c61f7d --- /dev/null +++ b/Source/WebCore/html/HTMLTableRowElement.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. ALl rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface HTMLTableRowElement : HTMLElement { + readonly attribute long rowIndex; + readonly attribute long sectionRowIndex; + readonly attribute HTMLCollection cells; + attribute [Reflect] DOMString align; + attribute [Reflect] DOMString bgColor; + attribute [Reflect=char] DOMString ch; + attribute [Reflect=charoff] DOMString chOff; + attribute [Reflect] DOMString vAlign; + HTMLElement insertCell(in [Optional=CallWithDefaultValue] long index) raises(DOMException); + void deleteCell(in [Optional=CallWithDefaultValue] long index) raises(DOMException); + }; + +} diff --git a/Source/WebCore/html/HTMLTableRowsCollection.cpp b/Source/WebCore/html/HTMLTableRowsCollection.cpp new file mode 100644 index 000000000..6a8567ca6 --- /dev/null +++ b/Source/WebCore/html/HTMLTableRowsCollection.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2008, 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLTableRowsCollection.h" + +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "HTMLTableRowElement.h" + +namespace WebCore { + +using namespace HTMLNames; + +static bool isInHead(Element* row) +{ + return row->parentNode() && static_cast<Element*>(row->parentNode())->hasLocalName(theadTag); +} + +static bool isInBody(Element* row) +{ + return row->parentNode() && static_cast<Element*>(row->parentNode())->hasLocalName(tbodyTag); +} + +static bool isInFoot(Element* row) +{ + return row->parentNode() && static_cast<Element*>(row->parentNode())->hasLocalName(tfootTag); +} + +HTMLTableRowElement* HTMLTableRowsCollection::rowAfter(HTMLTableElement* table, HTMLTableRowElement* previous) +{ + Node* child = 0; + + // Start by looking for the next row in this section. + // Continue only if there is none. + if (previous && previous->parentNode() != table) { + for (child = previous->nextSibling(); child; child = child->nextSibling()) { + if (child->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(child); + } + } + + // If still looking at head sections, find the first row in the next head section. + if (!previous) + child = table->firstChild(); + else if (isInHead(previous)) + child = previous->parentNode()->nextSibling(); + for (; child; child = child->nextSibling()) { + if (child->hasTagName(theadTag)) { + for (Node* grandchild = child->firstChild(); grandchild; grandchild = grandchild->nextSibling()) { + if (grandchild->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(grandchild); + } + } + } + + // If still looking at top level and bodies, find the next row in top level or the first in the next body section. + if (!previous || isInHead(previous)) + child = table->firstChild(); + else if (previous->parentNode() == table) + child = previous->nextSibling(); + else if (isInBody(previous)) + child = previous->parentNode()->nextSibling(); + for (; child; child = child->nextSibling()) { + if (child->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(child); + if (child->hasTagName(tbodyTag)) { + for (Node* grandchild = child->firstChild(); grandchild; grandchild = grandchild->nextSibling()) { + if (grandchild->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(grandchild); + } + } + } + + // Find the first row in the next foot section. + if (!previous || !isInFoot(previous)) + child = table->firstChild(); + else + child = previous->parentNode()->nextSibling(); + for (; child; child = child->nextSibling()) { + if (child->hasTagName(tfootTag)) { + for (Node* grandchild = child->firstChild(); grandchild; grandchild = grandchild->nextSibling()) { + if (grandchild->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(grandchild); + } + } + } + + return 0; +} + +HTMLTableRowElement* HTMLTableRowsCollection::lastRow(HTMLTableElement* table) +{ + for (Node* child = table->lastChild(); child; child = child->previousSibling()) { + if (child->hasTagName(tfootTag)) { + for (Node* grandchild = child->lastChild(); grandchild; grandchild = grandchild->previousSibling()) { + if (grandchild->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(grandchild); + } + } + } + + for (Node* child = table->lastChild(); child; child = child->previousSibling()) { + if (child->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(child); + if (child->hasTagName(tbodyTag)) { + for (Node* grandchild = child->lastChild(); grandchild; grandchild = grandchild->previousSibling()) { + if (grandchild->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(grandchild); + } + } + } + + for (Node* child = table->lastChild(); child; child = child->previousSibling()) { + if (child->hasTagName(theadTag)) { + for (Node* grandchild = child->lastChild(); grandchild; grandchild = grandchild->previousSibling()) { + if (grandchild->hasTagName(trTag)) + return static_cast<HTMLTableRowElement*>(grandchild); + } + } + } + + return 0; +} + +// Must call get() on the table in case that argument is compiled before dereferencing the +// table to get at the collection cache. Order of argument evaluation is undefined and can +// differ between compilers. +HTMLTableRowsCollection::HTMLTableRowsCollection(HTMLTableElement* table) + : HTMLCollection(table, OtherCollection) +{ +} + +PassRefPtr<HTMLTableRowsCollection> HTMLTableRowsCollection::create(HTMLTableElement* table) +{ + return adoptRef(new HTMLTableRowsCollection(table)); +} + +Element* HTMLTableRowsCollection::itemAfter(Element* previous) const +{ + ASSERT(base()); + ASSERT(!previous || previous->hasLocalName(trTag)); + return rowAfter(static_cast<HTMLTableElement*>(base()), static_cast<HTMLTableRowElement*>(previous)); +} + +} diff --git a/Source/WebCore/html/HTMLTableRowsCollection.h b/Source/WebCore/html/HTMLTableRowsCollection.h new file mode 100644 index 000000000..66002d08c --- /dev/null +++ b/Source/WebCore/html/HTMLTableRowsCollection.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLTableRowsCollection_h +#define HTMLTableRowsCollection_h + +#include "HTMLCollection.h" + +namespace WebCore { + +class HTMLTableElement; +class HTMLTableRowElement; + +class HTMLTableRowsCollection : public HTMLCollection { +public: + static PassRefPtr<HTMLTableRowsCollection> create(HTMLTableElement*); + + static HTMLTableRowElement* rowAfter(HTMLTableElement*, HTMLTableRowElement*); + static HTMLTableRowElement* lastRow(HTMLTableElement*); + +private: + HTMLTableRowsCollection(HTMLTableElement*); + + virtual Element* itemAfter(Element*) const; +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableSectionElement.cpp b/Source/WebCore/html/HTMLTableSectionElement.cpp new file mode 100644 index 000000000..73a8367af --- /dev/null +++ b/Source/WebCore/html/HTMLTableSectionElement.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2010 Apple 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 "HTMLTableSectionElement.h" + +#include "ExceptionCode.h" +#include "HTMLCollection.h" +#include "HTMLNames.h" +#include "HTMLTableRowElement.h" +#include "HTMLTableElement.h" +#include "NodeList.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLTableSectionElement::HTMLTableSectionElement(const QualifiedName& tagName, Document* document) + : HTMLTablePartElement(tagName, document) +{ +} + +PassRefPtr<HTMLTableSectionElement> HTMLTableSectionElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTableSectionElement(tagName, document)); +} + +// used by table row groups to share style decls created by the enclosing table. +void HTMLTableSectionElement::additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>& results) +{ + ContainerNode* p = parentNode(); + while (p && !p->hasTagName(tableTag)) + p = p->parentNode(); + if (!p) + return; + static_cast<HTMLTableElement*>(p)->addSharedGroupDecls(true, results); +} + +// these functions are rather slow, since we need to get the row at +// the index... but they aren't used during usual HTML parsing anyway +PassRefPtr<HTMLElement> HTMLTableSectionElement::insertRow(int index, ExceptionCode& ec) +{ + RefPtr<HTMLTableRowElement> row; + RefPtr<HTMLCollection> children = rows(); + int numRows = children ? (int)children->length() : 0; + if (index < -1 || index > numRows) + ec = INDEX_SIZE_ERR; // per the DOM + else { + row = HTMLTableRowElement::create(trTag, document()); + if (numRows == index || index == -1) + appendChild(row, ec); + else { + Node* n; + if (index < 1) + n = firstChild(); + else + n = children->item(index); + insertBefore(row, n, ec); + } + } + return row.release(); +} + +void HTMLTableSectionElement::deleteRow(int index, ExceptionCode& ec) +{ + RefPtr<HTMLCollection> children = rows(); + int numRows = children ? (int)children->length() : 0; + if (index == -1) + index = numRows - 1; + if (index >= 0 && index < numRows) { + RefPtr<Node> row = children->item(index); + HTMLElement::removeChild(row.get(), ec); + } else + ec = INDEX_SIZE_ERR; +} + +int HTMLTableSectionElement::numRows() const +{ + int rows = 0; + const Node *n = firstChild(); + while (n) { + if (n->hasTagName(trTag)) + rows++; + n = n->nextSibling(); + } + + return rows; +} + +String HTMLTableSectionElement::align() const +{ + return getAttribute(alignAttr); +} + +void HTMLTableSectionElement::setAlign(const String &value) +{ + setAttribute(alignAttr, value); +} + +String HTMLTableSectionElement::ch() const +{ + return getAttribute(charAttr); +} + +void HTMLTableSectionElement::setCh(const String &value) +{ + setAttribute(charAttr, value); +} + +String HTMLTableSectionElement::chOff() const +{ + return getAttribute(charoffAttr); +} + +void HTMLTableSectionElement::setChOff(const String &value) +{ + setAttribute(charoffAttr, value); +} + +String HTMLTableSectionElement::vAlign() const +{ + return getAttribute(valignAttr); +} + +void HTMLTableSectionElement::setVAlign(const String &value) +{ + setAttribute(valignAttr, value); +} + +PassRefPtr<HTMLCollection> HTMLTableSectionElement::rows() +{ + return ensureCachedHTMLCollection(TSectionRows); +} + +} diff --git a/Source/WebCore/html/HTMLTableSectionElement.h b/Source/WebCore/html/HTMLTableSectionElement.h new file mode 100644 index 000000000..a84cbde6d --- /dev/null +++ b/Source/WebCore/html/HTMLTableSectionElement.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple 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 HTMLTableSectionElement_h +#define HTMLTableSectionElement_h + +#include "HTMLTablePartElement.h" + +namespace WebCore { + +class HTMLTableSectionElement : public HTMLTablePartElement { +public: + static PassRefPtr<HTMLTableSectionElement> create(const QualifiedName&, Document*); + + PassRefPtr<HTMLElement> insertRow(int index, ExceptionCode&); + void deleteRow(int index, ExceptionCode&); + + int numRows() const; + + String align() const; + void setAlign(const String&); + + String ch() const; + void setCh(const String&); + + String chOff() const; + void setChOff(const String&); + + String vAlign() const; + void setVAlign(const String&); + + PassRefPtr<HTMLCollection> rows(); + +private: + HTMLTableSectionElement(const QualifiedName& tagName, Document*); + + virtual bool canHaveAdditionalAttributeStyleDecls() const { return true; } + virtual void additionalAttributeStyleDecls(Vector<CSSMutableStyleDeclaration*>&); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLTableSectionElement.idl b/Source/WebCore/html/HTMLTableSectionElement.idl new file mode 100644 index 000000000..9c0e53b86 --- /dev/null +++ b/Source/WebCore/html/HTMLTableSectionElement.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * + * 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. + */ + +module html { + + interface [ + GenerateNativeConverter + ] HTMLTableSectionElement : HTMLElement { + attribute [Reflect] DOMString align; + attribute [Reflect=char] DOMString ch; + attribute [Reflect=charoff] DOMString chOff; + attribute [Reflect] DOMString vAlign; + readonly attribute HTMLCollection rows; + HTMLElement insertRow(in [Optional=CallWithDefaultValue] long index) raises(DOMException); + void deleteRow(in [Optional=CallWithDefaultValue] long index) raises(DOMException); + }; + +} diff --git a/Source/WebCore/html/HTMLTagNames.in b/Source/WebCore/html/HTMLTagNames.in new file mode 100644 index 000000000..f1c30a01f --- /dev/null +++ b/Source/WebCore/html/HTMLTagNames.in @@ -0,0 +1,135 @@ +namespace="HTML" +namespacePrefix="xhtml" +namespaceURI="http://www.w3.org/1999/xhtml" +fallbackInterfaceName="HTMLUnknownElement" + +a interfaceName=HTMLAnchorElement +abbr interfaceName=HTMLElement +acronym interfaceName=HTMLElement +address interfaceName=HTMLElement +applet +area +article interfaceName=HTMLElement +aside interfaceName=HTMLElement +audio wrapperOnlyIfMediaIsAvailable, conditional=VIDEO, constructorNeedsCreatedByParser +b interfaceName=HTMLElement +base +basefont interfaceName=HTMLBaseFontElement +bdi interfaceName=HTMLElement +bdo interfaceName=HTMLElement +bgsound interfaceName=HTMLElement +big interfaceName=HTMLElement +blockquote interfaceName=HTMLQuoteElement +body +br interfaceName=HTMLBRElement +button constructorNeedsFormElement +canvas +caption interfaceName=HTMLTableCaptionElement +center interfaceName=HTMLElement +cite interfaceName=HTMLElement +code interfaceName=HTMLElement +col interfaceName=HTMLTableColElement +colgroup interfaceName=HTMLTableColElement +command interfaceName=HTMLElement +datalist interfaceName=HTMLDataListElement, conditional=DATALIST +dd interfaceName=HTMLElement +del interfaceName=HTMLModElement +details conditional=DETAILS +dfn interfaceName=HTMLElement +dir interfaceName=HTMLDirectoryElement +div +dl interfaceName=HTMLDListElement +dt interfaceName=HTMLElement +em interfaceName=HTMLElement +embed constructorNeedsCreatedByParser +fieldset interfaceName=HTMLFieldSetElement, constructorNeedsFormElement +figcaption interfaceName=HTMLElement +figure interfaceName=HTMLElement +font +footer interfaceName=HTMLElement +form +frame +frameset interfaceName=HTMLFrameSetElement +h1 interfaceName=HTMLHeadingElement +h2 interfaceName=HTMLHeadingElement +h3 interfaceName=HTMLHeadingElement +h4 interfaceName=HTMLHeadingElement +h5 interfaceName=HTMLHeadingElement +h6 interfaceName=HTMLHeadingElement +head +header interfaceName=HTMLElement +hgroup interfaceName=HTMLElement +hr interfaceName=HTMLHRElement +html +i interfaceName=HTMLElement +iframe interfaceName=HTMLIFrameElement +image mapToTagName=img +img interfaceName=HTMLImageElement, constructorNeedsFormElement +input constructorNeedsFormElement, constructorNeedsCreatedByParser +ins interfaceName=HTMLModElement +isindex interfaceName=HTMLIsIndexElement, constructorNeedsFormElement +kbd interfaceName=HTMLElement +keygen constructorNeedsFormElement +label +layer interfaceName=HTMLElement +legend constructorNeedsFormElement +li interfaceName=HTMLLIElement +link constructorNeedsCreatedByParser +listing interfaceName=HTMLPreElement +map +mark interfaceName=HTMLElement +marquee +menu +meta +meter interfaceName=HTMLMeterElement, constructorNeedsFormElement, conditional=METER_TAG +nav interfaceName=HTMLElement +nobr interfaceName=HTMLElement +noembed interfaceName=HTMLElement +noframes interfaceName=HTMLElement +nolayer interfaceName=HTMLElement +object constructorNeedsFormElement, constructorNeedsCreatedByParser +ol interfaceName=HTMLOListElement +optgroup interfaceName=HTMLOptGroupElement, constructorNeedsFormElement +option constructorNeedsFormElement +output constructorNeedsFormElement +p interfaceName=HTMLParagraphElement +param +plaintext interfaceName=HTMLElement +pre +progress interfaceName=HTMLProgressElement, constructorNeedsFormElement, conditional=PROGRESS_TAG +q interfaceName=HTMLQuoteElement +rp interfaceName=HTMLElement +rt interfaceName=HTMLElement +ruby interfaceName=HTMLElement +s interfaceName=HTMLElement +samp interfaceName=HTMLElement +script constructorNeedsCreatedByParser +section interfaceName=HTMLElement +select constructorNeedsFormElement +small interfaceName=HTMLElement +source wrapperOnlyIfMediaIsAvailable, conditional=VIDEO +span +strike interfaceName=HTMLElement +strong interfaceName=HTMLElement +style constructorNeedsCreatedByParser +sub interfaceName=HTMLElement +summary interfaceName=HTMLSummaryElement, JSInterfaceName=HTMLElement, conditional=DETAILS +sup interfaceName=HTMLElement +table +tbody interfaceName=HTMLTableSectionElement +td interfaceName=HTMLTableCellElement +textarea interfaceName=HTMLTextAreaElement, constructorNeedsFormElement +tfoot interfaceName=HTMLTableSectionElement +th interfaceName=HTMLTableCellElement +thead interfaceName=HTMLTableSectionElement +title +tr interfaceName=HTMLTableRowElement +track wrapperOnlyIfMediaIsAvailable, conditional=VIDEO_TRACK +tt interfaceName=HTMLElement +u interfaceName=HTMLElement +ul interfaceName=HTMLUListElement +var interfaceName=HTMLElement +video wrapperOnlyIfMediaIsAvailable, conditional=VIDEO, constructorNeedsCreatedByParser +wbr interfaceName=HTMLElement +xmp interfaceName=HTMLPreElement +noscript interfaceName=HTMLElement diff --git a/Source/WebCore/html/HTMLTextAreaElement.cpp b/Source/WebCore/html/HTMLTextAreaElement.cpp new file mode 100644 index 000000000..b5ac63390 --- /dev/null +++ b/Source/WebCore/html/HTMLTextAreaElement.cpp @@ -0,0 +1,473 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * + * 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 "HTMLTextAreaElement.h" + +#include "Attribute.h" +#include "BeforeTextInsertedEvent.h" +#include "CSSValueKeywords.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "ExceptionCode.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "RenderTextControlMultiLine.h" +#include "ShadowRoot.h" +#include "Text.h" +#include "TextControlInnerElements.h" +#include "TextIterator.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +static const int defaultRows = 2; +static const int defaultCols = 20; + +// On submission, LF characters are converted into CRLF. +// This function returns number of characters considering this. +static unsigned computeLengthForSubmission(const String& text) +{ + unsigned count = numGraphemeClusters(text); + unsigned length = text.length(); + for (unsigned i = 0; i < length; i++) { + if (text[i] == '\n') + count++; + } + return count; +} + +HTMLTextAreaElement::HTMLTextAreaElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) + : HTMLTextFormControlElement(tagName, document, form) + , m_rows(defaultRows) + , m_cols(defaultCols) + , m_wrap(SoftWrap) + , m_isDirty(false) + , m_wasModifiedByUser(false) +{ + ASSERT(hasTagName(textareaTag)); + setFormControlValueMatchesRenderer(true); +} + +PassRefPtr<HTMLTextAreaElement> HTMLTextAreaElement::create(const QualifiedName& tagName, Document* document, HTMLFormElement* form) +{ + RefPtr<HTMLTextAreaElement> textArea = adoptRef(new HTMLTextAreaElement(tagName, document, form)); + textArea->createShadowSubtree(); + return textArea.release(); +} + +void HTMLTextAreaElement::createShadowSubtree() +{ + ExceptionCode ec = 0; + ensureShadowRoot()->appendChild(TextControlInnerTextElement::create(document()), ec); +} + +const AtomicString& HTMLTextAreaElement::formControlType() const +{ + DEFINE_STATIC_LOCAL(const AtomicString, textarea, ("textarea")); + return textarea; +} + +bool HTMLTextAreaElement::saveFormControlState(String& result) const +{ + String currentValue = value(); + if (currentValue == defaultValue()) + return false; + result = currentValue; + return true; +} + +void HTMLTextAreaElement::restoreFormControlState(const String& state) +{ + setValue(state); +} + +void HTMLTextAreaElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + setLastChangeWasNotUserEdit(); + if (!m_isDirty) + setNonDirtyValue(defaultValue()); + setInnerTextValue(value()); + HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +void HTMLTextAreaElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == rowsAttr) { + int rows = attr->value().toInt(); + if (rows <= 0) + rows = defaultRows; + if (m_rows != rows) { + m_rows = rows; + if (renderer()) + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + } + } else if (attr->name() == colsAttr) { + int cols = attr->value().toInt(); + if (cols <= 0) + cols = defaultCols; + if (m_cols != cols) { + m_cols = cols; + if (renderer()) + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + } + } else if (attr->name() == wrapAttr) { + // The virtual/physical values were a Netscape extension of HTML 3.0, now deprecated. + // The soft/hard /off values are a recommendation for HTML 4 extension by IE and NS 4. + WrapMethod wrap; + if (equalIgnoringCase(attr->value(), "physical") || equalIgnoringCase(attr->value(), "hard") || equalIgnoringCase(attr->value(), "on")) + wrap = HardWrap; + else if (equalIgnoringCase(attr->value(), "off")) + wrap = NoWrap; + else + wrap = SoftWrap; + if (wrap != m_wrap) { + m_wrap = wrap; + + if (shouldWrapText()) { + addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePreWrap); + addCSSProperty(attr, CSSPropertyWordWrap, CSSValueBreakWord); + } else { + addCSSProperty(attr, CSSPropertyWhiteSpace, CSSValuePre); + addCSSProperty(attr, CSSPropertyWordWrap, CSSValueNormal); + } + + if (renderer()) + renderer()->setNeedsLayoutAndPrefWidthsRecalc(); + } + } else if (attr->name() == accesskeyAttr) { + // ignore for the moment + } else if (attr->name() == alignAttr) { + // Don't map 'align' attribute. This matches what Firefox, Opera and IE do. + // See http://bugs.webkit.org/show_bug.cgi?id=7075 + } else if (attr->name() == maxlengthAttr) + setNeedsValidityCheck(); + else + HTMLTextFormControlElement::parseMappedAttribute(attr); +} + +RenderObject* HTMLTextAreaElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderTextControlMultiLine(this); +} + +bool HTMLTextAreaElement::appendFormData(FormDataList& encoding, bool) +{ + if (name().isEmpty()) + return false; + + document()->updateLayout(); + + const String& text = (m_wrap == HardWrap) ? valueWithHardLineBreaks() : value(); + encoding.appendData(name(), text); + + const AtomicString& dirnameAttrValue = fastGetAttribute(dirnameAttr); + if (!dirnameAttrValue.isNull()) + encoding.appendData(dirnameAttrValue, directionForFormData()); + return true; +} + +void HTMLTextAreaElement::reset() +{ + setNonDirtyValue(defaultValue()); +} + +bool HTMLTextAreaElement::isKeyboardFocusable(KeyboardEvent*) const +{ + // If a given text area can be focused at all, then it will always be keyboard focusable. + return isFocusable(); +} + +bool HTMLTextAreaElement::isMouseFocusable() const +{ + return isFocusable(); +} + +void HTMLTextAreaElement::updateFocusAppearance(bool restorePreviousSelection) +{ + if (!restorePreviousSelection || !hasCachedSelection()) { + // If this is the first focus, set a caret at the beginning of the text. + // This matches some browsers' behavior; see bug 11746 Comment #15. + // http://bugs.webkit.org/show_bug.cgi?id=11746#c15 + setSelectionRange(0, 0); + } else + restoreCachedSelection(); + + if (document()->frame()) + document()->frame()->selection()->revealSelection(); +} + +void HTMLTextAreaElement::defaultEventHandler(Event* event) +{ + if (renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(eventNames().interfaceForWheelEvent) || event->type() == eventNames().blurEvent)) + forwardEvent(event); + else if (renderer() && event->isBeforeTextInsertedEvent()) + handleBeforeTextInsertedEvent(static_cast<BeforeTextInsertedEvent*>(event)); + + HTMLTextFormControlElement::defaultEventHandler(event); +} + +void HTMLTextAreaElement::subtreeHasChanged() +{ + setChangedSinceLastFormControlChangeEvent(true); + setFormControlValueMatchesRenderer(false); + setNeedsValidityCheck(); + + if (!focused()) + return; + + if (Frame* frame = document()->frame()) + frame->editor()->textDidChangeInTextArea(this); + // When typing in a textarea, childrenChanged is not called, so we need to force the directionality check. + calculateAndAdjustDirectionality(); +} + +void HTMLTextAreaElement::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event) const +{ + ASSERT(event); + ASSERT(renderer()); + int signedMaxLength = maxLength(); + if (signedMaxLength < 0) + return; + unsigned unsignedMaxLength = static_cast<unsigned>(signedMaxLength); + + unsigned currentLength = computeLengthForSubmission(innerTextValue()); + // selectionLength represents the selection length of this text field to be + // removed by this insertion. + // If the text field has no focus, we don't need to take account of the + // selection length. The selection is the source of text drag-and-drop in + // that case, and nothing in the text field will be removed. + unsigned selectionLength = focused() ? computeLengthForSubmission(plainText(document()->frame()->selection()->selection().toNormalizedRange().get())) : 0; + ASSERT(currentLength >= selectionLength); + unsigned baseLength = currentLength - selectionLength; + unsigned appendableLength = unsignedMaxLength > baseLength ? unsignedMaxLength - baseLength : 0; + event->setText(sanitizeUserInputValue(event->text(), appendableLength)); +} + +String HTMLTextAreaElement::sanitizeUserInputValue(const String& proposedValue, unsigned maxLength) +{ + return proposedValue.left(numCharactersInGraphemeClusters(proposedValue, maxLength)); +} + +HTMLElement* HTMLTextAreaElement::innerTextElement() const +{ + Node* node = shadowRoot()->firstChild(); + ASSERT(!node || node->hasTagName(divTag)); + return toHTMLElement(node); +} + +void HTMLTextAreaElement::rendererWillBeDestroyed() +{ + updateValue(); +} + +void HTMLTextAreaElement::updateValue() const +{ + if (formControlValueMatchesRenderer()) + return; + + ASSERT(renderer()); + m_value = innerTextValue(); + const_cast<HTMLTextAreaElement*>(this)->setFormControlValueMatchesRenderer(true); + const_cast<HTMLTextAreaElement*>(this)->notifyFormStateChanged(); + m_isDirty = true; + m_wasModifiedByUser = true; + const_cast<HTMLTextAreaElement*>(this)->updatePlaceholderVisibility(false); +} + +String HTMLTextAreaElement::value() const +{ + updateValue(); + return m_value; +} + +void HTMLTextAreaElement::setValue(const String& value) +{ + setValueCommon(value); + m_isDirty = true; + setNeedsValidityCheck(); +} + +void HTMLTextAreaElement::setNonDirtyValue(const String& value) +{ + setValueCommon(value); + m_isDirty = false; + setNeedsValidityCheck(); +} + +void HTMLTextAreaElement::setValueCommon(const String& newValue) +{ + m_wasModifiedByUser = false; + // Code elsewhere normalizes line endings added by the user via the keyboard or pasting. + // We normalize line endings coming from JavaScript here. + String normalizedValue = newValue.isNull() ? "" : newValue; + normalizedValue.replace("\r\n", "\n"); + normalizedValue.replace('\r', '\n'); + + // Return early because we don't want to move the caret or trigger other side effects + // when the value isn't changing. This matches Firefox behavior, at least. + if (normalizedValue == value()) + return; + + m_value = normalizedValue; + setInnerTextValue(m_value); + setLastChangeWasNotUserEdit(); + updatePlaceholderVisibility(false); + setNeedsStyleRecalc(); + setFormControlValueMatchesRenderer(true); + + // Set the caret to the end of the text value. + if (document()->focusedNode() == this) { + unsigned endOfString = m_value.length(); + setSelectionRange(endOfString, endOfString); + } + + notifyFormStateChanged(); + setTextAsOfLastFormControlChangeEvent(normalizedValue); +} + +String HTMLTextAreaElement::defaultValue() const +{ + String value = ""; + + // Since there may be comments, ignore nodes other than text nodes. + for (Node* n = firstChild(); n; n = n->nextSibling()) { + if (n->isTextNode()) + value += static_cast<Text*>(n)->data(); + } + + return value; +} + +void HTMLTextAreaElement::setDefaultValue(const String& defaultValue) +{ + // To preserve comments, remove only the text nodes, then add a single text node. + + Vector<RefPtr<Node> > textNodes; + for (Node* n = firstChild(); n; n = n->nextSibling()) { + if (n->isTextNode()) + textNodes.append(n); + } + ExceptionCode ec; + size_t size = textNodes.size(); + for (size_t i = 0; i < size; ++i) + removeChild(textNodes[i].get(), ec); + + // Normalize line endings. + String value = defaultValue; + value.replace("\r\n", "\n"); + value.replace('\r', '\n'); + + insertBefore(document()->createTextNode(value), firstChild(), ec); + + if (!m_isDirty) + setNonDirtyValue(value); +} + +int HTMLTextAreaElement::maxLength() const +{ + bool ok; + int value = getAttribute(maxlengthAttr).string().toInt(&ok); + return ok && value >= 0 ? value : -1; +} + +void HTMLTextAreaElement::setMaxLength(int newValue, ExceptionCode& ec) +{ + if (newValue < 0) + ec = INDEX_SIZE_ERR; + else + setAttribute(maxlengthAttr, String::number(newValue)); +} + +bool HTMLTextAreaElement::tooLong(const String& value, NeedsToCheckDirtyFlag check) const +{ + // Return false for the default value or value set by script even if it is + // longer than maxLength. + if (check == CheckDirtyFlag && !m_wasModifiedByUser) + return false; + + int max = maxLength(); + if (max < 0) + return false; + return computeLengthForSubmission(value) > static_cast<unsigned>(max); +} + +bool HTMLTextAreaElement::isValidValue(const String& candidate) const +{ + return !valueMissing(candidate) && !tooLong(candidate, IgnoreDirtyFlag); +} + +void HTMLTextAreaElement::accessKeyAction(bool) +{ + focus(); +} + +void HTMLTextAreaElement::setCols(int cols) +{ + setAttribute(colsAttr, String::number(cols)); +} + +void HTMLTextAreaElement::setRows(int rows) +{ + setAttribute(rowsAttr, String::number(rows)); +} + +bool HTMLTextAreaElement::shouldUseInputMethod() +{ + return true; +} + +HTMLElement* HTMLTextAreaElement::placeholderElement() const +{ + return m_placeholder.get(); +} + +void HTMLTextAreaElement::updatePlaceholderText() +{ + ExceptionCode ec = 0; + String placeholderText = strippedPlaceholder(); + if (placeholderText.isEmpty()) { + if (m_placeholder) { + shadowRoot()->removeChild(m_placeholder.get(), ec); + ASSERT(!ec); + m_placeholder.clear(); + } + return; + } + if (!m_placeholder) { + m_placeholder = HTMLDivElement::create(document()); + m_placeholder->setShadowPseudoId("-webkit-input-placeholder"); + shadowRoot()->insertBefore(m_placeholder, shadowRoot()->firstChild()->nextSibling(), ec); + ASSERT(!ec); + } + m_placeholder->setInnerText(placeholderText, ec); + ASSERT(!ec); +} + +} diff --git a/Source/WebCore/html/HTMLTextAreaElement.h b/Source/WebCore/html/HTMLTextAreaElement.h new file mode 100644 index 000000000..f455da7fd --- /dev/null +++ b/Source/WebCore/html/HTMLTextAreaElement.h @@ -0,0 +1,119 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple 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 HTMLTextAreaElement_h +#define HTMLTextAreaElement_h + +#include "HTMLTextFormControlElement.h" + +namespace WebCore { + +class BeforeTextInsertedEvent; +class VisibleSelection; + +class HTMLTextAreaElement : public HTMLTextFormControlElement { +public: + static PassRefPtr<HTMLTextAreaElement> create(const QualifiedName&, Document*, HTMLFormElement*); + + int cols() const { return m_cols; } + int rows() const { return m_rows; } + + bool shouldWrapText() const { return m_wrap != NoWrap; } + + virtual String value() const; + void setValue(const String&); + String defaultValue() const; + void setDefaultValue(const String&); + int textLength() const { return value().length(); } + virtual int maxLength() const; + void setMaxLength(int, ExceptionCode&); + bool valueMissing(const String& value) const { return isRequiredFormControl() && !disabled() && !readOnly() && value.isEmpty(); } + bool tooLong(const String&, NeedsToCheckDirtyFlag) const; + bool isValidValue(const String&) const; + + virtual HTMLElement* innerTextElement() const; + + void rendererWillBeDestroyed(); + + void setCols(int); + void setRows(int); + +private: + HTMLTextAreaElement(const QualifiedName&, Document*, HTMLFormElement*); + + enum WrapMethod { NoWrap, SoftWrap, HardWrap }; + + void createShadowSubtree(); + + void handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*) const; + static String sanitizeUserInputValue(const String&, unsigned maxLength); + void updateValue() const; + void setNonDirtyValue(const String&); + void setValueCommon(const String&); + + virtual bool supportsPlaceholder() const { return true; } + virtual HTMLElement* placeholderElement() const; + virtual void updatePlaceholderText(); + virtual bool isEmptyValue() const { return value().isEmpty(); } + + virtual bool isOptionalFormControl() const { return !isRequiredFormControl(); } + virtual bool isRequiredFormControl() const { return required(); } + + virtual void defaultEventHandler(Event*); + + virtual void subtreeHasChanged(); + + virtual bool isEnumeratable() const { return true; } + + virtual const AtomicString& formControlType() const; + + virtual bool saveFormControlState(String& value) const; + virtual void restoreFormControlState(const String&); + + virtual bool isTextFormControl() const { return true; } + + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + virtual void parseMappedAttribute(Attribute*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual bool appendFormData(FormDataList&, bool); + virtual void reset(); + virtual bool isMouseFocusable() const; + virtual bool isKeyboardFocusable(KeyboardEvent*) const; + virtual void updateFocusAppearance(bool restorePreviousSelection); + + virtual void accessKeyAction(bool sendMouseEvents); + + virtual bool shouldUseInputMethod(); + + int m_rows; + int m_cols; + WrapMethod m_wrap; + RefPtr<HTMLElement> m_placeholder; + mutable String m_value; + mutable bool m_isDirty; + mutable bool m_wasModifiedByUser; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLTextAreaElement.idl b/Source/WebCore/html/HTMLTextAreaElement.idl new file mode 100644 index 000000000..62f3e6b1d --- /dev/null +++ b/Source/WebCore/html/HTMLTextAreaElement.idl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2006 Samuel Weinig <sam.weinig@gmail.com> + * Copyright (C) 2011 Motorola Mobility, 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. + */ + +module html { + + interface HTMLTextAreaElement : HTMLElement { + attribute [ConvertNullToNullString] DOMString defaultValue; + readonly attribute HTMLFormElement form; + readonly attribute ValidityState validity; + attribute long cols; + attribute [Reflect] DOMString dirName; + attribute [Reflect] boolean disabled; + attribute [Reflect] boolean autofocus; + attribute long maxLength setter raises(DOMException); + attribute [ConvertNullToNullString] DOMString name; + attribute [Reflect] DOMString placeholder; + attribute [Reflect] boolean readOnly; + attribute [Reflect] boolean required; + attribute long rows; + attribute [Reflect] DOMString wrap; + readonly attribute DOMString type; + attribute [ConvertNullToNullString] DOMString value; + readonly attribute unsigned long textLength; + + void select(); + + readonly attribute boolean willValidate; + readonly attribute DOMString validationMessage; + boolean checkValidity(); + void setCustomValidity(in [ConvertUndefinedOrNullToNullString] DOMString error); + + // WinIE & FireFox extension: + attribute long selectionStart; + attribute long selectionEnd; + attribute DOMString selectionDirection; +#if defined(LANGUAGE_OBJECTIVE_C) && LANGUAGE_OBJECTIVE_C + void setSelectionRange(in long start, in long end); +#else + void setSelectionRange(in [Optional=CallWithDefaultValue] long start, + in [Optional=CallWithDefaultValue] long end, + in [Optional] DOMString direction); +#endif + readonly attribute NodeList labels; + }; + +} diff --git a/Source/WebCore/html/HTMLTextFormControlElement.cpp b/Source/WebCore/html/HTMLTextFormControlElement.cpp new file mode 100644 index 000000000..04c926c88 --- /dev/null +++ b/Source/WebCore/html/HTMLTextFormControlElement.cpp @@ -0,0 +1,603 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * + * 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 "HTMLTextFormControlElement.h" + +#include "AXObjectCache.h" +#include "Attribute.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "Event.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLBRElement.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderBox.h" +#include "RenderTextControl.h" +#include "RenderTheme.h" +#include "ScriptEventListener.h" +#include "Text.h" +#include "TextIterator.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +using namespace HTMLNames; +using namespace std; + +HTMLTextFormControlElement::HTMLTextFormControlElement(const QualifiedName& tagName, Document* doc, HTMLFormElement* form) + : HTMLFormControlElementWithState(tagName, doc, form) + , m_lastChangeWasUserEdit(false) + , m_cachedSelectionStart(-1) + , m_cachedSelectionEnd(-1) + , m_cachedSelectionDirection(SelectionHasNoDirection) +{ +} + +HTMLTextFormControlElement::~HTMLTextFormControlElement() +{ +} + +void HTMLTextFormControlElement::insertedIntoDocument() +{ + HTMLFormControlElement::insertedIntoDocument(); + String initialValue = value(); + setTextAsOfLastFormControlChangeEvent(initialValue.isNull() ? emptyString() : initialValue); +} + +void HTMLTextFormControlElement::dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode) +{ + if (supportsPlaceholder()) + updatePlaceholderVisibility(false); + handleFocusEvent(); + HTMLFormControlElementWithState::dispatchFocusEvent(oldFocusedNode); +} + +void HTMLTextFormControlElement::dispatchBlurEvent(PassRefPtr<Node> newFocusedNode) +{ + if (supportsPlaceholder()) + updatePlaceholderVisibility(false); + handleBlurEvent(); + HTMLFormControlElementWithState::dispatchBlurEvent(newFocusedNode); +} + +void HTMLTextFormControlElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().webkitEditableContentChangedEvent && renderer() && renderer()->isTextControl()) { + m_lastChangeWasUserEdit = true; + subtreeHasChanged(); + return; + } + + HTMLFormControlElementWithState::defaultEventHandler(event); +} + +void HTMLTextFormControlElement::forwardEvent(Event* event) +{ + if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent) + return; + innerTextElement()->defaultEventHandler(event); +} + +String HTMLTextFormControlElement::strippedPlaceholder() const +{ + // According to the HTML5 specification, we need to remove CR and LF from + // the attribute value. + const AtomicString& attributeValue = getAttribute(placeholderAttr); + if (!attributeValue.contains(newlineCharacter) && !attributeValue.contains(carriageReturn)) + return attributeValue; + + StringBuilder stripped; + unsigned length = attributeValue.length(); + stripped.reserveCapacity(length); + for (unsigned i = 0; i < length; ++i) { + UChar character = attributeValue[i]; + if (character == newlineCharacter || character == carriageReturn) + continue; + stripped.append(character); + } + return stripped.toString(); +} + +static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != carriageReturn; } + +bool HTMLTextFormControlElement::isPlaceholderEmpty() const +{ + const AtomicString& attributeValue = getAttribute(placeholderAttr); + return attributeValue.string().find(isNotLineBreak) == notFound; +} + +bool HTMLTextFormControlElement::placeholderShouldBeVisible() const +{ + return supportsPlaceholder() + && isEmptyValue() + && isEmptySuggestedValue() + && !isPlaceholderEmpty() + && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused())) + && (!renderer() || renderer()->style()->visibility() == VISIBLE); +} + +void HTMLTextFormControlElement::updatePlaceholderVisibility(bool placeholderValueChanged) +{ + if (!supportsPlaceholder()) + return; + if (!placeholderElement() || placeholderValueChanged) + updatePlaceholderText(); + HTMLElement* placeholder = placeholderElement(); + if (!placeholder) + return; + ExceptionCode ec = 0; + placeholder->ensureInlineStyleDecl()->setProperty(CSSPropertyVisibility, placeholderShouldBeVisible() ? "visible" : "hidden", ec); + ASSERT(!ec); +} + +RenderTextControl* HTMLTextFormControlElement::textRendererAfterUpdateLayout() +{ + if (!isTextFormControl()) + return 0; + document()->updateLayoutIgnorePendingStylesheets(); + return toRenderTextControl(renderer()); +} + +void HTMLTextFormControlElement::setSelectionStart(int start) +{ + setSelectionRange(start, max(start, selectionEnd()), selectionDirection()); +} + +void HTMLTextFormControlElement::setSelectionEnd(int end) +{ + setSelectionRange(min(end, selectionStart()), end, selectionDirection()); +} + +void HTMLTextFormControlElement::setSelectionDirection(const String& direction) +{ + setSelectionRange(selectionStart(), selectionEnd(), direction); +} + +void HTMLTextFormControlElement::select() +{ + setSelectionRange(0, numeric_limits<int>::max(), SelectionHasNoDirection); +} + +String HTMLTextFormControlElement::selectedText() const +{ + if (!isTextFormControl()) + return String(); + return value().substring(selectionStart(), selectionEnd() - selectionStart()); +} + +void HTMLTextFormControlElement::dispatchFormControlChangeEvent() +{ + if (m_textAsOfLastFormControlChangeEvent != value()) { + HTMLElement::dispatchChangeEvent(); + setTextAsOfLastFormControlChangeEvent(value()); + } + setChangedSinceLastFormControlChangeEvent(false); +} + +static inline bool hasVisibleTextArea(RenderTextControl* textControl, HTMLElement* innerText) +{ + ASSERT(textControl); + return textControl->style()->visibility() != HIDDEN && innerText && innerText->renderer() && innerText->renderBox()->height(); +} + +void HTMLTextFormControlElement::setSelectionRange(int start, int end, const String& directionString) +{ + TextFieldSelectionDirection direction = SelectionHasNoDirection; + if (directionString == "forward") + direction = SelectionHasForwardDirection; + else if (directionString == "backward") + direction = SelectionHasBackwardDirection; + + return setSelectionRange(start, end, direction); +} + +void HTMLTextFormControlElement::setSelectionRange(int start, int end, TextFieldSelectionDirection direction) +{ + document()->updateLayoutIgnorePendingStylesheets(); + + if (!renderer() || !renderer()->isTextControl()) + return; + + end = max(end, 0); + start = min(max(start, 0), end); + + RenderTextControl* control = toRenderTextControl(renderer()); + if (!hasVisibleTextArea(control, innerTextElement())) { + cacheSelection(start, end, direction); + return; + } + VisiblePosition startPosition = control->visiblePositionForIndex(start); + VisiblePosition endPosition; + if (start == end) + endPosition = startPosition; + else + endPosition = control->visiblePositionForIndex(end); + + // startPosition and endPosition can be null position for example when + // "-webkit-user-select: none" style attribute is specified. + if (startPosition.isNotNull() && endPosition.isNotNull()) { + ASSERT(startPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == this + && endPosition.deepEquivalent().deprecatedNode()->shadowAncestorNode() == this); + } + VisibleSelection newSelection; + if (direction == SelectionHasBackwardDirection) + newSelection = VisibleSelection(endPosition, startPosition); + else + newSelection = VisibleSelection(startPosition, endPosition); + newSelection.setIsDirectional(direction != SelectionHasNoDirection); + + if (Frame* frame = document()->frame()) + frame->selection()->setSelection(newSelection); +} + +int HTMLTextFormControlElement::indexForVisiblePosition(const VisiblePosition& pos) const +{ + Position indexPosition = pos.deepEquivalent().parentAnchoredEquivalent(); + if (enclosingTextFormControl(indexPosition) != this) + return 0; + ExceptionCode ec = 0; + RefPtr<Range> range = Range::create(indexPosition.document()); + range->setStart(innerTextElement(), 0, ec); + ASSERT(!ec); + range->setEnd(indexPosition.containerNode(), indexPosition.offsetInContainerNode(), ec); + ASSERT(!ec); + return TextIterator::rangeLength(range.get()); +} + +int HTMLTextFormControlElement::selectionStart() const +{ + if (!isTextFormControl()) + return 0; + if (document()->focusedNode() != this && hasCachedSelection()) + return m_cachedSelectionStart; + + return computeSelectionStart(); +} + +int HTMLTextFormControlElement::computeSelectionStart() const +{ + ASSERT(isTextFormControl()); + Frame* frame = document()->frame(); + if (!frame) + return 0; + + return indexForVisiblePosition(frame->selection()->start()); +} + +int HTMLTextFormControlElement::selectionEnd() const +{ + if (!isTextFormControl()) + return 0; + if (document()->focusedNode() != this && hasCachedSelection()) + return m_cachedSelectionEnd; + return computeSelectionEnd(); +} + +int HTMLTextFormControlElement::computeSelectionEnd() const +{ + ASSERT(isTextFormControl()); + Frame* frame = document()->frame(); + if (!frame) + return 0; + + return indexForVisiblePosition(frame->selection()->end()); +} + +static const AtomicString& directionString(TextFieldSelectionDirection direction) +{ + DEFINE_STATIC_LOCAL(const AtomicString, none, ("none")); + DEFINE_STATIC_LOCAL(const AtomicString, forward, ("forward")); + DEFINE_STATIC_LOCAL(const AtomicString, backward, ("backward")); + + switch (direction) { + case SelectionHasNoDirection: + return none; + case SelectionHasForwardDirection: + return forward; + case SelectionHasBackwardDirection: + return backward; + } + + ASSERT_NOT_REACHED(); + return none; +} + +const AtomicString& HTMLTextFormControlElement::selectionDirection() const +{ + if (!isTextFormControl()) + return directionString(SelectionHasNoDirection); + if (document()->focusedNode() != this && hasCachedSelection()) + return directionString(m_cachedSelectionDirection); + + return directionString(computeSelectionDirection()); +} + +TextFieldSelectionDirection HTMLTextFormControlElement::computeSelectionDirection() const +{ + ASSERT(isTextFormControl()); + Frame* frame = document()->frame(); + if (!frame) + return SelectionHasNoDirection; + + const VisibleSelection& selection = frame->selection()->selection(); + return selection.isDirectional() ? (selection.isBaseFirst() ? SelectionHasForwardDirection : SelectionHasBackwardDirection) : SelectionHasNoDirection; +} + +static inline void setContainerAndOffsetForRange(Node* node, int offset, Node*& containerNode, int& offsetInContainer) +{ + if (node->isTextNode()) { + containerNode = node; + offsetInContainer = offset; + } else { + containerNode = node->parentNode(); + offsetInContainer = node->nodeIndex() + offset; + } +} + +PassRefPtr<Range> HTMLTextFormControlElement::selection() const +{ + if (!renderer() || !isTextFormControl() || !hasCachedSelection()) + return 0; + + int start = m_cachedSelectionStart; + int end = m_cachedSelectionEnd; + + ASSERT(start <= end); + HTMLElement* innerText = innerTextElement(); + if (!innerText) + return 0; + + if (!innerText->firstChild()) + return Range::create(document(), innerText, 0, innerText, 0); + + int offset = 0; + Node* startNode = 0; + Node* endNode = 0; + for (Node* node = innerText->firstChild(); node; node = node->traverseNextNode(innerText)) { + ASSERT(!node->firstChild()); + ASSERT(node->isTextNode() || node->hasTagName(brTag)); + int length = node->isTextNode() ? lastOffsetInNode(node) : 1; + + if (offset <= start && start <= offset + length) + setContainerAndOffsetForRange(node, start - offset, startNode, start); + + if (offset <= end && end <= offset + length) { + setContainerAndOffsetForRange(node, end - offset, endNode, end); + break; + } + + offset += length; + } + + if (!startNode || !endNode) + return 0; + + return Range::create(document(), startNode, start, endNode, end); +} + +void HTMLTextFormControlElement::restoreCachedSelection() +{ + setSelectionRange(m_cachedSelectionStart, m_cachedSelectionEnd, m_cachedSelectionDirection); +} + +void HTMLTextFormControlElement::selectionChanged(bool userTriggered) +{ + if (!renderer() || !isTextFormControl()) + return; + + // selectionStart() or selectionEnd() will return cached selection when this node doesn't have focus + cacheSelection(computeSelectionStart(), computeSelectionEnd(), computeSelectionDirection()); + + if (Frame* frame = document()->frame()) { + if (frame->selection()->isRange() && userTriggered) + dispatchEvent(Event::create(eventNames().selectEvent, true, false)); + } +} + +void HTMLTextFormControlElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == placeholderAttr) + updatePlaceholderVisibility(true); + else if (attr->name() == onselectAttr) + setAttributeEventListener(eventNames().selectEvent, createAttributeEventListener(this, attr)); + else if (attr->name() == onchangeAttr) + setAttributeEventListener(eventNames().changeEvent, createAttributeEventListener(this, attr)); + else + HTMLFormControlElementWithState::parseMappedAttribute(attr); +} + +void HTMLTextFormControlElement::notifyFormStateChanged() +{ + Frame* frame = document()->frame(); + if (!frame) + return; + + if (Page* page = frame->page()) + page->chrome()->client()->formStateDidChange(this); +} + +bool HTMLTextFormControlElement::lastChangeWasUserEdit() const +{ + if (!isTextFormControl()) + return false; + return m_lastChangeWasUserEdit; +} + +void HTMLTextFormControlElement::setInnerTextValue(const String& value) +{ + if (!isTextFormControl()) + return; + + bool textIsChanged = value != innerTextValue(); + if (textIsChanged || !innerTextElement()->hasChildNodes()) { + if (textIsChanged && document() && renderer() && AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->postNotification(renderer(), AXObjectCache::AXValueChanged, false); + + ExceptionCode ec = 0; + innerTextElement()->setInnerText(value, ec); + ASSERT(!ec); + + if (value.endsWith("\n") || value.endsWith("\r")) { + innerTextElement()->appendChild(HTMLBRElement::create(document()), ec); + ASSERT(!ec); + } + } + + setFormControlValueMatchesRenderer(true); +} + +static String finishText(StringBuilder& result) +{ + // Remove one trailing newline; there's always one that's collapsed out by rendering. + size_t size = result.length(); + if (size && result[size - 1] == '\n') + result.resize(--size); + return result.toString(); +} + +String HTMLTextFormControlElement::innerTextValue() const +{ + HTMLElement* innerText = innerTextElement(); + if (!innerText || !isTextFormControl()) + return emptyString(); + + StringBuilder result; + for (Node* node = innerText; node; node = node->traverseNextNode(innerText)) { + if (node->hasTagName(brTag)) + result.append(newlineCharacter); + else if (node->isTextNode()) + result.append(static_cast<Text*>(node)->data()); + } + return finishText(result); +} + +static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset) +{ + RootInlineBox* next; + for (; line; line = next) { + next = line->nextRootBox(); + if (next && !line->endsWithBreak()) { + ASSERT(line->lineBreakObj()); + breakNode = line->lineBreakObj()->node(); + breakOffset = line->lineBreakPos(); + line = next; + return; + } + } + breakNode = 0; + breakOffset = 0; +} + +String HTMLTextFormControlElement::valueWithHardLineBreaks() const +{ + // FIXME: It's not acceptable to ignore the HardWrap setting when there is no renderer. + // While we have no evidence this has ever been a practical problem, it would be best to fix it some day. + HTMLElement* innerText = innerTextElement(); + if (!innerText || !isTextFormControl()) + return value(); + + RenderBlock* renderer = toRenderBlock(innerText->renderer()); + if (!renderer) + return value(); + + Node* breakNode; + unsigned breakOffset; + RootInlineBox* line = renderer->firstRootBox(); + if (!line) + return value(); + + getNextSoftBreak(line, breakNode, breakOffset); + + StringBuilder result; + for (Node* node = innerText->firstChild(); node; node = node->traverseNextNode(innerText)) { + if (node->hasTagName(brTag)) + result.append(newlineCharacter); + else if (node->isTextNode()) { + String data = static_cast<Text*>(node)->data(); + unsigned length = data.length(); + unsigned position = 0; + while (breakNode == node && breakOffset <= length) { + if (breakOffset > position) { + result.append(data.characters() + position, breakOffset - position); + position = breakOffset; + result.append(newlineCharacter); + } + getNextSoftBreak(line, breakNode, breakOffset); + } + result.append(data.characters() + position, length - position); + } + while (breakNode == node) + getNextSoftBreak(line, breakNode, breakOffset); + } + return finishText(result); +} + +HTMLTextFormControlElement* enclosingTextFormControl(const Position& position) +{ + ASSERT(position.isNull() || position.anchorType() == Position::PositionIsOffsetInAnchor + || position.containerNode() || !position.anchorNode()->shadowAncestorNode()); + Node* container = position.containerNode(); + if (!container) + return 0; + Node* ancestor = container->shadowAncestorNode(); + return ancestor != container ? toTextFormControl(ancestor) : 0; +} + +static const Element* parentHTMLElement(const Element* element) +{ + while (element) { + element = element->parentElement(); + if (element && element->isHTMLElement()) + return element; + } + return 0; +} + +String HTMLTextFormControlElement::directionForFormData() const +{ + for (const Element* element = this; element; element = parentHTMLElement(element)) { + const AtomicString& dirAttributeValue = element->fastGetAttribute(dirAttr); + if (dirAttributeValue.isNull()) + continue; + + if (equalIgnoringCase(dirAttributeValue, "rtl") || equalIgnoringCase(dirAttributeValue, "ltr")) + return dirAttributeValue; + + if (equalIgnoringCase(dirAttributeValue, "auto")) { + bool isAuto; + TextDirection textDirection = static_cast<const HTMLElement*>(element)->directionalityIfhasDirAutoAttribute(isAuto); + return textDirection == RTL ? "rtl" : "ltr"; + } + } + + return "ltr"; +} + +} // namespace Webcore diff --git a/Source/WebCore/html/HTMLTextFormControlElement.h b/Source/WebCore/html/HTMLTextFormControlElement.h new file mode 100644 index 000000000..c13440cbf --- /dev/null +++ b/Source/WebCore/html/HTMLTextFormControlElement.h @@ -0,0 +1,148 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009, 2010, 2011 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 HTMLTextFormControlElement_h +#define HTMLTextFormControlElement_h + +#include "HTMLFormControlElementWithState.h" + +namespace WebCore { + +class Position; +class RenderTextControl; +class VisiblePosition; + +enum TextFieldSelectionDirection { SelectionHasNoDirection, SelectionHasForwardDirection, SelectionHasBackwardDirection }; + +class HTMLTextFormControlElement : public HTMLFormControlElementWithState { +public: + // Common flag for HTMLInputElement::tooLong() and HTMLTextAreaElement::tooLong(). + enum NeedsToCheckDirtyFlag {CheckDirtyFlag, IgnoreDirtyFlag}; + + virtual ~HTMLTextFormControlElement(); + + void forwardEvent(Event*); + + virtual void insertedIntoDocument(); + + // The derived class should return true if placeholder processing is needed. + virtual bool supportsPlaceholder() const = 0; + String strippedPlaceholder() const; + bool placeholderShouldBeVisible() const; + virtual HTMLElement* placeholderElement() const = 0; + void updatePlaceholderVisibility(bool); + + int indexForVisiblePosition(const VisiblePosition&) const; + int selectionStart() const; + int selectionEnd() const; + const AtomicString& selectionDirection() const; + void setSelectionStart(int); + void setSelectionEnd(int); + void setSelectionDirection(const String&); + void select(); + void setSelectionRange(int start, int end, const String& direction); + void setSelectionRange(int start, int end, TextFieldSelectionDirection = SelectionHasNoDirection); + PassRefPtr<Range> selection() const; + String selectedText() const; + + virtual void dispatchFormControlChangeEvent(); + + virtual int maxLength() const = 0; + virtual String value() const = 0; + + virtual HTMLElement* innerTextElement() const = 0; + + void selectionChanged(bool userTriggered); + void notifyFormStateChanged(); + bool lastChangeWasUserEdit() const; + void setInnerTextValue(const String&); + String innerTextValue() const; + + String directionForFormData() const; + +protected: + HTMLTextFormControlElement(const QualifiedName&, Document*, HTMLFormElement*); + virtual void updatePlaceholderText() = 0; + + virtual void parseMappedAttribute(Attribute*); + + void setTextAsOfLastFormControlChangeEvent(const String& text) { m_textAsOfLastFormControlChangeEvent = text; } + + void cacheSelection(int start, int end, TextFieldSelectionDirection direction) + { + m_cachedSelectionStart = start; + m_cachedSelectionEnd = end; + m_cachedSelectionDirection = direction; + } + + void restoreCachedSelection(); + bool hasCachedSelection() const { return m_cachedSelectionStart >= 0; } + + virtual void defaultEventHandler(Event*); + virtual void subtreeHasChanged() = 0; + + void setLastChangeWasNotUserEdit() { m_lastChangeWasUserEdit = false; } + + String valueWithHardLineBreaks() const; + +private: + int computeSelectionStart() const; + int computeSelectionEnd() const; + TextFieldSelectionDirection computeSelectionDirection() const; + + virtual void dispatchFocusEvent(PassRefPtr<Node> oldFocusedNode); + virtual void dispatchBlurEvent(PassRefPtr<Node> newFocusedNode); + + bool isPlaceholderEmpty() const; + + // Returns true if user-editable value is empty. Used to check placeholder visibility. + virtual bool isEmptyValue() const = 0; + // Returns true if suggested value is empty. Used to check placeholder visibility. + virtual bool isEmptySuggestedValue() const { return true; } + // Called in dispatchFocusEvent(), after placeholder process, before calling parent's dispatchFocusEvent(). + virtual void handleFocusEvent() { } + // Called in dispatchBlurEvent(), after placeholder process, before calling parent's dispatchBlurEvent(). + virtual void handleBlurEvent() { } + + RenderTextControl* textRendererAfterUpdateLayout(); + + String m_textAsOfLastFormControlChangeEvent; + bool m_lastChangeWasUserEdit; + + int m_cachedSelectionStart; + int m_cachedSelectionEnd; + TextFieldSelectionDirection m_cachedSelectionDirection; +}; + +// This function returns 0 when node is an input element and not a text field. +inline HTMLTextFormControlElement* toTextFormControl(Node* node) +{ + return (node && node->isElementNode() && static_cast<Element*>(node)->isTextFormControl()) ? static_cast<HTMLTextFormControlElement*>(node) : 0; +} + +HTMLTextFormControlElement* enclosingTextFormControl(const Position&); + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLTitleElement.cpp b/Source/WebCore/html/HTMLTitleElement.cpp new file mode 100644 index 000000000..69f1f84bc --- /dev/null +++ b/Source/WebCore/html/HTMLTitleElement.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2010 Apple 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 "HTMLTitleElement.h" + +#include "Document.h" +#include "HTMLNames.h" +#include "NodeRenderingContext.h" +#include "RenderStyle.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLTitleElement::HTMLTitleElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(titleTag)); +} + +PassRefPtr<HTMLTitleElement> HTMLTitleElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTitleElement(tagName, document)); +} + +void HTMLTitleElement::insertedIntoDocument() +{ + HTMLElement::insertedIntoDocument(); + document()->setTitleElement(m_title, this); +} + +void HTMLTitleElement::removedFromDocument() +{ + HTMLElement::removedFromDocument(); + document()->removeTitle(this); +} + +void HTMLTitleElement::childrenChanged(bool changedByParser, Node* beforeChange, Node* afterChange, int childCountDelta) +{ + m_title = textWithDirection(); + if (inDocument()) + document()->setTitleElement(m_title, this); + HTMLElement::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta); +} + +String HTMLTitleElement::text() const +{ + String val = ""; + + for (Node *n = firstChild(); n; n = n->nextSibling()) { + if (n->isTextNode()) + val += static_cast<Text*>(n)->data(); + } + + return val; +} + +StringWithDirection HTMLTitleElement::textWithDirection() +{ + TextDirection direction = LTR; + if (RenderStyle* style = computedStyle()) + direction = style->direction(); + else if (RefPtr<RenderStyle> style = styleForRenderer()) + direction = style->direction(); + return StringWithDirection(text(), direction); +} + +void HTMLTitleElement::setText(const String &value) +{ + ExceptionCode ec = 0; + int numChildren = childNodeCount(); + + if (numChildren == 1 && firstChild()->isTextNode()) + static_cast<Text*>(firstChild())->setData(value, ec); + else { + // We make a copy here because entity of "value" argument can be Document::m_title, + // which goes empty during removeChildren() invocation below, + // which causes HTMLTitleElement::childrenChanged(), which ends up Document::setTitle(). + String valueCopy(value); + + if (numChildren > 0) + removeChildren(); + + appendChild(document()->createTextNode(valueCopy.impl()), ec); + } +} + +} diff --git a/Source/WebCore/html/HTMLTitleElement.h b/Source/WebCore/html/HTMLTitleElement.h new file mode 100644 index 000000000..692099783 --- /dev/null +++ b/Source/WebCore/html/HTMLTitleElement.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2010 Apple 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 HTMLTitleElement_h +#define HTMLTitleElement_h + +#include "HTMLElement.h" +#include "StringWithDirection.h" + +namespace WebCore { + +class HTMLTitleElement : public HTMLElement { +public: + static PassRefPtr<HTMLTitleElement> create(const QualifiedName&, Document*); + + String text() const; + void setText(const String&); + + StringWithDirection textWithDirection(); + +private: + HTMLTitleElement(const QualifiedName&, Document*); + + virtual void insertedIntoDocument(); + virtual void removedFromDocument(); + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + + StringWithDirection m_title; +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLTitleElement.idl b/Source/WebCore/html/HTMLTitleElement.idl new file mode 100644 index 000000000..de857e8ee --- /dev/null +++ b/Source/WebCore/html/HTMLTitleElement.idl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + */ + +module html { + + interface HTMLTitleElement : HTMLElement { + attribute [ConvertNullToNullString] DOMString text; + }; + +} diff --git a/Source/WebCore/html/HTMLTrackElement.cpp b/Source/WebCore/html/HTMLTrackElement.cpp new file mode 100644 index 000000000..13a77a810 --- /dev/null +++ b/Source/WebCore/html/HTMLTrackElement.cpp @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) +#include "HTMLTrackElement.h" + +#include "ContentSecurityPolicy.h" +#include "Event.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "Logging.h" +#include "RuntimeEnabledFeatures.h" +#include "ScriptEventListener.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +#if !LOG_DISABLED +static String urlForLogging(const KURL& url) +{ + static const unsigned maximumURLLengthForLogging = 128; + + if (url.string().length() < maximumURLLengthForLogging) + return url.string(); + return url.string().substring(0, maximumURLLengthForLogging) + "..."; +} +#endif + +inline HTMLTrackElement::HTMLTrackElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , m_hasBeenConfigured(false) +{ + LOG(Media, "HTMLTrackElement::HTMLTrackElement - %p", this); + ASSERT(hasTagName(trackTag)); +} + +HTMLTrackElement::~HTMLTrackElement() +{ + if (m_track) + m_track->clearClient(); +} + +PassRefPtr<HTMLTrackElement> HTMLTrackElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLTrackElement(tagName, document)); +} + +void HTMLTrackElement::insertedIntoTree(bool deep) +{ + HTMLElement::insertedIntoTree(deep); + + if (HTMLMediaElement* parent = mediaElement()) + parent->trackWasAdded(this); +} + +void HTMLTrackElement::willRemove() +{ + if (HTMLMediaElement* parent = mediaElement()) + parent->trackWillBeRemoved(this); + + HTMLElement::willRemove(); +} + +void HTMLTrackElement::parseMappedAttribute(Attribute* attribute) +{ + const QualifiedName& attrName = attribute->name(); + + if (attrName == onloadAttr) + setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attribute)); + else if (attrName == onerrorAttr) + setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attribute)); + else + HTMLElement::parseMappedAttribute(attribute); +} + +void HTMLTrackElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + HTMLElement::attributeChanged(attr, preserveDecls); + + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return; + + const QualifiedName& attrName = attr->name(); + if (attrName == srcAttr) { + if (!getAttribute(srcAttr).isEmpty() && mediaElement()) + scheduleLoad(); + + // 4.8.10.12.3 Sourcing out-of-band text tracks + // As the kind, label, and srclang attributes are set, changed, or removed, the text track must update accordingly... + } else if (attrName == kindAttr) + track()->setKind(attr->value()); + else if (attrName == labelAttr) + track()->setLabel(attr->value()); + else if (attrName == srclangAttr) + track()->setLanguage(attr->value()); +} + +KURL HTMLTrackElement::src() const +{ + return document()->completeURL(getAttribute(srcAttr)); +} + +void HTMLTrackElement::setSrc(const String& url) +{ + setAttribute(srcAttr, url); +} + +String HTMLTrackElement::kind() +{ + return track()->kind(); +} + +void HTMLTrackElement::setKind(const String& kind) +{ + setAttribute(kindAttr, kind); +} + +String HTMLTrackElement::srclang() const +{ + return getAttribute(srclangAttr); +} + +void HTMLTrackElement::setSrclang(const String& srclang) +{ + setAttribute(srclangAttr, srclang); +} + +String HTMLTrackElement::label() const +{ + return getAttribute(labelAttr); +} + +void HTMLTrackElement::setLabel(const String& label) +{ + setAttribute(labelAttr, label); +} + +bool HTMLTrackElement::isDefault() const +{ + return fastHasAttribute(defaultAttr); +} + +void HTMLTrackElement::setIsDefault(bool isDefault) +{ + setBooleanAttribute(defaultAttr, isDefault); +} + +LoadableTextTrack* HTMLTrackElement::ensureTrack() +{ + if (!m_track) { + // The kind attribute is an enumerated attribute, limited only to know values. It defaults to 'subtitles' if missing or invalid. + String kind = getAttribute(kindAttr); + if (!TextTrack::isValidKindKeyword(kind)) + kind = TextTrack::subtitlesKeyword(); + m_track = LoadableTextTrack::create(this, kind, label(), srclang(), isDefault()); + } + return m_track.get(); +} + +TextTrack* HTMLTrackElement::track() +{ + return ensureTrack(); +} + +bool HTMLTrackElement::isURLAttribute(Attribute* attribute) const +{ + return attribute->name() == srcAttr || HTMLElement::isURLAttribute(attribute); +} + +void HTMLTrackElement::scheduleLoad() +{ + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return; + + if (!mediaElement()) + return; + + if (!fastHasAttribute(srcAttr)) + return; + + // 4.8.10.12.3 Sourcing out-of-band text tracks + + // 1. Set the text track readiness state to loading. + setReadyState(HTMLTrackElement::LOADING); + + KURL url = getNonEmptyURLAttribute(srcAttr); + if (!canLoadUrl(url)) { + didCompleteLoad(ensureTrack(), HTMLTrackElement::Failure); + return; + } + + ensureTrack()->scheduleLoad(url); +} + +bool HTMLTrackElement::canLoadUrl(const KURL& url) +{ + if (!RuntimeEnabledFeatures::webkitVideoTrackEnabled()) + return false; + + HTMLMediaElement* parent = mediaElement(); + if (!parent) + return false; + + // 4.8.10.12.3 Sourcing out-of-band text tracks + + // 4. Download: If URL is not the empty string, perform a potentially CORS-enabled fetch of URL, with the + // mode being the state of the media element's crossorigin content attribute, the origin being the + // origin of the media element's Document, and the default origin behaviour set to fail. + if (url.isEmpty()) + return false; + + if (!document()->contentSecurityPolicy()->allowMediaFromSource(url)) { + DEFINE_STATIC_LOCAL(String, consoleMessage, ("Text track load denied by Content Security Policy.")); + document()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage); + LOG(Media, "HTMLTrackElement::canLoadUrl(%s) -> rejected by Content Security Policy", urlForLogging(url).utf8().data()); + return false; + } + + return dispatchBeforeLoadEvent(url.string()); +} + +void HTMLTrackElement::didCompleteLoad(LoadableTextTrack*, LoadStatus status) +{ + ExceptionCode ec = 0; + + // 4.8.10.12.3 Sourcing out-of-band text tracks (continued) + + // 4. Download: ... + // If the fetching algorithm fails for any reason (network error, the server returns an error + // code, a cross-origin check fails, etc), or if URL is the empty string or has the wrong origin + // as determined by the condition at the start of this step, or if the fetched resource is not in + // a supported format, then queue a task to first change the text track readiness state to failed + // to load and then fire a simple event named error at the track element; and then, once that task + // is queued, move on to the step below labeled monitoring. + + if (status == Failure) { + setReadyState(HTMLTrackElement::TRACK_ERROR); + dispatchEvent(Event::create(eventNames().errorEvent, false, false), ec); + return; + } + + // If the fetching algorithm does not fail, then the final task that is queued by the networking + // task source must run the following steps: + // 1. Change the text track readiness state to loaded. + setReadyState(HTMLTrackElement::LOADED); + + // 2. If the file was successfully processed, fire a simple event named load at the + // track element. + dispatchEvent(Event::create(eventNames().loadEvent, false, false), ec); +} + +// NOTE: The values in the TextTrack::ReadinessState enum must stay in sync with those in HTMLTrackElement::ReadyState. +COMPILE_ASSERT(HTMLTrackElement::NONE == static_cast<HTMLTrackElement::ReadyState>(TextTrack::NotLoaded), TextTrackEnumNotLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumNONE); +COMPILE_ASSERT(HTMLTrackElement::LOADING == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loading), TextTrackEnumLoadingIsWrong_ShouldBe_HTMLTrackElementEnumLOADING); +COMPILE_ASSERT(HTMLTrackElement::LOADED == static_cast<HTMLTrackElement::ReadyState>(TextTrack::Loaded), TextTrackEnumLoaded_Is_Wrong_Should_Be_HTMLTrackElementEnumLOADED); +COMPILE_ASSERT(HTMLTrackElement::TRACK_ERROR == static_cast<HTMLTrackElement::ReadyState>(TextTrack::FailedToLoad), TextTrackEnumFailedToLoad_Is_Wrong_Should_Be_HTMLTrackElementEnumTRACK_ERROR); + +void HTMLTrackElement::setReadyState(ReadyState state) +{ + ensureTrack()->setReadinessState(static_cast<TextTrack::ReadinessState>(state)); + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackReadyStateChanged(m_track.get()); +} + +HTMLTrackElement::ReadyState HTMLTrackElement::readyState() +{ + return static_cast<ReadyState>(ensureTrack()->readinessState()); +} + +const AtomicString& HTMLTrackElement::mediaElementCrossOriginAttribute() const +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->fastGetAttribute(HTMLNames::crossoriginAttr); + + return nullAtom; +} + +void HTMLTrackElement::textTrackKindChanged(TextTrack* track) +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackKindChanged(track); +} + +void HTMLTrackElement::textTrackModeChanged(TextTrack* track) +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackModeChanged(track); +} + +void HTMLTrackElement::textTrackAddCues(TextTrack* track, const TextTrackCueList* cues) +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackAddCues(track, cues); +} + +void HTMLTrackElement::textTrackRemoveCues(TextTrack* track, const TextTrackCueList* cues) +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackAddCues(track, cues); +} + +void HTMLTrackElement::textTrackAddCue(TextTrack* track, PassRefPtr<TextTrackCue> cue) +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackAddCue(track, cue); +} + +void HTMLTrackElement::textTrackRemoveCue(TextTrack* track, PassRefPtr<TextTrackCue> cue) +{ + if (HTMLMediaElement* parent = mediaElement()) + return parent->textTrackRemoveCue(track, cue); +} + +HTMLMediaElement* HTMLTrackElement::mediaElement() const +{ + Element* parent = parentElement(); + if (parent && parent->isMediaElement()) + return static_cast<HTMLMediaElement*>(parentNode()); + + return 0; +} + +#if ENABLE(MICRODATA) +String HTMLTrackElement::itemValueText() const +{ + return getURLAttribute(srcAttr); +} + +void HTMLTrackElement::setItemValueText(const String& value, ExceptionCode& ec) +{ + setAttribute(srcAttr, value, ec); +} +#endif + +} + +#endif diff --git a/Source/WebCore/html/HTMLTrackElement.h b/Source/WebCore/html/HTMLTrackElement.h new file mode 100644 index 000000000..86d2aecd2 --- /dev/null +++ b/Source/WebCore/html/HTMLTrackElement.h @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLTrackElement_h +#define HTMLTrackElement_h + +#if ENABLE(VIDEO_TRACK) + +#include "HTMLElement.h" +#include "LoadableTextTrack.h" +#include "TextTrack.h" + +namespace WebCore { + +class HTMLMediaElement; + +class HTMLTrackElement : public HTMLElement, public TextTrackClient { +public: + static PassRefPtr<HTMLTrackElement> create(const QualifiedName&, Document*); + + KURL src() const; + void setSrc(const String&); + + String kind(); + void setKind(const String&); + + String srclang() const; + void setSrclang(const String&); + + String label() const; + void setLabel(const String&); + + bool isDefault() const; + void setIsDefault(bool); + + enum ReadyState { NONE = 0, LOADING = 1, LOADED = 2, TRACK_ERROR = 3 }; + ReadyState readyState(); + void setReadyState(ReadyState); + + TextTrack* track(); + + void scheduleLoad(); + + enum LoadStatus { Failure, Success }; + virtual void didCompleteLoad(LoadableTextTrack*, LoadStatus); + + const AtomicString& mediaElementCrossOriginAttribute() const; + + bool hasBeenConfigured() const { return m_hasBeenConfigured; } + void setHasBeenConfigured(bool flag) { m_hasBeenConfigured = flag; } + +private: + HTMLTrackElement(const QualifiedName&, Document*); + virtual ~HTMLTrackElement(); + + virtual void parseMappedAttribute(Attribute*); + virtual void attributeChanged(Attribute*, bool preserveDecls); + + virtual void insertedIntoTree(bool); + virtual void willRemove(); + virtual bool isURLAttribute(Attribute*) const; + +#if ENABLE(MICRODATA) + virtual String itemValueText() const OVERRIDE; + virtual void setItemValueText(const String&, ExceptionCode&) OVERRIDE; +#endif + + HTMLMediaElement* mediaElement() const; + + // TextTrackClient + virtual void textTrackModeChanged(TextTrack*); + virtual void textTrackKindChanged(TextTrack*); + virtual void textTrackAddCues(TextTrack*, const TextTrackCueList*); + virtual void textTrackRemoveCues(TextTrack*, const TextTrackCueList*); + virtual void textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue>); + virtual void textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue>); + + LoadableTextTrack* ensureTrack(); + virtual bool canLoadUrl(const KURL&); + + RefPtr<LoadableTextTrack> m_track; + bool m_hasBeenConfigured; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/html/HTMLTrackElement.idl b/Source/WebCore/html/HTMLTrackElement.idl new file mode 100644 index 000000000..839711e80 --- /dev/null +++ b/Source/WebCore/html/HTMLTrackElement.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=VIDEO_TRACK, + EnabledAtRuntime=webkitVideoTrack + ] HTMLTrackElement : HTMLElement { + attribute [Reflect, URL] DOMString src; + attribute DOMString kind; + attribute DOMString srclang; + attribute DOMString label; + attribute [Reflect=default] boolean isDefault; + + const unsigned short NONE = 0; + const unsigned short LOADING = 1; + const unsigned short LOADED = 2; + // Reflect is used for ERROR because it conflicts with a windows define. + const [Reflect=TRACK_ERROR] unsigned short ERROR = 3; + readonly attribute unsigned short readyState; + + readonly attribute TextTrack track; +}; +} diff --git a/Source/WebCore/html/HTMLUListElement.cpp b/Source/WebCore/html/HTMLUListElement.cpp new file mode 100644 index 000000000..4b121aa58 --- /dev/null +++ b/Source/WebCore/html/HTMLUListElement.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 "HTMLUListElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLUListElement::HTMLUListElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(ulTag)); +} + +PassRefPtr<HTMLUListElement> HTMLUListElement::create(Document* document) +{ + return adoptRef(new HTMLUListElement(ulTag, document)); +} + +PassRefPtr<HTMLUListElement> HTMLUListElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLUListElement(tagName, document)); +} + +bool HTMLUListElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + if (attrName == typeAttr) { + result = eUnorderedList; + return false; + } + + return HTMLElement::mapToEntry(attrName, result); +} + +void HTMLUListElement::parseMappedAttribute(Attribute* attr) +{ + if (attr->name() == typeAttr) + addCSSProperty(attr, CSSPropertyListStyleType, attr->value()); + else + HTMLElement::parseMappedAttribute(attr); +} + +} diff --git a/Source/WebCore/html/HTMLUListElement.h b/Source/WebCore/html/HTMLUListElement.h new file mode 100644 index 000000000..f91bf1c3b --- /dev/null +++ b/Source/WebCore/html/HTMLUListElement.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2010 Apple 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 HTMLUListElement_h +#define HTMLUListElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLUListElement : public HTMLElement { +public: + static PassRefPtr<HTMLUListElement> create(Document*); + static PassRefPtr<HTMLUListElement> create(const QualifiedName&, Document*); + +private: + HTMLUListElement(const QualifiedName&, Document*); + + virtual bool mapToEntry(const QualifiedName&, MappedAttributeEntry&) const; + virtual void parseMappedAttribute(Attribute*); +}; + +} //namespace + +#endif diff --git a/Source/WebCore/html/HTMLUListElement.idl b/Source/WebCore/html/HTMLUListElement.idl new file mode 100644 index 000000000..221dcca58 --- /dev/null +++ b/Source/WebCore/html/HTMLUListElement.idl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2006, 2010 Apple 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. + */ + +module html { + + interface HTMLUListElement : HTMLElement { + attribute [Reflect] boolean compact; + attribute [Reflect] DOMString type; + }; + +} diff --git a/Source/WebCore/html/HTMLUnknownElement.h b/Source/WebCore/html/HTMLUnknownElement.h new file mode 100644 index 000000000..b1259c580 --- /dev/null +++ b/Source/WebCore/html/HTMLUnknownElement.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLUnknownElement_h +#define HTMLUnknownElement_h + +#include "HTMLElement.h" + +namespace WebCore { + +class HTMLUnknownElement : public HTMLElement { +public: + static PassRefPtr<HTMLUnknownElement> create(const QualifiedName& tagName, Document* document) + { + return adoptRef(new HTMLUnknownElement(tagName, document)); + } + +private: + HTMLUnknownElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + { + } +}; + +} // namespace + +#endif diff --git a/Source/WebCore/html/HTMLUnknownElement.idl b/Source/WebCore/html/HTMLUnknownElement.idl new file mode 100644 index 000000000..9e4f90afa --- /dev/null +++ b/Source/WebCore/html/HTMLUnknownElement.idl @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2011 Code Aurora Forum. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of Code Aurora Forum, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface HTMLUnknownElement : HTMLElement { + }; + +} diff --git a/Source/WebCore/html/HTMLVideoElement.cpp b/Source/WebCore/html/HTMLVideoElement.cpp new file mode 100644 index 000000000..fed2fa435 --- /dev/null +++ b/Source/WebCore/html/HTMLVideoElement.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "HTMLVideoElement.h" + +#include "Attribute.h" +#include "CSSPropertyNames.h" +#include "Chrome.h" +#include "ChromeClient.h" +#include "Document.h" +#include "ExceptionCode.h" +#include "Frame.h" +#include "HTMLImageLoader.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderImage.h" +#include "RenderVideo.h" +#include "ScriptController.h" + +namespace WebCore { + +using namespace HTMLNames; + +inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document* document, bool createdByParser) + : HTMLMediaElement(tagName, document, createdByParser) +{ + ASSERT(hasTagName(videoTag)); +} + +PassRefPtr<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document* document, bool createdByParser) +{ + return adoptRef(new HTMLVideoElement(tagName, document, createdByParser)); +} + +bool HTMLVideoElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + return HTMLElement::rendererIsNeeded(context); +} + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) +RenderObject* HTMLVideoElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderVideo(this); +} +#endif + +void HTMLVideoElement::attach() +{ + HTMLMediaElement::attach(); + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + updateDisplayState(); + if (shouldDisplayPosterImage()) { + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(this)); + m_imageLoader->updateFromElement(); + if (renderer()) + toRenderImage(renderer())->imageResource()->setCachedImage(m_imageLoader->image()); + } +#endif +} + +void HTMLVideoElement::detach() +{ + HTMLMediaElement::detach(); + + if (!shouldDisplayPosterImage() && m_imageLoader) + m_imageLoader.clear(); +} + +void HTMLVideoElement::parseMappedAttribute(Attribute* attr) +{ + const QualifiedName& attrName = attr->name(); + + if (attrName == posterAttr) { + // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState. + HTMLMediaElement::setDisplayMode(Unknown); + updateDisplayState(); +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (shouldDisplayPosterImage()) { + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(this)); + m_imageLoader->updateFromElementIgnoringPreviousError(); + } else { + if (m_imageLoader) + m_imageLoader.clear(); + if (renderer()) + toRenderImage(renderer())->imageResource()->setCachedImage(0); + } +#endif + } else if (attrName == widthAttr) + addCSSLength(attr, CSSPropertyWidth, attr->value()); + else if (attrName == heightAttr) + addCSSLength(attr, CSSPropertyHeight, attr->value()); + else + HTMLMediaElement::parseMappedAttribute(attr); +} + +bool HTMLVideoElement::supportsFullscreen() const +{ + Page* page = document() ? document()->page() : 0; + if (!page) + return false; + + if (!player() || !player()->supportsFullscreen() || !player()->hasVideo()) + return false; + + // Check with the platform client. +#if ENABLE(FULLSCREEN_API) + if (page->chrome()->client()->supportsFullScreenForElement(this, false)) + return true; +#endif + + return page->chrome()->client()->supportsFullscreenForNode(this); +} + +unsigned HTMLVideoElement::videoWidth() const +{ + if (!player()) + return 0; + return player()->naturalSize().width(); +} + +unsigned HTMLVideoElement::videoHeight() const +{ + if (!player()) + return 0; + return player()->naturalSize().height(); +} + +unsigned HTMLVideoElement::width() const +{ + bool ok; + unsigned w = getAttribute(widthAttr).string().toUInt(&ok); + return ok ? w : 0; +} + +unsigned HTMLVideoElement::height() const +{ + bool ok; + unsigned h = getAttribute(heightAttr).string().toUInt(&ok); + return ok ? h : 0; +} + +bool HTMLVideoElement::isURLAttribute(Attribute* attribute) const +{ + return HTMLMediaElement::isURLAttribute(attribute) + || attribute->name() == posterAttr; +} + +const QualifiedName& HTMLVideoElement::imageSourceAttributeName() const +{ + return posterAttr; +} + +void HTMLVideoElement::setDisplayMode(DisplayMode mode) +{ + DisplayMode oldMode = displayMode(); + KURL poster = getNonEmptyURLAttribute(posterAttr); + + if (!poster.isEmpty()) { + // We have a poster path, but only show it until the user triggers display by playing or seeking and the + // media engine has something to display. + if (mode == Video) { + if (oldMode != Video && player()) + player()->prepareForRendering(); + if (!hasAvailableVideoFrame()) + mode = PosterWaitingForVideo; + } + } else if (oldMode != Video && player()) + player()->prepareForRendering(); + + HTMLMediaElement::setDisplayMode(mode); + + if (player() && player()->canLoadPoster()) { + bool canLoad = true; + if (!poster.isEmpty()) { + Frame* frame = document()->frame(); + FrameLoader* loader = frame ? frame->loader() : 0; + canLoad = loader && loader->willLoadMediaElementURL(poster); + } + if (canLoad) + player()->setPoster(poster); + } + +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + if (renderer() && displayMode() != oldMode) + renderer()->updateFromElement(); +#endif +} + +void HTMLVideoElement::updateDisplayState() +{ + if (getNonEmptyURLAttribute(posterAttr).isEmpty()) + setDisplayMode(Video); + else if (displayMode() < Poster) + setDisplayMode(Poster); +} + +void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect) +{ + MediaPlayer* player = HTMLMediaElement::player(); + if (!player) + return; + + player->setVisible(true); // Make player visible or it won't draw. + player->paintCurrentFrameInContext(context, destRect); +} + +bool HTMLVideoElement::hasAvailableVideoFrame() const +{ + if (!player()) + return false; + + return player()->hasVideo() && player()->hasAvailableVideoFrame(); +} + +void HTMLVideoElement::webkitEnterFullscreen(ExceptionCode& ec) +{ + if (isFullscreen()) + return; + + // Generate an exception if this isn't called in response to a user gesture, or if the + // element does not support fullscreen. + if ((userGestureRequiredForFullscreen() && !ScriptController::processingUserGesture()) || !supportsFullscreen()) { + ec = INVALID_STATE_ERR; + return; + } + + enterFullscreen(); +} + +void HTMLVideoElement::webkitExitFullscreen() +{ + if (isFullscreen()) + exitFullscreen(); +} + +bool HTMLVideoElement::webkitSupportsFullscreen() +{ + return supportsFullscreen(); +} + +bool HTMLVideoElement::webkitDisplayingFullscreen() +{ + return isFullscreen(); +} + +void HTMLVideoElement::didMoveToNewDocument(Document* oldDocument) +{ + if (m_imageLoader) + m_imageLoader->elementDidMoveToNewDocument(); + HTMLMediaElement::didMoveToNewDocument(oldDocument); +} + +#if ENABLE(MEDIA_STATISTICS) +unsigned HTMLVideoElement::webkitDecodedFrameCount() const +{ + if (!player()) + return 0; + + return player()->decodedFrameCount(); +} + +unsigned HTMLVideoElement::webkitDroppedFrameCount() const +{ + if (!player()) + return 0; + + return player()->droppedFrameCount(); +} +#endif + +} + +#endif diff --git a/Source/WebCore/html/HTMLVideoElement.h b/Source/WebCore/html/HTMLVideoElement.h new file mode 100644 index 000000000..af5acbda6 --- /dev/null +++ b/Source/WebCore/html/HTMLVideoElement.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLVideoElement_h +#define HTMLVideoElement_h + +#if ENABLE(VIDEO) + +#include "HTMLMediaElement.h" + +namespace WebCore { + +class HTMLImageLoader; + +class HTMLVideoElement : public HTMLMediaElement { +public: + static PassRefPtr<HTMLVideoElement> create(const QualifiedName&, Document*, bool); + + unsigned width() const; + unsigned height() const; + + unsigned videoWidth() const; + unsigned videoHeight() const; + + // Fullscreen + void webkitEnterFullscreen(ExceptionCode&); + void webkitExitFullscreen(); + bool webkitSupportsFullscreen(); + bool webkitDisplayingFullscreen(); + + // FIXME: Maintain "FullScreen" capitalization scheme for backwards compatibility. + // https://bugs.webkit.org/show_bug.cgi?id=36081 + void webkitEnterFullScreen(ExceptionCode& ec) { webkitEnterFullscreen(ec); } + void webkitExitFullScreen() { webkitExitFullscreen(); } + +#if ENABLE(MEDIA_STATISTICS) + // Statistics + unsigned webkitDecodedFrameCount() const; + unsigned webkitDroppedFrameCount() const; +#endif + + // Used by canvas to gain raw pixel access + void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); + + bool shouldDisplayPosterImage() const { return displayMode() == Poster || displayMode() == PosterWaitingForVideo; } + +private: + HTMLVideoElement(const QualifiedName&, Document*, bool); + + virtual bool rendererIsNeeded(const NodeRenderingContext&); +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); +#endif + virtual void attach(); + virtual void detach(); + virtual void parseMappedAttribute(Attribute*); + virtual bool isVideo() const { return true; } + virtual bool hasVideo() const { return player() && player()->hasVideo(); } + virtual bool supportsFullscreen() const; + virtual bool isURLAttribute(Attribute*) const; + virtual const QualifiedName& imageSourceAttributeName() const; + + virtual bool hasAvailableVideoFrame() const; + virtual void updateDisplayState(); + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + virtual void setDisplayMode(DisplayMode); + + OwnPtr<HTMLImageLoader> m_imageLoader; + +}; + +} //namespace + +#endif +#endif diff --git a/Source/WebCore/html/HTMLVideoElement.idl b/Source/WebCore/html/HTMLVideoElement.idl new file mode 100644 index 000000000..1099681a5 --- /dev/null +++ b/Source/WebCore/html/HTMLVideoElement.idl @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2007, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=VIDEO, + GenerateNativeConverter + ] HTMLVideoElement : HTMLMediaElement { + attribute [Reflect] unsigned long width; + attribute [Reflect] unsigned long height; + readonly attribute unsigned long videoWidth; + readonly attribute unsigned long videoHeight; + attribute [Reflect, URL] DOMString poster; + + readonly attribute boolean webkitSupportsFullscreen; + readonly attribute boolean webkitDisplayingFullscreen; + + void webkitEnterFullscreen() raises (DOMException); + void webkitExitFullscreen(); + + // Note the different capitalization of the "S" in FullScreen. + void webkitEnterFullScreen() raises (DOMException); + void webkitExitFullScreen(); + + // The number of frames that have been decoded and made available for + // playback. + readonly attribute [Conditional=MEDIA_STATISTICS] unsigned long webkitDecodedFrameCount; + + // The number of decoded frames that have been dropped by the player + // for performance reasons during playback. + readonly attribute [Conditional=MEDIA_STATISTICS] unsigned long webkitDroppedFrameCount; + }; +} diff --git a/Source/WebCore/html/HTMLViewSourceDocument.cpp b/Source/WebCore/html/HTMLViewSourceDocument.cpp new file mode 100644 index 000000000..9317d2d1d --- /dev/null +++ b/Source/WebCore/html/HTMLViewSourceDocument.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLViewSourceDocument.h" + +#include "Attribute.h" +#include "DOMImplementation.h" +#include "HTMLAnchorElement.h" +#include "HTMLBRElement.h" +#include "HTMLBaseElement.h" +#include "HTMLBodyElement.h" +#include "HTMLDivElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLNames.h" +#include "HTMLTableCellElement.h" +#include "HTMLTableElement.h" +#include "HTMLTableRowElement.h" +#include "HTMLTableSectionElement.h" +#include "HTMLToken.h" +#include "HTMLViewSourceParser.h" +#include "SegmentedString.h" +#include "Text.h" +#include "TextViewSourceParser.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, const String& mimeType) + : HTMLDocument(frame, url) + , m_type(mimeType) +{ + setUsesBeforeAfterRules(true); + setIsViewSource(true); + + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser() +{ + if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type)) + return HTMLViewSourceParser::create(this); + + return TextViewSourceParser::create(this); +} + +void HTMLViewSourceDocument::createContainingTable() +{ + RefPtr<HTMLHtmlElement> html = HTMLHtmlElement::create(this); + parserAddChild(html); + html->attach(); + RefPtr<HTMLBodyElement> body = HTMLBodyElement::create(this); + html->parserAddChild(body); + body->attach(); + + // Create a line gutter div that can be used to make sure the gutter extends down the height of the whole + // document. + RefPtr<HTMLDivElement> div = HTMLDivElement::create(this); + div->setAttribute(classAttr, "webkit-line-gutter-backdrop"); + body->parserAddChild(div); + div->attach(); + + RefPtr<HTMLTableElement> table = HTMLTableElement::create(this); + body->parserAddChild(table); + table->attach(); + m_tbody = HTMLTableSectionElement::create(tbodyTag, this); + table->parserAddChild(m_tbody); + m_tbody->attach(); + m_current = m_tbody; +} + +void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token) +{ + if (!m_current) + createContainingTable(); + + switch (token.type()) { + case HTMLTokenTypes::Uninitialized: + ASSERT_NOT_REACHED(); + break; + case HTMLTokenTypes::DOCTYPE: + processDoctypeToken(source, token); + break; + case HTMLTokenTypes::EndOfFile: + if (!m_tbody->hasChildNodes()) + addLine(String()); + break; + case HTMLTokenTypes::StartTag: + case HTMLTokenTypes::EndTag: + processTagToken(source, token); + break; + case HTMLTokenTypes::Comment: + processCommentToken(source, token); + break; + case HTMLTokenTypes::Character: + processCharacterToken(source, token); + break; + } +} + +void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&) +{ + if (!m_current) + createContainingTable(); + m_current = addSpanWithClassName("webkit-html-doctype"); + addText(source, "webkit-html-doctype"); + m_current = m_td; +} + +void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token) +{ + m_current = addSpanWithClassName("webkit-html-tag"); + + AtomicString tagName(token.name().data(), token.name().size()); + + unsigned index = 0; + HTMLToken::AttributeList::const_iterator iter = token.attributes().begin(); + while (index < source.length()) { + if (iter == token.attributes().end()) { + // We want to show the remaining characters in the token. + index = addRange(source, index, source.length(), ""); + ASSERT(index == source.length()); + break; + } + + AtomicString name(iter->m_name.data(), iter->m_name.size()); + String value(iter->m_value.data(), iter->m_value.size()); + + index = addRange(source, index, iter->m_nameRange.m_start - token.startIndex(), ""); + index = addRange(source, index, iter->m_nameRange.m_end - token.startIndex(), "webkit-html-attribute-name"); + + if (tagName == baseTag && name == hrefAttr) + m_current = addBase(value); + + index = addRange(source, index, iter->m_valueRange.m_start - token.startIndex(), ""); + + bool isLink = name == srcAttr || name == hrefAttr; + index = addRange(source, index, iter->m_valueRange.m_end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag); + + ++iter; + } + m_current = m_td; +} + +void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&) +{ + m_current = addSpanWithClassName("webkit-html-comment"); + addText(source, "webkit-html-comment"); + m_current = m_td; +} + +void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&) +{ + addText(source, ""); +} + +PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const AtomicString& className) +{ + if (m_current == m_tbody) { + addLine(className); + return m_current; + } + + RefPtr<HTMLElement> span = HTMLElement::create(spanTag, this); + span->setAttribute(classAttr, className); + m_current->parserAddChild(span); + span->attach(); + return span.release(); +} + +void HTMLViewSourceDocument::addLine(const AtomicString& className) +{ + // Create a table row. + RefPtr<HTMLTableRowElement> trow = HTMLTableRowElement::create(this); + m_tbody->parserAddChild(trow); + trow->attach(); + + // Create a cell that will hold the line number (it is generated in the stylesheet using counters). + RefPtr<HTMLTableCellElement> td = HTMLTableCellElement::create(tdTag, this); + td->setAttribute(classAttr, "webkit-line-number"); + trow->parserAddChild(td); + td->attach(); + + // Create a second cell for the line contents + td = HTMLTableCellElement::create(tdTag, this); + td->setAttribute(classAttr, "webkit-line-content"); + trow->parserAddChild(td); + td->attach(); + m_current = m_td = td; + +#ifdef DEBUG_LINE_NUMBERS + RefPtr<Text> lineNumberText = Text::create(this, String::number(parser()->lineNumber() + 1) + " "); + td->addChild(lineNumberText); + lineNumberText->attach(); +#endif + + // Open up the needed spans. + if (!className.isEmpty()) { + if (className == "webkit-html-attribute-name" || className == "webkit-html-attribute-value") + m_current = addSpanWithClassName("webkit-html-tag"); + m_current = addSpanWithClassName(className); + } +} + +void HTMLViewSourceDocument::finishLine() +{ + if (!m_current->hasChildNodes()) { + RefPtr<HTMLBRElement> br = HTMLBRElement::create(this); + m_current->parserAddChild(br); + br->attach(); + } + m_current = m_tbody; +} + +void HTMLViewSourceDocument::addText(const String& text, const AtomicString& className) +{ + if (text.isEmpty()) + return; + + // Add in the content, splitting on newlines. + Vector<String> lines; + text.split('\n', true, lines); + unsigned size = lines.size(); + for (unsigned i = 0; i < size; i++) { + String substring = lines[i]; + if (m_current == m_tbody) + addLine(className); + if (substring.isEmpty()) { + if (i == size - 1) + break; + finishLine(); + continue; + } + RefPtr<Text> t = Text::create(this, substring); + m_current->parserAddChild(t); + t->attach(); + if (i < size - 1) + finishLine(); + } +} + +int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor) +{ + ASSERT(start <= end); + if (start == end) + return start; + + String text = source.substring(start, end - start); + if (!className.isEmpty()) { + if (isLink) + m_current = addLink(text, isAnchor); + else + m_current = addSpanWithClassName(className); + } + addText(text, className); + if (!className.isEmpty() && m_current != m_tbody) + m_current = static_cast<Element*>(m_current->parentNode()); + return end; +} + +PassRefPtr<Element> HTMLViewSourceDocument::addBase(const AtomicString& href) +{ + RefPtr<HTMLBaseElement> base = HTMLBaseElement::create(baseTag, this); + base->setAttribute(hrefAttr, href); + m_current->parserAddChild(base); + base->attach(); + return base.release(); +} + +PassRefPtr<Element> HTMLViewSourceDocument::addLink(const AtomicString& url, bool isAnchor) +{ + if (m_current == m_tbody) + addLine("webkit-html-tag"); + + // Now create a link for the attribute value instead of a span. + RefPtr<HTMLAnchorElement> anchor = HTMLAnchorElement::create(this); + const char* classValue; + if (isAnchor) + classValue = "webkit-html-attribute-value webkit-html-external-link"; + else + classValue = "webkit-html-attribute-value webkit-html-resource-link"; + anchor->setAttribute(classAttr, classValue); + anchor->setAttribute(targetAttr, "_blank"); + anchor->setAttribute(hrefAttr, url); + m_current->parserAddChild(anchor); + anchor->attach(); + return anchor.release(); +} + +} diff --git a/Source/WebCore/html/HTMLViewSourceDocument.h b/Source/WebCore/html/HTMLViewSourceDocument.h new file mode 100644 index 000000000..73c966ecf --- /dev/null +++ b/Source/WebCore/html/HTMLViewSourceDocument.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 HTMLViewSourceDocument_h +#define HTMLViewSourceDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class HTMLTableCellElement; +class HTMLTableSectionElement; +class HTMLToken; + +class HTMLViewSourceDocument : public HTMLDocument { +public: + static PassRefPtr<HTMLViewSourceDocument> create(Frame* frame, const KURL& url, const String& mimeType) + { + return adoptRef(new HTMLViewSourceDocument(frame, url, mimeType)); + } + + void addSource(const String&, HTMLToken&); + +private: + HTMLViewSourceDocument(Frame*, const KURL&, const String& mimeType); + + // Returns HTMLViewSourceParser or TextDocumentParser based on m_type. + virtual PassRefPtr<DocumentParser> createParser(); + + void processDoctypeToken(const String& source, HTMLToken&); + void processTagToken(const String& source, HTMLToken&); + void processCommentToken(const String& source, HTMLToken&); + void processCharacterToken(const String& source, HTMLToken&); + + void createContainingTable(); + PassRefPtr<Element> addSpanWithClassName(const AtomicString&); + void addLine(const AtomicString& className); + void finishLine(); + void addText(const String& text, const AtomicString& className); + int addRange(const String& source, int start, int end, const String& className, bool isLink = false, bool isAnchor = false); + PassRefPtr<Element> addLink(const AtomicString& url, bool isAnchor); + PassRefPtr<Element> addBase(const AtomicString& href); + + String m_type; + RefPtr<Element> m_current; + RefPtr<HTMLTableSectionElement> m_tbody; + RefPtr<HTMLTableCellElement> m_td; +}; + +} + +#endif // HTMLViewSourceDocument_h diff --git a/Source/WebCore/html/HiddenInputType.cpp b/Source/WebCore/html/HiddenInputType.cpp new file mode 100644 index 000000000..cc3ab5a02 --- /dev/null +++ b/Source/WebCore/html/HiddenInputType.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HiddenInputType.h" + +#include "FormDataList.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +PassOwnPtr<InputType> HiddenInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new HiddenInputType(element)); +} + +const AtomicString& HiddenInputType::formControlType() const { + return InputTypeNames::hidden(); +} + +bool HiddenInputType::supportsValidation() const +{ + return false; +} + +RenderObject* HiddenInputType::createRenderer(RenderArena*, RenderStyle*) const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +void HiddenInputType::accessKeyAction(bool) +{ +} + +bool HiddenInputType::rendererIsNeeded() +{ + return false; +} + +bool HiddenInputType::storesValueSeparateFromAttribute() +{ + return false; +} + +void HiddenInputType::setValue(const String& sanitizedValue, bool, bool) +{ + element()->setAttribute(valueAttr, sanitizedValue); +} + +bool HiddenInputType::isHiddenType() const +{ + return true; +} + +bool HiddenInputType::appendFormData(FormDataList& encoding, bool isMultipartForm) const +{ + if (equalIgnoringCase(element()->name(), "_charset_")) { + encoding.appendData(element()->name(), String(encoding.encoding().name())); + return true; + } + return InputType::appendFormData(encoding, isMultipartForm); +} + +bool HiddenInputType::shouldRespectHeightAndWidthAttributes() +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/HiddenInputType.h b/Source/WebCore/html/HiddenInputType.h new file mode 100644 index 000000000..07a3fecdf --- /dev/null +++ b/Source/WebCore/html/HiddenInputType.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HiddenInputType_h +#define HiddenInputType_h + +#include "InputType.h" + +namespace WebCore { + +class HiddenInputType : public InputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + HiddenInputType(HTMLInputElement* element) : InputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool supportsValidation() const OVERRIDE; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; + virtual void accessKeyAction(bool sendMouseEvents) OVERRIDE; + virtual bool rendererIsNeeded() OVERRIDE; + virtual bool storesValueSeparateFromAttribute() OVERRIDE; + virtual bool isHiddenType() const OVERRIDE; + virtual bool shouldRespectHeightAndWidthAttributes() OVERRIDE; + virtual void setValue(const String&, bool, bool) OVERRIDE; + virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; +}; + +} // namespace WebCore + +#endif // HiddenInputType_h diff --git a/Source/WebCore/html/ImageData.cpp b/Source/WebCore/html/ImageData.cpp new file mode 100644 index 000000000..ad2413663 --- /dev/null +++ b/Source/WebCore/html/ImageData.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageData.h" + +namespace WebCore { + +PassRefPtr<ImageData> ImageData::create(const IntSize& size) +{ + return adoptRef(new ImageData(size)); +} + +PassRefPtr<ImageData> ImageData::create(const IntSize& size, PassRefPtr<ByteArray> byteArray) +{ + return adoptRef(new ImageData(size, byteArray)); +} + +ImageData::ImageData(const IntSize& size) + : m_size(size) + , m_data(CanvasPixelArray::create(size.width() * size.height() * 4)) +{ +} + +ImageData::ImageData(const IntSize& size, PassRefPtr<ByteArray> byteArray) + : m_size(size) + , m_data(CanvasPixelArray::create(byteArray)) +{ +} + +} + diff --git a/Source/WebCore/html/ImageData.h b/Source/WebCore/html/ImageData.h new file mode 100644 index 000000000..35d08e71d --- /dev/null +++ b/Source/WebCore/html/ImageData.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageData_h +#define ImageData_h + +#include "CanvasPixelArray.h" +#include "IntSize.h" +#include <wtf/ByteArray.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class ImageData : public RefCounted<ImageData> { +public: + static PassRefPtr<ImageData> create(const IntSize&); + static PassRefPtr<ImageData> create(const IntSize&, PassRefPtr<ByteArray>); + + IntSize size() const { return m_size; } + int width() const { return m_size.width(); } + int height() const { return m_size.height(); } + CanvasPixelArray* data() const { return m_data.get(); } + +private: + ImageData(const IntSize&); + ImageData(const IntSize&, PassRefPtr<ByteArray>); + + IntSize m_size; + RefPtr<CanvasPixelArray> m_data; +}; + +} // namespace WebCore + +#endif // ImageData_h diff --git a/Source/WebCore/html/ImageData.idl b/Source/WebCore/html/ImageData.idl new file mode 100644 index 000000000..605020577 --- /dev/null +++ b/Source/WebCore/html/ImageData.idl @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + CustomToJS + ] ImageData { + readonly attribute long width; + readonly attribute long height; +#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT + readonly attribute CanvasPixelArray data; +#endif + }; + +} diff --git a/Source/WebCore/html/ImageDocument.cpp b/Source/WebCore/html/ImageDocument.cpp new file mode 100644 index 000000000..d4a73d381 --- /dev/null +++ b/Source/WebCore/html/ImageDocument.cpp @@ -0,0 +1,419 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageDocument.h" + +#include "CachedImage.h" +#include "DocumentLoader.h" +#include "EventListener.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "FrameView.h" +#include "HTMLHtmlElement.h" +#include "HTMLImageElement.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "RawDataDocumentParser.h" +#include "Settings.h" + +using std::min; + +namespace WebCore { + +using namespace HTMLNames; + +class ImageEventListener : public EventListener { +public: + static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); } + static const ImageEventListener* cast(const EventListener* listener) + { + return listener->type() == ImageEventListenerType + ? static_cast<const ImageEventListener*>(listener) + : 0; + } + + virtual bool operator==(const EventListener& other); + +private: + ImageEventListener(ImageDocument* document) + : EventListener(ImageEventListenerType) + , m_doc(document) + { + } + + virtual void handleEvent(ScriptExecutionContext*, Event*); + + ImageDocument* m_doc; +}; + +class ImageDocumentParser : public RawDataDocumentParser { +public: + static PassRefPtr<ImageDocumentParser> create(ImageDocument* document) + { + return adoptRef(new ImageDocumentParser(document)); + } + + ImageDocument* document() const + { + return static_cast<ImageDocument*>(RawDataDocumentParser::document()); + } + +private: + ImageDocumentParser(ImageDocument* document) + : RawDataDocumentParser(document) + { + } + + virtual void appendBytes(DocumentWriter*, const char*, size_t); + virtual void finish(); +}; + +class ImageDocumentElement : public HTMLImageElement { +public: + static PassRefPtr<ImageDocumentElement> create(ImageDocument*); + +private: + ImageDocumentElement(ImageDocument* document) + : HTMLImageElement(imgTag, document) + , m_imageDocument(document) + { + } + + virtual ~ImageDocumentElement(); + virtual void didMoveToNewDocument(Document* oldDocument) OVERRIDE; + + ImageDocument* m_imageDocument; +}; + +inline PassRefPtr<ImageDocumentElement> ImageDocumentElement::create(ImageDocument* document) +{ + return adoptRef(new ImageDocumentElement(document)); +} + +// -------- + +static float pageZoomFactor(const Document* document) +{ + Frame* frame = document->frame(); + return frame ? frame->pageZoomFactor() : 1; +} + +void ImageDocumentParser::appendBytes(DocumentWriter*, const char*, size_t) +{ + Frame* frame = document()->frame(); + Settings* settings = frame->settings(); + if (!frame->loader()->client()->allowImage(!settings || settings->areImagesEnabled(), document()->url())) + return; + + CachedImage* cachedImage = document()->cachedImage(); + cachedImage->data(frame->loader()->documentLoader()->mainResourceData(), false); + + document()->imageUpdated(); +} + +void ImageDocumentParser::finish() +{ + if (!isStopped() && document()->imageElement()) { + CachedImage* cachedImage = document()->cachedImage(); + RefPtr<SharedBuffer> data = document()->frame()->loader()->documentLoader()->mainResourceData(); + + // If this is a multipart image, make a copy of the current part, since the resource data + // will be overwritten by the next part. + if (document()->frame()->loader()->documentLoader()->isLoadingMultipartContent()) + data = data->copy(); + + cachedImage->data(data.release(), true); + cachedImage->finish(); + + cachedImage->setResponse(document()->frame()->loader()->documentLoader()->response()); + + // Report the natural image size in the page title, regardless of zoom + // level. + LayoutSize size = cachedImage->imageSizeForRenderer(document()->imageElement()->renderer(), 1.0f); + if (size.width()) { + // Compute the title, we use the decoded filename of the resource, falling + // back on the (decoded) hostname if there is no path. + String fileName = decodeURLEscapeSequences(document()->url().lastPathComponent()); + if (fileName.isEmpty()) + fileName = document()->url().host(); + document()->setTitle(imageTitle(fileName, size)); + } + + document()->imageUpdated(); + } + + document()->finishedParsing(); +} + +// -------- + +ImageDocument::ImageDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) + , m_imageElement(0) + , m_imageSizeIsKnown(false) + , m_didShrinkImage(false) + , m_shouldShrinkImage(shouldShrinkToFit()) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> ImageDocument::createParser() +{ + return ImageDocumentParser::create(this); +} + +void ImageDocument::createDocumentStructure() +{ + ExceptionCode ec; + + RefPtr<Element> rootElement = Document::createElement(htmlTag, false); + appendChild(rootElement, ec); + static_cast<HTMLHtmlElement*>(rootElement.get())->insertedByParser(); + + if (frame() && frame()->loader()) + frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr<Element> body = Document::createElement(bodyTag, false); + body->setAttribute(styleAttr, "margin: 0px;"); + + rootElement->appendChild(body, ec); + + RefPtr<ImageDocumentElement> imageElement = ImageDocumentElement::create(this); + + imageElement->setAttribute(styleAttr, "-webkit-user-select: none"); + imageElement->setLoadManually(true); + imageElement->setSrc(url().string()); + + body->appendChild(imageElement, ec); + + if (shouldShrinkToFit()) { + // Add event listeners + RefPtr<EventListener> listener = ImageEventListener::create(this); + if (DOMWindow* domWindow = this->domWindow()) + domWindow->addEventListener("resize", listener, false); + imageElement->addEventListener("click", listener.release(), false); + } + + m_imageElement = imageElement.get(); +} + +float ImageDocument::scale() const +{ + if (!m_imageElement) + return 1.0f; + + FrameView* view = frame()->view(); + if (!view) + return 1; + + LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); + LayoutSize windowSize = LayoutSize(view->width(), view->height()); + + float widthScale = (float)windowSize.width() / imageSize.width(); + float heightScale = (float)windowSize.height() / imageSize.height(); + + return min(widthScale, heightScale); +} + +void ImageDocument::resizeImageToFit() +{ + if (!m_imageElement) + return; + + LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); + + float scale = this->scale(); + m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); + m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale)); + + ExceptionCode ec; + m_imageElement->style()->setProperty(CSSPropertyCursor, "-webkit-zoom-in", false, ec); +} + +void ImageDocument::imageClicked(int x, int y) +{ + if (!m_imageSizeIsKnown || imageFitsInWindow()) + return; + + m_shouldShrinkImage = !m_shouldShrinkImage; + + if (m_shouldShrinkImage) + windowSizeChanged(); + else { + restoreImageSize(); + + updateLayout(); + + float scale = this->scale(); + + int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2); + int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2); + + frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY)); + } +} + +void ImageDocument::imageUpdated() +{ + ASSERT(m_imageElement); + + if (m_imageSizeIsKnown) + return; + + if (m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)).isEmpty()) + return; + + m_imageSizeIsKnown = true; + + if (shouldShrinkToFit()) { + // Force resizing of the image + windowSizeChanged(); + } +} + +void ImageDocument::restoreImageSize() +{ + if (!m_imageElement || !m_imageSizeIsKnown) + return; + + LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); + m_imageElement->setWidth(imageSize.width()); + m_imageElement->setHeight(imageSize.height()); + + ExceptionCode ec; + if (imageFitsInWindow()) + m_imageElement->style()->removeProperty(CSSPropertyCursor, ec); + else + m_imageElement->style()->setProperty(CSSPropertyCursor, "-webkit-zoom-out", false, ec); + + m_didShrinkImage = false; +} + +bool ImageDocument::imageFitsInWindow() const +{ + if (!m_imageElement) + return true; + + FrameView* view = frame()->view(); + if (!view) + return true; + + LayoutSize imageSize = m_imageElement->cachedImage()->imageSizeForRenderer(m_imageElement->renderer(), pageZoomFactor(this)); + LayoutSize windowSize = LayoutSize(view->width(), view->height()); + + return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height(); +} + +void ImageDocument::windowSizeChanged() +{ + if (!m_imageElement || !m_imageSizeIsKnown) + return; + + bool fitsInWindow = imageFitsInWindow(); + + // If the image has been explicitly zoomed in, restore the cursor if the image fits + // and set it to a zoom out cursor if the image doesn't fit + if (!m_shouldShrinkImage) { + ExceptionCode ec; + + if (fitsInWindow) + m_imageElement->style()->removeProperty(CSSPropertyCursor, ec); + else + m_imageElement->style()->setProperty(CSSPropertyCursor, "-webkit-zoom-out", false, ec); + return; + } + + if (m_didShrinkImage) { + // If the window has been resized so that the image fits, restore the image size + // otherwise update the restored image size. + if (fitsInWindow) + restoreImageSize(); + else + resizeImageToFit(); + } else { + // If the image isn't resized but needs to be, then resize it. + if (!fitsInWindow) { + resizeImageToFit(); + m_didShrinkImage = true; + } + } +} + +CachedImage* ImageDocument::cachedImage() +{ + if (!m_imageElement) + createDocumentStructure(); + + return m_imageElement->cachedImage(); +} + +bool ImageDocument::shouldShrinkToFit() const +{ + return frame()->page()->settings()->shrinksStandaloneImagesToFit() && + frame()->page()->mainFrame() == frame(); +} + +// -------- + +void ImageEventListener::handleEvent(ScriptExecutionContext*, Event* event) +{ + if (event->type() == eventNames().resizeEvent) + m_doc->windowSizeChanged(); + else if (event->type() == eventNames().clickEvent && event->isMouseEvent()) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); + } +} + +bool ImageEventListener::operator==(const EventListener& listener) +{ + if (const ImageEventListener* imageEventListener = ImageEventListener::cast(&listener)) + return m_doc == imageEventListener->m_doc; + return false; +} + +// -------- + +ImageDocumentElement::~ImageDocumentElement() +{ + if (m_imageDocument) + m_imageDocument->disconnectImageElement(); +} + +void ImageDocumentElement::didMoveToNewDocument(Document* oldDocument) +{ + if (m_imageDocument) { + m_imageDocument->disconnectImageElement(); + m_imageDocument = 0; + } + HTMLImageElement::didMoveToNewDocument(oldDocument); +} + +} diff --git a/Source/WebCore/html/ImageDocument.h b/Source/WebCore/html/ImageDocument.h new file mode 100644 index 000000000..faf92070f --- /dev/null +++ b/Source/WebCore/html/ImageDocument.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 ImageDocument_h +#define ImageDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class CachedImage; +class ImageDocumentElement; + +class ImageDocument : public HTMLDocument { +public: + static PassRefPtr<ImageDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new ImageDocument(frame, url)); + } + + CachedImage* cachedImage(); + ImageDocumentElement* imageElement() const { return m_imageElement; } + void disconnectImageElement() { m_imageElement = 0; } + + void windowSizeChanged(); + void imageUpdated(); + void imageClicked(int x, int y); + +private: + ImageDocument(Frame*, const KURL&); + + virtual PassRefPtr<DocumentParser> createParser(); + virtual bool isImageDocument() const { return true; } + + void createDocumentStructure(); + void resizeImageToFit(); + void restoreImageSize(); + bool imageFitsInWindow() const; + bool shouldShrinkToFit() const; + float scale() const; + + ImageDocumentElement* m_imageElement; + + // Whether enough of the image has been loaded to determine its size + bool m_imageSizeIsKnown; + + // Whether the image is shrunk to fit or not + bool m_didShrinkImage; + + // Whether the image should be shrunk or not + bool m_shouldShrinkImage; +}; + +} + +#endif // ImageDocument_h diff --git a/Source/WebCore/html/ImageInputType.cpp b/Source/WebCore/html/ImageInputType.cpp new file mode 100644 index 000000000..03009e81d --- /dev/null +++ b/Source/WebCore/html/ImageInputType.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 "ImageInputType.h" + +#include "FormDataList.h" +#include "HTMLFormElement.h" +#include "HTMLImageLoader.h" +#include "HTMLInputElement.h" +#include "MouseEvent.h" +#include "RenderImage.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +inline ImageInputType::ImageInputType(HTMLInputElement* element) + : BaseButtonInputType(element) +{ +} + +PassOwnPtr<InputType> ImageInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new ImageInputType(element)); +} + +const AtomicString& ImageInputType::formControlType() const +{ + return InputTypeNames::image(); +} + +bool ImageInputType::isFormDataAppendable() const +{ + return true; +} + +bool ImageInputType::appendFormData(FormDataList& encoding, bool) const +{ + if (!element()->isActivatedSubmit()) + return false; + const AtomicString& name = element()->name(); + if (name.isEmpty()) { + encoding.appendData("x", m_clickLocation.x()); + encoding.appendData("y", m_clickLocation.y()); + return true; + } + + DEFINE_STATIC_LOCAL(String, dotXString, (".x")); + DEFINE_STATIC_LOCAL(String, dotYString, (".y")); + encoding.appendData(name + dotXString, m_clickLocation.x()); + encoding.appendData(name + dotYString, m_clickLocation.y()); + + if (!element()->value().isEmpty()) + encoding.appendData(name, element()->value()); + return true; +} + +bool ImageInputType::supportsValidation() const +{ + return false; +} + +void ImageInputType::handleDOMActivateEvent(Event* event) +{ + RefPtr<HTMLInputElement> element = this->element(); + if (element->disabled() || !element->form()) + return; + element->setActivatedSubmit(true); + if (event->underlyingEvent() && event->underlyingEvent()->isMouseEvent()) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event->underlyingEvent()); + m_clickLocation = IntPoint(mouseEvent->offsetX(), mouseEvent->offsetY()); + } else + m_clickLocation = IntPoint(); + element->form()->prepareForSubmission(event); // Event handlers can run. + element->setActivatedSubmit(false); + event->setDefaultHandled(); +} + +RenderObject* ImageInputType::createRenderer(RenderArena* arena, RenderStyle*) const +{ + RenderImage* image = new (arena) RenderImage(element()); + image->setImageResource(RenderImageResource::create()); + return image; +} + +void ImageInputType::altAttributeChanged() +{ + RenderImage* image = toRenderImage(element()->renderer()); + if (!image) + return; + image->updateAltText(); +} + +void ImageInputType::srcAttributeChanged() +{ + if (!element()->renderer()) + return; + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(element())); + m_imageLoader->updateFromElementIgnoringPreviousError(); +} + +void ImageInputType::attach() +{ + BaseButtonInputType::attach(); + + if (!m_imageLoader) + m_imageLoader = adoptPtr(new HTMLImageLoader(element())); + m_imageLoader->updateFromElement(); + + RenderImage* renderer = toRenderImage(element()->renderer()); + if (!renderer) + return; + + if (!m_imageLoader->haveFiredBeforeLoadEvent()) + return; + + RenderImageResource* imageResource = renderer->imageResource(); + imageResource->setCachedImage(m_imageLoader->image()); + + // If we have no image at all because we have no src attribute, set + // image height and width for the alt text instead. + if (!m_imageLoader->image() && !imageResource->cachedImage()) + renderer->setImageSizeForAltText(); +} + +void ImageInputType::willMoveToNewOwnerDocument() +{ + BaseButtonInputType::willMoveToNewOwnerDocument(); + if (m_imageLoader) + m_imageLoader->elementDidMoveToNewDocument(); +} + +bool ImageInputType::shouldRespectAlignAttribute() +{ + return true; +} + +bool ImageInputType::canBeSuccessfulSubmitButton() +{ + return true; +} + +bool ImageInputType::isImageButton() const +{ + return true; +} + +bool ImageInputType::isEnumeratable() +{ + return false; +} + +bool ImageInputType::shouldRespectHeightAndWidthAttributes() +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/ImageInputType.h b/Source/WebCore/html/ImageInputType.h new file mode 100644 index 000000000..93db075d6 --- /dev/null +++ b/Source/WebCore/html/ImageInputType.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageInputType_h +#define ImageInputType_h + +#include "BaseButtonInputType.h" +#include "IntPoint.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class HTMLImageLoader; + +class ImageInputType : public BaseButtonInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + ImageInputType(HTMLInputElement*); + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool isFormDataAppendable() const OVERRIDE; + virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; + virtual bool supportsValidation() const OVERRIDE; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; + virtual void handleDOMActivateEvent(Event*) OVERRIDE; + virtual void altAttributeChanged() OVERRIDE; + virtual void srcAttributeChanged() OVERRIDE; + virtual void attach() OVERRIDE; + virtual void willMoveToNewOwnerDocument() OVERRIDE; + virtual bool shouldRespectAlignAttribute() OVERRIDE; + virtual bool canBeSuccessfulSubmitButton() OVERRIDE; + virtual bool isImageButton() const OVERRIDE; + virtual bool isEnumeratable() OVERRIDE; + virtual bool shouldRespectHeightAndWidthAttributes() OVERRIDE; + + OwnPtr<HTMLImageLoader> m_imageLoader; + IntPoint m_clickLocation; // Valid only during HTMLFormElement::prepareForSubmission(). +}; + +} // namespace WebCore + +#endif // ImageInputType_h diff --git a/Source/WebCore/html/InputType.cpp b/Source/WebCore/html/InputType.cpp new file mode 100644 index 000000000..510239278 --- /dev/null +++ b/Source/WebCore/html/InputType.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * (C) 2006 Alexey Proskuryakov (ap@nypop.com) + * Copyright (C) 2007 Samuel Weinig (sam@webkit.org) + * Copyright (C) 2010 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 "InputType.h" + +#include "BeforeTextInsertedEvent.h" +#include "ButtonInputType.h" +#include "CheckboxInputType.h" +#include "ColorInputType.h" +#include "DateComponents.h" +#include "DateInputType.h" +#include "DateTimeInputType.h" +#include "DateTimeLocalInputType.h" +#include "EmailInputType.h" +#include "ExceptionCode.h" +#include "FileInputType.h" +#include "FormDataList.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HiddenInputType.h" +#include "ImageInputType.h" +#include "IsIndexInputType.h" +#include "KeyboardEvent.h" +#include "LocalizedStrings.h" +#include "MonthInputType.h" +#include "NumberInputType.h" +#include "Page.h" +#include "PasswordInputType.h" +#include "RadioInputType.h" +#include "RangeInputType.h" +#include "RegularExpression.h" +#include "RenderObject.h" +#include "ResetInputType.h" +#include "SearchInputType.h" +#include "ShadowRoot.h" +#include "SubmitInputType.h" +#include "TelephoneInputType.h" +#include "TextInputType.h" +#include "TimeInputType.h" +#include "URLInputType.h" +#include "WeekInputType.h" +#include <limits> +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +using namespace std; + +typedef PassOwnPtr<InputType> (*InputTypeFactoryFunction)(HTMLInputElement*); +typedef HashMap<String, InputTypeFactoryFunction, CaseFoldingHash> InputTypeFactoryMap; + +static PassOwnPtr<InputTypeFactoryMap> createInputTypeFactoryMap() +{ + OwnPtr<InputTypeFactoryMap> map = adoptPtr(new InputTypeFactoryMap); + map->add(InputTypeNames::button(), ButtonInputType::create); + map->add(InputTypeNames::checkbox(), CheckboxInputType::create); +#if ENABLE(INPUT_COLOR) + map->add(InputTypeNames::color(), ColorInputType::create); +#endif +#if ENABLE(INPUT_TYPE_DATE) + map->add(InputTypeNames::date(), DateInputType::create); +#endif +#if ENABLE(INPUT_TYPE_DATETIME) + map->add(InputTypeNames::datetime(), DateTimeInputType::create); +#endif +#if ENABLE(INPUT_TYPE_DATETIMELOCAL) + map->add(InputTypeNames::datetimelocal(), DateTimeLocalInputType::create); +#endif + map->add(InputTypeNames::email(), EmailInputType::create); + map->add(InputTypeNames::file(), FileInputType::create); + map->add(InputTypeNames::hidden(), HiddenInputType::create); + map->add(InputTypeNames::image(), ImageInputType::create); + map->add(InputTypeNames::isindex(), IsIndexInputType::create); +#if ENABLE(INPUT_TYPE_MONTH) + map->add(InputTypeNames::month(), MonthInputType::create); +#endif + map->add(InputTypeNames::number(), NumberInputType::create); + map->add(InputTypeNames::password(), PasswordInputType::create); + map->add(InputTypeNames::radio(), RadioInputType::create); + map->add(InputTypeNames::range(), RangeInputType::create); + map->add(InputTypeNames::reset(), ResetInputType::create); + map->add(InputTypeNames::search(), SearchInputType::create); + map->add(InputTypeNames::submit(), SubmitInputType::create); + map->add(InputTypeNames::telephone(), TelephoneInputType::create); +#if ENABLE(INPUT_TYPE_TIME) + map->add(InputTypeNames::time(), TimeInputType::create); +#endif + map->add(InputTypeNames::url(), URLInputType::create); +#if ENABLE(INPUT_TYPE_WEEK) + map->add(InputTypeNames::week(), WeekInputType::create); +#endif + // No need to register "text" because it is the default type. + return map.release(); +} + +PassOwnPtr<InputType> InputType::create(HTMLInputElement* element, const String& typeName) +{ + static const InputTypeFactoryMap* factoryMap = createInputTypeFactoryMap().leakPtr(); + PassOwnPtr<InputType> (*factory)(HTMLInputElement*) = typeName.isEmpty() ? 0 : factoryMap->get(typeName); + if (!factory) + factory = TextInputType::create; + return factory(element); +} + +PassOwnPtr<InputType> InputType::createText(HTMLInputElement* element) +{ + return TextInputType::create(element); +} + +InputType::~InputType() +{ +} + +bool InputType::isTextField() const +{ + return false; +} + +bool InputType::isTextType() const +{ + return false; +} + +bool InputType::isRangeControl() const +{ + return false; +} + +bool InputType::saveFormControlState(String& result) const +{ + String currentValue = element()->value(); + if (currentValue == element()->defaultValue()) + return false; + result = currentValue; + return true; +} + +void InputType::restoreFormControlState(const String& state) const +{ + element()->setValue(state); +} + +bool InputType::isFormDataAppendable() const +{ + // There is no form data unless there's a name for non-image types. + return !element()->name().isEmpty(); +} + +bool InputType::appendFormData(FormDataList& encoding, bool) const +{ + // Always successful. + encoding.appendData(element()->name(), element()->value()); + return true; +} + +double InputType::valueAsDate() const +{ + return DateComponents::invalidMilliseconds(); +} + +void InputType::setValueAsDate(double, ExceptionCode& ec) const +{ + ec = INVALID_STATE_ERR; +} + +double InputType::valueAsNumber() const +{ + return numeric_limits<double>::quiet_NaN(); +} + +void InputType::setValueAsNumber(double, bool, ExceptionCode& ec) const +{ + ec = INVALID_STATE_ERR; +} + +bool InputType::supportsValidation() const +{ + return true; +} + +bool InputType::typeMismatchFor(const String&) const +{ + return false; +} + +bool InputType::typeMismatch() const +{ + return false; +} + +bool InputType::supportsRequired() const +{ + // Almost all validatable types support @required. + return supportsValidation(); +} + +bool InputType::valueMissing(const String&) const +{ + return false; +} + +bool InputType::patternMismatch(const String&) const +{ + return false; +} + +bool InputType::rangeUnderflow(const String&) const +{ + return false; +} + +bool InputType::rangeOverflow(const String&) const +{ + return false; +} + +bool InputType::supportsRangeLimitation() const +{ + return false; +} + +double InputType::defaultValueForStepUp() const +{ + return 0; +} + +double InputType::minimum() const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +double InputType::maximum() const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +bool InputType::sizeShouldIncludeDecoration(int, int& preferredSize) const +{ + preferredSize = element()->size(); + return false; +} + +bool InputType::stepMismatch(const String&, double) const +{ + // Non-supported types should be rejected by HTMLInputElement::getAllowedValueStep(). + ASSERT_NOT_REACHED(); + return false; +} + +double InputType::stepBase() const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +double InputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const +{ + if (decimalPlaces) + *decimalPlaces = 0; + return stepBase(); +} + +double InputType::defaultStep() const +{ + return numeric_limits<double>::quiet_NaN(); +} + +double InputType::stepScaleFactor() const +{ + return numeric_limits<double>::quiet_NaN(); +} + +bool InputType::parsedStepValueShouldBeInteger() const +{ + return false; +} + +bool InputType::scaledStepValueShouldBeInteger() const +{ + return false; +} + +double InputType::acceptableError(double) const +{ + return 0; +} + +String InputType::typeMismatchText() const +{ + return validationMessageTypeMismatchText(); +} + +String InputType::valueMissingText() const +{ + return validationMessageValueMissingText(); +} + +void InputType::handleClickEvent(MouseEvent*) +{ +} + +void InputType::handleMouseDownEvent(MouseEvent*) +{ +} + +void InputType::handleDOMActivateEvent(Event*) +{ +} + +void InputType::handleKeydownEvent(KeyboardEvent*) +{ +} + +void InputType::handleKeypressEvent(KeyboardEvent*) +{ +} + +void InputType::handleKeyupEvent(KeyboardEvent*) +{ +} + +void InputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*) +{ +} + +void InputType::handleWheelEvent(WheelEvent*) +{ +} + +void InputType::forwardEvent(Event*) +{ +} + +bool InputType::shouldSubmitImplicitly(Event* event) +{ + return event->isKeyboardEvent() && event->type() == eventNames().keypressEvent && static_cast<KeyboardEvent*>(event)->charCode() == '\r'; +} + +PassRefPtr<HTMLFormElement> InputType::formForSubmission() const +{ + return element()->form(); +} + +RenderObject* InputType::createRenderer(RenderArena*, RenderStyle* style) const +{ + return RenderObject::createObject(element(), style); +} + +void InputType::createShadowSubtree() +{ +} + +void InputType::destroyShadowSubtree() +{ + element()->removeShadowRoot(); +} + +double InputType::parseToDouble(const String&, double defaultValue) const +{ + return defaultValue; +} + +double InputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const +{ + if (decimalPlaces) + *decimalPlaces = 0; + return parseToDouble(src, defaultValue); +} + +bool InputType::parseToDateComponents(const String&, DateComponents*) const +{ + ASSERT_NOT_REACHED(); + return false; +} + +String InputType::serialize(double) const +{ + ASSERT_NOT_REACHED(); + return String(); +} + +void InputType::dispatchSimulatedClickIfActive(KeyboardEvent* event) const +{ + if (element()->active()) + element()->dispatchSimulatedClick(event); + event->setDefaultHandled(); +} + +Chrome* InputType::chrome() const +{ + if (Page* page = element()->document()->page()) + return page->chrome(); + return 0; +} + +bool InputType::canSetStringValue() const +{ + return true; +} + +bool InputType::isKeyboardFocusable() const +{ + return true; +} + +bool InputType::shouldUseInputMethod() const +{ + return false; +} + +void InputType::handleBlurEvent() +{ +} + +void InputType::accessKeyAction(bool) +{ + element()->focus(false); +} + +void InputType::attach() +{ +} + +void InputType::detach() +{ +} + +void InputType::altAttributeChanged() +{ +} + +void InputType::srcAttributeChanged() +{ +} + +void InputType::willMoveToNewOwnerDocument() +{ +} + +bool InputType::shouldRespectAlignAttribute() +{ + return false; +} + +bool InputType::canChangeFromAnotherType() const +{ + return true; +} + +void InputType::minOrMaxAttributeChanged() +{ +} + +void InputType::stepAttributeChanged() +{ +} + +bool InputType::canBeSuccessfulSubmitButton() +{ + return false; +} + +HTMLElement* InputType::placeholderElement() const +{ + return 0; +} + +bool InputType::rendererIsNeeded() +{ + return true; +} + +FileList* InputType::files() +{ + return 0; +} + +bool InputType::getTypeSpecificValue(String&) +{ + return false; +} + +String InputType::fallbackValue() const +{ + return String(); +} + +String InputType::defaultValue() const +{ + return String(); +} + +bool InputType::canSetSuggestedValue() +{ + return false; +} + +bool InputType::shouldSendChangeEventAfterCheckedChanged() +{ + return true; +} + +bool InputType::storesValueSeparateFromAttribute() +{ + return true; +} + +void InputType::setValue(const String& sanitizedValue, bool, bool sendChangeEvent) +{ + element()->setValueInternal(sanitizedValue, sendChangeEvent); + element()->setNeedsStyleRecalc(); +} + +void InputType::dispatchChangeEventInResponseToSetValue() +{ + element()->dispatchFormControlChangeEvent(); +} + +bool InputType::canSetValue(const String&) +{ + return true; +} + +PassOwnPtr<ClickHandlingState> InputType::willDispatchClick() +{ + return nullptr; +} + +void InputType::didDispatchClick(Event*, const ClickHandlingState&) +{ +} + +String InputType::visibleValue() const +{ + return element()->value(); +} + +String InputType::convertFromVisibleValue(const String& visibleValue) const +{ + return visibleValue; +} + +bool InputType::isAcceptableValue(const String&) +{ + return true; +} + +String InputType::sanitizeValue(const String& proposedValue) const +{ + return proposedValue; +} + +bool InputType::hasUnacceptableValue() +{ + return false; +} + +void InputType::receiveDroppedFiles(const Vector<String>&) +{ + ASSERT_NOT_REACHED(); +} + +Icon* InputType::icon() const +{ + ASSERT_NOT_REACHED(); + return 0; +} + +bool InputType::shouldResetOnDocumentActivation() +{ + return false; +} + +bool InputType::shouldRespectListAttribute() +{ + return false; +} + +bool InputType::shouldRespectSpeechAttribute() +{ + return false; +} + +bool InputType::isTextButton() const +{ + return false; +} + +bool InputType::isRadioButton() const +{ + return false; +} + +bool InputType::isSearchField() const +{ + return false; +} + +bool InputType::isHiddenType() const +{ + return false; +} + +bool InputType::isPasswordField() const +{ + return false; +} + +bool InputType::isCheckbox() const +{ + return false; +} + +bool InputType::isEmailField() const +{ + return false; +} + +bool InputType::isFileUpload() const +{ + return false; +} + +bool InputType::isImageButton() const +{ + return false; +} + +bool InputType::isNumberField() const +{ + return false; +} + +bool InputType::isSubmitButton() const +{ + return false; +} + +bool InputType::isTelephoneField() const +{ + return false; +} + +bool InputType::isURLField() const +{ + return false; +} + +bool InputType::isEnumeratable() +{ + return true; +} + +bool InputType::isCheckable() +{ + return false; +} + +bool InputType::isSteppable() const +{ + return false; +} + +#if ENABLE(INPUT_COLOR) +bool InputType::isColorControl() const +{ + return false; +} +#endif + +bool InputType::shouldRespectHeightAndWidthAttributes() +{ + return false; +} + +bool InputType::supportsPlaceholder() const +{ + return false; +} + +void InputType::updatePlaceholderText() +{ +} + +void InputType::multipleAttributeChanged() +{ +} + +void InputType::disabledAttributeChanged() +{ +} + +void InputType::readonlyAttributeChanged() +{ +} + +String InputType::defaultToolTip() const +{ + return String(); +} + +namespace InputTypeNames { + +// The type names must be lowercased because they will be the return values of +// input.type and input.type must be lowercase according to DOM Level 2. + +const AtomicString& button() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("button")); + return name; +} + +const AtomicString& checkbox() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("checkbox")); + return name; +} + +#if ENABLE(INPUT_COLOR) +const AtomicString& color() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("color")); + return name; +} +#endif + +const AtomicString& date() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("date")); + return name; +} + +const AtomicString& datetime() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("datetime")); + return name; +} + +const AtomicString& datetimelocal() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("datetime-local")); + return name; +} + +const AtomicString& email() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("email")); + return name; +} + +const AtomicString& file() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("file")); + return name; +} + +const AtomicString& hidden() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("hidden")); + return name; +} + +const AtomicString& image() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("image")); + return name; +} + +const AtomicString& isindex() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("khtml_isindex")); + return name; +} + +const AtomicString& month() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("month")); + return name; +} + +const AtomicString& number() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("number")); + return name; +} + +const AtomicString& password() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("password")); + return name; +} + +const AtomicString& radio() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("radio")); + return name; +} + +const AtomicString& range() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("range")); + return name; +} + +const AtomicString& reset() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("reset")); + return name; +} + +const AtomicString& search() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("search")); + return name; +} + +const AtomicString& submit() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("submit")); + return name; +} + +const AtomicString& telephone() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("tel")); + return name; +} + +const AtomicString& text() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("text")); + return name; +} + +const AtomicString& time() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("time")); + return name; +} + +const AtomicString& url() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("url")); + return name; +} + +const AtomicString& week() +{ + DEFINE_STATIC_LOCAL(AtomicString, name, ("week")); + return name; +} + +} // namespace WebCore::InputTypeNames + +} // namespace WebCore diff --git a/Source/WebCore/html/InputType.h b/Source/WebCore/html/InputType.h new file mode 100644 index 000000000..c13fa0833 --- /dev/null +++ b/Source/WebCore/html/InputType.h @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef InputType_h +#define InputType_h + +#include <wtf/Forward.h> +#include <wtf/FastAllocBase.h> +#include <wtf/Noncopyable.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class BeforeTextInsertedEvent; +class Chrome; +class Color; +class DateComponents; +class Event; +class FileList; +class FormDataList; +class HTMLElement; +class HTMLFormElement; +class HTMLInputElement; +class Icon; +class KeyboardEvent; +class MouseEvent; +class Node; +class RenderArena; +class RenderObject; +class RenderStyle; +class WheelEvent; + +typedef int ExceptionCode; + +struct ClickHandlingState { + WTF_MAKE_FAST_ALLOCATED; +public: + bool checked; + bool indeterminate; + RefPtr<HTMLInputElement> checkedRadioButton; +}; + +// An InputType object represents the type-specific part of an HTMLInputElement. +// Do not expose instances of InputType and classes derived from it to classes +// other than HTMLInputElement. +class InputType { + WTF_MAKE_NONCOPYABLE(InputType); WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<InputType> create(HTMLInputElement*, const String&); + static PassOwnPtr<InputType> createText(HTMLInputElement*); + virtual ~InputType(); + + virtual const AtomicString& formControlType() const = 0; + virtual bool canChangeFromAnotherType() const; + + // Type query functions + + // Any time we are using one of these functions it's best to refactor + // to add a virtual function to allow the input type object to do the + // work instead, or at least make a query function that asks a higher + // level question. These functions make the HTMLInputElement class + // inflexible because it's harder to add new input types if there is + // scattered code with special cases for various types. + +#if ENABLE(INPUT_COLOR) + virtual bool isColorControl() const; +#endif + virtual bool isCheckbox() const; + virtual bool isEmailField() const; + virtual bool isFileUpload() const; + virtual bool isHiddenType() const; + virtual bool isImageButton() const; + virtual bool isNumberField() const; + virtual bool isPasswordField() const; + virtual bool isRadioButton() const; + virtual bool isRangeControl() const; + virtual bool isSearchField() const; + virtual bool isSubmitButton() const; + virtual bool isTelephoneField() const; + virtual bool isTextButton() const; + virtual bool isTextField() const; + virtual bool isTextType() const; + virtual bool isURLField() const; + + // Form value functions + + virtual bool saveFormControlState(String&) const; + virtual void restoreFormControlState(const String&) const; + virtual bool isFormDataAppendable() const; + virtual bool appendFormData(FormDataList&, bool multipart) const; + + // DOM property functions + + virtual bool getTypeSpecificValue(String&); // Checked first, before internal storage or the value attribute. + virtual String fallbackValue() const; // Checked last, if both internal storage and value attribute are missing. + 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, bool sendChangeEvent, ExceptionCode&) const; + + // Validation functions + + virtual bool supportsValidation() const; + virtual bool typeMismatchFor(const String&) const; + // Type check for the current input value. We do nothing for some types + // though typeMismatchFor() does something for them because of value + // sanitization. + virtual bool typeMismatch() const; + virtual bool supportsRequired() const; + virtual bool valueMissing(const String&) const; + virtual bool patternMismatch(const String&) const; + virtual bool rangeUnderflow(const String&) const; + virtual bool rangeOverflow(const String&) const; + virtual bool supportsRangeLimitation() const; + virtual double defaultValueForStepUp() const; + virtual double minimum() const; + virtual double maximum() const; + virtual bool sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const; + virtual bool stepMismatch(const String&, double step) const; + virtual double stepBase() const; + virtual double stepBaseWithDecimalPlaces(unsigned*) const; + virtual double defaultStep() const; + virtual double stepScaleFactor() const; + virtual bool parsedStepValueShouldBeInteger() const; + virtual bool scaledStepValueShouldBeInteger() const; + virtual double acceptableError(double) const; + virtual String typeMismatchText() const; + virtual String valueMissingText() const; + virtual bool canSetStringValue() const; + virtual String visibleValue() const; + virtual String convertFromVisibleValue(const String&) const; + virtual bool isAcceptableValue(const String&); + // Returing the null string means "use the default value." + // This function must be called only by HTMLInputElement::sanitizeValue(). + virtual String sanitizeValue(const String&) const; + virtual bool hasUnacceptableValue(); + + // Event handlers + + virtual void handleClickEvent(MouseEvent*); + virtual void handleMouseDownEvent(MouseEvent*); + virtual PassOwnPtr<ClickHandlingState> willDispatchClick(); + virtual void didDispatchClick(Event*, const ClickHandlingState&); + virtual void handleDOMActivateEvent(Event*); + virtual void handleKeydownEvent(KeyboardEvent*); + virtual void handleKeypressEvent(KeyboardEvent*); + virtual void handleKeyupEvent(KeyboardEvent*); + virtual void handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*); + virtual void handleWheelEvent(WheelEvent*); + virtual void forwardEvent(Event*); + // Helpers for event handlers. + virtual bool shouldSubmitImplicitly(Event*); + virtual PassRefPtr<HTMLFormElement> formForSubmission() const; + virtual bool isKeyboardFocusable() const; + virtual bool shouldUseInputMethod() const; + virtual void handleBlurEvent(); + virtual void accessKeyAction(bool sendMouseEvents); + virtual bool canBeSuccessfulSubmitButton(); + + + // Shadow tree handling + + virtual void createShadowSubtree(); + virtual void destroyShadowSubtree(); + + virtual HTMLElement* containerElement() const { return 0; } + virtual HTMLElement* innerBlockElement() const { return 0; } + virtual HTMLElement* innerTextElement() const { return 0; } + virtual HTMLElement* innerSpinButtonElement() const { return 0; } + virtual HTMLElement* resultsButtonElement() const { return 0; } + virtual HTMLElement* cancelButtonElement() const { return 0; } +#if ENABLE(INPUT_SPEECH) + virtual HTMLElement* speechButtonElement() const { return 0; } +#endif + virtual HTMLElement* placeholderElement() const; + + // Miscellaneous functions + + virtual bool rendererIsNeeded(); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const; + virtual void attach(); + virtual void detach(); + virtual void minOrMaxAttributeChanged(); + virtual void stepAttributeChanged(); + virtual void altAttributeChanged(); + virtual void srcAttributeChanged(); + virtual void willMoveToNewOwnerDocument(); + virtual bool shouldRespectAlignAttribute(); + virtual FileList* files(); + virtual void receiveDroppedFiles(const Vector<String>&); + virtual Icon* icon() const; + // Should return true if the corresponding renderer for a type can display a suggested value. + virtual bool canSetSuggestedValue(); + virtual bool shouldSendChangeEventAfterCheckedChanged(); + virtual bool canSetValue(const String&); + virtual bool storesValueSeparateFromAttribute(); + virtual void setValue(const String&, bool valueChanged, bool sendChangeEvent); + virtual void dispatchChangeEventInResponseToSetValue(); + virtual bool shouldResetOnDocumentActivation(); + virtual bool shouldRespectListAttribute(); + virtual bool shouldRespectSpeechAttribute(); + virtual bool isEnumeratable(); + virtual bool isCheckable(); + virtual bool isSteppable() const; + virtual bool shouldRespectHeightAndWidthAttributes(); + virtual bool supportsPlaceholder() const; + virtual void updatePlaceholderText(); + virtual void multipleAttributeChanged(); + virtual void disabledAttributeChanged(); + virtual void readonlyAttributeChanged(); + virtual String defaultToolTip() const; + + // Parses the specified string for the type, and return + // the double 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; + + // Parses the specified string for this InputType, and returns true if it + // is successfully parsed. An instance pointed by the DateComponents* + // parameter will have parsed values and be modified even if the parsing + // 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 + // 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; + +protected: + InputType(HTMLInputElement* element) : m_element(element) { } + HTMLInputElement* element() const { return m_element; } + void dispatchSimulatedClickIfActive(KeyboardEvent*) const; + // We can't make this a static const data member because VC++ doesn't like it. + static double defaultStepBase() { return 0.0; } + Chrome* chrome() const; + +private: + // Raw pointer because the HTMLInputElement object owns this InputType object. + HTMLInputElement* m_element; +}; + +namespace InputTypeNames { + +const AtomicString& button(); +const AtomicString& checkbox(); +#if ENABLE(INPUT_COLOR) +const AtomicString& color(); +#endif +const AtomicString& date(); +const AtomicString& datetime(); +const AtomicString& datetimelocal(); +const AtomicString& email(); +const AtomicString& file(); +const AtomicString& hidden(); +const AtomicString& image(); +const AtomicString& isindex(); +const AtomicString& month(); +const AtomicString& number(); +const AtomicString& password(); +const AtomicString& radio(); +const AtomicString& range(); +const AtomicString& reset(); +const AtomicString& search(); +const AtomicString& submit(); +const AtomicString& telephone(); +const AtomicString& text(); +const AtomicString& time(); +const AtomicString& url(); +const AtomicString& week(); + +} // namespace WebCore::InputTypeNames + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/IsIndexInputType.cpp b/Source/WebCore/html/IsIndexInputType.cpp new file mode 100644 index 000000000..a275f28eb --- /dev/null +++ b/Source/WebCore/html/IsIndexInputType.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "IsIndexInputType.h" + +#include "Document.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> IsIndexInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new IsIndexInputType(element)); +} + +const AtomicString& IsIndexInputType::formControlType() const +{ + return emptyAtom; +} + +bool IsIndexInputType::supportsRequired() const +{ + return false; +} + +PassRefPtr<HTMLFormElement> IsIndexInputType::formForSubmission() const +{ + RefPtr<HTMLFormElement> form = InputType::formForSubmission(); + if (form) + return form.release(); + // If there is no form, then create a temporary form just to be used for submission. + Document* document = element()->document(); + form = HTMLFormElement::create(document); + form->registerFormElement(element()); + form->setMethod("GET"); + if (!document->baseURL().isEmpty()) { + // We treat the href property of the <base> element as the form action, as per section 7.5 + // "Queries and Indexes" of the HTML 2.0 spec. <http://www.w3.org/MarkUp/html-spec/html-spec_7.html#SEC7.5>. + form->setAction(document->baseURL().string()); + } + return form.release(); +} + +bool IsIndexInputType::shouldRespectListAttribute() +{ + return false; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/IsIndexInputType.h b/Source/WebCore/html/IsIndexInputType.h new file mode 100644 index 000000000..2cf702f85 --- /dev/null +++ b/Source/WebCore/html/IsIndexInputType.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef IsIndexInputType_h +#define IsIndexInputType_h + +#include "TextFieldInputType.h" +#include <wtf/text/AtomicString.h> + +namespace WebCore { + +class IsIndexInputType : public TextFieldInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + IsIndexInputType(HTMLInputElement* element) : TextFieldInputType(element) { }; + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool supportsRequired() const OVERRIDE; + virtual PassRefPtr<HTMLFormElement> formForSubmission() const OVERRIDE; + virtual bool shouldRespectListAttribute() OVERRIDE; +}; + +} // namespace WebCore + +#endif // IsIndexInputType_h diff --git a/Source/WebCore/html/LabelsNodeList.cpp b/Source/WebCore/html/LabelsNodeList.cpp new file mode 100644 index 000000000..5354c2d91 --- /dev/null +++ b/Source/WebCore/html/LabelsNodeList.cpp @@ -0,0 +1,50 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 Nokia 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 "LabelsNodeList.h" + +#include "Element.h" +#include "HTMLLabelElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +LabelsNodeList::LabelsNodeList(Node* forNode ) + : DynamicSubtreeNodeList(forNode->document()) , m_forNode(forNode) +{ +} + +LabelsNodeList::~LabelsNodeList() +{ + m_forNode->removeCachedLabelsNodeList(this); +} + +bool LabelsNodeList::nodeMatches(Element* testNode) const +{ + return testNode->hasTagName(labelTag) && static_cast<HTMLLabelElement*>(testNode)->control() == m_forNode; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/LabelsNodeList.h b/Source/WebCore/html/LabelsNodeList.h new file mode 100644 index 000000000..b89b2085c --- /dev/null +++ b/Source/WebCore/html/LabelsNodeList.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2010 Nokia 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 LabelsNodeList_h +#define LabelsNodeList_h + +#include "DynamicNodeList.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class LabelsNodeList : public DynamicSubtreeNodeList { +public: + static PassRefPtr<LabelsNodeList> create(Node* forNode) + { + return adoptRef(new LabelsNodeList(forNode)); + } + ~LabelsNodeList(); + +protected: + LabelsNodeList(Node* forNode); + + virtual bool nodeMatches(Element*) const; + +private: + RefPtr<Node> m_forNode; +}; + +} // namespace WebCore + +#endif // LabelsNodeList_h diff --git a/Source/WebCore/html/LinkRelAttribute.cpp b/Source/WebCore/html/LinkRelAttribute.cpp new file mode 100644 index 000000000..5261df2d0 --- /dev/null +++ b/Source/WebCore/html/LinkRelAttribute.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "LinkRelAttribute.h" + +namespace WebCore { + +LinkRelAttribute::LinkRelAttribute() + : m_isStyleSheet(false) + , m_iconType(InvalidIcon) + , m_isAlternate(false) + , m_isDNSPrefetch(false) +#if ENABLE(LINK_PREFETCH) + , m_isLinkPrefetch(false) + , m_isLinkPrerender(false) + , m_isLinkSubresource(false) +#endif +{ +} + +LinkRelAttribute::LinkRelAttribute(const String& rel) + : m_isStyleSheet(false) + , m_iconType(InvalidIcon) + , m_isAlternate(false) + , m_isDNSPrefetch(false) +#if ENABLE(LINK_PREFETCH) + , m_isLinkPrefetch(false) + , m_isLinkPrerender(false) + , m_isLinkSubresource(false) +#endif +{ + if (equalIgnoringCase(rel, "stylesheet")) + m_isStyleSheet = true; + else if (equalIgnoringCase(rel, "icon") || equalIgnoringCase(rel, "shortcut icon")) + m_iconType = Favicon; +#if ENABLE(TOUCH_ICON_LOADING) + else if (equalIgnoringCase(rel, "apple-touch-icon")) + m_iconType = TouchIcon; + else if (equalIgnoringCase(rel, "apple-touch-icon-precomposed")) + m_iconType = TouchPrecomposedIcon; +#endif + else if (equalIgnoringCase(rel, "dns-prefetch")) + m_isDNSPrefetch = true; + else if (equalIgnoringCase(rel, "alternate stylesheet") || equalIgnoringCase(rel, "stylesheet alternate")) { + m_isStyleSheet = true; + m_isAlternate = true; + } else { + // Tokenize the rel attribute and set bits based on specific keywords that we find. + String relCopy = rel; + relCopy.replace('\n', ' '); + Vector<String> list; + relCopy.split(' ', list); + Vector<String>::const_iterator end = list.end(); + for (Vector<String>::const_iterator it = list.begin(); it != end; ++it) { + if (equalIgnoringCase(*it, "stylesheet")) + m_isStyleSheet = true; + else if (equalIgnoringCase(*it, "alternate")) + m_isAlternate = true; + else if (equalIgnoringCase(*it, "icon")) + m_iconType = Favicon; +#if ENABLE(TOUCH_ICON_LOADING) + else if (equalIgnoringCase(*it, "apple-touch-icon")) + m_iconType = TouchIcon; + else if (equalIgnoringCase(*it, "apple-touch-icon-precomposed")) + m_iconType = TouchPrecomposedIcon; +#endif +#if ENABLE(LINK_PREFETCH) + else if (equalIgnoringCase(*it, "prefetch")) + m_isLinkPrefetch = true; + else if (equalIgnoringCase(*it, "prerender")) + m_isLinkPrerender = true; + else if (equalIgnoringCase(*it, "subresource")) + m_isLinkSubresource = true; +#endif + } + } +} + +} diff --git a/Source/WebCore/html/LinkRelAttribute.h b/Source/WebCore/html/LinkRelAttribute.h new file mode 100644 index 000000000..3178194b4 --- /dev/null +++ b/Source/WebCore/html/LinkRelAttribute.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef LinkRelAttribute_h +#define LinkRelAttribute_h + +#include "IconURL.h" + +namespace WebCore { + +struct LinkRelAttribute { +public: + bool m_isStyleSheet; + IconType m_iconType; + bool m_isAlternate; + bool m_isDNSPrefetch; +#if ENABLE(LINK_PREFETCH) + bool m_isLinkPrefetch; + bool m_isLinkPrerender; + bool m_isLinkSubresource; +#endif + + LinkRelAttribute(); + explicit LinkRelAttribute(const String&); +}; + +} + +#endif + diff --git a/Source/WebCore/html/LoadableTextTrack.cpp b/Source/WebCore/html/LoadableTextTrack.cpp new file mode 100644 index 000000000..b48d8c7cb --- /dev/null +++ b/Source/WebCore/html/LoadableTextTrack.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "LoadableTextTrack.h" + +#include "Event.h" +#include "HTMLTrackElement.h" +#include "ScriptEventListener.h" +#include "ScriptExecutionContext.h" +#include "TextTrackCueList.h" + +namespace WebCore { + +LoadableTextTrack::LoadableTextTrack(HTMLTrackElement* track, const String& kind, const String& label, const String& language, bool isDefault) + : TextTrack(track->document(), track, kind, label, language, TrackElement) + , m_trackElement(track) + , m_loadTimer(this, &LoadableTextTrack::loadTimerFired) + , m_isDefault(isDefault) +{ +} + +LoadableTextTrack::~LoadableTextTrack() +{ +} + +void LoadableTextTrack::clearClient() +{ + m_trackElement = 0; + TextTrack::clearClient(); +} + +void LoadableTextTrack::scheduleLoad(const KURL& url) +{ + if (url == m_url) + return; + + // 4.8.10.12.3 Sourcing out-of-band text tracks (continued) + + // 2. Let URL be the track URL of the track element. + m_url = url; + + // 3. Asynchronously run the remaining steps, while continuing with whatever task + // was responsible for creating the text track or changing the text track mode. + if (!m_loadTimer.isActive()) + m_loadTimer.startOneShot(0); +} + +void LoadableTextTrack::loadTimerFired(Timer<LoadableTextTrack>*) +{ + if (m_loader) + m_loader->cancelLoad(); + + if (!m_trackElement) + return; + + // 4.8.10.12.3 Sourcing out-of-band text tracks (continued) + + // 4. Download: If URL is not the empty string, perform a potentially CORS-enabled fetch of URL, with the + // mode being the state of the media element's crossorigin content attribute, the origin being the + // origin of the media element's Document, and the default origin behaviour set to fail. + m_loader = TextTrackLoader::create(this, static_cast<ScriptExecutionContext*>(m_trackElement->document())); + if (!m_loader->load(m_url, m_trackElement->mediaElementCrossOriginAttribute())) + m_trackElement->didCompleteLoad(this, HTMLTrackElement::Failure); +} + +void LoadableTextTrack::newCuesAvailable(TextTrackLoader* loader) +{ + ASSERT_UNUSED(loader, m_loader == loader); + + Vector<RefPtr<TextTrackCue> > newCues; + m_loader->getNewCues(newCues); + + if (!m_cues) + m_cues = TextTrackCueList::create(); + + for (size_t i = 0; i < newCues.size(); ++i) { + newCues[i]->setTrack(this); + m_cues->add(newCues[i]); + } + + if (client()) + client()->textTrackAddCues(this, m_cues.get()); +} + +void LoadableTextTrack::cueLoadingStarted(TextTrackLoader* loader) +{ + ASSERT_UNUSED(loader, m_loader == loader); +} + +void LoadableTextTrack::cueLoadingCompleted(TextTrackLoader* loader, bool loadingFailed) +{ + ASSERT_UNUSED(loader, m_loader == loader); + + if (!m_trackElement) + return; + + m_trackElement->didCompleteLoad(this, loadingFailed ? HTMLTrackElement::Failure : HTMLTrackElement::Success); +} + +void LoadableTextTrack::fireCueChangeEvent() +{ + TextTrack::fireCueChangeEvent(); + ExceptionCode ec = 0; + m_trackElement->dispatchEvent(Event::create(eventNames().cuechangeEvent, false, false), ec); +} + +size_t LoadableTextTrack::trackElementIndex() +{ + ASSERT(m_trackElement); + ASSERT(m_trackElement->parentNode()); + + size_t index = 0; + for (Node* node = m_trackElement->parentNode()->firstChild(); node; node = node->nextSibling()) { + if (!node->hasTagName(trackTag)) + continue; + if (node == m_trackElement) + return index; + ++index; + } + ASSERT_NOT_REACHED(); + + return 0; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/LoadableTextTrack.h b/Source/WebCore/html/LoadableTextTrack.h new file mode 100644 index 000000000..b70122536 --- /dev/null +++ b/Source/WebCore/html/LoadableTextTrack.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LoadableTextTrack_h +#define LoadableTextTrack_h + +#if ENABLE(VIDEO_TRACK) + +#include "TextTrack.h" +#include "TextTrackLoader.h" +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class HTMLTrackElement; +class LoadableTextTrack; + +class LoadableTextTrackClient : public TextTrackClient { +public: + virtual ~LoadableTextTrackClient() { } + + virtual bool canLoadUrl(LoadableTextTrack*, const KURL&) { return false; } + virtual void loadingCompleted(LoadableTextTrack*, bool /* loadingFailed */) { } +}; + +class LoadableTextTrack : public TextTrack, private TextTrackLoaderClient { +public: + static PassRefPtr<LoadableTextTrack> create(HTMLTrackElement* track, const String& kind, const String& label, const String& language, bool isDefault) + { + return adoptRef(new LoadableTextTrack(track, kind, label, language, isDefault)); + } + virtual ~LoadableTextTrack(); + + void scheduleLoad(const KURL&); + + virtual void clearClient(); + + size_t trackElementIndex(); + HTMLTrackElement* trackElement() { return m_trackElement; } + +private: + // TextTrackLoaderClient + virtual bool shouldLoadCues(TextTrackLoader*) { return true; } + virtual void newCuesAvailable(TextTrackLoader*); + virtual void cueLoadingStarted(TextTrackLoader*); + virtual void cueLoadingCompleted(TextTrackLoader*, bool loadingFailed); + + LoadableTextTrack(HTMLTrackElement*, const String& kind, const String& label, const String& language, bool isDefault); + + void loadTimerFired(Timer<LoadableTextTrack>*); + + virtual void fireCueChangeEvent(); + + HTMLTrackElement* m_trackElement; + Timer<LoadableTextTrack> m_loadTimer; + OwnPtr<TextTrackLoader> m_loader; + KURL m_url; + bool m_isDefault; +}; +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/MediaController.cpp b/Source/WebCore/html/MediaController.cpp new file mode 100644 index 000000000..806af55b9 --- /dev/null +++ b/Source/WebCore/html/MediaController.cpp @@ -0,0 +1,595 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "MediaController.h" + +#include "Clock.h" +#include "ExceptionCode.h" +#include "HTMLMediaElement.h" +#include "TimeRanges.h" +#include <wtf/StdLibExtras.h> +#include <wtf/text/AtomicString.h> + +using namespace WebCore; +using namespace std; + +PassRefPtr<MediaController> MediaController::create(ScriptExecutionContext* context) +{ + return adoptRef(new MediaController(context)); +} + +MediaController::MediaController(ScriptExecutionContext* context) + : m_paused(false) + , m_defaultPlaybackRate(1) + , m_volume(1) + , m_muted(false) + , m_readyState(HAVE_NOTHING) + , m_playbackState(WAITING) + , m_asyncEventTimer(this, &MediaController::asyncEventTimerFired) + , m_closedCaptionsVisible(false) + , m_clock(Clock::create()) + , m_scriptExecutionContext(context) +{ +} + +MediaController::~MediaController() +{ +} + +void MediaController::addMediaElement(HTMLMediaElement* element) +{ + ASSERT(element); + ASSERT(!m_mediaElements.contains(element)); + + m_mediaElements.append(element); + bringElementUpToSpeed(element); +} + +void MediaController::removeMediaElement(HTMLMediaElement* element) +{ + ASSERT(element); + ASSERT(m_mediaElements.contains(element)); + m_mediaElements.remove(m_mediaElements.find(element)); +} + +bool MediaController::containsMediaElement(HTMLMediaElement* element) const +{ + return m_mediaElements.contains(element); +} + +PassRefPtr<TimeRanges> MediaController::buffered() const +{ + if (m_mediaElements.isEmpty()) + return TimeRanges::create(); + + // The buffered attribute must return a new static normalized TimeRanges object that represents + // the intersection of the ranges of the media resources of the slaved media elements that the + // user agent has buffered, at the time the attribute is evaluated. + RefPtr<TimeRanges> bufferedRanges = m_mediaElements.first()->buffered(); + for (size_t index = 1; index < m_mediaElements.size(); ++index) + bufferedRanges->intersectWith(m_mediaElements[index]->buffered().get()); + return bufferedRanges; +} + +PassRefPtr<TimeRanges> MediaController::seekable() const +{ + if (m_mediaElements.isEmpty()) + return TimeRanges::create(); + + // The seekable attribute must return a new static normalized TimeRanges object that represents + // the intersection of the ranges of the media resources of the slaved media elements that the + // user agent is able to seek to, at the time the attribute is evaluated. + RefPtr<TimeRanges> seekableRanges = m_mediaElements.first()->seekable(); + for (size_t index = 1; index < m_mediaElements.size(); ++index) + seekableRanges->intersectWith(m_mediaElements[index]->seekable().get()); + return seekableRanges; +} + +PassRefPtr<TimeRanges> MediaController::played() +{ + if (m_mediaElements.isEmpty()) + return TimeRanges::create(); + + // The played attribute must return a new static normalized TimeRanges object that represents + // the union of the ranges of the media resources of the slaved media elements that the + // user agent has so far rendered, at the time the attribute is evaluated. + RefPtr<TimeRanges> playedRanges = m_mediaElements.first()->played(); + for (size_t index = 1; index < m_mediaElements.size(); ++index) + playedRanges->unionWith(m_mediaElements[index]->played().get()); + return playedRanges; +} + +float MediaController::duration() const +{ + // FIXME: Investigate caching the maximum duration and only updating the cached value + // when the slaved media elements' durations change. + float maxDuration = 0; + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + float duration = m_mediaElements[index]->duration(); + if (isnan(duration)) + continue; + maxDuration = max(maxDuration, duration); + } + return maxDuration; +} + +float MediaController::currentTime() const +{ + if (m_mediaElements.isEmpty()) + return 0; + + return m_clock->currentTime(); +} + +void MediaController::setCurrentTime(float time, ExceptionCode& code) +{ + // When the user agent is to seek the media controller to a particular new playback position, + // it must follow these steps: + // If the new playback position is less than zero, then set it to zero. + time = max(0.0f, time); + + // If the new playback position is greater than the media controller duration, then set it + // to the media controller duration. + time = min(time, duration()); + + // Set the media controller position to the new playback position. + m_clock->setCurrentTime(time); + + // Seek each slaved media element to the new playback position relative to the media element timeline. + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->seek(time, code); +} + +void MediaController::play() +{ + // When the play() method is invoked, if the MediaController is a paused media controller, + if (!m_paused) + return; + + // the user agent must change the MediaController into a playing media controller, + m_paused = false; + // queue a task to fire a simple event named play at the MediaController, + scheduleEvent(eventNames().playEvent); + // and then report the controller state of the MediaController. + reportControllerState(); +} + +void MediaController::pause() +{ + // When the pause() method is invoked, if the MediaController is a playing media controller, + if (m_paused) + return; + + // then the user agent must change the MediaController into a paused media controller, + m_paused = true; + // queue a task to fire a simple event named pause at the MediaController, + scheduleEvent(eventNames().pauseEvent); + // and then report the controller state of the MediaController. + reportControllerState(); +} + +void MediaController::setDefaultPlaybackRate(float rate) +{ + if (m_defaultPlaybackRate == rate) + return; + + // The defaultPlaybackRate attribute, on setting, must set the MediaController's media controller + // default playback rate to the new value, + m_defaultPlaybackRate = rate; + + // then queue a task to fire a simple event named ratechange at the MediaController. + scheduleEvent(eventNames().ratechangeEvent); +} + +float MediaController::playbackRate() const +{ + return m_clock->playRate(); +} + +void MediaController::setPlaybackRate(float rate) +{ + if (m_clock->playRate() == rate) + return; + + // The playbackRate attribute, on setting, must set the MediaController's media controller + // playback rate to the new value, + m_clock->setPlayRate(rate); + + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->updatePlaybackRate(); + + // then queue a task to fire a simple event named ratechange at the MediaController. + scheduleEvent(eventNames().ratechangeEvent); +} + +void MediaController::setVolume(float level, ExceptionCode& code) +{ + if (m_volume == level) + return; + + // If the new value is outside the range 0.0 to 1.0 inclusive, then, on setting, an + // IndexSizeError exception must be raised instead. + if (level < 0 || level > 1) { + code = INDEX_SIZE_ERR; + return; + } + + // The volume attribute, on setting, if the new value is in the range 0.0 to 1.0 inclusive, + // must set the MediaController's media controller volume multiplier to the new value + m_volume = level; + + // and queue a task to fire a simple event named volumechange at the MediaController. + scheduleEvent(eventNames().volumechangeEvent); + + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->updateVolume(); +} + +void MediaController::setMuted(bool flag) +{ + if (m_muted == flag) + return; + + // The muted attribute, on setting, must set the MediaController's media controller mute override + // to the new value + m_muted = flag; + + // and queue a task to fire a simple event named volumechange at the MediaController. + scheduleEvent(eventNames().volumechangeEvent); + + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->updateVolume(); +} + +void MediaController::reportControllerState() +{ + updateReadyState(); + updatePlaybackState(); +} + +static AtomicString eventNameForReadyState(MediaControllerInterface::ReadyState state) +{ + switch (state) { + case MediaControllerInterface::HAVE_NOTHING: + return eventNames().emptiedEvent; + case MediaControllerInterface::HAVE_METADATA: + return eventNames().loadedmetadataEvent; + case MediaControllerInterface::HAVE_CURRENT_DATA: + return eventNames().loadeddataEvent; + case MediaControllerInterface::HAVE_FUTURE_DATA: + return eventNames().canplayEvent; + case MediaControllerInterface::HAVE_ENOUGH_DATA: + return eventNames().canplaythroughEvent; + default: + ASSERT_NOT_REACHED(); + return nullAtom; + } +} + +void MediaController::updateReadyState() +{ + ReadyState oldReadyState = m_readyState; + ReadyState newReadyState; + + if (m_mediaElements.isEmpty()) { + // If the MediaController has no slaved media elements, let new readiness state be 0. + newReadyState = HAVE_NOTHING; + } else { + // Otherwise, let it have the lowest value of the readyState IDL attributes of all of its + // slaved media elements. + newReadyState = m_mediaElements.first()->readyState(); + for (size_t index = 1; index < m_mediaElements.size(); ++index) + newReadyState = min(newReadyState, m_mediaElements[index]->readyState()); + } + + if (newReadyState == oldReadyState) + return; + + // If the MediaController's most recently reported readiness state is greater than new readiness + // state then queue a task to fire a simple event at the MediaController object, whose name is the + // event name corresponding to the value of new readiness state given in the table below. [omitted] + if (oldReadyState > newReadyState) { + scheduleEvent(eventNameForReadyState(newReadyState)); + return; + } + + // If the MediaController's most recently reported readiness state is less than the new readiness + // state, then run these substeps: + // 1. Let next state be the MediaController's most recently reported readiness state. + ReadyState nextState = oldReadyState; + do { + // 2. Loop: Increment next state by one. + nextState = static_cast<ReadyState>(nextState + 1); + // 3. Queue a task to fire a simple event at the MediaController object, whose name is the + // event name corresponding to the value of next state given in the table below. [omitted] + scheduleEvent(eventNameForReadyState(nextState)); + // If next state is less than new readiness state, then return to the step labeled loop + } while (nextState < newReadyState); + + // Let the MediaController's most recently reported readiness state be new readiness state. + m_readyState = newReadyState; +} + +void MediaController::updatePlaybackState() +{ + PlaybackState oldPlaybackState = m_playbackState; + PlaybackState newPlaybackState; + + // Initialize new playback state by setting it to the state given for the first matching + // condition from the following list: + if (m_mediaElements.isEmpty()) { + // If the MediaController has no slaved media elements + // Let new playback state be waiting. + newPlaybackState = WAITING; + } else if (hasEnded()) { + // If all of the MediaController's slaved media elements have ended playback and the media + // controller playback rate is positive or zero + // Let new playback state be ended. + newPlaybackState = ENDED; + } else if (isBlocked()) { + // If the MediaController is a blocked media controller + // Let new playback state be waiting. + newPlaybackState = WAITING; + } else { + // Otherwise + // Let new playback state be playing. + newPlaybackState = PLAYING; + } + + // If the MediaController's most recently reported playback state is not equal to new playback state + if (newPlaybackState == oldPlaybackState) + return; + + // and the new playback state is ended, + if (newPlaybackState == ENDED) { + // then queue a task that, if the MediaController object is a playing media controller, and + // all of the MediaController's slaved media elements have still ended playback, and the + // media controller playback rate is still positive or zero, + if (!m_paused && hasEnded()) { + // changes the MediaController object to a paused media controller + m_paused = true; + + // and then fires a simple event named pause at the MediaController object. + scheduleEvent(eventNames().pauseEvent); + } + } + + // If the MediaController's most recently reported playback state is not equal to new playback state + // then queue a task to fire a simple event at the MediaController object, whose name is playing + // if new playback state is playing, ended if new playback state is ended, and waiting otherwise. + AtomicString eventName; + switch (newPlaybackState) { + case WAITING: + eventName = eventNames().waitingEvent; + m_clock->stop(); + break; + case ENDED: + eventName = eventNames().endedEvent; + m_clock->stop(); + break; + case PLAYING: + eventName = eventNames().playingEvent; + m_clock->start(); + break; + default: + ASSERT_NOT_REACHED(); + } + scheduleEvent(eventName); + + // Let the MediaController's most recently reported playback state be new playback state. + m_playbackState = newPlaybackState; + + updateMediaElements(); +} + +void MediaController::updateMediaElements() +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->updatePlayState(); +} + +void MediaController::bringElementUpToSpeed(HTMLMediaElement* element) +{ + ASSERT(element); + ASSERT(m_mediaElements.contains(element)); + + // When the user agent is to bring a media element up to speed with its new media controller, + // it must seek that media element to the MediaController's media controller position relative + // to the media element's timeline. + ExceptionCode ignoredCode = 0; + element->seek(currentTime(), ignoredCode); +} + +bool MediaController::isBlocked() const +{ + // A MediaController is a blocked media controller if the MediaController is a paused media + // controller, + if (m_paused) + return true; + + if (m_mediaElements.isEmpty()) + return false; + + bool allPaused = true; + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + HTMLMediaElement* element = m_mediaElements[index]; + // or if any of its slaved media elements are blocked media elements, + if (element->isBlocked()) + return true; + + // or if any of its slaved media elements whose autoplaying flag is true still have their + // paused attribute set to true, + if (element->isAutoplaying() && element->paused()) + return true; + + if (!element->paused()) + allPaused = false; + } + + // or if all of its slaved media elements have their paused attribute set to true. + return allPaused; +} + +bool MediaController::hasEnded() const +{ + // If the ... media controller playback rate is positive or zero + if (m_clock->playRate() < 0) + return false; + + // [and] all of the MediaController's slaved media elements have ended playback ... let new + // playback state be ended. + if (m_mediaElements.isEmpty()) + return false; + + bool allHaveEnded = true; + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (!m_mediaElements[index]->ended()) + allHaveEnded = false; + } + return allHaveEnded; +} + +void MediaController::scheduleEvent(const AtomicString& eventName) +{ + m_pendingEvents.append(Event::create(eventName, false, true)); + if (!m_asyncEventTimer.isActive()) + m_asyncEventTimer.startOneShot(0); +} + +void MediaController::asyncEventTimerFired(Timer<MediaController>*) +{ + Vector<RefPtr<Event> > pendingEvents; + ExceptionCode ec = 0; + + m_pendingEvents.swap(pendingEvents); + size_t count = pendingEvents.size(); + for (size_t index = 0; index < count; ++index) + dispatchEvent(pendingEvents[index].release(), ec); +} + +bool MediaController::hasAudio() const +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (m_mediaElements[index]->hasAudio()) + return true; + } + return false; +} + +bool MediaController::hasVideo() const +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (m_mediaElements[index]->hasVideo()) + return true; + } + return false; +} + +bool MediaController::hasClosedCaptions() const +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (m_mediaElements[index]->hasClosedCaptions()) + return true; + } + return false; +} + +void MediaController::setClosedCaptionsVisible(bool visible) +{ + m_closedCaptionsVisible = visible; + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->setClosedCaptionsVisible(visible); +} + +bool MediaController::supportsScanning() const +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (!m_mediaElements[index]->supportsScanning()) + return false; + } + return true; +} + +void MediaController::beginScrubbing() +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->beginScrubbing(); + if (m_playbackState == PLAYING) + m_clock->stop(); +} + +void MediaController::endScrubbing() +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->endScrubbing(); + if (m_playbackState == PLAYING) + m_clock->start(); +} + +bool MediaController::canPlay() const +{ + if (m_paused) + return true; + + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (!m_mediaElements[index]->canPlay()) + return false; + } + return true; +} + +bool MediaController::isLiveStream() const +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (!m_mediaElements[index]->isLiveStream()) + return false; + } + return true; +} + +bool MediaController::hasCurrentSrc() const +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) { + if (!m_mediaElements[index]->hasCurrentSrc()) + return false; + } + return true; +} + +void MediaController::returnToRealtime() +{ + for (size_t index = 0; index < m_mediaElements.size(); ++index) + m_mediaElements[index]->returnToRealtime(); +} + +const AtomicString& MediaController::interfaceName() const +{ + return eventNames().interfaceForMediaController; +} + +#endif diff --git a/Source/WebCore/html/MediaController.h b/Source/WebCore/html/MediaController.h new file mode 100644 index 000000000..686cd0938 --- /dev/null +++ b/Source/WebCore/html/MediaController.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaController_h +#define MediaController_h + +#if ENABLE(VIDEO) + +#include "ActiveDOMObject.h" +#include "Event.h" +#include "EventListener.h" +#include "EventTarget.h" +#include "MediaControllerInterface.h" +#include "Timer.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Clock; +class HTMLMediaElement; +class Event; +class ScriptExecutionContext; + +class MediaController : public RefCounted<MediaController>, public MediaControllerInterface, public EventTarget { +public: + static PassRefPtr<MediaController> create(ScriptExecutionContext*); + virtual ~MediaController(); + + void addMediaElement(HTMLMediaElement*); + void removeMediaElement(HTMLMediaElement*); + bool containsMediaElement(HTMLMediaElement*) const; + + const String& mediaGroup() const { return m_mediaGroup; } + + virtual PassRefPtr<TimeRanges> buffered() const; + virtual PassRefPtr<TimeRanges> seekable() const; + virtual PassRefPtr<TimeRanges> played(); + + virtual float duration() const; + virtual float currentTime() const; + virtual void setCurrentTime(float, ExceptionCode&); + + virtual bool paused() const { return m_paused; } + virtual void play(); + virtual void pause(); + + virtual float defaultPlaybackRate() const { return m_defaultPlaybackRate; } + virtual void setDefaultPlaybackRate(float); + + virtual float playbackRate() const; + virtual void setPlaybackRate(float); + + virtual float volume() const { return m_volume; } + virtual void setVolume(float, ExceptionCode&); + + virtual bool muted() const { return m_muted; } + virtual void setMuted(bool); + + virtual ReadyState readyState() const { return m_readyState; } + + enum PlaybackState { WAITING, PLAYING, ENDED }; + virtual PlaybackState playbackState() const { return m_playbackState; } + + virtual bool supportsFullscreen() const { return false; } + virtual bool isFullscreen() const { return false; } + virtual void enterFullscreen() { } + + virtual bool hasAudio() const; + virtual bool hasVideo() const; + virtual bool hasClosedCaptions() const; + virtual void setClosedCaptionsVisible(bool); + virtual bool closedCaptionsVisible() const { return m_closedCaptionsVisible; } + + virtual bool supportsScanning() const; + + virtual void beginScrubbing(); + virtual void endScrubbing(); + + virtual bool canPlay() const; + + virtual bool isLiveStream() const; + + virtual bool hasCurrentSrc() const; + + virtual void returnToRealtime(); + + bool isBlocked() const; + + // EventTarget + using RefCounted<MediaController>::ref; + using RefCounted<MediaController>::deref; + +private: + MediaController(ScriptExecutionContext*); + void reportControllerState(); + void updateReadyState(); + void updatePlaybackState(); + void updateMediaElements(); + void bringElementUpToSpeed(HTMLMediaElement*); + void scheduleEvent(const AtomicString& eventName); + void asyncEventTimerFired(Timer<MediaController>*); + bool hasEnded() const; + + // EventTarget + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual const AtomicString& interfaceName() const; + virtual ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; }; + virtual EventTargetData* eventTargetData() { return &m_eventTargetData; } + virtual EventTargetData* ensureEventTargetData() { return &m_eventTargetData; } + EventTargetData m_eventTargetData; + + friend class HTMLMediaElement; + friend class MediaControllerEventListener; + Vector<HTMLMediaElement*> m_mediaElements; + bool m_paused; + float m_defaultPlaybackRate; + float m_volume; + bool m_muted; + ReadyState m_readyState; + PlaybackState m_playbackState; + Vector<RefPtr<Event> > m_pendingEvents; + Timer<MediaController> m_asyncEventTimer; + String m_mediaGroup; + bool m_closedCaptionsVisible; + PassRefPtr<Clock> m_clock; + ScriptExecutionContext* m_scriptExecutionContext; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/MediaController.idl b/Source/WebCore/html/MediaController.idl new file mode 100644 index 000000000..f7b9fb224 --- /dev/null +++ b/Source/WebCore/html/MediaController.idl @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=VIDEO, + Constructor, + CallWith=ScriptExecutionContext, + GenerateToJS, + EventTarget + ] MediaController { + readonly attribute TimeRanges buffered; + readonly attribute TimeRanges seekable; + + readonly attribute double duration; + attribute double currentTime + setter raises (DOMException); + + readonly attribute boolean paused; + readonly attribute TimeRanges played; + void play(); + void pause(); + + attribute double defaultPlaybackRate; + attribute double playbackRate; + + attribute double volume + setter raises (DOMException); + attribute boolean muted; + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; +} diff --git a/Source/WebCore/html/MediaControllerInterface.h b/Source/WebCore/html/MediaControllerInterface.h new file mode 100644 index 000000000..7bcf06c5f --- /dev/null +++ b/Source/WebCore/html/MediaControllerInterface.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControllerInterface_h +#define MediaControllerInterface_h + +#if ENABLE(VIDEO) + +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class TimeRanges; + +typedef int ExceptionCode; + +class MediaControllerInterface { +public: + virtual ~MediaControllerInterface() { }; + + // MediaController IDL: + virtual PassRefPtr<TimeRanges> buffered() const = 0; + virtual PassRefPtr<TimeRanges> seekable() const = 0; + virtual PassRefPtr<TimeRanges> played() = 0; + + virtual float duration() const = 0; + virtual float currentTime() const = 0; + virtual void setCurrentTime(float, ExceptionCode&) = 0; + + virtual bool paused() const = 0; + virtual void play() = 0; + virtual void pause() = 0; + + virtual float defaultPlaybackRate() const = 0; + virtual void setDefaultPlaybackRate(float) = 0; + + virtual float playbackRate() const = 0; + virtual void setPlaybackRate(float) = 0; + + virtual float volume() const = 0; + virtual void setVolume(float, ExceptionCode&) = 0; + + virtual bool muted() const = 0; + virtual void setMuted(bool) = 0; + + enum ReadyState { HAVE_NOTHING, HAVE_METADATA, HAVE_CURRENT_DATA, HAVE_FUTURE_DATA, HAVE_ENOUGH_DATA }; + virtual ReadyState readyState() const = 0; + + // MediaControlElements: + virtual bool supportsFullscreen() const = 0; + virtual bool isFullscreen() const = 0; + virtual void enterFullscreen() = 0; + + virtual bool hasAudio() const = 0; + virtual bool hasVideo() const = 0; + virtual bool hasClosedCaptions() const = 0; + virtual void setClosedCaptionsVisible(bool) = 0; + virtual bool closedCaptionsVisible() const = 0; + + virtual bool supportsScanning() const = 0; + + virtual void beginScrubbing() = 0; + virtual void endScrubbing() = 0; + + virtual bool canPlay() const = 0; + + virtual bool isLiveStream() const = 0; + + virtual bool hasCurrentSrc() const = 0; + + virtual void returnToRealtime() = 0; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/html/MediaDocument.cpp b/Source/WebCore/html/MediaDocument.cpp new file mode 100644 index 000000000..cb971f58e --- /dev/null +++ b/Source/WebCore/html/MediaDocument.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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(VIDEO) +#include "MediaDocument.h" + +#include "DocumentLoader.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLEmbedElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLNames.h" +#include "HTMLVideoElement.h" +#include "KeyboardEvent.h" +#include "MainResourceLoader.h" +#include "NodeList.h" +#include "RawDataDocumentParser.h" +#include "ScriptController.h" + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: Share more code with PluginDocumentParser. +class MediaDocumentParser : public RawDataDocumentParser { +public: + static PassRefPtr<MediaDocumentParser> create(MediaDocument* document) + { + return adoptRef(new MediaDocumentParser(document)); + } + +private: + MediaDocumentParser(Document* document) + : RawDataDocumentParser(document) + , m_mediaElement(0) + { + } + + virtual void appendBytes(DocumentWriter*, const char*, size_t); + + void createDocumentStructure(); + + HTMLMediaElement* m_mediaElement; +}; + +void MediaDocumentParser::createDocumentStructure() +{ + ExceptionCode ec; + RefPtr<Element> rootElement = document()->createElement(htmlTag, false); + document()->appendChild(rootElement, ec); + document()->setCSSTarget(rootElement.get()); + static_cast<HTMLHtmlElement*>(rootElement.get())->insertedByParser(); + + if (document()->frame()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr<Element> body = document()->createElement(bodyTag, false); + rootElement->appendChild(body, ec); + + RefPtr<Element> mediaElement = document()->createElement(videoTag, false); + + m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get()); + m_mediaElement->setAttribute(controlsAttr, ""); + m_mediaElement->setAttribute(autoplayAttr, ""); + + m_mediaElement->setAttribute(nameAttr, "media"); + m_mediaElement->setSrc(document()->url()); + + body->appendChild(mediaElement, ec); + + Frame* frame = document()->frame(); + if (!frame) + return; + + frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(DoNotBufferData); +} + +void MediaDocumentParser::appendBytes(DocumentWriter*, const char*, size_t) +{ + if (m_mediaElement) + return; + + createDocumentStructure(); + finish(); +} + +MediaDocument::MediaDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) + , m_replaceMediaElementTimer(this, &MediaDocument::replaceMediaElementTimerFired) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +MediaDocument::~MediaDocument() +{ + ASSERT(!m_replaceMediaElementTimer.isActive()); +} + +PassRefPtr<DocumentParser> MediaDocument::createParser() +{ + return MediaDocumentParser::create(this); +} + +static inline HTMLVideoElement* descendentVideoElement(Node* node) +{ + ASSERT(node); + + if (node->hasTagName(videoTag)) + return static_cast<HTMLVideoElement*>(node); + + RefPtr<NodeList> nodeList = node->getElementsByTagNameNS(videoTag.namespaceURI(), videoTag.localName()); + + if (nodeList.get()->length() > 0) + return static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); + + return 0; +} + +static inline HTMLVideoElement* ancestorVideoElement(Node* node) +{ + while (node && !node->hasTagName(videoTag)) + node = node->parentOrHostNode(); + + return static_cast<HTMLVideoElement*>(node); +} + +void MediaDocument::defaultEventHandler(Event* event) +{ + // Match the default Quicktime plugin behavior to allow + // clicking and double-clicking to pause and play the media. + Node* targetNode = event->target()->toNode(); + if (!targetNode) + return; + + if (HTMLVideoElement* video = ancestorVideoElement(targetNode)) { + if (event->type() == eventNames().clickEvent) { + if (!video->canPlay()) { + video->pause(); + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().dblclickEvent) { + if (video->canPlay()) { + video->play(); + event->setDefaultHandled(); + } + } + } + + if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent()) { + HTMLVideoElement* video = descendentVideoElement(targetNode); + if (!video) + return; + + KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event); + if (keyboardEvent->keyIdentifier() == "U+0020") { // space + if (video->paused()) { + if (video->canPlay()) + video->play(); + } else + video->pause(); + event->setDefaultHandled(); + } + } +} + +void MediaDocument::mediaElementSawUnsupportedTracks() +{ + // The HTMLMediaElement was told it has something that the underlying + // MediaPlayer cannot handle so we should switch from <video> to <embed> + // and let the plugin handle this. Don't do it immediately as this + // function may be called directly from a media engine callback, and + // replaceChild will destroy the element, media player, and media engine. + m_replaceMediaElementTimer.startOneShot(0); +} + +void MediaDocument::replaceMediaElementTimerFired(Timer<MediaDocument>*) +{ + HTMLElement* htmlBody = body(); + if (!htmlBody) + return; + + // Set body margin width and height to 0 as that is what a PluginDocument uses. + htmlBody->setAttribute(marginwidthAttr, "0"); + htmlBody->setAttribute(marginheightAttr, "0"); + + if (HTMLVideoElement* videoElement = descendentVideoElement(htmlBody)) { + RefPtr<Element> element = Document::createElement(embedTag, false); + HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(element.get()); + + embedElement->setAttribute(widthAttr, "100%"); + embedElement->setAttribute(heightAttr, "100%"); + embedElement->setAttribute(nameAttr, "plugin"); + embedElement->setAttribute(srcAttr, url().string()); + + DocumentLoader* documentLoader = loader(); + ASSERT(documentLoader); + if (documentLoader) + embedElement->setAttribute(typeAttr, documentLoader->writer()->mimeType()); + + ExceptionCode ec; + videoElement->parentNode()->replaceChild(embedElement, videoElement, ec); + } +} + +} +#endif diff --git a/Source/WebCore/html/MediaDocument.h b/Source/WebCore/html/MediaDocument.h new file mode 100644 index 000000000..2d812963b --- /dev/null +++ b/Source/WebCore/html/MediaDocument.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008,2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 MediaDocument_h +#define MediaDocument_h + +#if ENABLE(VIDEO) + +#include "HTMLDocument.h" + +namespace WebCore { + +class MediaDocument : public HTMLDocument { +public: + static PassRefPtr<MediaDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new MediaDocument(frame, url)); + } + virtual ~MediaDocument(); + + void mediaElementSawUnsupportedTracks(); + +private: + MediaDocument(Frame*, const KURL&); + + virtual bool isMediaDocument() const { return true; } + virtual PassRefPtr<DocumentParser> createParser(); + + virtual void defaultEventHandler(Event*); + + void replaceMediaElementTimerFired(Timer<MediaDocument>*); + + Timer<MediaDocument> m_replaceMediaElementTimer; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/html/MediaError.h b/Source/WebCore/html/MediaError.h new file mode 100644 index 000000000..b68c37065 --- /dev/null +++ b/Source/WebCore/html/MediaError.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaError_h +#define MediaError_h + +#if ENABLE(VIDEO) + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class MediaError : public RefCounted<MediaError> { +public: + enum Code { MEDIA_ERR_ABORTED = 1, MEDIA_ERR_NETWORK, MEDIA_ERR_DECODE, MEDIA_ERR_SRC_NOT_SUPPORTED }; + + static PassRefPtr<MediaError> create(Code code) { return adoptRef(new MediaError(code)); } + + Code code() const { return m_code; } + +private: + MediaError(Code code) : m_code(code) { } + + Code m_code; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/MediaError.idl b/Source/WebCore/html/MediaError.idl new file mode 100644 index 000000000..fceb1f551 --- /dev/null +++ b/Source/WebCore/html/MediaError.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=VIDEO + ] MediaError { + const unsigned short MEDIA_ERR_ABORTED = 1; + const unsigned short MEDIA_ERR_NETWORK = 2; + const unsigned short MEDIA_ERR_DECODE = 3; + const unsigned short MEDIA_ERR_SRC_NOT_SUPPORTED = 4; + readonly attribute unsigned short code; + }; +} diff --git a/Source/WebCore/html/MediaFragmentURIParser.cpp b/Source/WebCore/html/MediaFragmentURIParser.cpp new file mode 100644 index 000000000..3ecc6f586 --- /dev/null +++ b/Source/WebCore/html/MediaFragmentURIParser.cpp @@ -0,0 +1,327 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "MediaFragmentURIParser.h" + +#include "HTMLElement.h" +#include "ProcessingInstruction.h" +#include "SegmentedString.h" +#include "Text.h" +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> + +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) +{ + StringBuilder digits; + + // http://www.ietf.org/rfc/rfc2326.txt + // DIGIT ; any positive number + while (position < length && isASCIIDigit(input[position])) + digits.append(input[position++]); + return digits.toString(); +} + +static String collectFraction(const LChar* input, unsigned length, unsigned& position) +{ + StringBuilder digits; + + // http://www.ietf.org/rfc/rfc2326.txt + // [ "." *DIGIT ] + if (input[position] != '.') + return String(); + + digits.append(input[position++]); + while (position < length && isASCIIDigit(input[position])) + digits.append(input[position++]); + return digits.toString(); +} + +double MediaFragmentURIParser::invalidTimeValue() +{ + return invalidMediaTime; +} + +MediaFragmentURIParser::MediaFragmentURIParser(const KURL& url) + : m_url(url) + , m_timeFormat(None) + , m_startTime(invalidMediaTime) + , m_endTime(invalidMediaTime) +{ +} + +double MediaFragmentURIParser::startTime() +{ + if (!m_url.isValid()) + return invalidMediaTime; + if (m_timeFormat == None) + parseTimeFragment(); + return m_startTime; +} + +double MediaFragmentURIParser::endTime() +{ + if (!m_url.isValid()) + return invalidMediaTime; + if (m_timeFormat == None) + parseTimeFragment(); + return m_endTime; +} + +void MediaFragmentURIParser::parseFragments() +{ + if (!m_url.hasFragmentIdentifier()) + return; + String fragmentString = m_url.fragmentIdentifier(); + if (fragmentString.isEmpty()) + return; + + unsigned offset = 0; + unsigned end = fragmentString.length(); + while (offset < end) { + // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#processing-name-value-components + // 1. Parse the octet string according to the namevalues syntax, yielding a list of + // name-value pairs, where name and value are both octet string. In accordance + // with RFC 3986, the name and value components must be parsed and separated before + // percent-encoded octets are decoded. + size_t parameterStart = offset; + size_t parameterEnd = fragmentString.find('&', offset); + if (parameterEnd == notFound) + parameterEnd = end; + + size_t equalOffset = fragmentString.find('=', offset); + if (equalOffset == notFound || equalOffset > parameterEnd) { + offset = parameterEnd + 1; + continue; + } + + // 2. For each name-value pair: + // a. Decode percent-encoded octets in name and value as defined by RFC 3986. If either + // name or value are not valid percent-encoded strings, then remove the name-value pair + // from the list. + const UChar* fragmentStart = fragmentString.characters(); + String name = decodeURLEscapeSequences(String(fragmentStart + parameterStart, equalOffset - parameterStart)); + String value; + if (equalOffset != parameterEnd) + value = decodeURLEscapeSequences(String(fragmentStart + equalOffset + 1, parameterEnd - equalOffset - 1)); + + // b. Convert name and value to Unicode strings by interpreting them as UTF-8. If either + // name or value are not valid UTF-8 strings, then remove the name-value pair from the list. + bool validUTF8 = true; + if (!name.isEmpty()) { + name = name.utf8(true).data(); + validUTF8 = !name.isEmpty(); + } + if (validUTF8 && !value.isEmpty()) { + value = value.utf8(true).data(); + validUTF8 = !value.isEmpty(); + } + + if (validUTF8) + m_fragments.append(make_pair(name, value)); + + offset = parameterEnd + 1; + } +} + +void MediaFragmentURIParser::parseTimeFragment() +{ + ASSERT(m_timeFormat == None); + + if (m_fragments.isEmpty()) + parseFragments(); + + m_timeFormat = Invalid; + + for (unsigned i = 0; i < m_fragments.size(); ++i) { + pair<String, String>& fragment = m_fragments[i]; + + ASSERT(fragment.first.is8Bit()); + ASSERT(fragment.second.is8Bit()); + + // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-time + // Temporal clipping is denoted by the name t, and specified as an interval with a begin + // time and an end time + if (fragment.first != "t") + continue; + + // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npt-time + // Temporal clipping can be specified either as Normal Play Time (npt) RFC 2326, as SMPTE timecodes, + // SMPTE, or as real-world clock time (clock) RFC 2326. Begin and end times are always specified + // 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; + if (parseNPTFragment(fragment.second.characters8(), fragment.second.length(), start, end)) { + m_startTime = start; + m_endTime = end; + m_timeFormat = NormalPlayTime; + + // Although we have a valid fragment, don't return yet because when a fragment dimensions + // occurs multiple times, only the last occurrence of that dimension is used: + // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#error-uri-general + // Multiple occurrences of the same dimension: only the last valid occurrence of a dimension + // (e.g., t=10 in #t=2&t=10) is interpreted, all previous occurrences (valid or invalid) + // SHOULD be ignored by the UA. + } + } + m_fragments.clear(); +} + +bool MediaFragmentURIParser::parseNPTFragment(const LChar* timeString, unsigned length, double& startTime, double& endTime) +{ + unsigned offset = 0; + if (length >= nptIdentiferLength && timeString[0] == 'n' && timeString[1] == 'p' && timeString[2] == 't' && timeString[3] == ':') + offset += nptIdentiferLength; + + if (offset == length) + return false; + + // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-time + // If a single number only is given, this corresponds to the begin time except if it is preceded + // by a comma that would in this case indicate the end time. + if (timeString[offset] == ',') + startTime = 0; + else { + if (!parseNPTTime(timeString, length, offset, startTime)) + return false; + } + + if (offset == length) + return true; + + if (timeString[offset] != ',') + return false; + if (++offset == length) + return false; + + if (!parseNPTTime(timeString, length, offset, endTime)) + return false; + + if (offset != length) + return false; + + if (startTime >= endTime) + return false; + + return true; +} + +bool MediaFragmentURIParser::parseNPTTime(const LChar* timeString, unsigned length, unsigned& offset, double& time) +{ + enum Mode { minutes, hours }; + Mode mode = minutes; + + if (offset >= length || !isASCIIDigit(timeString[offset])) + return false; + + // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npttimedef + // Normal Play Time can either be specified as seconds, with an optional + // fractional part to indicate miliseconds, or as colon-separated hours, + // minutes and seconds (again with an optional fraction). Minutes and + // seconds must be specified as exactly two digits, hours and fractional + // seconds can be any number of digits. The hours, minutes and seconds + // specification for NPT is a convenience only, it does not signal frame + // accuracy. The specification of the "npt:" identifier is optional since + // NPT is the default time scheme. This specification builds on the RTSP + // specification of NPT RFC 2326. + // + // ; defined in RFC 2326 + // npt-sec = 1*DIGIT [ "." *DIGIT ] ; definitions taken + // npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT] ; from RFC 2326 + // npt-mmss = npt-mm ":" npt-ss [ "." *DIGIT] + // npt-hh = 1*DIGIT ; any positive number + // npt-mm = 2DIGIT ; 0-59 + // npt-ss = 2DIGIT ; 0-59 + + String digits1 = collectDigits(timeString, length, offset); + int value1 = digits1.toInt(); + if (offset >= length || timeString[offset] == ',') { + time = value1; + return true; + } + + double fraction = 0; + if (timeString[offset] == '.') { + if (offset == length) + return true; + String digits = collectFraction(timeString, length, offset); + fraction = digits.toDouble(); + time = value1 + fraction; + return true; + } + + if (digits1.length() < 2) + return false; + if (digits1.length() > 2) + mode = hours; + + // Collect the next sequence of 0-9 after ':' + if (offset >= length || timeString[offset++] != ':') + return false; + if (offset >= length || !isASCIIDigit(timeString[(offset)])) + return false; + String digits2 = collectDigits(timeString, length, offset); + int value2 = digits2.toInt(); + if (digits2.length() != 2) + return false; + + // Detect whether this timestamp includes hours. + int value3; + if (mode == hours || (offset < length && timeString[offset] == ':')) { + if (offset >= length || timeString[offset++] != ':') + return false; + if (offset >= length || !isASCIIDigit(timeString[offset])) + return false; + String digits3 = collectDigits(timeString, length, offset); + if (digits3.length() != 2) + return false; + value3 = digits3.toInt(); + } else { + value3 = value2; + value2 = value1; + value1 = 0; + } + + if (offset < length && timeString[offset] == '.') + fraction = collectFraction(timeString, length, offset).toDouble(); + + time = (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + fraction; + return true; +} + +} +#endif diff --git a/Source/WebCore/html/MediaFragmentURIParser.h b/Source/WebCore/html/MediaFragmentURIParser.h new file mode 100644 index 000000000..9ed57ac1a --- /dev/null +++ b/Source/WebCore/html/MediaFragmentURIParser.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaFragmentURIParser_h +#define MediaFragmentURIParser_h + +#if ENABLE(VIDEO) + +#include "KURL.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class KURL; + +class MediaFragmentURIParser FINAL { +public: + + MediaFragmentURIParser(const KURL&); + + double startTime(); + double endTime(); + + static double invalidTimeValue(); + +private: + + void parseFragments(); + + enum TimeFormat { None, Invalid, NormalPlayTime, SMPTETimeCode, WallClockTimeCode }; + void parseTimeFragment(); + bool parseNPTFragment(const LChar*, unsigned length, double& startTime, double& endTime); + bool parseNPTTime(const LChar*, unsigned length, unsigned& offset, double& time); + + KURL m_url; + TimeFormat m_timeFormat; + double m_startTime; + double m_endTime; + Vector<std::pair<String, String> > m_fragments; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/MicroDataItemValue.cpp b/Source/WebCore/html/MicroDataItemValue.cpp new file mode 100644 index 000000000..c6eb463d6 --- /dev/null +++ b/Source/WebCore/html/MicroDataItemValue.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(MICRODATA) +#include "MicroDataItemValue.h" + +#include "Node.h" + +namespace WebCore { + +MicroDataItemValue::MicroDataItemValue(const String& string) + : m_type(STRING) + , m_string(string) +{ +} + +MicroDataItemValue::MicroDataItemValue(Node* node) + : m_type(NODE) + , m_node(node) +{ +} + +PassRefPtr<MicroDataItemValue> MicroDataItemValue::createFromString(const String& string) +{ + return adoptRef(new MicroDataItemValue(string)); +} + +PassRefPtr<MicroDataItemValue> MicroDataItemValue::createFromNode(Node* node) +{ + return adoptRef(new MicroDataItemValue(node)); +} + +} // namespace WebCore + +#endif // ENABLE(MICRODATA) diff --git a/Source/WebCore/html/MicroDataItemValue.h b/Source/WebCore/html/MicroDataItemValue.h new file mode 100644 index 000000000..db859ac53 --- /dev/null +++ b/Source/WebCore/html/MicroDataItemValue.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2011 Motorola Mobility, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * + * Neither the name of Motorola Mobility, Inc. nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MicroDataItemValue_h +#define MicroDataItemValue_h + +#if ENABLE(MICRODATA) + +#include "PlatformString.h" + +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Node; + +class MicroDataItemValue : public RefCounted<MicroDataItemValue> { +private: + enum Type {STRING, NODE}; + +public: + static PassRefPtr<MicroDataItemValue> createFromString(const String&); + static PassRefPtr<MicroDataItemValue> createFromNode(Node*); + + bool isNode() { return m_type == NODE; } + Node* getNode() { return m_node; } + String getString() { return m_string; } + +private: + MicroDataItemValue(const String&); + MicroDataItemValue(Node*); + + Type m_type; + String m_string; + Node* m_node; +}; + +} // namespace WebCore + +#endif // ENABLE(MICRODATA) + +#endif // MicroDataItemValue_h + diff --git a/Source/WebCore/html/MonthInputType.cpp b/Source/WebCore/html/MonthInputType.cpp new file mode 100644 index 000000000..f61c3989f --- /dev/null +++ b/Source/WebCore/html/MonthInputType.cpp @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "MonthInputType.h" + +#include "DateComponents.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/CurrentTime.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> +#include <wtf/PassOwnPtr.h> + +#if ENABLE(INPUT_TYPE_MONTH) + +namespace WebCore { + +using namespace HTMLNames; + +static const double monthDefaultStep = 1.0; +static const double monthStepScaleFactor = 1.0; + +PassOwnPtr<InputType> MonthInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new MonthInputType(element)); +} + +const AtomicString& MonthInputType::formControlType() const +{ + return InputTypeNames::month(); +} + +DateComponents::Type MonthInputType::dateType() const +{ + return DateComponents::Month; +} + +double MonthInputType::valueAsDate() const +{ + DateComponents date; + if (!parseToDateComponents(element()->value(), &date)) + return DateComponents::invalidMilliseconds(); + double msec = date.millisecondsSinceEpoch(); + ASSERT(isfinite(msec)); + return msec; +} + +String MonthInputType::serializeWithMilliseconds(double value) const +{ + DateComponents date; + if (!date.setMillisecondsSinceEpochForMonth(value)) + return String(); + return serializeWithComponents(date); +} + +double MonthInputType::defaultValueForStepUp() const +{ + double current = currentTimeMS(); + double utcOffset = calculateUTCOffset(); + double dstOffset = calculateDSTOffset(current, utcOffset); + int offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute); + current += offset * msPerMinute; + + DateComponents date; + date.setMillisecondsSinceEpochForMonth(current); + double months = date.monthsSinceEpoch(); + ASSERT(isfinite(months)); + return months; +} + +double MonthInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumMonth()); +} + +double MonthInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumMonth()); +} + +double MonthInputType::defaultStep() const +{ + return monthDefaultStep; +} + +double MonthInputType::stepScaleFactor() const +{ + return monthStepScaleFactor; +} + +bool MonthInputType::parsedStepValueShouldBeInteger() const +{ + return true; +} + +double MonthInputType::parseToDouble(const String& src, double defaultValue) const +{ + DateComponents date; + if (!parseToDateComponents(src, &date)) + return defaultValue; + double months = date.monthsSinceEpoch(); + ASSERT(isfinite(months)); + return months; +} + +bool MonthInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const +{ + ASSERT(out); + unsigned end; + return out->parseMonth(characters, length, 0, end) && end == length; +} + +bool MonthInputType::setMillisecondToDateComponents(double value, DateComponents* date) const +{ + ASSERT(date); + return date->setMonthsSinceEpoch(value); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/MonthInputType.h b/Source/WebCore/html/MonthInputType.h new file mode 100644 index 000000000..031096ef6 --- /dev/null +++ b/Source/WebCore/html/MonthInputType.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MonthInputType_h +#define MonthInputType_h + +#include "BaseDateAndTimeInputType.h" + +#if ENABLE(INPUT_TYPE_MONTH) + +namespace WebCore { + +class MonthInputType : public BaseDateAndTimeInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + MonthInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + 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 double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual bool parsedStepValueShouldBeInteger() const OVERRIDE; + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; +}; + +} // namespace WebCore + +#endif +#endif // MonthInputType_h diff --git a/Source/WebCore/html/NumberInputType.cpp b/Source/WebCore/html/NumberInputType.cpp new file mode 100644 index 000000000..6d0e972b3 --- /dev/null +++ b/Source/WebCore/html/NumberInputType.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "NumberInputType.h" + +#include "BeforeTextInsertedEvent.h" +#include "ExceptionCode.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "KeyboardEvent.h" +#include "LocalizedNumber.h" +#include "RenderTextControl.h" +#include <limits> +#include <wtf/ASCIICType.h> +#include <wtf/MathExtras.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +using namespace HTMLNames; +using namespace std; + +static const double numberDefaultStep = 1.0; +static const double numberStepScaleFactor = 1.0; + +static unsigned lengthBeforeDecimalPoint(double value) +{ + // If value is negative, '-' should be counted. + + double absoluteValue = fabs(value); + if (absoluteValue < 1) + return value < 0 ? 2 : 1; + + unsigned length = static_cast<unsigned>(log10(floor(absoluteValue))) + 1; + if (value < 0) + length += 1; + + return length; +} + +PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new NumberInputType(element)); +} + +const AtomicString& NumberInputType::formControlType() const +{ + return InputTypeNames::number(); +} + +double NumberInputType::valueAsNumber() const +{ + return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); +} + +void NumberInputType::setValueAsNumber(double newValue, bool sendChangeEvent, ExceptionCode& ec) const +{ + if (newValue < -numeric_limits<float>::max()) { + ec = INVALID_STATE_ERR; + return; + } + if (newValue > numeric_limits<float>::max()) { + ec = INVALID_STATE_ERR; + return; + } + element()->setValue(serialize(newValue), sendChangeEvent); +} + +bool NumberInputType::typeMismatchFor(const String& value) const +{ + return !value.isEmpty() && !parseToDoubleForNumberType(value, 0); +} + +bool NumberInputType::typeMismatch() const +{ + ASSERT(!typeMismatchFor(element()->value())); + return false; +} + +bool NumberInputType::rangeUnderflow(const String& value) const +{ + const double nan = numeric_limits<double>::quiet_NaN(); + double doubleValue = parseToDouble(value, nan); + return isfinite(doubleValue) && doubleValue < minimum(); +} + +bool NumberInputType::rangeOverflow(const String& value) const +{ + const double nan = numeric_limits<double>::quiet_NaN(); + double doubleValue = parseToDouble(value, nan); + return isfinite(doubleValue) && doubleValue > maximum(); +} + +bool NumberInputType::supportsRangeLimitation() const +{ + return true; +} + +double NumberInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), -numeric_limits<float>::max()); +} + +double NumberInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), numeric_limits<float>::max()); +} + +bool NumberInputType::sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const +{ + preferredSize = defaultSize; + + unsigned minValueDecimalPlaces; + double minValueDouble; + String minValue = element()->fastGetAttribute(minAttr); + if (!parseToDoubleForNumberTypeWithDecimalPlaces(minValue, &minValueDouble, &minValueDecimalPlaces)) + return false; + + unsigned maxValueDecimalPlaces; + double maxValueDouble; + String maxValue = element()->fastGetAttribute(maxAttr); + if (!parseToDoubleForNumberTypeWithDecimalPlaces(maxValue, &maxValueDouble, &maxValueDecimalPlaces)) + return false; + + if (maxValueDouble < minValueDouble) { + maxValueDouble = minValueDouble; + maxValueDecimalPlaces = minValueDecimalPlaces; + } + + unsigned stepValueDecimalPlaces; + double stepValueDouble; + String stepValue = element()->fastGetAttribute(stepAttr); + if (equalIgnoringCase(stepValue, "any")) + return false; + if (!parseToDoubleForNumberTypeWithDecimalPlaces(stepValue, &stepValueDouble, &stepValueDecimalPlaces)) { + stepValueDouble = 1; + stepValueDecimalPlaces = 0; + } + + unsigned length = lengthBeforeDecimalPoint(minValueDouble); + length = max(length, lengthBeforeDecimalPoint(maxValueDouble)); + length = max(length, lengthBeforeDecimalPoint(stepValueDouble)); + + unsigned lengthAfterDecimalPoint = minValueDecimalPlaces; + lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, maxValueDecimalPlaces); + lengthAfterDecimalPoint = max(lengthAfterDecimalPoint, stepValueDecimalPlaces); + + // '.' should be counted if the value has decimal places. + if (lengthAfterDecimalPoint > 0) + length += lengthAfterDecimalPoint + 1; + + preferredSize = length; + return true; +} + +bool NumberInputType::isSteppable() const +{ + return true; +} + +bool NumberInputType::stepMismatch(const String& value, double step) const +{ + double doubleValue; + if (!parseToDoubleForNumberType(value, &doubleValue)) + return false; + doubleValue = fabs(doubleValue - stepBase()); + if (isinf(doubleValue)) + return false; + // double'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) > 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 - step * round(doubleValue / step)); + // Accepts erros in lower fractional part which IEEE 754 single-precision + // can't represent. + double computedAcceptableError = acceptableError(step); + return computedAcceptableError < remainder && remainder < (step - computedAcceptableError); +} + +double NumberInputType::stepBase() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), defaultStepBase()); +} + +double NumberInputType::stepBaseWithDecimalPlaces(unsigned* decimalPlaces) const +{ + return parseToDoubleWithDecimalPlaces(element()->fastGetAttribute(minAttr), defaultStepBase(), decimalPlaces); +} + +double NumberInputType::defaultStep() const +{ + return numberDefaultStep; +} + +double NumberInputType::stepScaleFactor() const +{ + return numberStepScaleFactor; +} + +void NumberInputType::handleKeydownEvent(KeyboardEvent* event) +{ + handleKeydownEventForSpinButton(event); + if (!event->defaultHandled()) + TextFieldInputType::handleKeydownEvent(event); +} + +void NumberInputType::handleWheelEvent(WheelEvent* event) +{ + handleWheelEventForSpinButton(event); +} + +double NumberInputType::parseToDouble(const String& src, double defaultValue) const +{ + double numberValue; + if (!parseToDoubleForNumberType(src, &numberValue)) + return defaultValue; + ASSERT(isfinite(numberValue)); + return numberValue; +} + +double NumberInputType::parseToDoubleWithDecimalPlaces(const String& src, double defaultValue, unsigned *decimalPlaces) const +{ + double numberValue; + if (!parseToDoubleForNumberTypeWithDecimalPlaces(src, &numberValue, decimalPlaces)) + return defaultValue; + ASSERT(isfinite(numberValue)); + return numberValue; +} + +String NumberInputType::serialize(double value) const +{ + if (!isfinite(value)) + return String(); + return serializeForNumberType(value); +} + +double NumberInputType::acceptableError(double step) const +{ + return step / pow(2.0, FLT_MANT_DIG); +} + +void NumberInputType::handleBlurEvent() +{ + // Reset the renderer value, which might be unmatched with the element value. + element()->setFormControlValueMatchesRenderer(false); + + // We need to reset the renderer value explicitly because an unacceptable + // renderer value should be purged before style calculation. + element()->updateInnerTextValue(); +} + +String NumberInputType::visibleValue() const +{ + String currentValue = element()->value(); + if (currentValue.isEmpty()) + return currentValue; + double doubleValue = numeric_limits<double>::quiet_NaN(); + unsigned decimalPlace; + parseToDoubleForNumberTypeWithDecimalPlaces(currentValue, &doubleValue, &decimalPlace); + String localized = formatLocalizedNumber(doubleValue, decimalPlace); + return localized.isEmpty() ? currentValue : localized; +} + +String NumberInputType::convertFromVisibleValue(const String& visibleValue) const +{ + if (visibleValue.isEmpty()) + return visibleValue; + double parsedNumber = parseLocalizedNumber(visibleValue); + return isfinite(parsedNumber) ? serializeForNumberType(parsedNumber) : visibleValue; +} + +bool NumberInputType::isAcceptableValue(const String& proposedValue) +{ + return proposedValue.isEmpty() || isfinite(parseLocalizedNumber(proposedValue)) || parseToDoubleForNumberType(proposedValue, 0); +} + +String NumberInputType::sanitizeValue(const String& proposedValue) const +{ + if (proposedValue.isEmpty()) + return proposedValue; + return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string(); +} + +bool NumberInputType::hasUnacceptableValue() +{ + return element()->renderer() && !isAcceptableValue(element()->innerTextValue()); +} + +bool NumberInputType::shouldRespectSpeechAttribute() +{ + return true; +} + +bool NumberInputType::supportsPlaceholder() const +{ + return true; +} + +bool NumberInputType::isNumberField() const +{ + return true; +} + +void NumberInputType::minOrMaxAttributeChanged() +{ + InputType::minOrMaxAttributeChanged(); + + if (element()->renderer()) + element()->renderer()->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void NumberInputType::stepAttributeChanged() +{ + InputType::stepAttributeChanged(); + + if (element()->renderer()) + element()->renderer()->setNeedsLayoutAndPrefWidthsRecalc(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/NumberInputType.h b/Source/WebCore/html/NumberInputType.h new file mode 100644 index 000000000..26e7986c3 --- /dev/null +++ b/Source/WebCore/html/NumberInputType.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NumberInputType_h +#define NumberInputType_h + +#include "TextFieldInputType.h" + +namespace WebCore { + +class NumberInputType : public TextFieldInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + NumberInputType(HTMLInputElement* element) : TextFieldInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual double valueAsNumber() const OVERRIDE; + virtual void setValueAsNumber(double, bool sendChangeEvent, ExceptionCode&) const OVERRIDE; + virtual bool typeMismatchFor(const String&) const OVERRIDE; + virtual bool typeMismatch() const OVERRIDE; + virtual bool rangeUnderflow(const String&) const OVERRIDE; + virtual bool rangeOverflow(const String&) const OVERRIDE; + virtual bool supportsRangeLimitation() const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual bool sizeShouldIncludeDecoration(int defaultSize, int& preferredSize) const OVERRIDE; + virtual bool isSteppable() const OVERRIDE; + virtual bool stepMismatch(const String&, double) const OVERRIDE; + virtual double stepBase() const OVERRIDE; + virtual double stepBaseWithDecimalPlaces(unsigned*) const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() 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 double acceptableError(double) const OVERRIDE; + virtual void handleBlurEvent() OVERRIDE; + virtual String visibleValue() const OVERRIDE; + virtual String convertFromVisibleValue(const String&) const OVERRIDE; + virtual bool isAcceptableValue(const String&) OVERRIDE; + virtual String sanitizeValue(const String&) const OVERRIDE; + virtual bool hasUnacceptableValue() OVERRIDE; + virtual bool shouldRespectSpeechAttribute() OVERRIDE; + virtual bool supportsPlaceholder() const OVERRIDE; + virtual bool isNumberField() const OVERRIDE; + virtual void minOrMaxAttributeChanged() OVERRIDE; + virtual void stepAttributeChanged() OVERRIDE; +}; + +} // namespace WebCore + +#endif // NumberInputType_h diff --git a/Source/WebCore/html/PasswordInputType.cpp b/Source/WebCore/html/PasswordInputType.cpp new file mode 100644 index 000000000..2a9ad0200 --- /dev/null +++ b/Source/WebCore/html/PasswordInputType.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PasswordInputType.h" + +#include <wtf/Assertions.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> PasswordInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new PasswordInputType(element)); +} + +const AtomicString& PasswordInputType::formControlType() const +{ + return InputTypeNames::password(); +} + +bool PasswordInputType::saveFormControlState(String&) const +{ + // Should never save/restore password fields. + return false; +} + +void PasswordInputType::restoreFormControlState(const String&) const +{ + // Should never save/restore password fields. + ASSERT_NOT_REACHED(); +} + +bool PasswordInputType::shouldUseInputMethod() const +{ + // Input methods are disabled for the password field because otherwise + // anyone can access the underlying password and display it in clear text. + return false; +} + +bool PasswordInputType::shouldResetOnDocumentActivation() +{ + return true; +} + +bool PasswordInputType::shouldRespectListAttribute() +{ + return false; +} + +bool PasswordInputType::shouldRespectSpeechAttribute() +{ + return true; +} + +bool PasswordInputType::isPasswordField() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/PasswordInputType.h b/Source/WebCore/html/PasswordInputType.h new file mode 100644 index 000000000..e9ca18e44 --- /dev/null +++ b/Source/WebCore/html/PasswordInputType.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PasswordInputType_h +#define PasswordInputType_h + +#include "BaseTextInputType.h" + +namespace WebCore { + +class PasswordInputType : public BaseTextInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + PasswordInputType(HTMLInputElement* element) : BaseTextInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool saveFormControlState(String&) const OVERRIDE; + virtual void restoreFormControlState(const String&) const OVERRIDE; + virtual bool shouldUseInputMethod() const OVERRIDE; + virtual bool shouldResetOnDocumentActivation() OVERRIDE; + virtual bool shouldRespectListAttribute() OVERRIDE; + virtual bool shouldRespectSpeechAttribute() OVERRIDE; + virtual bool isPasswordField() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // PasswordInputType_h diff --git a/Source/WebCore/html/PluginDocument.cpp b/Source/WebCore/html/PluginDocument.cpp new file mode 100644 index 000000000..c5a571fb1 --- /dev/null +++ b/Source/WebCore/html/PluginDocument.cpp @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDocument.h" + +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "FrameView.h" +#include "HTMLEmbedElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLNames.h" +#include "MainResourceLoader.h" +#include "NodeList.h" +#include "Page.h" +#include "RawDataDocumentParser.h" +#include "RenderEmbeddedObject.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: Share more code with MediaDocumentParser. +class PluginDocumentParser : public RawDataDocumentParser { +public: + static PassRefPtr<PluginDocumentParser> create(PluginDocument* document) + { + return adoptRef(new PluginDocumentParser(document)); + } + +private: + PluginDocumentParser(Document* document) + : RawDataDocumentParser(document) + , m_embedElement(0) + { + } + + virtual void appendBytes(DocumentWriter*, const char*, size_t); + + void createDocumentStructure(); + + HTMLEmbedElement* m_embedElement; +}; + +void PluginDocumentParser::createDocumentStructure() +{ + ExceptionCode ec; + RefPtr<Element> rootElement = document()->createElement(htmlTag, false); + document()->appendChild(rootElement, ec); + static_cast<HTMLHtmlElement*>(rootElement.get())->insertedByParser(); + + if (document()->frame() && document()->frame()->loader()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr<Element> body = document()->createElement(bodyTag, false); + body->setAttribute(marginwidthAttr, "0"); + body->setAttribute(marginheightAttr, "0"); + body->setAttribute(styleAttr, "background-color: rgb(38,38,38)"); + + rootElement->appendChild(body, ec); + + RefPtr<Element> embedElement = document()->createElement(embedTag, false); + + m_embedElement = static_cast<HTMLEmbedElement*>(embedElement.get()); + m_embedElement->setAttribute(widthAttr, "100%"); + m_embedElement->setAttribute(heightAttr, "100%"); + + m_embedElement->setAttribute(nameAttr, "plugin"); + m_embedElement->setAttribute(srcAttr, document()->url().string()); + + DocumentLoader* loader = document()->loader(); + ASSERT(loader); + if (loader) + m_embedElement->setAttribute(typeAttr, loader->writer()->mimeType()); + + static_cast<PluginDocument*>(document())->setPluginNode(m_embedElement); + + body->appendChild(embedElement, ec); +} + +void PluginDocumentParser::appendBytes(DocumentWriter*, const char*, size_t) +{ + if (m_embedElement) + return; + + createDocumentStructure(); + + Frame* frame = document()->frame(); + if (!frame) + return; + Settings* settings = frame->settings(); + if (!settings || !frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) + return; + + document()->updateLayout(); + + // Below we assume that renderer->widget() to have been created by + // document()->updateLayout(). However, in some cases, updateLayout() will + // recurse too many times and delay its post-layout tasks (such as creating + // the widget). Here we kick off the pending post-layout tasks so that we + // can synchronously redirect data to the plugin. + frame->view()->flushAnyPendingPostLayoutTasks(); + + if (RenderPart* renderer = m_embedElement->renderPart()) { + if (Widget* widget = renderer->widget()) { + frame->loader()->client()->redirectDataToPlugin(widget); + // In a plugin document, the main resource is the plugin. If we have a null widget, that means + // the loading of the plugin was cancelled, which gives us a null mainResourceLoader(), so we + // need to have this call in a null check of the widget or of mainResourceLoader(). + frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(DoNotBufferData); + } + } + + finish(); +} + +PluginDocument::PluginDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) + , m_shouldLoadPluginManually(true) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> PluginDocument::createParser() +{ + return PluginDocumentParser::create(this); +} + +Widget* PluginDocument::pluginWidget() +{ + if (m_pluginNode && m_pluginNode->renderer()) { + ASSERT(m_pluginNode->renderer()->isEmbeddedObject()); + return toRenderEmbeddedObject(m_pluginNode->renderer())->widget(); + } + return 0; +} + +Node* PluginDocument::pluginNode() +{ + return m_pluginNode.get(); +} + +void PluginDocument::detach() +{ + // Release the plugin node so that we don't have a circular reference. + m_pluginNode = 0; + HTMLDocument::detach(); +} + +void PluginDocument::cancelManualPluginLoad() +{ + // PluginDocument::cancelManualPluginLoad should only be called once, but there are issues + // with how many times we call beforeload on object elements. <rdar://problem/8441094>. + if (!shouldLoadPluginManually()) + return; + + frame()->loader()->activeDocumentLoader()->mainResourceLoader()->cancel(); + setShouldLoadPluginManually(false); +} + +} diff --git a/Source/WebCore/html/PluginDocument.h b/Source/WebCore/html/PluginDocument.h new file mode 100644 index 000000000..6daa97cf5 --- /dev/null +++ b/Source/WebCore/html/PluginDocument.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 PluginDocument_h +#define PluginDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class Node; +class Widget; + +class PluginDocument : public HTMLDocument { +public: + static PassRefPtr<PluginDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new PluginDocument(frame, url)); + } + + void setPluginNode(Node* pluginNode) { m_pluginNode = pluginNode; } + + Widget* pluginWidget(); + Node* pluginNode(); + + virtual void detach() OVERRIDE; + + void cancelManualPluginLoad(); + + bool shouldLoadPluginManually() { return m_shouldLoadPluginManually; } + +private: + PluginDocument(Frame*, const KURL&); + + virtual PassRefPtr<DocumentParser> createParser() OVERRIDE; + virtual bool isPluginDocument() const OVERRIDE { return true; } + + void setShouldLoadPluginManually(bool loadManually) { m_shouldLoadPluginManually = loadManually; } + + bool m_shouldLoadPluginManually; + RefPtr<Node> m_pluginNode; +}; + +inline PluginDocument* toPluginDocument(Document* document) +{ + ASSERT(!document || document->isPluginDocument()); + return static_cast<PluginDocument*>(document); +} + +inline const PluginDocument* toPluginDocument(const Document* document) +{ + ASSERT(!document || document->isPluginDocument()); + return static_cast<const PluginDocument*>(document); +} + +// This will catch anyone doing an unnecessary cast. +void toPluginDocument(const PluginDocument*); + +} + +#endif // PluginDocument_h diff --git a/Source/WebCore/html/RadioInputType.cpp b/Source/WebCore/html/RadioInputType.cpp new file mode 100644 index 000000000..7e69517a3 --- /dev/null +++ b/Source/WebCore/html/RadioInputType.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2005, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2010 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 "RadioInputType.h" + +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "Settings.h" +#include "SpatialNavigation.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +PassOwnPtr<InputType> RadioInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new RadioInputType(element)); +} + +const AtomicString& RadioInputType::formControlType() const +{ + return InputTypeNames::radio(); +} + +bool RadioInputType::valueMissing(const String&) const +{ + return !element()->checkedRadioButtons().checkedButtonForGroup(element()->name()); +} + +String RadioInputType::valueMissingText() const +{ + return validationMessageValueMissingForRadioText(); +} + +void RadioInputType::handleClickEvent(MouseEvent* event) +{ + event->setDefaultHandled(); +} + +void RadioInputType::handleKeydownEvent(KeyboardEvent* event) +{ + BaseCheckableInputType::handleKeydownEvent(event); + if (event->defaultHandled()) + return; + const String& key = event->keyIdentifier(); + if (key != "Up" && key != "Down" && key != "Left" && key != "Right") + return; + + // Left and up mean "previous radio button". + // Right and down mean "next radio button". + // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves + // to the right). Seems strange, but we'll match it. + // However, when using Spatial Navigation, we need to be able to navigate without changing the selection. + Document* document = element()->document(); + if (isSpatialNavigationEnabled(document->frame())) + return; + bool forward = (key == "Down" || key == "Right"); + + // We can only stay within the form's children if the form hasn't been demoted to a leaf because + // of malformed HTML. + Node* node = element(); + while ((node = (forward ? node->traverseNextNode() : node->traversePreviousNode()))) { + // Once we encounter a form element, we know we're through. + if (node->hasTagName(formTag)) + break; + // Look for more radio buttons. + if (!node->hasTagName(inputTag)) + continue; + HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node); + if (inputElement->form() != element()->form()) + break; + if (inputElement->isRadioButton() && inputElement->name() == element()->name() && inputElement->isFocusable()) { + document->setFocusedNode(inputElement); + inputElement->dispatchSimulatedClick(event, false, false); + event->setDefaultHandled(); + return; + } + } +} + +void RadioInputType::handleKeyupEvent(KeyboardEvent* event) +{ + const String& key = event->keyIdentifier(); + if (key != "U+0020") + return; + // If an unselected radio is tabbed into (because the entire group has nothing + // checked, or because of some explicit .focus() call), then allow space to check it. + if (element()->checked()) + return; + dispatchSimulatedClickIfActive(event); +} + +bool RadioInputType::isKeyboardFocusable() const +{ + // When using Spatial Navigation, every radio button should be focusable. + if (isSpatialNavigationEnabled(element()->document()->frame())) + return true; + + // Never allow keyboard tabbing to leave you in the same radio group. Always + // skip any other elements in the group. + Node* currentFocusedNode = element()->document()->focusedNode(); + if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) { + HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode); + if (focusedInput->isRadioButton() && focusedInput->form() == element()->form() && focusedInput->name() == element()->name()) + return false; + } + + // Allow keyboard focus if we're checked or if nothing in the group is checked. + return element()->checked() || !element()->checkedRadioButtons().checkedButtonForGroup(element()->name()); +} + +void RadioInputType::attach() +{ + InputType::attach(); + element()->updateCheckedRadioButtons(); +} + +bool RadioInputType::shouldSendChangeEventAfterCheckedChanged() +{ + // Don't send a change event for a radio button that's getting unchecked. + // This was done to match the behavior of other browsers. + return element()->checked(); +} + +PassOwnPtr<ClickHandlingState> RadioInputType::willDispatchClick() +{ + // An event handler can use preventDefault or "return false" to reverse the selection we do here. + // The ClickHandlingState object contains what we need to undo what we did here in didDispatchClick. + + // We want radio groups to end up in sane states, i.e., to have something checked. + // Therefore if nothing is currently selected, we won't allow the upcoming action to be "undone", since + // we want some object in the radio group to actually get selected. + + OwnPtr<ClickHandlingState> state = adoptPtr(new ClickHandlingState); + + state->checked = element()->checked(); + state->indeterminate = element()->indeterminate(); + state->checkedRadioButton = element()->checkedRadioButtons().checkedButtonForGroup(element()->name()); + + if (element()->indeterminate()) + element()->setIndeterminate(false); + element()->setChecked(true, true); + + return state.release(); +} + +void RadioInputType::didDispatchClick(Event* event, const ClickHandlingState& state) +{ + if (event->defaultPrevented() || event->defaultHandled()) { + // Restore the original selected radio button if possible. + // Make sure it is still a radio button and only do the restoration if it still belongs to our group. + HTMLInputElement* checkedRadioButton = state.checkedRadioButton.get(); + if (checkedRadioButton + && checkedRadioButton->isRadioButton() + && checkedRadioButton->form() == element()->form() + && checkedRadioButton->name() == element()->name()) { + checkedRadioButton->setChecked(true); + } + element()->setIndeterminate(state.indeterminate); + } + + // The work we did in willDispatchClick was default handling. + event->setDefaultHandled(); +} + +bool RadioInputType::isRadioButton() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/RadioInputType.h b/Source/WebCore/html/RadioInputType.h new file mode 100644 index 000000000..525ed691c --- /dev/null +++ b/Source/WebCore/html/RadioInputType.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RadioInputType_h +#define RadioInputType_h + +#include "BaseCheckableInputType.h" + +namespace WebCore { + +class RadioInputType : public BaseCheckableInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + RadioInputType(HTMLInputElement* element) : BaseCheckableInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool valueMissing(const String&) const OVERRIDE; + virtual String valueMissingText() const OVERRIDE; + virtual void handleClickEvent(MouseEvent*) OVERRIDE; + virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; + virtual void handleKeyupEvent(KeyboardEvent*) OVERRIDE; + virtual bool isKeyboardFocusable() const OVERRIDE; + virtual void attach() OVERRIDE; + virtual bool shouldSendChangeEventAfterCheckedChanged() OVERRIDE; + virtual PassOwnPtr<ClickHandlingState> willDispatchClick() OVERRIDE; + virtual void didDispatchClick(Event*, const ClickHandlingState&) OVERRIDE; + virtual bool isRadioButton() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // RadioInputType_h diff --git a/Source/WebCore/html/RangeInputType.cpp b/Source/WebCore/html/RangeInputType.cpp new file mode 100644 index 000000000..acfe1cca5 --- /dev/null +++ b/Source/WebCore/html/RangeInputType.cpp @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RangeInputType.h" + +#include "AXObjectCache.h" +#include "HTMLDivElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "KeyboardEvent.h" +#include "MouseEvent.h" +#include "PlatformMouseEvent.h" +#include "RenderSlider.h" +#include "ShadowRoot.h" +#include "SliderThumbElement.h" +#include "StepRange.h" +#include <limits> +#include <wtf/MathExtras.h> +#include <wtf/PassOwnPtr.h> + +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 rangeStepScaleFactor = 1.0; + +PassOwnPtr<InputType> RangeInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new RangeInputType(element)); +} + +bool RangeInputType::isRangeControl() const +{ + return true; +} + +const AtomicString& RangeInputType::formControlType() const +{ + return InputTypeNames::range(); +} + +double RangeInputType::valueAsNumber() const +{ + return parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); +} + +void RangeInputType::setValueAsNumber(double newValue, bool sendChangeEvent, ExceptionCode&) const +{ + element()->setValue(serialize(newValue), sendChangeEvent); +} + +bool RangeInputType::supportsRequired() const +{ + return false; +} + +bool RangeInputType::rangeUnderflow(const String& value) const +{ + // Guaranteed by sanitization. + ASSERT_UNUSED(value, parseToDouble(value, numeric_limits<double>::quiet_NaN()) >= minimum()); + return false; +} + +bool RangeInputType::rangeOverflow(const String& value) const +{ + // Guaranteed by sanitization. + ASSERT_UNUSED(value, parseToDouble(value, numeric_limits<double>::quiet_NaN()) <= maximum()); + return false; +} + +bool RangeInputType::supportsRangeLimitation() const +{ + return true; +} + +double RangeInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), rangeDefaultMinimum); +} + +double RangeInputType::maximum() const +{ + double max = parseToDouble(element()->fastGetAttribute(maxAttr), rangeDefaultMaximum); + // A remedy for the inconsistent min/max values. + // Sets the maximum to the default or the minimum value. + double min = minimum(); + if (max < min) + max = std::max(min, rangeDefaultMaximum); + return max; +} + +bool RangeInputType::isSteppable() const +{ + return true; +} + +bool RangeInputType::stepMismatch(const String&, double) const +{ + // stepMismatch doesn't occur for type=range. RenderSlider guarantees the + // value matches to step on user input, and sanitization takes care + // of the general case. + return false; +} + +double RangeInputType::stepBase() const +{ + return minimum(); +} + +double RangeInputType::defaultStep() const +{ + return rangeDefaultStep; +} + +double RangeInputType::stepScaleFactor() const +{ + return rangeStepScaleFactor; +} + +void RangeInputType::handleMouseDownEvent(MouseEvent* event) +{ + if (element()->disabled() || element()->readOnly()) + return; + + Node* targetNode = event->target()->toNode(); + if (event->button() != LeftButton || !targetNode || (targetNode != element() && !targetNode->isDescendantOf(element()->shadowRoot()))) + return; + SliderThumbElement* thumb = sliderThumbElementOf(element()); + if (targetNode == thumb) + return; + thumb->dragFrom(event->absoluteLocation()); +} + +void RangeInputType::handleKeydownEvent(KeyboardEvent* event) +{ + if (element()->disabled() || element()->readOnly()) + return; + + const String& key = event->keyIdentifier(); + + double current = parseToDouble(element()->value(), numeric_limits<double>::quiet_NaN()); + ASSERT(isfinite(current)); + + 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 = (maximum() - minimum()) / 100; + bigStep = step * 10; + } else { + if (!element()->getAllowedValueStep(&step)) + ASSERT_NOT_REACHED(); + + bigStep = (maximum() - minimum()) / 10; + if (bigStep < step) + bigStep = step; + } + + bool isVertical = false; + if (element()->renderer()) { + ControlPart part = element()->renderer()->style()->appearance(); + isVertical = part == SliderVerticalPart || part == MediaVolumeSliderPart; + } + + double newValue; + if (key == "Up") + newValue = current + step; + else if (key == "Down") + newValue = current - step; + else if (key == "Left") + newValue = isVertical ? current + step : current - step; + else if (key == "Right") + newValue = isVertical ? current - step : current + step; + else if (key == "PageUp") + newValue = current + bigStep; + else if (key == "PageDown") + newValue = current - bigStep; + else if (key == "Home") + newValue = isVertical ? maximum() : minimum(); + else if (key == "End") + newValue = isVertical ? minimum() : maximum(); + else + return; // Did not match any key binding. + + newValue = StepRange(element()).clampValue(newValue); + + if (newValue != current) { + ExceptionCode ec; + bool sendChangeEvent = true; + setValueAsNumber(newValue, sendChangeEvent, ec); + + if (AXObjectCache::accessibilityEnabled()) + element()->document()->axObjectCache()->postNotification(element()->renderer(), AXObjectCache::AXValueChanged, true); + element()->dispatchFormControlChangeEvent(); + } + + event->setDefaultHandled(); +} + +void RangeInputType::createShadowSubtree() +{ + Document* document = element()->document(); + RefPtr<HTMLDivElement> track = HTMLDivElement::create(document); + track->setShadowPseudoId("-webkit-slider-runnable-track"); + ExceptionCode ec = 0; + track->appendChild(SliderThumbElement::create(document), ec); + RefPtr<HTMLElement> container = SliderContainerElement::create(document); + container->appendChild(track.release(), ec); + container->appendChild(TrackLimiterElement::create(document), ec); + element()->ensureShadowRoot()->appendChild(container.release(), ec); +} + +RenderObject* RangeInputType::createRenderer(RenderArena* arena, RenderStyle*) const +{ + return new (arena) RenderSlider(element()); +} + +double RangeInputType::parseToDouble(const String& src, double defaultValue) const +{ + double numberValue; + if (!parseToDoubleForNumberType(src, &numberValue)) + return defaultValue; + ASSERT(isfinite(numberValue)); + return numberValue; +} + +String RangeInputType::serialize(double value) const +{ + if (!isfinite(value)) + return String(); + return serializeForNumberType(value); +} + +// FIXME: Could share this with BaseButtonInputType and BaseCheckableInputType if we had a common base class. +void RangeInputType::accessKeyAction(bool sendMouseEvents) +{ + InputType::accessKeyAction(sendMouseEvents); + + // Send mouse button events if the caller specified sendMouseEvents. + // FIXME: The comment above is no good. It says what we do, but not why. + element()->dispatchSimulatedClick(0, sendMouseEvents); +} + +void RangeInputType::minOrMaxAttributeChanged() +{ + InputType::minOrMaxAttributeChanged(); + + // Sanitize the value. + if (element()->hasDirtyValue()) + element()->setValue(element()->value()); + element()->setNeedsStyleRecalc(); +} + +void RangeInputType::setValue(const String& value, bool valueChanged, bool sendChangeEvent) +{ + InputType::setValue(value, valueChanged, sendChangeEvent); + + if (!valueChanged) + return; + + sliderThumbElementOf(element())->setPositionFromValue(); +} + +String RangeInputType::fallbackValue() const +{ + return serializeForNumberType(StepRange(element()).defaultValue()); +} + +String RangeInputType::sanitizeValue(const String& proposedValue) const +{ + return serializeForNumberType(StepRange(element()).clampValue(proposedValue)); +} + +bool RangeInputType::shouldRespectListAttribute() +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/RangeInputType.h b/Source/WebCore/html/RangeInputType.h new file mode 100644 index 000000000..e75193098 --- /dev/null +++ b/Source/WebCore/html/RangeInputType.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RangeInputType_h +#define RangeInputType_h + +#include "InputType.h" + +namespace WebCore { + +class SliderThumbElement; + +class RangeInputType : public InputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +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, bool sendChangeEvent, ExceptionCode&) const OVERRIDE; + virtual bool supportsRequired() const OVERRIDE; + virtual bool rangeUnderflow(const String&) const OVERRIDE; + virtual bool rangeOverflow(const String&) const OVERRIDE; + virtual bool supportsRangeLimitation() const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual bool isSteppable() const OVERRIDE; + virtual bool stepMismatch(const String&, double) const OVERRIDE; + virtual double stepBase() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual void handleMouseDownEvent(MouseEvent*) OVERRIDE; + 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 void accessKeyAction(bool sendMouseEvents) OVERRIDE; + virtual void minOrMaxAttributeChanged() OVERRIDE; + virtual void setValue(const String&, bool valueChanged, bool sendChangeEvent) OVERRIDE; + virtual String fallbackValue() const OVERRIDE; + virtual String sanitizeValue(const String& proposedValue) const OVERRIDE; + virtual bool shouldRespectListAttribute() OVERRIDE; +}; + +} // namespace WebCore + +#endif // RangeInputType_h diff --git a/Source/WebCore/html/ResetInputType.cpp b/Source/WebCore/html/ResetInputType.cpp new file mode 100644 index 000000000..457ea4590 --- /dev/null +++ b/Source/WebCore/html/ResetInputType.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ResetInputType.h" + +#include "Event.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "LocalizedStrings.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> ResetInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new ResetInputType(element)); +} + +const AtomicString& ResetInputType::formControlType() const +{ + return InputTypeNames::reset(); +} + +bool ResetInputType::supportsValidation() const +{ + return false; +} + +void ResetInputType::handleDOMActivateEvent(Event* event) +{ + if (element()->disabled() || !element()->form()) + return; + element()->form()->reset(); + event->setDefaultHandled(); +} + +String ResetInputType::defaultValue() const +{ + return resetButtonDefaultLabel(); +} + +bool ResetInputType::isTextButton() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/ResetInputType.h b/Source/WebCore/html/ResetInputType.h new file mode 100644 index 000000000..51baaf478 --- /dev/null +++ b/Source/WebCore/html/ResetInputType.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ResetInputType_h +#define ResetInputType_h + +#include "BaseButtonInputType.h" + +namespace WebCore { + +class ResetInputType : public BaseButtonInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + ResetInputType(HTMLInputElement* element) : BaseButtonInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool supportsValidation() const OVERRIDE; + virtual void handleDOMActivateEvent(Event*) OVERRIDE; + virtual String defaultValue() const OVERRIDE; + virtual bool isTextButton() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // ResetInputType_h diff --git a/Source/WebCore/html/SearchInputType.cpp b/Source/WebCore/html/SearchInputType.cpp new file mode 100644 index 000000000..6f500daf3 --- /dev/null +++ b/Source/WebCore/html/SearchInputType.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SearchInputType.h" + +#include "HTMLInputElement.h" +#include "KeyboardEvent.h" +#include "RenderTextControlSingleLine.h" +#include "ShadowRoot.h" +#include "TextControlInnerElements.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +inline SearchInputType::SearchInputType(HTMLInputElement* element) + : BaseTextInputType(element) + , m_searchEventTimer(this, &SearchInputType::searchEventTimerFired) +{ +} + +PassOwnPtr<InputType> SearchInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new SearchInputType(element)); +} + +const AtomicString& SearchInputType::formControlType() const +{ + return InputTypeNames::search(); +} + +bool SearchInputType::shouldRespectSpeechAttribute() +{ + return true; +} + +bool SearchInputType::isSearchField() const +{ + return true; +} + +bool SearchInputType::needsContainer() const +{ + return true; +} + +void SearchInputType::createShadowSubtree() +{ + ASSERT(!m_resultsButton); + ASSERT(!m_cancelButton); + + TextFieldInputType::createShadowSubtree(); + HTMLElement* container = containerElement(); + HTMLElement* textWrapper = innerBlockElement(); + ASSERT(container); + ASSERT(textWrapper); + + ExceptionCode ec = 0; + m_resultsButton = SearchFieldResultsButtonElement::create(element()->document()); + container->insertBefore(m_resultsButton, textWrapper, ec); + + m_cancelButton = SearchFieldCancelButtonElement::create(element()->document()); + container->insertBefore(m_cancelButton, textWrapper->nextSibling(), ec); +} + +HTMLElement* SearchInputType::resultsButtonElement() const +{ + return m_resultsButton.get(); +} + +HTMLElement* SearchInputType::cancelButtonElement() const +{ + return m_cancelButton.get(); +} + +void SearchInputType::handleKeydownEvent(KeyboardEvent* event) +{ + if (element()->disabled() || element()->readOnly()) { + TextFieldInputType::handleKeydownEvent(event); + return; + } + + const String& key = event->keyIdentifier(); + if (key == "U+001B") { + RefPtr<HTMLInputElement> input = element(); + input->setValueForUser(""); + input->onSearch(); + event->setDefaultHandled(); + return; + } + TextFieldInputType::handleKeydownEvent(event); +} + +void SearchInputType::destroyShadowSubtree() +{ + TextFieldInputType::destroyShadowSubtree(); + m_resultsButton.clear(); + m_cancelButton.clear(); +} + +void SearchInputType::startSearchEventTimer() +{ + ASSERT(element()->renderer()); + unsigned length = element()->innerTextValue().length(); + + if (!length) { + stopSearchEventTimer(); + element()->onSearch(); + return; + } + + // After typing the first key, we wait 0.5 seconds. + // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on. + m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length)); +} + +void SearchInputType::stopSearchEventTimer() +{ + m_searchEventTimer.stop(); +} + +void SearchInputType::searchEventTimerFired(Timer<SearchInputType>*) +{ + element()->onSearch(); +} + + +} // namespace WebCore diff --git a/Source/WebCore/html/SearchInputType.h b/Source/WebCore/html/SearchInputType.h new file mode 100644 index 000000000..ab19103af --- /dev/null +++ b/Source/WebCore/html/SearchInputType.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SearchInputType_h +#define SearchInputType_h + +#include "BaseTextInputType.h" +#include "Timer.h" + +namespace WebCore { + +class SearchFieldCancelButtonElement; +class SearchFieldResultsButtonElement; + +class SearchInputType : public BaseTextInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + + void startSearchEventTimer(); + void stopSearchEventTimer(); + +private: + SearchInputType(HTMLInputElement*); + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool shouldRespectSpeechAttribute() OVERRIDE; + virtual bool isSearchField() const OVERRIDE; + virtual bool needsContainer() const OVERRIDE; + virtual void createShadowSubtree() OVERRIDE; + virtual void destroyShadowSubtree() OVERRIDE; + virtual HTMLElement* resultsButtonElement() const OVERRIDE; + virtual HTMLElement* cancelButtonElement() const OVERRIDE; + virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; + + void searchEventTimerFired(Timer<SearchInputType>*); + + RefPtr<HTMLElement> m_resultsButton; + RefPtr<HTMLElement> m_cancelButton; + Timer<SearchInputType> m_searchEventTimer; +}; + +} // namespace WebCore + +#endif // SearchInputType_h diff --git a/Source/WebCore/html/StepRange.cpp b/Source/WebCore/html/StepRange.cpp new file mode 100644 index 000000000..68b0ebd37 --- /dev/null +++ b/Source/WebCore/html/StepRange.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple 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 "StepRange.h" + +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include <wtf/MathExtras.h> +#include <wtf/text/WTFString.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +StepRange::StepRange(const HTMLInputElement* element) +{ + if (element->hasAttribute(precisionAttr)) { + step = 1.0; + hasStep = !equalIgnoringCase(element->getAttribute(precisionAttr), "float"); + } else + hasStep = element->getAllowedValueStep(&step); + + maximum = element->maximum(); + minimum = element->minimum(); +} + +double StepRange::clampValue(double value) +{ + double clampedValue = max(minimum, min(value, maximum)); + if (!hasStep) + return clampedValue; + // Rounds clampedValue to minimum + N * step. + clampedValue = minimum + round((clampedValue - minimum) / step) * step; + if (clampedValue > maximum) + clampedValue -= step; + ASSERT(clampedValue >= minimum); + ASSERT(clampedValue <= maximum); + return clampedValue; +} + +double StepRange::clampValue(const String& stringValue) +{ + double value; + bool parseSuccess = parseToDoubleForNumberType(stringValue, &value); + if (!parseSuccess) + value = (minimum + maximum) / 2; + return clampValue(value); +} + +double StepRange::valueFromElement(HTMLInputElement* element, bool* wasClamped) +{ + double oldValue; + bool parseSuccess = parseToDoubleForNumberType(element->value(), &oldValue); + if (!parseSuccess) + oldValue = (minimum + maximum) / 2; + double newValue = clampValue(oldValue); + + if (wasClamped) + *wasClamped = !parseSuccess || newValue != oldValue; + + return newValue; +} + +} diff --git a/Source/WebCore/html/StepRange.h b/Source/WebCore/html/StepRange.h new file mode 100644 index 000000000..2fa1393c5 --- /dev/null +++ b/Source/WebCore/html/StepRange.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple 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 StepRange_h +#define StepRange_h + +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class HTMLInputElement; + +class StepRange { + WTF_MAKE_NONCOPYABLE(StepRange); +public: + bool hasStep; + double step; + double minimum; + double maximum; // maximum must be >= minimum. + + explicit StepRange(const HTMLInputElement*); + double clampValue(double value); + double clampValue(const String& stringValue); + + // Clamp the middle value according to the step + double defaultValue() + { + return clampValue((minimum + maximum) / 2); + } + + // Map value into 0-1 range + double proportionFromValue(double value) + { + if (minimum == maximum) + return 0; + + return (value - minimum) / (maximum - minimum); + } + + // Map from 0-1 range to value + double valueFromProportion(double proportion) + { + return minimum + proportion * (maximum - minimum); + } + + double valueFromElement(HTMLInputElement*, bool* wasClamped = 0); +}; + +} + +#endif // StepRange_h diff --git a/Source/WebCore/html/SubmitInputType.cpp b/Source/WebCore/html/SubmitInputType.cpp new file mode 100644 index 000000000..937b20051 --- /dev/null +++ b/Source/WebCore/html/SubmitInputType.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SubmitInputType.h" + +#include "Event.h" +#include "FormDataList.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "LocalizedStrings.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> SubmitInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new SubmitInputType(element)); +} + +const AtomicString& SubmitInputType::formControlType() const +{ + return InputTypeNames::submit(); +} + +bool SubmitInputType::appendFormData(FormDataList& encoding, bool) const +{ + if (!element()->isActivatedSubmit()) + return false; + encoding.appendData(element()->name(), element()->valueWithDefault()); + return true; +} + +bool SubmitInputType::supportsRequired() const +{ + return false; +} + +void SubmitInputType::handleDOMActivateEvent(Event* event) +{ + RefPtr<HTMLInputElement> element = this->element(); + if (element->disabled() || !element->form()) + return; + element->setActivatedSubmit(true); + element->form()->prepareForSubmission(event); // Event handlers can run. + element->setActivatedSubmit(false); + event->setDefaultHandled(); +} + +bool SubmitInputType::canBeSuccessfulSubmitButton() +{ + return true; +} + +String SubmitInputType::defaultValue() const +{ + return submitButtonDefaultLabel(); +} + +bool SubmitInputType::isSubmitButton() const +{ + return true; +} + +bool SubmitInputType::isTextButton() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/SubmitInputType.h b/Source/WebCore/html/SubmitInputType.h new file mode 100644 index 000000000..de822bc8f --- /dev/null +++ b/Source/WebCore/html/SubmitInputType.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SubmitInputType_h +#define SubmitInputType_h + +#include "BaseButtonInputType.h" + +namespace WebCore { + +class SubmitInputType : public BaseButtonInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + SubmitInputType(HTMLInputElement* element) : BaseButtonInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool appendFormData(FormDataList&, bool) const OVERRIDE; + virtual bool supportsRequired() const OVERRIDE; + virtual void handleDOMActivateEvent(Event*) OVERRIDE; + virtual bool canBeSuccessfulSubmitButton() OVERRIDE; + virtual String defaultValue() const OVERRIDE; + virtual bool isSubmitButton() const OVERRIDE; + virtual bool isTextButton() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // SubmitInputType_h diff --git a/Source/WebCore/html/TelephoneInputType.cpp b/Source/WebCore/html/TelephoneInputType.cpp new file mode 100644 index 000000000..c1c77f00d --- /dev/null +++ b/Source/WebCore/html/TelephoneInputType.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TelephoneInputType.h" + +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> TelephoneInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new TelephoneInputType(element)); +} + +const AtomicString& TelephoneInputType::formControlType() const +{ + return InputTypeNames::telephone(); +} + +bool TelephoneInputType::shouldRespectSpeechAttribute() +{ + return true; +} + +bool TelephoneInputType::isTelephoneField() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/TelephoneInputType.h b/Source/WebCore/html/TelephoneInputType.h new file mode 100644 index 000000000..923fad635 --- /dev/null +++ b/Source/WebCore/html/TelephoneInputType.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TelephoneInputType_h +#define TelephoneInputType_h + +#include "BaseTextInputType.h" + +namespace WebCore { + +class TelephoneInputType : public BaseTextInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + TelephoneInputType(HTMLInputElement* element) : BaseTextInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool shouldRespectSpeechAttribute() OVERRIDE; + virtual bool isTelephoneField() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // TelephoneInputType_h diff --git a/Source/WebCore/html/TextDocument.cpp b/Source/WebCore/html/TextDocument.cpp new file mode 100644 index 000000000..9334a397b --- /dev/null +++ b/Source/WebCore/html/TextDocument.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextDocument.h" + +#include "TextDocumentParser.h" + +namespace WebCore { + +TextDocument::TextDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> TextDocument::createParser() +{ + return TextDocumentParser::create(this); +} + +} diff --git a/Source/WebCore/html/TextDocument.h b/Source/WebCore/html/TextDocument.h new file mode 100644 index 000000000..2ea49f81a --- /dev/null +++ b/Source/WebCore/html/TextDocument.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 TextDocument_h +#define TextDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class TextDocument : public HTMLDocument { +public: + static PassRefPtr<TextDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new TextDocument(frame, url)); + } + +private: + TextDocument(Frame*, const KURL&); + + virtual PassRefPtr<DocumentParser> createParser(); +}; + +} + +#endif diff --git a/Source/WebCore/html/TextFieldInputType.cpp b/Source/WebCore/html/TextFieldInputType.cpp new file mode 100644 index 000000000..c9c908335 --- /dev/null +++ b/Source/WebCore/html/TextFieldInputType.cpp @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextFieldInputType.h" + +#include "BeforeTextInsertedEvent.h" +#include "FormDataList.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "KeyboardEvent.h" +#include "Page.h" +#include "RenderLayer.h" +#include "RenderTextControlSingleLine.h" +#include "RenderTheme.h" +#include "ShadowRoot.h" +#include "TextControlInnerElements.h" +#include "TextEvent.h" +#include "TextIterator.h" +#include "WheelEvent.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +using namespace HTMLNames; + +TextFieldInputType::TextFieldInputType(HTMLInputElement* element) + : InputType(element) +{ +} + +TextFieldInputType::~TextFieldInputType() +{ +} + +bool TextFieldInputType::isTextField() const +{ + return true; +} + +bool TextFieldInputType::valueMissing(const String& value) const +{ + return value.isEmpty(); +} + +bool TextFieldInputType::canSetSuggestedValue() +{ + return true; +} + +void TextFieldInputType::setValue(const String& sanitizedValue, bool valueChanged, bool sendChangeEvent) +{ + InputType::setValue(sanitizedValue, valueChanged, sendChangeEvent); + + if (valueChanged) + element()->updateInnerTextValue(); + + unsigned max = visibleValue().length(); + if (element()->focused()) + element()->setSelectionRange(max, max); + else + element()->cacheSelectionInResponseToSetValue(max); +} + +void TextFieldInputType::dispatchChangeEventInResponseToSetValue() +{ + // If the user is still editing this field, dispatch an input event rather than a change event. + // The change event will be dispatched when editing finishes. + if (element()->focused()) { + element()->dispatchFormControlInputEvent(); + return; + } + InputType::dispatchChangeEventInResponseToSetValue(); +} + +void TextFieldInputType::handleKeydownEvent(KeyboardEvent* event) +{ + if (!element()->focused()) + return; + Frame* frame = element()->document()->frame(); + if (!frame || !frame->editor()->doTextFieldCommandFromEvent(element(), event)) + return; + event->setDefaultHandled(); +} + +void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event) +{ + if (element()->disabled() || element()->readOnly()) + return; + const String& key = event->keyIdentifier(); + int step = 0; + if (key == "Up") + step = 1; + else if (key == "Down") + step = -1; + else + return; + element()->stepUpFromRenderer(step); + event->setDefaultHandled(); +} + +void TextFieldInputType::handleWheelEventForSpinButton(WheelEvent* event) +{ + if (element()->disabled() || element()->readOnly() || !element()->focused()) + return; + int step = 0; + if (event->wheelDeltaY() > 0) + step = 1; + else if (event->wheelDeltaY() < 0) + step = -1; + else + return; + element()->stepUpFromRenderer(step); + event->setDefaultHandled(); +} + +void TextFieldInputType::forwardEvent(Event* event) +{ + if (element()->renderer() && (event->isMouseEvent() || event->isDragEvent() || event->hasInterface(eventNames().interfaceForWheelEvent) || event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent)) { + RenderTextControlSingleLine* renderTextControl = toRenderTextControlSingleLine(element()->renderer()); + if (event->type() == eventNames().blurEvent) { + if (RenderBox* innerTextRenderer = innerTextElement()->renderBox()) { + if (RenderLayer* innerLayer = innerTextRenderer->layer()) + innerLayer->scrollToOffset(!renderTextControl->style()->isLeftToRightDirection() ? innerLayer->scrollWidth() : 0, 0, RenderLayer::ScrollOffsetClamped); + } + + renderTextControl->capsLockStateMayHaveChanged(); + } else if (event->type() == eventNames().focusEvent) + renderTextControl->capsLockStateMayHaveChanged(); + + element()->forwardEvent(event); + } +} + +bool TextFieldInputType::shouldSubmitImplicitly(Event* event) +{ + return (event->type() == eventNames().textInputEvent && event->hasInterface(eventNames().interfaceForTextEvent) && static_cast<TextEvent*>(event)->data() == "\n") || InputType::shouldSubmitImplicitly(event); +} + +RenderObject* TextFieldInputType::createRenderer(RenderArena* arena, RenderStyle*) const +{ + return new (arena) RenderTextControlSingleLine(element()); +} + +bool TextFieldInputType::needsContainer() const +{ +#if ENABLE(INPUT_SPEECH) + return element()->isSpeechEnabled(); +#else + return false; +#endif +} + +void TextFieldInputType::createShadowSubtree() +{ + ASSERT(!m_innerText); + ASSERT(!m_innerBlock); + ASSERT(!m_innerSpinButton); + + Document* document = element()->document(); + RefPtr<RenderTheme> theme = document->page() ? document->page()->theme() : RenderTheme::defaultTheme(); + bool shouldHaveSpinButton = theme->shouldHaveSpinButton(element()); + bool createsContainer = shouldHaveSpinButton || needsContainer(); + + ExceptionCode ec = 0; + m_innerText = TextControlInnerTextElement::create(document); + if (!createsContainer) { + element()->ensureShadowRoot()->appendChild(m_innerText, ec); + return; + } + + ShadowRoot* shadowRoot = element()->ensureShadowRoot(); + m_container = HTMLDivElement::create(document); + m_container->setShadowPseudoId("-webkit-textfield-decoration-container"); + shadowRoot->appendChild(m_container, ec); + + m_innerBlock = TextControlInnerElement::create(document); + m_innerBlock->appendChild(m_innerText, ec); + m_container->appendChild(m_innerBlock, ec); + +#if ENABLE(INPUT_SPEECH) + ASSERT(!m_speechButton); + if (element()->isSpeechEnabled()) { + m_speechButton = InputFieldSpeechButtonElement::create(document); + m_container->appendChild(m_speechButton, ec); + } +#endif + + if (shouldHaveSpinButton) { + m_innerSpinButton = SpinButtonElement::create(document); + m_container->appendChild(m_innerSpinButton, ec); + } +} + +HTMLElement* TextFieldInputType::containerElement() const +{ + return m_container.get(); +} + +HTMLElement* TextFieldInputType::innerBlockElement() const +{ + return m_innerBlock.get(); +} + +HTMLElement* TextFieldInputType::innerTextElement() const +{ + ASSERT(m_innerText); + return m_innerText.get(); +} + +HTMLElement* TextFieldInputType::innerSpinButtonElement() const +{ + return m_innerSpinButton.get(); +} + +#if ENABLE(INPUT_SPEECH) +HTMLElement* TextFieldInputType::speechButtonElement() const +{ + return m_speechButton.get(); +} +#endif + +HTMLElement* TextFieldInputType::placeholderElement() const +{ + return m_placeholder.get(); +} + +void TextFieldInputType::destroyShadowSubtree() +{ + InputType::destroyShadowSubtree(); + m_innerText.clear(); + m_placeholder.clear(); + m_innerBlock.clear(); +#if ENABLE(INPUT_SPEECH) + m_speechButton.clear(); +#endif + m_innerSpinButton.clear(); + m_container.clear(); +} + +void TextFieldInputType::disabledAttributeChanged() +{ + if (m_innerSpinButton) + m_innerSpinButton->releaseCapture(); +} + +void TextFieldInputType::readonlyAttributeChanged() +{ + if (m_innerSpinButton) + m_innerSpinButton->releaseCapture(); +} + +bool TextFieldInputType::shouldUseInputMethod() const +{ + return true; +} + +static bool isASCIILineBreak(UChar c) +{ + return c == '\r' || c == '\n'; +} + +static String limitLength(const String& string, int maxLength) +{ + unsigned newLength = numCharactersInGraphemeClusters(string, maxLength); + for (unsigned i = 0; i < newLength; ++i) { + const UChar current = string[i]; + if (current < ' ' && current != '\t') { + newLength = i; + break; + } + } + return string.left(newLength); +} + +String TextFieldInputType::sanitizeValue(const String& proposedValue) const +{ + return limitLength(proposedValue.removeCharacters(isASCIILineBreak), HTMLInputElement::maximumLength); +} + +void TextFieldInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event) +{ + // Make sure that the text to be inserted will not violate the maxLength. + + // We use RenderTextControlSingleLine::text() instead of InputElement::value() + // because they can be mismatched by sanitizeValue() in + // HTMLInputElement::subtreeHasChanged() in some cases. + unsigned oldLength = numGraphemeClusters(element()->innerTextValue()); + + // selectionLength represents the selection length of this text field to be + // removed by this insertion. + // If the text field has no focus, we don't need to take account of the + // selection length. The selection is the source of text drag-and-drop in + // that case, and nothing in the text field will be removed. + unsigned selectionLength = element()->focused() ? numGraphemeClusters(plainText(element()->document()->frame()->selection()->selection().toNormalizedRange().get())) : 0; + ASSERT(oldLength >= selectionLength); + + // Selected characters will be removed by the next text event. + unsigned baseLength = oldLength - selectionLength; + unsigned maxLength = static_cast<unsigned>(isTextType() ? element()->maxLength() : HTMLInputElement::maximumLength); // maxLength can never be negative. + unsigned appendableLength = maxLength > baseLength ? maxLength - baseLength : 0; + + // Truncate the inserted text to avoid violating the maxLength and other constraints. + String eventText = event->text(); + eventText.replace("\r\n", " "); + eventText.replace('\r', ' '); + eventText.replace('\n', ' '); + + event->setText(limitLength(eventText, appendableLength)); +} + +bool TextFieldInputType::shouldRespectListAttribute() +{ + return true; +} + +void TextFieldInputType::updatePlaceholderText() +{ + if (!supportsPlaceholder()) + return; + ExceptionCode ec = 0; + String placeholderText = element()->strippedPlaceholder(); + if (placeholderText.isEmpty()) { + if (m_placeholder) { + m_placeholder->parentNode()->removeChild(m_placeholder.get(), ec); + ASSERT(!ec); + m_placeholder.clear(); + } + return; + } + if (!m_placeholder) { + m_placeholder = HTMLDivElement::create(element()->document()); + m_placeholder->setShadowPseudoId("-webkit-input-placeholder"); + element()->shadowRoot()->insertBefore(m_placeholder, m_container ? m_container->nextSibling() : innerTextElement()->nextSibling(), ec); + ASSERT(!ec); + } + m_placeholder->setInnerText(placeholderText, ec); + ASSERT(!ec); +} + +bool TextFieldInputType::appendFormData(FormDataList& list, bool multipart) const +{ + InputType::appendFormData(list, multipart); + const AtomicString& dirnameAttrValue = element()->fastGetAttribute(dirnameAttr); + if (!dirnameAttrValue.isNull()) + list.appendData(dirnameAttrValue, element()->directionForFormData()); + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/TextFieldInputType.h b/Source/WebCore/html/TextFieldInputType.h new file mode 100644 index 000000000..161884d67 --- /dev/null +++ b/Source/WebCore/html/TextFieldInputType.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextFieldInputType_h +#define TextFieldInputType_h + +#include "InputType.h" + +namespace WebCore { + +class FormDataList; +class SpinButtonElement; + +// The class represents types of which UI contain text fields. +// It supports not only the types for BaseTextInputType but also type=number. +class TextFieldInputType : public InputType { +protected: + TextFieldInputType(HTMLInputElement*); + virtual ~TextFieldInputType(); + virtual bool canSetSuggestedValue() OVERRIDE; + virtual void handleKeydownEvent(KeyboardEvent*) OVERRIDE; + void handleKeydownEventForSpinButton(KeyboardEvent*); + void handleWheelEventForSpinButton(WheelEvent*); + + virtual HTMLElement* containerElement() const OVERRIDE; + virtual HTMLElement* innerBlockElement() const OVERRIDE; + virtual HTMLElement* innerTextElement() const OVERRIDE; + virtual HTMLElement* innerSpinButtonElement() const OVERRIDE; +#if ENABLE(INPUT_SPEECH) + virtual HTMLElement* speechButtonElement() const OVERRIDE; +#endif + +protected: + virtual bool needsContainer() const; + virtual void createShadowSubtree() OVERRIDE; + virtual void destroyShadowSubtree() OVERRIDE; + virtual void disabledAttributeChanged() OVERRIDE; + virtual void readonlyAttributeChanged() OVERRIDE; + +private: + virtual bool isTextField() const OVERRIDE; + virtual bool valueMissing(const String&) const OVERRIDE; + virtual void handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*) OVERRIDE; + virtual void forwardEvent(Event*) OVERRIDE; + virtual bool shouldSubmitImplicitly(Event*) OVERRIDE; + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*) const OVERRIDE; + virtual bool shouldUseInputMethod() const OVERRIDE; + virtual void setValue(const String&, bool valueChanged, bool sendChangeEvent) OVERRIDE; + virtual void dispatchChangeEventInResponseToSetValue() OVERRIDE; + virtual String sanitizeValue(const String&) const OVERRIDE; + virtual bool shouldRespectListAttribute() OVERRIDE; + virtual HTMLElement* placeholderElement() const OVERRIDE; + virtual void updatePlaceholderText() OVERRIDE; + virtual bool appendFormData(FormDataList&, bool multipart) const OVERRIDE; + + RefPtr<HTMLElement> m_container; + RefPtr<HTMLElement> m_innerBlock; + RefPtr<HTMLElement> m_innerText; + RefPtr<HTMLElement> m_placeholder; + RefPtr<SpinButtonElement> m_innerSpinButton; +#if ENABLE(INPUT_SPEECH) + RefPtr<HTMLElement> m_speechButton; +#endif +}; + +} // namespace WebCore + +#endif // TextFieldInputType_h diff --git a/Source/WebCore/html/TextInputType.cpp b/Source/WebCore/html/TextInputType.cpp new file mode 100644 index 000000000..a36997693 --- /dev/null +++ b/Source/WebCore/html/TextInputType.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextInputType.h" + +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> TextInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new TextInputType(element)); +} + +const AtomicString& TextInputType::formControlType() const +{ + return InputTypeNames::text(); +} + +bool TextInputType::shouldRespectSpeechAttribute() +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/TextInputType.h b/Source/WebCore/html/TextInputType.h new file mode 100644 index 000000000..421fe5770 --- /dev/null +++ b/Source/WebCore/html/TextInputType.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextInputType_h +#define TextInputType_h + +#include "BaseTextInputType.h" + +namespace WebCore { + +class TextInputType : public BaseTextInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + TextInputType(HTMLInputElement* element) : BaseTextInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool shouldRespectSpeechAttribute() OVERRIDE; +}; + +} // namespace WebCore + +#endif // TextInputType_h diff --git a/Source/WebCore/html/TextMetrics.h b/Source/WebCore/html/TextMetrics.h new file mode 100644 index 000000000..b272ef664 --- /dev/null +++ b/Source/WebCore/html/TextMetrics.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 TextMetrics_h +#define TextMetrics_h + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class TextMetrics : public RefCounted<TextMetrics> { +public: + static PassRefPtr<TextMetrics> create() { return adoptRef(new TextMetrics); } + + float width() const { return m_width; } + void setWidth(float w) { m_width = w; } + +private: + TextMetrics() + : m_width(0) + { } + + float m_width; +}; + +} // namespace WebCore + +#endif // TextMetrics_h diff --git a/Source/WebCore/html/TextMetrics.idl b/Source/WebCore/html/TextMetrics.idl new file mode 100644 index 000000000..1a315bac2 --- /dev/null +++ b/Source/WebCore/html/TextMetrics.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 TextMetrics { + readonly attribute float width; + }; + +} diff --git a/Source/WebCore/html/TextTrack.cpp b/Source/WebCore/html/TextTrack.cpp new file mode 100644 index 000000000..164fe3355 --- /dev/null +++ b/Source/WebCore/html/TextTrack.cpp @@ -0,0 +1,257 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "TextTrack.h" + +#include "Event.h" +#include "ExceptionCode.h" +#include "TextTrackCueList.h" +#include "TrackBase.h" + +namespace WebCore { + +const AtomicString& TextTrack::subtitlesKeyword() +{ + DEFINE_STATIC_LOCAL(const AtomicString, subtitles, ("subtitles")); + return subtitles; +} + +const AtomicString& TextTrack::captionsKeyword() +{ + DEFINE_STATIC_LOCAL(const AtomicString, captions, ("captions")); + return captions; +} + +const AtomicString& TextTrack::descriptionsKeyword() +{ + DEFINE_STATIC_LOCAL(const AtomicString, descriptions, ("descriptions")); + return descriptions; +} + +const AtomicString& TextTrack::chaptersKeyword() +{ + DEFINE_STATIC_LOCAL(const AtomicString, chapters, ("chapters")); + return chapters; +} + +const AtomicString& TextTrack::metadataKeyword() +{ + DEFINE_STATIC_LOCAL(const AtomicString, metadata, ("metadata")); + return metadata; +} + +TextTrack::TextTrack(ScriptExecutionContext* context, TextTrackClient* client, const String& kind, const String& label, const String& language, TextTrackType type) + : TrackBase(context, TrackBase::TextTrack) + , m_mediaElement(0) + , m_label(label) + , m_language(language) + , m_mode(TextTrack::DISABLED) + , m_client(client) + , m_trackType(type) + , m_readinessState(NotLoaded) + , m_showingByDefault(false) +{ + setKind(kind); +} + +TextTrack::~TextTrack() +{ + if (m_client && m_cues) + m_client->textTrackRemoveCues(this, m_cues.get()); + clearClient(); +} + +bool TextTrack::isValidKindKeyword(const String& value) +{ + if (equalIgnoringCase(value, subtitlesKeyword())) + return true; + if (equalIgnoringCase(value, captionsKeyword())) + return true; + if (equalIgnoringCase(value, descriptionsKeyword())) + return true; + if (equalIgnoringCase(value, chaptersKeyword())) + return true; + if (equalIgnoringCase(value, metadataKeyword())) + return true; + + return false; +} + +void TextTrack::setKind(const String& kind) +{ + String oldKind = m_kind; + + if (isValidKindKeyword(kind)) + m_kind = kind; + else + m_kind = subtitlesKeyword(); + + if (m_client && oldKind != m_kind) + m_client->textTrackKindChanged(this); +} + +void TextTrack::setMode(unsigned short mode, ExceptionCode& ec) +{ + // 4.8.10.12.5 On setting the mode, if the new value is not either 0, 1, or 2, + // the user agent must throw an INVALID_ACCESS_ERR exception. + if (mode > TextTrack::SHOWING) { + ec = INVALID_ACCESS_ERR; + return; + } + + if (m_mode == static_cast<Mode>(mode)) + return; + + // If the new value is 2 + // ... Note: If the mode had been showing by default, this will change it to showing, + // even though the value of mode would appear not to change. + m_mode = static_cast<Mode>(mode); + if (m_mode == TextTrack::SHOWING) + setShowingByDefault(false); + + if (m_client) + m_client->textTrackModeChanged(this); +} + +TextTrack::Mode TextTrack::mode() const +{ + // The text track "showing" and "showing by default" modes return SHOWING (numeric value 2) + if (m_showingByDefault) + return SHOWING; + return m_mode; +} + +TextTrackCueList* TextTrack::cues() +{ + if (!m_cues) + m_cues = TextTrackCueList::create(); + + // 4.8.10.12.5 If the text track mode ... is not the text track disabled mode, + // then the cues attribute must return a live TextTrackCueList object ... + // Otherwise, it must return null. When an object is returned, the + // same object must be returned each time. + // http://www.whatwg.org/specs/web-apps/current-work/#dom-texttrack-cues + if (m_cues && m_mode != TextTrack::DISABLED) + return m_cues.get(); + return 0; +} + +TextTrackCueList* TextTrack::activeCues() const +{ + // 4.8.10.12.5 If the text track mode ... is not the text track disabled mode, + // then the activeCues attribute must return a live TextTrackCueList object ... + // ... whose active flag was set when the script started, in text track cue + // order. Otherwise, it must return null. When an object is returned, the + // same object must be returned each time. + // http://www.whatwg.org/specs/web-apps/current-work/#dom-texttrack-activecues + if (m_cues && m_mode != TextTrack::DISABLED) + return m_cues->activeCues(); + return 0; +} + +void TextTrack::addCue(PassRefPtr<TextTrackCue> prpCue, ExceptionCode& ec) +{ + if (!prpCue) + return; + + RefPtr<TextTrackCue> cue = prpCue; + + // 4.8.10.12.4 Text track API + + // The addCue(cue) method of TextTrack objects, when invoked, must run the following steps: + + // 1. If the given cue is already associated with a text track other than + // the method's TextTrack object's text track, then throw an InvalidStateError + // exception and abort these steps. + TextTrack* cueTrack = cue->track(); + if (cueTrack && cueTrack != this) { + ec = INVALID_STATE_ERR; + return; + } + + // 2. Associate cue with the method's TextTrack object's text track, if it is + // not currently associated with a text track. + cue->setTrack(this); + + // 3. If the given cue is already listed in the method's TextTrack object's text + // track's text track list of cues, then throw an InvalidStateError exception. + // 4. Add cue to the method's TextTrack object's text track's text track list of cues. + if (!m_cues->add(cue)) { + ec = INVALID_STATE_ERR; + return; + } + + if (m_client) + m_client->textTrackAddCue(this, cue.get()); +} + +void TextTrack::removeCue(TextTrackCue* cue, ExceptionCode& ec) +{ + if (!cue) + return; + + // 4.8.10.12.4 Text track API + + // The removeCue(cue) method of TextTrack objects, when invoked, must run the following steps: + + // 1. If the given cue is not associated with the method's TextTrack + // object's text track, then throw an InvalidStateError exception. + if (cue->track() != this) { + ec = INVALID_STATE_ERR; + return; + } + + // 2. If the given cue is not currently listed in the method's TextTrack + // object's text track's text track list of cues, then throw a NotFoundError exception. + // 3. Remove cue from the method's TextTrack object's text track's text track list of cues. + if (!m_cues->remove(cue)) { + ec = INVALID_STATE_ERR; + return; + } + + cue->setTrack(0); + if (m_client) + m_client->textTrackRemoveCue(this, cue); +} + +void TextTrack::fireCueChangeEvent() +{ + ExceptionCode ec = 0; + dispatchEvent(Event::create(eventNames().cuechangeEvent, false, false), ec); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/TextTrack.h b/Source/WebCore/html/TextTrack.h new file mode 100644 index 000000000..9d69d5976 --- /dev/null +++ b/Source/WebCore/html/TextTrack.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextTrack_h +#define TextTrack_h + +#if ENABLE(VIDEO_TRACK) + +#include "ExceptionCode.h" +#include "TrackBase.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class HTMLMediaElement; +class TextTrack; +class TextTrackCue; +class TextTrackCueList; + +class TextTrackClient { +public: + virtual ~TextTrackClient() { } + virtual void textTrackKindChanged(TextTrack*) = 0; + virtual void textTrackModeChanged(TextTrack*) = 0; + virtual void textTrackAddCues(TextTrack*, const TextTrackCueList*) = 0; + virtual void textTrackRemoveCues(TextTrack*, const TextTrackCueList*) = 0; + virtual void textTrackAddCue(TextTrack*, PassRefPtr<TextTrackCue>) = 0; + virtual void textTrackRemoveCue(TextTrack*, PassRefPtr<TextTrackCue>) = 0; +}; + +class TextTrack : public TrackBase { +public: + static PassRefPtr<TextTrack> create(ScriptExecutionContext* context, TextTrackClient* client, const String& kind, const String& label, const String& language) + { + return adoptRef(new TextTrack(context, client, kind, label, language, AddTrack)); + } + virtual ~TextTrack(); + + void setMediaElement(HTMLMediaElement* element) { m_mediaElement = element; } + HTMLMediaElement* mediaElement() { return m_mediaElement; } + + String kind() const { return m_kind; } + void setKind(const String&); + + static const AtomicString& subtitlesKeyword(); + static const AtomicString& captionsKeyword(); + static const AtomicString& descriptionsKeyword(); + static const AtomicString& chaptersKeyword(); + static const AtomicString& metadataKeyword(); + static bool isValidKindKeyword(const String&); + + String label() const { return m_label; } + void setLabel(const String& label) { m_label = label; } + + String language() const { return m_language; } + void setLanguage(const String& language) { m_language = language; } + + enum Mode { DISABLED = 0, HIDDEN = 1, SHOWING = 2 }; + Mode mode() const; + void setMode(unsigned short, ExceptionCode&); + + bool showingByDefault() const { return m_showingByDefault; } + void setShowingByDefault(bool showing) { m_showingByDefault = showing; } + + enum ReadinessState { NotLoaded = 0, Loading = 1, Loaded = 2, FailedToLoad = 3 }; + ReadinessState readinessState() const { return m_readinessState; } + void setReadinessState(ReadinessState state) { m_readinessState = state; } + + TextTrackCueList* cues(); + TextTrackCueList* activeCues() const; + + virtual void clearClient() { m_client = 0; } + TextTrackClient* client() { return m_client; } + + void addCue(PassRefPtr<TextTrackCue>, ExceptionCode&); + void removeCue(TextTrackCue*, ExceptionCode&); + + virtual void fireCueChangeEvent(); + DEFINE_ATTRIBUTE_EVENT_LISTENER(cuechange); + + enum TextTrackType { TrackElement, AddTrack, InBand }; + TextTrackType trackType() const { return m_trackType; } + +protected: + TextTrack(ScriptExecutionContext*, TextTrackClient*, const String& kind, const String& label, const String& language, TextTrackType); + + RefPtr<TextTrackCueList> m_cues; + +private: + HTMLMediaElement* m_mediaElement; + String m_kind; + String m_label; + String m_language; + Mode m_mode; + TextTrackClient* m_client; + TextTrackType m_trackType; + ReadinessState m_readinessState; + bool m_showingByDefault; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/TextTrack.idl b/Source/WebCore/html/TextTrack.idl new file mode 100644 index 000000000..0ac01b438 --- /dev/null +++ b/Source/WebCore/html/TextTrack.idl @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=VIDEO_TRACK, + EnabledAtRuntime=webkitVideoTrack, + EventTarget, + CustomMarkFunction, + CustomIsReachable + ] TextTrack { + readonly attribute DOMString kind; + readonly attribute DOMString label; + readonly attribute DOMString language; + + const unsigned short DISABLED = 0; + const unsigned short HIDDEN = 1; + const unsigned short SHOWING = 2; + attribute unsigned short mode + setter raises (DOMException); + + readonly attribute TextTrackCueList cues; + readonly attribute TextTrackCueList activeCues; + attribute EventListener oncuechange; + + void addCue(in TextTrackCue cue) + raises (DOMException); + void removeCue(in TextTrackCue cue) + raises (DOMException); + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; +} diff --git a/Source/WebCore/html/TextTrackCue.cpp b/Source/WebCore/html/TextTrackCue.cpp new file mode 100644 index 000000000..55519e5bd --- /dev/null +++ b/Source/WebCore/html/TextTrackCue.cpp @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "TextTrackCue.h" + +#include "Event.h" +#include "DocumentFragment.h" +#include "TextTrack.h" +#include "WebVTTParser.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +TextTrackCue::TextTrackCue(ScriptExecutionContext* context, const String& id, double start, double end, const String& content, const String& settings, bool pauseOnExit) + : m_id(id) + , m_startTime(start) + , m_endTime(end) + , m_content(content) + , m_writingDirection(Horizontal) + , m_linePosition(-1) + , m_textPosition(50) + , m_cueSize(100) + , m_cueAlignment(Middle) + , m_scriptExecutionContext(context) + , m_isActive(false) + , m_pauseOnExit(pauseOnExit) + , m_snapToLines(true) +{ + parseSettings(settings); +} + +TextTrackCue::~TextTrackCue() +{ +} + +TextTrack* TextTrackCue::track() const +{ + return m_track.get(); +} + +void TextTrackCue::setTrack(PassRefPtr<TextTrack>track) +{ + m_track = track; +} + +String TextTrackCue::id() const +{ + return m_id; +} + +double TextTrackCue::startTime() const +{ + return m_startTime; +} + +double TextTrackCue::endTime() const +{ + return m_endTime; +} + +bool TextTrackCue::pauseOnExit() const +{ + return m_pauseOnExit; +} + +String TextTrackCue::direction() const +{ + switch (m_writingDirection) { + case Horizontal: + return "horizontal"; + case VerticalGrowingLeft: + return "vertical"; + case VerticalGrowingRight: + return "vertical-lr"; + default: + return ""; + } +} + +String TextTrackCue::alignment() const +{ + switch (m_cueAlignment) { + case Start: + return "start"; + case Middle: + return "middle"; + case End: + return "end"; + default: + return ""; + } +} + +String TextTrackCue::getCueAsSource() +{ + return m_content; +} + +PassRefPtr<DocumentFragment> TextTrackCue::getCueAsHTML() +{ + return m_documentFragment; +} + +void TextTrackCue::setCueHTML(PassRefPtr<DocumentFragment> fragment) +{ + m_documentFragment = fragment; +} + +bool TextTrackCue::isActive() +{ + return m_isActive && track() && track()->mode() != TextTrack::DISABLED; +} + +void TextTrackCue::setIsActive(bool active) +{ + m_isActive = active; + + // When a TextTrack's mode is disabled: No cues are active, no events are fired ... + if (!track() || track()->mode() == TextTrack::DISABLED) + return; + + ExceptionCode ec = 0; + if (active) + dispatchEvent(Event::create(eventNames().enterEvent, false, false), ec); + else + dispatchEvent(Event::create(eventNames().exitEvent, false, false), ec); + + if (m_track) + m_track->fireCueChangeEvent(); +} + +void TextTrackCue::parseSettings(const String& input) +{ + // 4.8.10.13.3 Parse the WebVTT settings. + // 1 - Initial setup. + unsigned position = 0; + while (position < input.length()) { + // Discard any space characters between or after settings (not in the spec, but we think it should be). + while (position < input.length() && WebVTTParser::isASpace(input[position])) + position++; + + // 2-4 Settings - get the next character representing a settings. + char setting = input[position++]; + if (position >= input.length()) + return; + + // 5-7 - If the character at position is not ':', set setting to empty string. + if (input[position++] != ':') + setting = '\0'; + if (position >= input.length()) + return; + + // 8 - Gather settings based on value of setting. + switch (setting) { + case 'D': + { + // 1-3 - Collect the next word and set the writing direction accordingly. + String writingDirection = WebVTTParser::collectWord(input, &position); + if (writingDirection == "vertical") + m_writingDirection = VerticalGrowingLeft; + else if (writingDirection == "vertical-lr") + m_writingDirection = VerticalGrowingRight; + } + break; + case 'L': + { + // 1-2 - Collect chars that are either '-', '%', or a digit. + StringBuilder linePositionBuilder; + while (position < input.length() && (input[position] == '-' || input[position] == '%' || isASCIIDigit(input[position]))) + linePositionBuilder.append(input[position++]); + if (position < input.length() && !WebVTTParser::isASpace(input[position])) + goto Otherwise; + String linePosition = linePositionBuilder.toString(); + + // 3-5 - If there is not at least one digit character, + // a '-' occurs anywhere other than the front, or + // a '%' occurs anywhere other than the end, then + // ignore this setting and keep looking. + if (linePosition.find('-', 1) != notFound || linePosition.reverseFind("%", linePosition.length() - 2) != notFound) + break; + + // 6 - If the first char is a '-' and the last char is a '%', ignore and keep looking. + if (linePosition[0] == '-' && linePosition[linePosition.length() - 1] == '%') + break; + + // 7 - Interpret as number (toInt ignores trailing non-digit characters, + // such as a possible '%'). + bool validNumber; + int number = linePosition.toInt(&validNumber); + if (!validNumber) + break; + + // 8 - If the last char is a '%' and the value is not between 0 and 100, ignore and keep looking. + if (linePosition[linePosition.length() - 1] == '%') { + if (number < 0 || number > 100) + break; + + // 10 - If '%' then set snap-to-lines flag to false. + m_snapToLines = false; + } + + // 9 - Set cue line position to the number found. + m_linePosition = number; + } + break; + case 'T': + { + // 1-2 - Collect characters that are digits. + String textPosition = WebVTTParser::collectDigits(input, &position); + if (position >= input.length()) + break; + + // 3 - Character at end must be '%', otherwise ignore and keep looking. + if (input[position++] != '%') + goto Otherwise; + + // 4-6 - Ensure no other chars in this setting and setting is not empty. + if (position < input.length() && !WebVTTParser::isASpace(input[position])) + goto Otherwise; + if (textPosition.isEmpty()) + break; + + // 7-8 - Interpret as number and make sure it is between 0 and 100 + // (toInt ignores trailing non-digit characters, such as a possible '%'). + bool validNumber; + int number = textPosition.toInt(&validNumber); + if (!validNumber) + break; + if (number < 0 || number > 100) + break; + + // 9 - Set cue text position to the number found. + m_textPosition = number; + } + break; + case 'S': + { + // 1-2 - Collect characters that are digits. + String cueSize = WebVTTParser::collectDigits(input, &position); + if (position >= input.length()) + break; + + // 3 - Character at end must be '%', otherwise ignore and keep looking. + if (input[position++] != '%') + goto Otherwise; + + // 4-6 - Ensure no other chars in this setting and setting is not empty. + if (position < input.length() && !WebVTTParser::isASpace(input[position])) + goto Otherwise; + if (cueSize.isEmpty()) + break; + + // 7-8 - Interpret as number and make sure it is between 0 and 100. + bool validNumber; + int number = cueSize.toInt(&validNumber); + if (!validNumber) + break; + if (number < 0 || number > 100) + break; + + // 9 - Set cue size to the number found. + m_cueSize = number; + } + break; + case 'A': + { + // 1-4 - Collect the next word and set the cue alignment accordingly. + String cueAlignment = WebVTTParser::collectWord(input, &position); + if (cueAlignment == "start") + m_cueAlignment = Start; + else if (cueAlignment == "middle") + m_cueAlignment = Middle; + else if (cueAlignment == "end") + m_cueAlignment = End; + } + break; + } + + continue; + +Otherwise: + // Collect a sequence of characters that are not space characters and discard them. + WebVTTParser::collectWord(input, &position); + } +} + +const AtomicString& TextTrackCue::interfaceName() const +{ + return eventNames().interfaceForTextTrackCue; +} + +ScriptExecutionContext* TextTrackCue::scriptExecutionContext() const +{ + return m_scriptExecutionContext; +} + +EventTargetData* TextTrackCue::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* TextTrackCue::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/TextTrackCue.h b/Source/WebCore/html/TextTrackCue.h new file mode 100644 index 000000000..9480afa0a --- /dev/null +++ b/Source/WebCore/html/TextTrackCue.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextTrackCue_h +#define TextTrackCue_h + +#if ENABLE(VIDEO_TRACK) + +#include "EventTarget.h" +#include "TextTrack.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class DocumentFragment; +class ScriptExecutionContext; +class TextTrack; + +class TextTrackCue : public RefCounted<TextTrackCue>, public EventTarget { +public: + static PassRefPtr<TextTrackCue> create(ScriptExecutionContext* context, const String& id, double start, double end, const String& content, const String& settings, bool pauseOnExit) + { + return adoptRef(new TextTrackCue(context, id, start, end, content, settings, pauseOnExit)); + } + + enum Direction { Horizontal, VerticalGrowingLeft, VerticalGrowingRight }; + enum Alignment { Start, Middle, End }; + + virtual ~TextTrackCue(); + + TextTrack* track() const; + void setTrack(PassRefPtr<TextTrack>); + + String id() const; + double startTime() const; + double endTime() const; + bool pauseOnExit() const; + + String direction() const; + bool snapToLines() const { return m_snapToLines; } + int linePosition() const { return m_linePosition; } + int textPosition() const { return m_textPosition; } + int size() const { return m_cueSize; } + String alignment() const; + + String getCueAsSource(); + PassRefPtr<DocumentFragment> getCueAsHTML(); + void setCueHTML(PassRefPtr<DocumentFragment>); + + bool isActive(); + void setIsActive(bool); + + virtual const AtomicString& interfaceName() const; + virtual ScriptExecutionContext* scriptExecutionContext() const; + + DEFINE_ATTRIBUTE_EVENT_LISTENER(enter); + DEFINE_ATTRIBUTE_EVENT_LISTENER(exit); + + using RefCounted<TextTrackCue>::ref; + using RefCounted<TextTrackCue>::deref; + +protected: + + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + +private: + TextTrackCue(ScriptExecutionContext*, const String& id, double start, double end, const String& content, const String& settings, bool pauseOnExit); + + void parseSettings(const String&); + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + String m_id; + double m_startTime; + double m_endTime; + String m_content; + Direction m_writingDirection; + int m_linePosition; + int m_textPosition; + int m_cueSize; + Alignment m_cueAlignment; + RefPtr<DocumentFragment> m_documentFragment; + RefPtr<TextTrack> m_track; + + EventTargetData m_eventTargetData; + ScriptExecutionContext* m_scriptExecutionContext; + + bool m_isActive; + bool m_pauseOnExit; + bool m_snapToLines; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/TextTrackCue.idl b/Source/WebCore/html/TextTrackCue.idl new file mode 100644 index 000000000..8f43aba2b --- /dev/null +++ b/Source/WebCore/html/TextTrackCue.idl @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=VIDEO_TRACK, + EnabledAtRuntime=webkitVideoTrack, + GenerateNativeConverter, + Constructor(in DOMString id, in double startTime, in double endTime, in DOMString text, in [Optional=CallWithDefaultValue] DOMString settings, in [Optional=CallWithDefaultValue] boolean pauseOnExit), + CallWith=ScriptExecutionContext, + EventTarget, + CustomMarkFunction, + CustomIsReachable + ] TextTrackCue { + readonly attribute TextTrack track; + readonly attribute DOMString id; + + readonly attribute double startTime; + readonly attribute double endTime; + readonly attribute boolean pauseOnExit; + + readonly attribute DOMString direction; + readonly attribute boolean snapToLines; + readonly attribute long linePosition; + readonly attribute long textPosition; + readonly attribute long size; + readonly attribute DOMString alignment; + + DOMString getCueAsSource(); + DocumentFragment getCueAsHTML(); + + attribute EventListener onenter; + attribute EventListener onexit; + + // EventTarget interface + void addEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +}
\ No newline at end of file diff --git a/Source/WebCore/html/TextTrackCueList.cpp b/Source/WebCore/html/TextTrackCueList.cpp new file mode 100644 index 000000000..34b71ca1b --- /dev/null +++ b/Source/WebCore/html/TextTrackCueList.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "TextTrackCueList.h" + +namespace WebCore { + +TextTrackCueList::TextTrackCueList() +{ +} + +unsigned long TextTrackCueList::length() const +{ + return m_list.size(); +} + +TextTrackCue* TextTrackCueList::item(unsigned index) const +{ + if (index < m_list.size()) + return m_list[index].get(); + return 0; +} + +TextTrackCue* TextTrackCueList::getCueById(const String& id) const +{ + for (size_t i = 0; i < m_list.size(); ++i) { + if (m_list[i]->id() == id) + return m_list[i].get(); + } + return 0; +} + +TextTrackCueList* TextTrackCueList::activeCues() +{ + if (!m_activeCues) + m_activeCues = create(); + + m_activeCues->clear(); + for (size_t i = 0; i < m_list.size(); ++i) { + RefPtr<TextTrackCue> cue = m_list[i]; + if (cue->isActive()) + m_activeCues->add(cue); + } + return m_activeCues.get(); +} + +bool TextTrackCueList::add(PassRefPtr<TextTrackCue> cue) +{ + return add(cue, 0, m_list.size()); +} + +bool TextTrackCueList::add(PassRefPtr<TextTrackCue> prpCue, size_t start, size_t end) +{ + ASSERT(start <= m_list.size()); + ASSERT(end <= m_list.size()); + + // Maintain text track cue order: + // http://www.whatwg.org/specs/web-apps/current-work/#text-track-cue-order + RefPtr<TextTrackCue> cue = prpCue; + if (start == end) { + if (!m_list.isEmpty() && (m_list[start - 1].get() == cue.get())) + return false; + + m_list.insert(start, cue); + return true; + } + + size_t index = (start + end) / 2; + if (cue->startTime() < m_list[index]->startTime() || (cue->startTime() == m_list[index]->startTime() && cue->endTime() > m_list[index]->endTime())) + return add(cue.release(), start, index); + + return add(cue.release(), index + 1, end); +} + +bool TextTrackCueList::remove(TextTrackCue* cue) +{ + size_t index = m_list.find(cue); + if (index == notFound) + return false; + + cue->setIsActive(false); + m_list.remove(index); + return true; +} + +bool TextTrackCueList::contains(TextTrackCue* cue) const +{ + return m_list.contains(cue); +} + +void TextTrackCueList::clear() +{ + m_list.clear(); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/TextTrackCueList.h b/Source/WebCore/html/TextTrackCueList.h new file mode 100644 index 000000000..7a7f90415 --- /dev/null +++ b/Source/WebCore/html/TextTrackCueList.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextTrackCueList_h +#define TextTrackCueList_h + +#if ENABLE(VIDEO_TRACK) + +#include "TextTrackCue.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class TextTrackCueList : public RefCounted<TextTrackCueList> { +public: + static PassRefPtr<TextTrackCueList> create() + { + return adoptRef(new TextTrackCueList); + } + + ~TextTrackCueList() { } + + unsigned long length() const; + TextTrackCue* item(unsigned index) const; + TextTrackCue* getCueById(const String&) const; + TextTrackCueList* activeCues(); + + bool add(PassRefPtr<TextTrackCue>); + bool remove(TextTrackCue*); + bool contains(TextTrackCue*) const; + +private: + TextTrackCueList(); + bool add(PassRefPtr<TextTrackCue>, size_t, size_t); + void clear(); + + Vector<RefPtr<TextTrackCue> > m_list; + RefPtr<TextTrackCueList> m_activeCues; + +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/TextTrackCueList.idl b/Source/WebCore/html/TextTrackCueList.idl new file mode 100644 index 000000000..c1b700d04 --- /dev/null +++ b/Source/WebCore/html/TextTrackCueList.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=VIDEO_TRACK, + EnabledAtRuntime=webkitVideoTrack, + HasIndexGetter + ] TextTrackCueList { + readonly attribute unsigned long length; + TextTrackCue item(in unsigned long index); + TextTrackCue getCueById(in DOMString id); + }; + +}
\ No newline at end of file diff --git a/Source/WebCore/html/TimeInputType.cpp b/Source/WebCore/html/TimeInputType.cpp new file mode 100644 index 000000000..71092d0a8 --- /dev/null +++ b/Source/WebCore/html/TimeInputType.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TimeInputType.h" + +#include "DateComponents.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/CurrentTime.h> +#include <wtf/DateMath.h> +#include <wtf/MathExtras.h> +#include <wtf/PassOwnPtr.h> + +#if ENABLE(INPUT_TYPE_TIME) + +namespace WebCore { + +using namespace HTMLNames; + +static const double timeDefaultStep = 60.0; +static const double timeStepScaleFactor = 1000.0; + +PassOwnPtr<InputType> TimeInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new TimeInputType(element)); +} + +const AtomicString& TimeInputType::formControlType() const +{ + return InputTypeNames::time(); +} + +DateComponents::Type TimeInputType::dateType() const +{ + return DateComponents::Time; +} + +double TimeInputType::defaultValueForStepUp() const +{ + double current = currentTimeMS(); + double utcOffset = calculateUTCOffset(); + double dstOffset = calculateDSTOffset(current, utcOffset); + int offset = static_cast<int>((utcOffset + dstOffset) / msPerMinute); + current += offset * msPerMinute; + + DateComponents date; + date.setMillisecondsSinceMidnight(current); + double milliseconds = date.millisecondsSinceEpoch(); + ASSERT(isfinite(milliseconds)); + return milliseconds; +} + +double TimeInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumTime()); +} + +double TimeInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumTime()); +} + +double TimeInputType::defaultStep() const +{ + return timeDefaultStep; +} + +double TimeInputType::stepScaleFactor() const +{ + return timeStepScaleFactor; +} + +bool TimeInputType::scaledStepValueShouldBeInteger() const +{ + return true; +} + +bool TimeInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const +{ + ASSERT(out); + unsigned end; + return out->parseTime(characters, length, 0, end) && end == length; +} + +bool TimeInputType::setMillisecondToDateComponents(double value, DateComponents* date) const +{ + ASSERT(date); + return date->setMillisecondsSinceMidnight(value); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/TimeInputType.h b/Source/WebCore/html/TimeInputType.h new file mode 100644 index 000000000..c06709e66 --- /dev/null +++ b/Source/WebCore/html/TimeInputType.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TimeInputType_h +#define TimeInputType_h + +#include "BaseDateAndTimeInputType.h" + +#if ENABLE(INPUT_TYPE_TIME) + +namespace WebCore { + +class TimeInputType : public BaseDateAndTimeInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + TimeInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual DateComponents::Type dateType() const OVERRIDE; + virtual double defaultValueForStepUp() const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual bool scaledStepValueShouldBeInteger() const OVERRIDE; + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; +}; + +} // namespace WebCore + +#endif +#endif // TimeInputType_h diff --git a/Source/WebCore/html/TimeRanges.cpp b/Source/WebCore/html/TimeRanges.cpp new file mode 100644 index 000000000..d68ea0371 --- /dev/null +++ b/Source/WebCore/html/TimeRanges.cpp @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "TimeRanges.h" + +#include "ExceptionCode.h" +#include <math.h> + +using namespace WebCore; +using namespace std; + +TimeRanges::TimeRanges(float start, float end) +{ + add(start, end); +} + +PassRefPtr<TimeRanges> TimeRanges::copy() const +{ + RefPtr<TimeRanges> newSession = TimeRanges::create(); + + unsigned size = m_ranges.size(); + for (unsigned i = 0; i < size; i++) + newSession->add(m_ranges[i].m_start, m_ranges[i].m_end); + + return newSession.release(); +} + +void TimeRanges::invert() +{ + RefPtr<TimeRanges> inverted = TimeRanges::create(); + float posInf = std::numeric_limits<float>::infinity(); + float negInf = -std::numeric_limits<float>::infinity(); + + if (!m_ranges.size()) + inverted->add(negInf, posInf); + else { + if (float start = m_ranges.first().m_start != negInf) + inverted->add(negInf, start); + + for (size_t index = 0; index + 1 < m_ranges.size(); ++index) + inverted->add(m_ranges[index].m_end, m_ranges[index + 1].m_start); + + if (float end = m_ranges.last().m_end != posInf) + inverted->add(end, posInf); + } + + m_ranges.swap(inverted->m_ranges); +} + +void TimeRanges::intersectWith(const TimeRanges* other) +{ + ASSERT(other); + RefPtr<TimeRanges> inverted = copy(); + RefPtr<TimeRanges> invertedOther = other->copy(); + inverted->unionWith(invertedOther.get()); + inverted->invert(); + + m_ranges.swap(inverted->m_ranges); +} + +void TimeRanges::unionWith(const TimeRanges* other) +{ + ASSERT(other); + RefPtr<TimeRanges> unioned = copy(); + for (size_t index = 0; index < other->m_ranges.size(); ++index) { + const Range& range = other->m_ranges[index]; + unioned->add(range.m_start, range.m_end); + } + + m_ranges.swap(unioned->m_ranges); +} + +float TimeRanges::start(unsigned index, ExceptionCode& ec) const +{ + if (index >= length()) { + ec = INDEX_SIZE_ERR; + return 0; + } + return m_ranges[index].m_start; +} + +float TimeRanges::end(unsigned index, ExceptionCode& ec) const +{ + if (index >= length()) { + ec = INDEX_SIZE_ERR; + return 0; + } + return m_ranges[index].m_end; +} + +void TimeRanges::add(float start, float end) +{ + ASSERT(start <= end); + unsigned int overlappingArcIndex; + Range addedRange(start, end); + + // For each present range check if we need to: + // - merge with the added range, in case we are overlapping or contiguous + // - Need to insert in place, we we are completely, not overlapping and not contiguous + // in between two ranges. + // + // TODO: Given that we assume that ranges are correctly ordered, this could be optimized. + + for (overlappingArcIndex = 0; overlappingArcIndex < m_ranges.size(); overlappingArcIndex++) { + if (addedRange.isOverlappingRange(m_ranges[overlappingArcIndex]) + || addedRange.isContiguousWithRange(m_ranges[overlappingArcIndex])) { + // We need to merge the addedRange and that range. + addedRange = addedRange.unionWithOverlappingOrContiguousRange(m_ranges[overlappingArcIndex]); + m_ranges.remove(overlappingArcIndex); + overlappingArcIndex--; + } else { + // Check the case for which there is no more to do + if (!overlappingArcIndex) { + if (addedRange.isBeforeRange(m_ranges[0])) { + // First index, and we are completely before that range (and not contiguous, nor overlapping). + // We just need to be inserted here. + break; + } + } else { + if (m_ranges[overlappingArcIndex - 1].isBeforeRange(addedRange) + && addedRange.isBeforeRange(m_ranges[overlappingArcIndex])) { + // We are exactly after the current previous range, and before the current range, while + // not overlapping with none of them. Insert here. + break; + } + } + } + } + + // Now that we are sure we don't overlap with any range, just add it. + m_ranges.insert(overlappingArcIndex, addedRange); +} + +bool TimeRanges::contain(float time) const +{ + ExceptionCode unused; + for (unsigned n = 0; n < length(); n++) { + if (time >= start(n, unused) && time <= end(n, unused)) + return true; + } + return false; +} + +float TimeRanges::nearest(float time) const +{ + ExceptionCode unused; + float closest = 0; + unsigned count = length(); + for (unsigned ndx = 0; ndx < count; ndx++) { + float startTime = start(ndx, unused); + float endTime = end(ndx, unused); + if (time >= startTime && time <= endTime) + return time; + if (fabs(startTime - time) < closest) + closest = fabsf(startTime - time); + else if (fabs(endTime - time) < closest) + closest = fabsf(endTime - time); + } + return closest; +} diff --git a/Source/WebCore/html/TimeRanges.h b/Source/WebCore/html/TimeRanges.h new file mode 100644 index 000000000..425769036 --- /dev/null +++ b/Source/WebCore/html/TimeRanges.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TimeRanges_h +#define TimeRanges_h + +#include <algorithm> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +typedef int ExceptionCode; + +class TimeRanges : public RefCounted<TimeRanges> { +public: + static PassRefPtr<TimeRanges> create() + { + return adoptRef(new TimeRanges); + } + static PassRefPtr<TimeRanges> create(float start, float end) + { + return adoptRef(new TimeRanges(start, end)); + } + + PassRefPtr<TimeRanges> copy() const; + void invert(); + void intersectWith(const TimeRanges*); + void unionWith(const TimeRanges*); + + unsigned length() const { return m_ranges.size(); } + float start(unsigned index, ExceptionCode&) const; + float end(unsigned index, ExceptionCode&) const; + + void add(float start, float end); + + bool contain(float time) const; + + float nearest(float time) const; + +private: + TimeRanges() { } + TimeRanges(float start, float end); + TimeRanges(const TimeRanges&); + + // We consider all the Ranges to be semi-bounded as follow: [start, end[ + struct Range { + Range() { } + Range(float start, float end) + { + m_start = start; + m_end = end; + } + float m_start; + float m_end; + + inline bool isPointInRange(float point) const + { + return m_start <= point && point < m_end; + } + + inline bool isOverlappingRange(const Range& range) const + { + return isPointInRange(range.m_start) || isPointInRange(range.m_end) || range.isPointInRange(m_start); + } + + inline bool isContiguousWithRange(const Range& range) const + { + return range.m_start == m_end || range.m_end == m_start; + } + + inline Range unionWithOverlappingOrContiguousRange(const Range& range) const + { + Range ret; + + ret.m_start = std::min(m_start, range.m_start); + ret.m_end = std::max(m_end, range.m_end); + + return ret; + } + + inline bool isBeforeRange(const Range& range) const + { + return range.m_start >= m_end; + } + }; + + Vector<Range> m_ranges; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/TimeRanges.idl b/Source/WebCore/html/TimeRanges.idl new file mode 100644 index 000000000..c37c360db --- /dev/null +++ b/Source/WebCore/html/TimeRanges.idl @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=VIDEO + ] TimeRanges { + readonly attribute unsigned long length; + float start(in unsigned long index) + raises (DOMException); + float end(in unsigned long index) + raises (DOMException); + }; + +} diff --git a/Source/WebCore/html/URLInputType.cpp b/Source/WebCore/html/URLInputType.cpp new file mode 100644 index 000000000..3a913b24b --- /dev/null +++ b/Source/WebCore/html/URLInputType.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "URLInputType.h" + +#include "HTMLInputElement.h" +#include "LocalizedStrings.h" +#include "KURL.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +PassOwnPtr<InputType> URLInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new URLInputType(element)); +} + +const AtomicString& URLInputType::formControlType() const +{ + return InputTypeNames::url(); +} + +bool URLInputType::typeMismatchFor(const String& value) const +{ + return !value.isEmpty() && !KURL(KURL(), value).isValid(); +} + +bool URLInputType::typeMismatch() const +{ + return typeMismatchFor(element()->value()); +} + +String URLInputType::typeMismatchText() const +{ + return validationMessageTypeMismatchForURLText(); +} + +bool URLInputType::isURLField() const +{ + return true; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/URLInputType.h b/Source/WebCore/html/URLInputType.h new file mode 100644 index 000000000..de684185d --- /dev/null +++ b/Source/WebCore/html/URLInputType.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef URLInputType_h +#define URLInputType_h + +#include "BaseTextInputType.h" + +namespace WebCore { + +class URLInputType : public BaseTextInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + URLInputType(HTMLInputElement* element) : BaseTextInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual bool typeMismatchFor(const String&) const OVERRIDE; + virtual bool typeMismatch() const OVERRIDE; + virtual String typeMismatchText() const OVERRIDE; + virtual bool isURLField() const OVERRIDE; +}; + +} // namespace WebCore + +#endif // URLInputType_h diff --git a/Source/WebCore/html/ValidationMessage.cpp b/Source/WebCore/html/ValidationMessage.cpp new file mode 100644 index 000000000..b69b2b494 --- /dev/null +++ b/Source/WebCore/html/ValidationMessage.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ValidationMessage.h" + +#include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" +#include "CSSValueKeywords.h" +#include "ExceptionCodePlaceholder.h" +#include "FormAssociatedElement.h" +#include "HTMLBRElement.h" +#include "HTMLDivElement.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderBlock.h" +#include "RenderObject.h" +#include "Settings.h" +#include "ShadowRoot.h" +#include "Text.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +ALWAYS_INLINE ValidationMessage::ValidationMessage(FormAssociatedElement* element) + : m_element(element) +{ +} + +ValidationMessage::~ValidationMessage() +{ + deleteBubbleTree(); +} + +PassOwnPtr<ValidationMessage> ValidationMessage::create(FormAssociatedElement* element) +{ + return adoptPtr(new ValidationMessage(element)); +} + +void ValidationMessage::setMessage(const String& message) +{ + // Don't modify the DOM tree in this context. + // If so, an assertion in Node::isFocusable() fails. + ASSERT(!message.isEmpty()); + m_message = message; + if (!m_bubble) + m_timer = adoptPtr(new Timer<ValidationMessage>(this, &ValidationMessage::buildBubbleTree)); + else + m_timer = adoptPtr(new Timer<ValidationMessage>(this, &ValidationMessage::setMessageDOMAndStartTimer)); + m_timer->startOneShot(0); +} + +void ValidationMessage::setMessageDOMAndStartTimer(Timer<ValidationMessage>*) +{ + ASSERT(m_messageHeading); + ASSERT(m_messageBody); + m_messageHeading->removeAllChildren(); + m_messageBody->removeAllChildren(); + Vector<String> lines; + m_message.split('\n', lines); + Document* doc = m_messageHeading->document(); + for (unsigned i = 0; i < lines.size(); ++i) { + if (i) { + m_messageBody->appendChild(Text::create(doc, lines[i]), ASSERT_NO_EXCEPTION); + if (i < lines.size() - 1) + m_messageBody->appendChild(HTMLBRElement::create(doc), ASSERT_NO_EXCEPTION); + } else + m_messageHeading->setInnerText(lines[i], ASSERT_NO_EXCEPTION); + } + + int magnification = doc->page() ? doc->page()->settings()->validationMessageTimerMaginification() : -1; + if (magnification <= 0) + m_timer.clear(); + else { + m_timer = adoptPtr(new Timer<ValidationMessage>(this, &ValidationMessage::deleteBubbleTree)); + m_timer->startOneShot(max(5.0, static_cast<double>(m_message.length()) * magnification / 1000)); + } +} + +static void adjustBubblePosition(const LayoutRect& hostRect, HTMLElement* bubble) +{ + ASSERT(bubble); + if (hostRect.isEmpty()) + return; + double hostX = hostRect.x(); + double hostY = hostRect.y(); + if (RenderBox* container = bubble->renderer()->containingBlock()) { + FloatPoint containerLocation = container->localToAbsolute(); + hostX -= containerLocation.x() + container->borderLeft(); + hostY -= containerLocation.y() + container->borderTop(); + } + + CSSInlineStyleDeclaration* style = bubble->ensureInlineStyleDecl(); + style->setProperty(CSSPropertyTop, hostY + hostRect.height(), CSSPrimitiveValue::CSS_PX); + // The 'left' value of ::-webkit-validation-bubble-arrow. + const int bubbleArrowTopOffset = 32; + double bubbleX = hostX; + if (hostRect.width() / 2 < bubbleArrowTopOffset) + bubbleX = max(hostX + hostRect.width() / 2 - bubbleArrowTopOffset, 0.0); + style->setProperty(CSSPropertyLeft, bubbleX, CSSPrimitiveValue::CSS_PX); +} + +void ValidationMessage::buildBubbleTree(Timer<ValidationMessage>*) +{ + HTMLElement* host = toHTMLElement(m_element); + Document* doc = host->document(); + m_bubble = HTMLDivElement::create(doc); + m_bubble->setShadowPseudoId("-webkit-validation-bubble"); + // Need to force position:absolute because RenderMenuList doesn't assume it + // contains non-absolute or non-fixed renderers as children. + m_bubble->ensureInlineStyleDecl()->setProperty(CSSPropertyPosition, CSSValueAbsolute); + ExceptionCode ec = 0; + host->ensureShadowRoot()->appendChild(m_bubble.get(), ec); + ASSERT(!ec); + adjustBubblePosition(host->getRect(), m_bubble.get()); + + RefPtr<HTMLDivElement> clipper = HTMLDivElement::create(doc); + clipper->setShadowPseudoId("-webkit-validation-bubble-arrow-clipper"); + RefPtr<HTMLDivElement> bubbleArrow = HTMLDivElement::create(doc); + bubbleArrow->setShadowPseudoId("-webkit-validation-bubble-arrow"); + clipper->appendChild(bubbleArrow.release(), ec); + ASSERT(!ec); + m_bubble->appendChild(clipper.release(), ec); + ASSERT(!ec); + + RefPtr<HTMLElement> message = HTMLDivElement::create(doc); + message->setShadowPseudoId("-webkit-validation-bubble-message"); + RefPtr<HTMLElement> icon = HTMLDivElement::create(doc); + icon->setShadowPseudoId("-webkit-validation-bubble-icon"); + message->appendChild(icon.release(), ASSERT_NO_EXCEPTION); + RefPtr<HTMLElement> textBlock = HTMLDivElement::create(doc); + textBlock->setShadowPseudoId("-webkit-validation-bubble-text-block"); + m_messageHeading = HTMLDivElement::create(doc); + m_messageHeading->setShadowPseudoId("-webkit-validation-bubble-heading"); + textBlock->appendChild(m_messageHeading, ASSERT_NO_EXCEPTION); + m_messageBody = HTMLDivElement::create(doc); + m_messageBody->setShadowPseudoId("-webkit-validation-bubble-body"); + textBlock->appendChild(m_messageBody, ASSERT_NO_EXCEPTION); + message->appendChild(textBlock.release(), ASSERT_NO_EXCEPTION); + m_bubble->appendChild(message.release(), ASSERT_NO_EXCEPTION); + + setMessageDOMAndStartTimer(); + + // FIXME: Use transition to show the bubble. +} + +void ValidationMessage::requestToHideMessage() +{ + // We must not modify the DOM tree in this context by the same reason as setMessage(). + m_timer = adoptPtr(new Timer<ValidationMessage>(this, &ValidationMessage::deleteBubbleTree)); + m_timer->startOneShot(0); +} + +void ValidationMessage::deleteBubbleTree(Timer<ValidationMessage>*) +{ + if (m_bubble) { + m_messageHeading = 0; + m_messageBody = 0; + HTMLElement* host = toHTMLElement(m_element); + ExceptionCode ec; + host->shadowRoot()->removeChild(m_bubble.get(), ec); + m_bubble = 0; + } + m_message = String(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/ValidationMessage.h b/Source/WebCore/html/ValidationMessage.h new file mode 100644 index 000000000..f71a7c87e --- /dev/null +++ b/Source/WebCore/html/ValidationMessage.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ValidationMessage_h +#define ValidationMessage_h + +#include "Timer.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class FormAssociatedElement; +class HTMLElement; + +class ValidationMessage { + WTF_MAKE_NONCOPYABLE(ValidationMessage); +public: + static PassOwnPtr<ValidationMessage> create(FormAssociatedElement*); + ~ValidationMessage(); + String message() const { return m_message; } + void setMessage(const String&); + void requestToHideMessage(); + +private: + ValidationMessage(FormAssociatedElement*); + void setMessageDOMAndStartTimer(Timer<ValidationMessage>* = 0); + void buildBubbleTree(Timer<ValidationMessage>*); + void deleteBubbleTree(Timer<ValidationMessage>* = 0); + + FormAssociatedElement* m_element; + String m_message; + OwnPtr<Timer<ValidationMessage> > m_timer; + RefPtr<HTMLElement> m_bubble; + RefPtr<HTMLElement> m_messageHeading; + RefPtr<HTMLElement> m_messageBody; +}; + +} // namespace WebCore + +#endif // ValidationMessage_h diff --git a/Source/WebCore/html/ValidityState.cpp b/Source/WebCore/html/ValidityState.cpp new file mode 100644 index 000000000..fe7312c2a --- /dev/null +++ b/Source/WebCore/html/ValidityState.cpp @@ -0,0 +1,214 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2009 Michelangelo De Simone <micdesim@gmail.com> + * Copyright (C) 2010 Apple 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 "ValidityState.h" + +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLSelectElement.h" +#include "HTMLTextAreaElement.h" +#include "HTMLTreeBuilder.h" +#include "LocalizedStrings.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +String ValidityState::validationMessage() const +{ + if (!toHTMLElement(m_control)->willValidate()) + return String(); + + if (customError()) + return m_customErrorMessage; + HTMLElement* element = toHTMLElement(m_control); + bool isInputElement = element->isFormControlElement() && element->hasTagName(inputTag); + bool isTextAreaElement = element->isFormControlElement() && element->hasTagName(textareaTag); + // The order of the following checks is meaningful. e.g. We'd like to show the + // valueMissing message even if the control has other validation errors. + if (valueMissing()) { + if (element->hasTagName(selectTag)) + return validationMessageValueMissingForSelectText(); + if (isInputElement) + return static_cast<HTMLInputElement*>(element)->valueMissingText(); + return validationMessageValueMissingText(); + } + if (typeMismatch()) { + if (isInputElement) + return static_cast<HTMLInputElement*>(element)->typeMismatchText(); + return validationMessageTypeMismatchText(); + } + if (patternMismatch()) + return validationMessagePatternMismatchText(); + if (tooLong()) { + if (!isInputElement && !isTextAreaElement) { + ASSERT_NOT_REACHED(); + return String(); + } + HTMLTextFormControlElement* text = static_cast<HTMLTextFormControlElement*>(element); + return validationMessageTooLongText(numGraphemeClusters(text->value()), text->maxLength()); + } + if (rangeUnderflow()) { + if (!isInputElement) { + ASSERT_NOT_REACHED(); + return String(); + } + return validationMessageRangeUnderflowText(static_cast<HTMLInputElement*>(element)->minimumString()); + } + if (rangeOverflow()) { + if (!isInputElement) { + ASSERT_NOT_REACHED(); + return String(); + } + return validationMessageRangeOverflowText(static_cast<HTMLInputElement*>(element)->maximumString()); + } + if (stepMismatch()) { + if (!isInputElement) { + ASSERT_NOT_REACHED(); + return String(); + } + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return validationMessageStepMismatchText(input->stepBaseString(), input->stepString()); + } + + return String(); +} + +void ValidityState::setCustomErrorMessage(const String& message) +{ + m_customErrorMessage = message; + if (m_control->isFormControlElement()) + static_cast<HTMLFormControlElement*>(m_control)->setNeedsValidityCheck(); +} + +bool ValidityState::valueMissing() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (element->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return input->valueMissing(input->value()); + } + if (element->hasTagName(textareaTag)) { + HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(element); + return textArea->valueMissing(textArea->value()); + } + if (element->hasTagName(selectTag)) + return toHTMLSelectElement(element)->valueMissing(); + return false; +} + +bool ValidityState::typeMismatch() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (!element->hasTagName(inputTag)) + return false; + return static_cast<HTMLInputElement*>(element)->typeMismatch(); +} + +bool ValidityState::patternMismatch() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (!element->hasTagName(inputTag)) + return false; + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return input->patternMismatch(input->value()); +} + +bool ValidityState::tooLong() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (element->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return input->tooLong(input->value(), HTMLTextFormControlElement::CheckDirtyFlag); + } + if (element->hasTagName(textareaTag)) { + HTMLTextAreaElement* textArea = static_cast<HTMLTextAreaElement*>(element); + return textArea->tooLong(textArea->value(), HTMLTextFormControlElement::CheckDirtyFlag); + } + return false; +} + +bool ValidityState::rangeUnderflow() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (!element->hasTagName(inputTag)) + return false; + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return input->rangeUnderflow(input->value()); +} + +bool ValidityState::rangeOverflow() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (!element->hasTagName(inputTag)) + return false; + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return input->rangeOverflow(input->value()); +} + +bool ValidityState::stepMismatch() const +{ + HTMLElement* element = toHTMLElement(m_control); + if (!element->willValidate()) + return false; + + if (!element->hasTagName(inputTag)) + return false; + HTMLInputElement* input = static_cast<HTMLInputElement*>(element); + return input->stepMismatch(input->value()); +} + +bool ValidityState::customError() const +{ + HTMLElement* element = toHTMLElement(m_control); + return element->willValidate() && !m_customErrorMessage.isEmpty(); +} + +bool ValidityState::valid() const +{ + bool someError = typeMismatch() || stepMismatch() || rangeUnderflow() || rangeOverflow() + || tooLong() || patternMismatch() || valueMissing() || customError(); + return !someError; +} + +} // namespace diff --git a/Source/WebCore/html/ValidityState.h b/Source/WebCore/html/ValidityState.h new file mode 100644 index 000000000..4a96488bd --- /dev/null +++ b/Source/WebCore/html/ValidityState.h @@ -0,0 +1,69 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2009 Michelangelo De Simone <micdesim@gmail.com> + * Copyright (C) 2010 Apple 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 ValidityState_h +#define ValidityState_h + +#include "FormAssociatedElement.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class ValidityState { + WTF_MAKE_NONCOPYABLE(ValidityState); WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<ValidityState> create(FormAssociatedElement* control) + { + return adoptPtr(new ValidityState(control)); + } + + void ref() { m_control->ref(); } + void deref() { m_control->deref(); } + + String validationMessage() const; + + void setCustomErrorMessage(const String&); + + bool valueMissing() const; + bool typeMismatch() const; + bool patternMismatch() const; + bool tooLong() const; + bool rangeUnderflow() const; + bool rangeOverflow() const; + bool stepMismatch() const; + bool customError() const; + bool valid() const; + +private: + ValidityState(FormAssociatedElement* control) : m_control(control) { } + + static bool isValidColorString(const String&); + static bool isValidEmailAddress(const String&); + + FormAssociatedElement* m_control; + String m_customErrorMessage; +}; + +} // namespace WebCore + +#endif // ValidityState_h diff --git a/Source/WebCore/html/ValidityState.idl b/Source/WebCore/html/ValidityState.idl new file mode 100644 index 000000000..601bfafed --- /dev/null +++ b/Source/WebCore/html/ValidityState.idl @@ -0,0 +1,38 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2009 Michelangelo De Simone <micdesim@gmail.com> + * + * 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. + * + */ + +module html { + + interface [ + OmitConstructor + ] ValidityState { + readonly attribute boolean valueMissing; + readonly attribute boolean typeMismatch; + readonly attribute boolean patternMismatch; + readonly attribute boolean tooLong; + readonly attribute boolean rangeUnderflow; + readonly attribute boolean rangeOverflow; + readonly attribute boolean stepMismatch; + readonly attribute boolean customError; + readonly attribute boolean valid; + }; +} diff --git a/Source/WebCore/html/VoidCallback.h b/Source/WebCore/html/VoidCallback.h new file mode 100644 index 000000000..c220411fe --- /dev/null +++ b/Source/WebCore/html/VoidCallback.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VoidCallback_h +#define VoidCallback_h + +#include <wtf/RefCounted.h> + +namespace WebCore { + +class VoidCallback : public RefCounted<VoidCallback> { +public: + virtual ~VoidCallback() { } + + virtual void handleEvent() = 0; + +protected: + VoidCallback() {} +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/VoidCallback.idl b/Source/WebCore/html/VoidCallback.idl new file mode 100644 index 000000000..2a199751e --- /dev/null +++ b/Source/WebCore/html/VoidCallback.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomNativeConverter, + OmitConstructor + ] VoidCallback { + void handleEvent(); + }; +} diff --git a/Source/WebCore/html/WeekInputType.cpp b/Source/WebCore/html/WeekInputType.cpp new file mode 100644 index 000000000..4e2bc2991 --- /dev/null +++ b/Source/WebCore/html/WeekInputType.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WeekInputType.h" + +#include "DateComponents.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include <wtf/PassOwnPtr.h> + +#if ENABLE(INPUT_TYPE_WEEK) + +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; + +PassOwnPtr<InputType> WeekInputType::create(HTMLInputElement* element) +{ + return adoptPtr(new WeekInputType(element)); +} + +const AtomicString& WeekInputType::formControlType() const +{ + return InputTypeNames::week(); +} + +DateComponents::Type WeekInputType::dateType() const +{ + return DateComponents::Week; +} + +double WeekInputType::minimum() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), DateComponents::minimumWeek()); +} + +double WeekInputType::maximum() const +{ + return parseToDouble(element()->fastGetAttribute(maxAttr), DateComponents::maximumWeek()); +} + +double WeekInputType::stepBase() const +{ + return parseToDouble(element()->fastGetAttribute(minAttr), weekDefaultStepBase); +} + +double WeekInputType::defaultStep() const +{ + return weekDefaultStep; +} + +double WeekInputType::stepScaleFactor() const +{ + return weekStepScaleFactor; +} + +bool WeekInputType::parsedStepValueShouldBeInteger() const +{ + return true; +} + +bool WeekInputType::parseToDateComponentsInternal(const UChar* characters, unsigned length, DateComponents* out) const +{ + ASSERT(out); + unsigned end; + return out->parseWeek(characters, length, 0, end) && end == length; +} + +bool WeekInputType::setMillisecondToDateComponents(double value, DateComponents* date) const +{ + ASSERT(date); + return date->setMillisecondsSinceEpochForWeek(value); +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/WeekInputType.h b/Source/WebCore/html/WeekInputType.h new file mode 100644 index 000000000..5eb11069d --- /dev/null +++ b/Source/WebCore/html/WeekInputType.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WeekInputType_h +#define WeekInputType_h + +#include "BaseDateAndTimeInputType.h" + +#if ENABLE(INPUT_TYPE_WEEK) + +namespace WebCore { + +class WeekInputType : public BaseDateAndTimeInputType { +public: + static PassOwnPtr<InputType> create(HTMLInputElement*); + +private: + WeekInputType(HTMLInputElement* element) : BaseDateAndTimeInputType(element) { } + virtual const AtomicString& formControlType() const OVERRIDE; + virtual DateComponents::Type dateType() const OVERRIDE; + virtual double minimum() const OVERRIDE; + virtual double maximum() const OVERRIDE; + virtual double stepBase() const OVERRIDE; + virtual double defaultStep() const OVERRIDE; + virtual double stepScaleFactor() const OVERRIDE; + virtual bool parsedStepValueShouldBeInteger() const OVERRIDE; + virtual bool parseToDateComponentsInternal(const UChar*, unsigned length, DateComponents*) const OVERRIDE; + virtual bool setMillisecondToDateComponents(double, DateComponents*) const OVERRIDE; +}; + +} // namespace WebCore + +#endif +#endif // WeekInputType_h diff --git a/Source/WebCore/html/canvas/ArrayBuffer.idl b/Source/WebCore/html/canvas/ArrayBuffer.idl new file mode 100644 index 000000000..9ba85ca2e --- /dev/null +++ b/Source/WebCore/html/canvas/ArrayBuffer.idl @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + GenerateIsReachable=Impl, + CustomConstructor, + NoStaticTables + ] ArrayBuffer { + readonly attribute int byteLength; + ArrayBuffer slice(in long begin, in [Optional] long end); + }; + +} diff --git a/Source/WebCore/html/canvas/ArrayBufferView.idl b/Source/WebCore/html/canvas/ArrayBufferView.idl new file mode 100644 index 000000000..0e3d76540 --- /dev/null +++ b/Source/WebCore/html/canvas/ArrayBufferView.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomToJS, + NoStaticTables, + OmitConstructor + ] ArrayBufferView { + readonly attribute ArrayBuffer buffer; + readonly attribute unsigned long byteOffset; + readonly attribute unsigned long byteLength; + }; +} diff --git a/Source/WebCore/html/canvas/CanvasContextAttributes.cpp b/Source/WebCore/html/canvas/CanvasContextAttributes.cpp new file mode 100644 index 000000000..d3d0398af --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasContextAttributes.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "CanvasContextAttributes.h" + +namespace WebCore { + +CanvasContextAttributes::CanvasContextAttributes() +{ +} + +CanvasContextAttributes::~CanvasContextAttributes() +{ +} + +} // namespace WebCore diff --git a/Source/WebCore/html/canvas/CanvasContextAttributes.h b/Source/WebCore/html/canvas/CanvasContextAttributes.h new file mode 100644 index 000000000..97483b366 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasContextAttributes.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasContextAttributes_h +#define CanvasContextAttributes_h + +#include <wtf/RefCounted.h> + +namespace WebCore { + +// A base class for any attributes that are needed which would affect +// the creation of the Canvas's rendering context. Currently only the +// WebGLRenderingContext uses this mechanism. + +class CanvasContextAttributes : public RefCounted<CanvasContextAttributes> { + public: + virtual ~CanvasContextAttributes(); + + protected: + CanvasContextAttributes(); +}; + +} // namespace WebCore + +#endif // CanvasContextAttributes_h diff --git a/Source/WebCore/html/canvas/CanvasGradient.cpp b/Source/WebCore/html/canvas/CanvasGradient.cpp new file mode 100644 index 000000000..8a7ace0cf --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasGradient.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CanvasGradient.h" + +#include "CanvasPattern.h" +#include "CanvasStyle.h" +#include "CSSParser.h" +#include "ExceptionCode.h" + +namespace WebCore { + +CanvasGradient::CanvasGradient(const FloatPoint& p0, const FloatPoint& p1) + : m_gradient(Gradient::create(p0, p1)) +#if ENABLE(DASHBOARD_SUPPORT) + , m_dashbardCompatibilityMode(false) +#endif +{ +} + +CanvasGradient::CanvasGradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1) + : m_gradient(Gradient::create(p0, r0, p1, r1)) +#if ENABLE(DASHBOARD_SUPPORT) + , m_dashbardCompatibilityMode(false) +#endif +{ +} + +void CanvasGradient::addColorStop(float value, const String& color, ExceptionCode& ec) +{ + if (!(value >= 0 && value <= 1.0f)) { + ec = INDEX_SIZE_ERR; + return; + } + + RGBA32 rgba = 0; + if (!parseColorOrCurrentColor(rgba, color, 0 /*canvas*/)) { +#if ENABLE(DASHBOARD_SUPPORT) + if (!m_dashbardCompatibilityMode) + ec = SYNTAX_ERR; +#else + ec = SYNTAX_ERR; +#endif + return; + } + + m_gradient->addColorStop(value, Color(rgba)); +} + +} // namespace diff --git a/Source/WebCore/html/canvas/CanvasGradient.h b/Source/WebCore/html/canvas/CanvasGradient.h new file mode 100644 index 000000000..1ff9c6559 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasGradient.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasGradient_h +#define CanvasGradient_h + +#include "Gradient.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + typedef int ExceptionCode; + + class CanvasGradient : public RefCounted<CanvasGradient> { + public: + static PassRefPtr<CanvasGradient> create(const FloatPoint& p0, const FloatPoint& p1) + { + return adoptRef(new CanvasGradient(p0, p1)); + } + static PassRefPtr<CanvasGradient> create(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1) + { + return adoptRef(new CanvasGradient(p0, r0, p1, r1)); + } + + Gradient* gradient() const { return m_gradient.get(); } + + void addColorStop(float value, const String& color, ExceptionCode&); + + void getColor(float value, float* r, float* g, float* b, float* a) const { m_gradient->getColor(value, r, g, b, a); } + +#if ENABLE(DASHBOARD_SUPPORT) + void setDashboardCompatibilityMode() { m_dashbardCompatibilityMode = true; } +#endif + + private: + CanvasGradient(const FloatPoint& p0, const FloatPoint& p1); + CanvasGradient(const FloatPoint& p0, float r0, const FloatPoint& p1, float r1); + + RefPtr<Gradient> m_gradient; +#if ENABLE(DASHBOARD_SUPPORT) + bool m_dashbardCompatibilityMode; +#endif + }; + +} //namespace + +#endif diff --git a/Source/WebCore/html/canvas/CanvasGradient.idl b/Source/WebCore/html/canvas/CanvasGradient.idl new file mode 100644 index 000000000..48f75bbda --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasGradient.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + InterfaceUUID=bb1108ea-6b8c-4a08-894a-218628630cdb, + ImplementationUUID=a2942ae6-2731-4286-98cc-9d5e79e20de1 + ] CanvasGradient { + + void addColorStop(in [Optional=CallWithDefaultValue] float offset, + in [Optional=CallWithDefaultValue] DOMString color) + raises (DOMException); + + }; + +} + diff --git a/Source/WebCore/html/canvas/CanvasPattern.cpp b/Source/WebCore/html/canvas/CanvasPattern.cpp new file mode 100644 index 000000000..818d7d3b7 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasPattern.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CanvasPattern.h" + +#include "ExceptionCode.h" +#include "PlatformString.h" + +namespace WebCore { + +void CanvasPattern::parseRepetitionType(const String& type, bool& repeatX, bool& repeatY, ExceptionCode& ec) +{ + ec = 0; + if (type.isEmpty() || type == "repeat") { + repeatX = true; + repeatY = true; + return; + } + if (type == "no-repeat") { + repeatX = false; + repeatY = false; + return; + } + if (type == "repeat-x") { + repeatX = true; + repeatY = false; + return; + } + if (type == "repeat-y") { + repeatX = false; + repeatY = true; + return; + } + ec = SYNTAX_ERR; +} + +CanvasPattern::CanvasPattern(PassRefPtr<Image> image, bool repeatX, bool repeatY, bool originClean) + : m_pattern(Pattern::create(image, repeatX, repeatY)) + , m_originClean(originClean) +{ +} + +} diff --git a/Source/WebCore/html/canvas/CanvasPattern.h b/Source/WebCore/html/canvas/CanvasPattern.h new file mode 100644 index 000000000..58848a992 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasPattern.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasPattern_h +#define CanvasPattern_h + +#include "Pattern.h" +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class Image; + + typedef int ExceptionCode; + + class CanvasPattern : public RefCounted<CanvasPattern> { + public: + static void parseRepetitionType(const String&, bool& repeatX, bool& repeatY, ExceptionCode&); + + static PassRefPtr<CanvasPattern> create(PassRefPtr<Image> image, bool repeatX, bool repeatY, bool originClean) + { + return adoptRef(new CanvasPattern(image, repeatX, repeatY, originClean)); + } + + Pattern* pattern() const { return m_pattern.get(); } + + bool originClean() const { return m_originClean; } + + private: + CanvasPattern(PassRefPtr<Image>, bool repeatX, bool repeatY, bool originClean); + + RefPtr<Pattern> m_pattern; + bool m_originClean; + }; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/canvas/CanvasPattern.idl b/Source/WebCore/html/canvas/CanvasPattern.idl new file mode 100644 index 000000000..1cac8f880 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasPattern.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + InterfaceUUID=c2131348-6d8c-47b5-86cc-d41aff34ce15, + ImplementationUUID=82f5d713-3d17-44dd-aa4a-7766fe345940 + ] CanvasPattern { + + }; + +} + diff --git a/Source/WebCore/html/canvas/CanvasPixelArray.cpp b/Source/WebCore/html/canvas/CanvasPixelArray.cpp new file mode 100644 index 000000000..65ac7ecf0 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasPixelArray.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CanvasPixelArray.h" + +namespace WebCore { + +PassRefPtr<CanvasPixelArray> CanvasPixelArray::create(unsigned length) +{ + return adoptRef(new CanvasPixelArray(length)); +} + +PassRefPtr<CanvasPixelArray> CanvasPixelArray::create(PassRefPtr<ByteArray> byteArray) +{ + return adoptRef(new CanvasPixelArray(byteArray)); +} + +CanvasPixelArray::CanvasPixelArray(unsigned length) + : m_data(ByteArray::create(length)) +{ +} + +CanvasPixelArray::CanvasPixelArray(PassRefPtr<ByteArray> byteArray) + : m_data(byteArray) +{ +} + +} diff --git a/Source/WebCore/html/canvas/CanvasPixelArray.h b/Source/WebCore/html/canvas/CanvasPixelArray.h new file mode 100644 index 000000000..7a94b1fe7 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasPixelArray.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasPixelArray_h +#define CanvasPixelArray_h + +#include <wtf/ByteArray.h> +#include <wtf/MathExtras.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class CanvasPixelArray : public RefCounted<CanvasPixelArray> { +public: + static PassRefPtr<CanvasPixelArray> create(unsigned length); + static PassRefPtr<CanvasPixelArray> create(PassRefPtr<ByteArray>); + + ByteArray* data() { return m_data.get(); } + const ByteArray* data() const { return m_data.get(); } + unsigned length() const { return m_data->length(); } + + void set(unsigned index, double value) + { + m_data->set(index, value); + } + + void set(unsigned index, unsigned char value) + { + m_data->set(index, value); + } + + bool get(unsigned index, unsigned char& result) const + { + return m_data->get(index, result); + } + + unsigned char get(unsigned index) const + { + return m_data->get(index); + } + +private: + CanvasPixelArray(unsigned length); + CanvasPixelArray(PassRefPtr<ByteArray>); + + RefPtr<ByteArray> m_data; +}; + +} // namespace WebCore + +#endif // CanvasPixelArray_h diff --git a/Source/WebCore/html/canvas/CanvasPixelArray.idl b/Source/WebCore/html/canvas/CanvasPixelArray.idl new file mode 100644 index 000000000..8b7edbd5d --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasPixelArray.idl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { +#if !defined(LANGUAGE_JAVASCRIPT) || !LANGUAGE_JAVASCRIPT || defined(V8_BINDING) && V8_BINDING + interface [ + OmitConstructor, + CustomHeader, + HasNumericIndexGetter, + HasCustomIndexSetter + ] CanvasPixelArray { +#if !defined(V8_BINDING) || !V8_BINDING + readonly attribute long length; +#endif + }; +#endif +} diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext.cpp b/Source/WebCore/html/canvas/CanvasRenderingContext.cpp new file mode 100644 index 000000000..b3f387eb7 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasRenderingContext.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CanvasRenderingContext.h" + +#include "CachedImage.h" +#include "CanvasPattern.h" +#include "HTMLCanvasElement.h" +#include "HTMLImageElement.h" +#include "HTMLVideoElement.h" +#include "KURL.h" +#include "SecurityOrigin.h" + +namespace WebCore { + +CanvasRenderingContext::CanvasRenderingContext(HTMLCanvasElement* canvas) + : m_canvas(canvas) +{ +} + +bool CanvasRenderingContext::wouldTaintOrigin(const CanvasPattern* pattern) +{ + if (canvas()->originClean() && pattern && !pattern->originClean()) + return true; + return false; +} + +bool CanvasRenderingContext::wouldTaintOrigin(const HTMLCanvasElement* sourceCanvas) +{ + if (canvas()->originClean() && sourceCanvas && !sourceCanvas->originClean()) + return true; + return false; +} + +bool CanvasRenderingContext::wouldTaintOrigin(const HTMLImageElement* image) +{ + if (!image || !canvas()->originClean()) + return false; + + CachedImage* cachedImage = image->cachedImage(); + if (!cachedImage->image()->hasSingleSecurityOrigin()) + return true; + + return wouldTaintOrigin(cachedImage->response().url()) && !cachedImage->passesAccessControlCheck(canvas()->securityOrigin()); +} + +bool CanvasRenderingContext::wouldTaintOrigin(const HTMLVideoElement* video) +{ +#if ENABLE(VIDEO) + // FIXME: This check is likely wrong when a redirect is involved. We need + // to test the finalURL. Please be careful when fixing this issue not to + // make currentSrc be the final URL because then the + // HTMLMediaElement.currentSrc DOM API would leak redirect destinations! + if (!video || !canvas()->originClean()) + return false; + + if (wouldTaintOrigin(video->currentSrc())) + return true; + + if (!video->hasSingleSecurityOrigin()) + return true; +#endif + + return false; +} + +bool CanvasRenderingContext::wouldTaintOrigin(const KURL& url) +{ + if (!canvas()->originClean() || m_cleanURLs.contains(url.string())) + return false; + + if (canvas()->securityOrigin()->taintsCanvas(url)) + return true; + + if (url.protocolIsData()) + return false; + + m_cleanURLs.add(url.string()); + return false; +} + +void CanvasRenderingContext::checkOrigin(const KURL& url) +{ + if (wouldTaintOrigin(url)) + canvas()->setOriginTainted(); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext.h b/Source/WebCore/html/canvas/CanvasRenderingContext.h new file mode 100644 index 000000000..53278788d --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasRenderingContext.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasRenderingContext_h +#define CanvasRenderingContext_h + +#include "GraphicsLayer.h" +#include "HTMLCanvasElement.h" +#include <wtf/HashSet.h> +#include <wtf/Noncopyable.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +class CanvasPattern; +class HTMLCanvasElement; +class HTMLImageElement; +class HTMLVideoElement; +class KURL; +class WebGLObject; + +class CanvasRenderingContext { + WTF_MAKE_NONCOPYABLE(CanvasRenderingContext); WTF_MAKE_FAST_ALLOCATED; +public: + virtual ~CanvasRenderingContext() { } + + void ref() { m_canvas->ref(); } + void deref() { m_canvas->deref(); } + HTMLCanvasElement* canvas() const { return m_canvas; } + + virtual bool is2d() const { return false; } + virtual bool is3d() const { return false; } + virtual bool isAccelerated() const { return false; } + + virtual void paintRenderingResultsToCanvas() {} + virtual bool paintsIntoCanvasBuffer() const { return true; } + +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const { return 0; } +#endif + +protected: + CanvasRenderingContext(HTMLCanvasElement*); + bool wouldTaintOrigin(const CanvasPattern*); + bool wouldTaintOrigin(const HTMLCanvasElement*); + bool wouldTaintOrigin(const HTMLImageElement*); + bool wouldTaintOrigin(const HTMLVideoElement*); + bool wouldTaintOrigin(const KURL&); + + template<class T> void checkOrigin(const T* arg) + { + if (wouldTaintOrigin(arg)) + canvas()->setOriginTainted(); + } + void checkOrigin(const KURL&); + +private: + HTMLCanvasElement* m_canvas; + HashSet<String> m_cleanURLs; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext.idl b/Source/WebCore/html/canvas/CanvasRenderingContext.idl new file mode 100644 index 000000000..b53bdce5a --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasRenderingContext.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + CustomMarkFunction, + GenerateIsReachable, + CustomToJS, + InterfaceUUID=98fb48ae-7216-489c-862b-8e1217fc4443, + ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54 + ] CanvasRenderingContext { + + readonly attribute HTMLCanvasElement canvas; + }; + +} + diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp new file mode 100644 index 000000000..948c21942 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -0,0 +1,2252 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * Copyright (C) 2008 Dirk Schulze <krit@webkit.org> + * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CanvasRenderingContext2D.h" + +#include "AffineTransform.h" +#include "CSSFontSelector.h" +#include "CSSMutableStyleDeclaration.h" +#include "CSSParser.h" +#include "CSSPropertyNames.h" +#include "CSSStyleSelector.h" +#include "CachedImage.h" +#include "CanvasGradient.h" +#include "CanvasPattern.h" +#include "CanvasStyle.h" +#include "Console.h" +#include "ExceptionCode.h" +#include "FloatConversion.h" +#include "FloatQuad.h" +#include "FontCache.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include "HTMLImageElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "HTMLVideoElement.h" +#include "ImageBuffer.h" +#include "ImageData.h" +#include "KURL.h" +#include "Page.h" +#include "RenderHTMLCanvas.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "StrokeStyleApplier.h" +#include "TextMetrics.h" +#include "TextRun.h" + +#if USE(ACCELERATED_COMPOSITING) +#include "RenderLayer.h" +#endif + +#include <wtf/ByteArray.h> +#include <wtf/CheckedArithmetic.h> +#include <wtf/MathExtras.h> +#include <wtf/OwnPtr.h> +#include <wtf/UnusedParam.h> + +#if USE(CG) +#include <ApplicationServices/ApplicationServices.h> +#endif + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +static const char* const defaultFont = "10px sans-serif"; + +static bool isOriginClean(CachedImage* cachedImage, SecurityOrigin* securityOrigin) +{ + if (!cachedImage->image()->hasSingleSecurityOrigin()) + return false; + if (cachedImage->passesAccessControlCheck(securityOrigin)) + return true; + return !securityOrigin->taintsCanvas(cachedImage->response().url()); +} + +class CanvasStrokeStyleApplier : public StrokeStyleApplier { +public: + CanvasStrokeStyleApplier(CanvasRenderingContext2D* canvasContext) + : m_canvasContext(canvasContext) + { + } + + virtual void strokeStyle(GraphicsContext* c) + { + c->setStrokeThickness(m_canvasContext->lineWidth()); + c->setLineCap(m_canvasContext->getLineCap()); + c->setLineJoin(m_canvasContext->getLineJoin()); + c->setMiterLimit(m_canvasContext->miterLimit()); + } + +private: + CanvasRenderingContext2D* m_canvasContext; +}; + +CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode) + : CanvasRenderingContext(canvas) + , m_stateStack(1) + , m_usesCSSCompatibilityParseMode(usesCSSCompatibilityParseMode) +#if ENABLE(DASHBOARD_SUPPORT) + , m_usesDashboardCompatibilityMode(usesDashboardCompatibilityMode) +#endif +{ +#if !ENABLE(DASHBOARD_SUPPORT) + ASSERT_UNUSED(usesDashboardCompatibilityMode, !usesDashboardCompatibilityMode); +#endif +} + +void CanvasRenderingContext2D::unwindStateStack() +{ + // Ensure that the state stack in the ImageBuffer's context + // is cleared before destruction, to avoid assertions in the + // GraphicsContext dtor. + if (size_t stackSize = m_stateStack.size()) { + if (GraphicsContext* context = canvas()->existingDrawingContext()) { + while (--stackSize) + context->restore(); + } + } +} + +CanvasRenderingContext2D::~CanvasRenderingContext2D() +{ +#if !ASSERT_DISABLED + unwindStateStack(); +#endif +} + +bool CanvasRenderingContext2D::isAccelerated() const +{ +#if USE(IOSURFACE_CANVAS_BACKING_STORE) || ENABLE(ACCELERATED_2D_CANVAS) + return canvas()->hasCreatedImageBuffer() && drawingContext() && drawingContext()->isAcceleratedContext(); +#else + return false; +#endif +} + +bool CanvasRenderingContext2D::paintsIntoCanvasBuffer() const +{ +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) + if (!isAccelerated()) + return true; + + RenderBox* renderBox = canvas()->renderBox(); + if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) + return false; +#endif + return true; +} + + +void CanvasRenderingContext2D::reset() +{ + unwindStateStack(); + m_stateStack.resize(1); + m_stateStack.first() = State(); + m_path.clear(); +#if USE(ACCELERATED_COMPOSITING) + RenderBox* renderBox = canvas()->renderBox(); + if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) + renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); +#endif +} + +CanvasRenderingContext2D::State::State() + : m_strokeStyle(CanvasStyle::createFromRGBA(Color::black)) + , m_fillStyle(CanvasStyle::createFromRGBA(Color::black)) + , m_lineWidth(1) + , m_lineCap(ButtCap) + , m_lineJoin(MiterJoin) + , m_miterLimit(10) + , m_shadowBlur(0) + , m_shadowColor(Color::transparent) + , m_globalAlpha(1) + , m_globalComposite(CompositeSourceOver) + , m_invertibleCTM(true) + , m_lineDashOffset(0) + , m_textAlign(StartTextAlign) + , m_textBaseline(AlphabeticTextBaseline) + , m_unparsedFont(defaultFont) + , m_realizedFont(false) +{ +} + +CanvasRenderingContext2D::State::State(const State& other) + : FontSelectorClient() + , m_unparsedStrokeColor(other.m_unparsedStrokeColor) + , m_unparsedFillColor(other.m_unparsedFillColor) + , m_strokeStyle(other.m_strokeStyle) + , m_fillStyle(other.m_fillStyle) + , m_lineWidth(other.m_lineWidth) + , m_lineCap(other.m_lineCap) + , m_lineJoin(other.m_lineJoin) + , m_miterLimit(other.m_miterLimit) + , m_shadowOffset(other.m_shadowOffset) + , m_shadowBlur(other.m_shadowBlur) + , m_shadowColor(other.m_shadowColor) + , m_globalAlpha(other.m_globalAlpha) + , m_globalComposite(other.m_globalComposite) + , m_transform(other.m_transform) + , m_invertibleCTM(other.m_invertibleCTM) + , m_textAlign(other.m_textAlign) + , m_textBaseline(other.m_textBaseline) + , m_unparsedFont(other.m_unparsedFont) + , m_font(other.m_font) + , m_realizedFont(other.m_realizedFont) +{ + if (m_realizedFont) + m_font.fontSelector()->registerForInvalidationCallbacks(this); +} + +CanvasRenderingContext2D::State& CanvasRenderingContext2D::State::operator=(const State& other) +{ + if (this == &other) + return *this; + + if (m_realizedFont) + m_font.fontSelector()->unregisterForInvalidationCallbacks(this); + + m_unparsedStrokeColor = other.m_unparsedStrokeColor; + m_unparsedFillColor = other.m_unparsedFillColor; + m_strokeStyle = other.m_strokeStyle; + m_fillStyle = other.m_fillStyle; + m_lineWidth = other.m_lineWidth; + m_lineCap = other.m_lineCap; + m_lineJoin = other.m_lineJoin; + m_miterLimit = other.m_miterLimit; + m_shadowOffset = other.m_shadowOffset; + m_shadowBlur = other.m_shadowBlur; + m_shadowColor = other.m_shadowColor; + m_globalAlpha = other.m_globalAlpha; + m_globalComposite = other.m_globalComposite; + m_transform = other.m_transform; + m_invertibleCTM = other.m_invertibleCTM; + m_textAlign = other.m_textAlign; + m_textBaseline = other.m_textBaseline; + m_unparsedFont = other.m_unparsedFont; + m_font = other.m_font; + m_realizedFont = other.m_realizedFont; + + if (m_realizedFont) + m_font.fontSelector()->registerForInvalidationCallbacks(this); + + return *this; +} + +CanvasRenderingContext2D::State::~State() +{ + if (m_realizedFont) + m_font.fontSelector()->unregisterForInvalidationCallbacks(this); +} + +void CanvasRenderingContext2D::State::fontsNeedUpdate(FontSelector* fontSelector) +{ + ASSERT_ARG(fontSelector, fontSelector == m_font.fontSelector()); + ASSERT(m_realizedFont); + + m_font.update(fontSelector); +} + +void CanvasRenderingContext2D::save() +{ + ASSERT(m_stateStack.size() >= 1); + m_stateStack.append(state()); + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->save(); +} + +void CanvasRenderingContext2D::restore() +{ + ASSERT(m_stateStack.size() >= 1); + if (m_stateStack.size() <= 1) + return; + m_path.transform(state().m_transform); + m_stateStack.removeLast(); + m_path.transform(state().m_transform.inverse()); + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->restore(); +} + +void CanvasRenderingContext2D::setAllAttributesToDefault() +{ + state().m_globalAlpha = 1; + state().m_shadowOffset = FloatSize(); + state().m_shadowBlur = 0; + state().m_shadowColor = Color::transparent; + state().m_globalComposite = CompositeSourceOver; + + GraphicsContext* context = drawingContext(); + if (!context) + return; + + context->setLegacyShadow(FloatSize(), 0, Color::transparent, ColorSpaceDeviceRGB); + context->setAlpha(1); + context->setCompositeOperation(CompositeSourceOver); +} + +CanvasStyle* CanvasRenderingContext2D::strokeStyle() const +{ + return state().m_strokeStyle.get(); +} + +void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> prpStyle) +{ + RefPtr<CanvasStyle> style = prpStyle; + + if (!style) + return; + + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style)) + return; + + if (style->isCurrentColor()) { + if (style->hasOverrideAlpha()) + style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha())); + else + style = CanvasStyle::createFromRGBA(currentColor(canvas())); + } else + checkOrigin(style->canvasPattern()); + + state().m_strokeStyle = style.release(); + GraphicsContext* c = drawingContext(); + if (!c) + return; + state().m_strokeStyle->applyStrokeColor(c); + state().m_unparsedStrokeColor = String(); +} + +CanvasStyle* CanvasRenderingContext2D::fillStyle() const +{ + return state().m_fillStyle.get(); +} + +void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> prpStyle) +{ + RefPtr<CanvasStyle> style = prpStyle; + + if (!style) + return; + + if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style)) + return; + + if (style->isCurrentColor()) { + if (style->hasOverrideAlpha()) + style = CanvasStyle::createFromRGBA(colorWithOverrideAlpha(currentColor(canvas()), style->overrideAlpha())); + else + style = CanvasStyle::createFromRGBA(currentColor(canvas())); + } else + checkOrigin(style->canvasPattern()); + + state().m_fillStyle = style.release(); + GraphicsContext* c = drawingContext(); + if (!c) + return; + state().m_fillStyle->applyFillColor(c); + state().m_unparsedFillColor = String(); +} + +float CanvasRenderingContext2D::lineWidth() const +{ + return state().m_lineWidth; +} + +void CanvasRenderingContext2D::setLineWidth(float width) +{ + if (!(isfinite(width) && width > 0)) + return; + state().m_lineWidth = width; + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setStrokeThickness(width); +} + +String CanvasRenderingContext2D::lineCap() const +{ + return lineCapName(state().m_lineCap); +} + +void CanvasRenderingContext2D::setLineCap(const String& s) +{ + LineCap cap; + if (!parseLineCap(s, cap)) + return; + state().m_lineCap = cap; + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setLineCap(cap); +} + +String CanvasRenderingContext2D::lineJoin() const +{ + return lineJoinName(state().m_lineJoin); +} + +void CanvasRenderingContext2D::setLineJoin(const String& s) +{ + LineJoin join; + if (!parseLineJoin(s, join)) + return; + state().m_lineJoin = join; + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setLineJoin(join); +} + +float CanvasRenderingContext2D::miterLimit() const +{ + return state().m_miterLimit; +} + +void CanvasRenderingContext2D::setMiterLimit(float limit) +{ + if (!(isfinite(limit) && limit > 0)) + return; + state().m_miterLimit = limit; + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setMiterLimit(limit); +} + +float CanvasRenderingContext2D::shadowOffsetX() const +{ + return state().m_shadowOffset.width(); +} + +void CanvasRenderingContext2D::setShadowOffsetX(float x) +{ + if (!isfinite(x)) + return; + state().m_shadowOffset.setWidth(x); + applyShadow(); +} + +float CanvasRenderingContext2D::shadowOffsetY() const +{ + return state().m_shadowOffset.height(); +} + +void CanvasRenderingContext2D::setShadowOffsetY(float y) +{ + if (!isfinite(y)) + return; + state().m_shadowOffset.setHeight(y); + applyShadow(); +} + +float CanvasRenderingContext2D::shadowBlur() const +{ + return state().m_shadowBlur; +} + +void CanvasRenderingContext2D::setShadowBlur(float blur) +{ + if (!(isfinite(blur) && blur >= 0)) + return; + state().m_shadowBlur = blur; + applyShadow(); +} + +String CanvasRenderingContext2D::shadowColor() const +{ + return Color(state().m_shadowColor).serialized(); +} + +void CanvasRenderingContext2D::setShadowColor(const String& color) +{ + if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas())) + return; + + applyShadow(); +} + +const DashArray* CanvasRenderingContext2D::webkitLineDash() const +{ + return &state().m_lineDash; +} + +void CanvasRenderingContext2D::setWebkitLineDash(const DashArray& dash) +{ + state().m_lineDash = dash; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setLineDash(state().m_lineDash, state().m_lineDashOffset); +} + +float CanvasRenderingContext2D::webkitLineDashOffset() const +{ + return state().m_lineDashOffset; +} + +void CanvasRenderingContext2D::setWebkitLineDashOffset(float offset) +{ + if (!isfinite(offset)) + return; + + state().m_lineDashOffset = offset; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setLineDash(state().m_lineDash, state().m_lineDashOffset); +} + +float CanvasRenderingContext2D::globalAlpha() const +{ + return state().m_globalAlpha; +} + +void CanvasRenderingContext2D::setGlobalAlpha(float alpha) +{ + if (!(alpha >= 0 && alpha <= 1)) + return; + state().m_globalAlpha = alpha; + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setAlpha(alpha); +} + +String CanvasRenderingContext2D::globalCompositeOperation() const +{ + return compositeOperatorName(state().m_globalComposite); +} + +void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operation) +{ + CompositeOperator op; + if (!parseCompositeOperator(operation, op)) + return; + state().m_globalComposite = op; + GraphicsContext* c = drawingContext(); + if (!c) + return; + c->setCompositeOperation(op); +} + +void CanvasRenderingContext2D::scale(float sx, float sy) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + if (!isfinite(sx) | !isfinite(sy)) + return; + + AffineTransform newTransform = state().m_transform; + newTransform.scaleNonUniform(sx, sy); + if (!newTransform.isInvertible()) { + state().m_invertibleCTM = false; + return; + } + + state().m_transform = newTransform; + c->scale(FloatSize(sx, sy)); + m_path.transform(AffineTransform().scaleNonUniform(1.0 / sx, 1.0 / sy)); +} + +void CanvasRenderingContext2D::rotate(float angleInRadians) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + if (!isfinite(angleInRadians)) + return; + + AffineTransform newTransform = state().m_transform; + newTransform.rotate(angleInRadians / piDouble * 180.0); + if (!newTransform.isInvertible()) { + state().m_invertibleCTM = false; + return; + } + + state().m_transform = newTransform; + c->rotate(angleInRadians); + m_path.transform(AffineTransform().rotate(-angleInRadians / piDouble * 180.0)); +} + +void CanvasRenderingContext2D::translate(float tx, float ty) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + if (!isfinite(tx) | !isfinite(ty)) + return; + + AffineTransform newTransform = state().m_transform; + newTransform.translate(tx, ty); + if (!newTransform.isInvertible()) { + state().m_invertibleCTM = false; + return; + } + + state().m_transform = newTransform; + c->translate(tx, ty); + m_path.transform(AffineTransform().translate(-tx, -ty)); +} + +void CanvasRenderingContext2D::transform(float m11, float m12, float m21, float m22, float dx, float dy) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | !isfinite(m12) | !isfinite(m22) | !isfinite(dy)) + return; + + AffineTransform transform(m11, m12, m21, m22, dx, dy); + AffineTransform newTransform = state().m_transform * transform; + if (!newTransform.isInvertible()) { + state().m_invertibleCTM = false; + return; + } + + state().m_transform = newTransform; + c->concatCTM(transform); + m_path.transform(transform.inverse()); +} + +void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, float m22, float dx, float dy) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + + if (!isfinite(m11) | !isfinite(m21) | !isfinite(dx) | !isfinite(m12) | !isfinite(m22) | !isfinite(dy)) + return; + + AffineTransform ctm = state().m_transform; + if (!ctm.isInvertible()) + return; + c->concatCTM(c->getCTM().inverse()); + c->concatCTM(canvas()->baseTransform()); + state().m_transform = ctm.inverse() * state().m_transform; + m_path.transform(ctm); + + state().m_invertibleCTM = true; + transform(m11, m12, m21, m22, dx, dy); +} + +void CanvasRenderingContext2D::setStrokeColor(const String& color) +{ + if (color == state().m_unparsedStrokeColor) + return; + setStrokeStyle(CanvasStyle::createFromString(color, canvas()->document())); + state().m_unparsedStrokeColor = color; +} + +void CanvasRenderingContext2D::setStrokeColor(float grayLevel) +{ + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) + return; + setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); +} + +void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) +{ + setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); +} + +void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) +{ + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) + return; + setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); +} + +void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a) +{ + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a)) + return; + setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); +} + +void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a) +{ + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a)) + return; + setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); +} + +void CanvasRenderingContext2D::setFillColor(const String& color) +{ + if (color == state().m_unparsedFillColor) + return; + setFillStyle(CanvasStyle::createFromString(color, canvas()->document())); + state().m_unparsedFillColor = color; +} + +void CanvasRenderingContext2D::setFillColor(float grayLevel) +{ + if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) + return; + setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); +} + +void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) +{ + setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); +} + +void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) +{ + if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) + return; + setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); +} + +void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) +{ + if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a)) + return; + setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); +} + +void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a) +{ + if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a)) + return; + setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); +} + +void CanvasRenderingContext2D::beginPath() +{ + m_path.clear(); +} + +void CanvasRenderingContext2D::closePath() +{ + if (m_path.isEmpty()) + return; + + FloatRect boundRect = m_path.fastBoundingRect(); + if (boundRect.width() || boundRect.height()) + m_path.closeSubpath(); +} + +void CanvasRenderingContext2D::moveTo(float x, float y) +{ + if (!isfinite(x) | !isfinite(y)) + return; + if (!state().m_invertibleCTM) + return; + m_path.moveTo(FloatPoint(x, y)); +} + +void CanvasRenderingContext2D::lineTo(float x, float y) +{ + if (!isfinite(x) | !isfinite(y)) + return; + if (!state().m_invertibleCTM) + return; + + FloatPoint p1 = FloatPoint(x, y); + if (!m_path.hasCurrentPoint()) + m_path.moveTo(p1); + else if (p1 != m_path.currentPoint()) + m_path.addLineTo(FloatPoint(x, y)); +} + +void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, float y) +{ + if (!isfinite(cpx) | !isfinite(cpy) | !isfinite(x) | !isfinite(y)) + return; + if (!state().m_invertibleCTM) + return; + if (!m_path.hasCurrentPoint()) + m_path.moveTo(FloatPoint(cpx, cpy)); + + FloatPoint p1 = FloatPoint(x, y); + if (p1 != m_path.currentPoint()) + m_path.addQuadCurveTo(FloatPoint(cpx, cpy), p1); +} + +void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) +{ + if (!isfinite(cp1x) | !isfinite(cp1y) | !isfinite(cp2x) | !isfinite(cp2y) | !isfinite(x) | !isfinite(y)) + return; + if (!state().m_invertibleCTM) + return; + if (!m_path.hasCurrentPoint()) + m_path.moveTo(FloatPoint(cp1x, cp1y)); + + FloatPoint p1 = FloatPoint(x, y); + if (p1 != m_path.currentPoint()) + m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), p1); +} + +void CanvasRenderingContext2D::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionCode& ec) +{ + ec = 0; + if (!isfinite(x1) | !isfinite(y1) | !isfinite(x2) | !isfinite(y2) | !isfinite(r)) + return; + + if (r < 0) { + ec = INDEX_SIZE_ERR; + return; + } + + if (!state().m_invertibleCTM) + return; + + FloatPoint p1 = FloatPoint(x1, y1); + FloatPoint p2 = FloatPoint(x2, y2); + + if (!m_path.hasCurrentPoint()) + m_path.moveTo(p1); + else if (p1 == m_path.currentPoint() || p1 == p2 || !r) + lineTo(x1, y1); + else + m_path.addArcTo(p1, p2, r); +} + +void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec) +{ + ec = 0; + if (!isfinite(x) | !isfinite(y) | !isfinite(r) | !isfinite(sa) | !isfinite(ea)) + return; + + if (r < 0) { + ec = INDEX_SIZE_ERR; + return; + } + + if (!r || sa == ea) { + // The arc is empty but we still need to draw the connecting line + lineTo(x + r * cosf(sa), y + r * sinf(sa)); + return; + } + + if (!state().m_invertibleCTM) + return; + + // If 'sa' and 'ea' differ by more than 2Pi, just add a circle starting/ending at 'sa' + if (anticlockwise && sa - ea >= 2 * piFloat) { + m_path.addArc(FloatPoint(x, y), r, sa, sa - 2 * piFloat, anticlockwise); + return; + } + if (!anticlockwise && ea - sa >= 2 * piFloat) { + m_path.addArc(FloatPoint(x, y), r, sa, sa + 2 * piFloat, anticlockwise); + return; + } + + m_path.addArc(FloatPoint(x, y), r, sa, ea, anticlockwise); +} + +static bool validateRectForCanvas(float& x, float& y, float& width, float& height) +{ + if (!isfinite(x) | !isfinite(y) | !isfinite(width) | !isfinite(height)) + return false; + + if (!width && !height) + return false; + + if (width < 0) { + width = -width; + x -= width; + } + + if (height < 0) { + height = -height; + y -= height; + } + + return true; +} + +void CanvasRenderingContext2D::rect(float x, float y, float width, float height) +{ + if (!state().m_invertibleCTM) + return; + + if (!isfinite(x) || !isfinite(y) || !isfinite(width) || !isfinite(height)) + return; + + if (!width && !height) { + m_path.moveTo(FloatPoint(x, y)); + return; + } + + m_path.addRect(FloatRect(x, y, width, height)); +} + +#if ENABLE(DASHBOARD_SUPPORT) +void CanvasRenderingContext2D::clearPathForDashboardBackwardCompatibilityMode() +{ + if (m_usesDashboardCompatibilityMode) + m_path.clear(); +} +#endif + +static bool isFullCanvasCompositeMode(CompositeOperator op) +{ + // See 4.8.11.1.3 Compositing + // CompositeSourceAtop and CompositeDestinationOut are not listed here as the platforms already + // implement the specification's behavior. + return op == CompositeSourceIn || op == CompositeSourceOut || op == CompositeDestinationIn || op == CompositeDestinationAtop; +} + +void CanvasRenderingContext2D::fill() +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + if (!m_path.isEmpty()) { + if (isFullCanvasCompositeMode(state().m_globalComposite)) { + fullCanvasCompositedFill(m_path); + didDrawEntireCanvas(); + } else if (state().m_globalComposite == CompositeCopy) { + clearCanvas(); + c->fillPath(m_path); + didDrawEntireCanvas(); + } else { + c->fillPath(m_path); + didDraw(m_path.fastBoundingRect()); + } + } + +#if ENABLE(DASHBOARD_SUPPORT) + clearPathForDashboardBackwardCompatibilityMode(); +#endif +} + +void CanvasRenderingContext2D::stroke() +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + if (!m_path.isEmpty()) { + FloatRect dirtyRect = m_path.fastBoundingRect(); + // Fast approximation of the stroke's bounding rect. + // This yields a slightly oversized rect but is very fast + // compared to Path::strokeBoundingRect(). + dirtyRect.inflate(state().m_miterLimit + state().m_lineWidth); + + c->strokePath(m_path); + didDraw(dirtyRect); + } + +#if ENABLE(DASHBOARD_SUPPORT) + clearPathForDashboardBackwardCompatibilityMode(); +#endif +} + +void CanvasRenderingContext2D::clip() +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + c->canvasClip(m_path); +#if ENABLE(DASHBOARD_SUPPORT) + clearPathForDashboardBackwardCompatibilityMode(); +#endif +} + +bool CanvasRenderingContext2D::isPointInPath(const float x, const float y) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return false; + if (!state().m_invertibleCTM) + return false; + + FloatPoint point(x, y); + AffineTransform ctm = state().m_transform; + FloatPoint transformedPoint = ctm.inverse().mapPoint(point); + if (!isfinite(transformedPoint.x()) || !isfinite(transformedPoint.y())) + return false; + return m_path.contains(transformedPoint); +} + +void CanvasRenderingContext2D::clearRect(float x, float y, float width, float height) +{ + if (!validateRectForCanvas(x, y, width, height)) + return; + GraphicsContext* context = drawingContext(); + if (!context) + return; + if (!state().m_invertibleCTM) + return; + FloatRect rect(x, y, width, height); + + save(); + setAllAttributesToDefault(); + context->clearRect(rect); + didDraw(rect); + restore(); +} + +void CanvasRenderingContext2D::fillRect(float x, float y, float width, float height) +{ + if (!validateRectForCanvas(x, y, width, height)) + return; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + // from the HTML5 Canvas spec: + // If x0 = x1 and y0 = y1, then the linear gradient must paint nothing + // If x0 = x1 and y0 = y1 and r0 = r1, then the radial gradient must paint nothing + Gradient* gradient = c->fillGradient(); + if (gradient && gradient->isZeroSize()) + return; + + FloatRect rect(x, y, width, height); + + if (rectContainsCanvas(rect)) { + c->fillRect(rect); + didDrawEntireCanvas(); + } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { + fullCanvasCompositedFill(rect); + didDrawEntireCanvas(); + } else if (state().m_globalComposite == CompositeCopy) { + clearCanvas(); + c->fillRect(rect); + didDrawEntireCanvas(); + } else { + c->fillRect(rect); + didDraw(rect); + } +} + +void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height) +{ + if (!validateRectForCanvas(x, y, width, height)) + return; + strokeRect(x, y, width, height, state().m_lineWidth); +} + +void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float height, float lineWidth) +{ + if (!validateRectForCanvas(x, y, width, height)) + return; + + if (!(lineWidth >= 0)) + return; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + FloatRect rect(x, y, width, height); + + FloatRect boundingRect = rect; + boundingRect.inflate(lineWidth / 2); + + c->strokeRect(rect, lineWidth); + didDraw(boundingRect); +} + +#if USE(CG) +static inline CGSize adjustedShadowSize(CGFloat width, CGFloat height) +{ + // Work around <rdar://problem/5539388> by ensuring that shadow offsets will get truncated + // to the desired integer. + static const CGFloat extraShadowOffset = narrowPrecisionToCGFloat(1.0 / 128); + if (width > 0) + width += extraShadowOffset; + else if (width < 0) + width -= extraShadowOffset; + + if (height > 0) + height += extraShadowOffset; + else if (height < 0) + height -= extraShadowOffset; + + return CGSizeMake(width, height); +} +#endif + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur) +{ + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + state().m_shadowColor = Color::transparent; + applyShadow(); +} + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color) +{ + if (!parseColorOrCurrentColor(state().m_shadowColor, color, canvas())) + return; + + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + applyShadow(); +} + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel) +{ + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f); + + GraphicsContext* c = drawingContext(); + if (!c) + return; + + c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); +} + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur, const String& color, float alpha) +{ + RGBA32 rgba; + + if (!parseColorOrCurrentColor(rgba, color, canvas())) + return; + + state().m_shadowColor = colorWithOverrideAlpha(rgba, alpha); + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + + c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); +} + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float grayLevel, float alpha) +{ + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + state().m_shadowColor = makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha); + + GraphicsContext* c = drawingContext(); + if (!c) + return; + + c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); +} + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float r, float g, float b, float a) +{ + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + state().m_shadowColor = makeRGBA32FromFloats(r, g, b, a); + + GraphicsContext* c = drawingContext(); + if (!c) + return; + + c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); +} + +void CanvasRenderingContext2D::setShadow(float width, float height, float blur, float c, float m, float y, float k, float a) +{ + state().m_shadowOffset = FloatSize(width, height); + state().m_shadowBlur = blur; + state().m_shadowColor = makeRGBAFromCMYKA(c, m, y, k, a); + + GraphicsContext* dc = drawingContext(); + if (!dc) + return; +#if USE(CG) + const CGFloat components[5] = { c, m, y, k, a }; + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceCMYK(); + CGColorRef shadowColor = CGColorCreate(colorSpace, components); + CGColorSpaceRelease(colorSpace); + CGContextSetShadowWithColor(dc->platformContext(), adjustedShadowSize(width, -height), blur, shadowColor); + CGColorRelease(shadowColor); +#else + dc->setLegacyShadow(FloatSize(width, -height), blur, state().m_shadowColor, ColorSpaceDeviceRGB); +#endif +} + +void CanvasRenderingContext2D::clearShadow() +{ + state().m_shadowOffset = FloatSize(); + state().m_shadowBlur = 0; + state().m_shadowColor = Color::transparent; + applyShadow(); +} + +void CanvasRenderingContext2D::applyShadow() +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + + float width = state().m_shadowOffset.width(); + float height = state().m_shadowOffset.height(); + c->setLegacyShadow(FloatSize(width, -height), state().m_shadowBlur, state().m_shadowColor, ColorSpaceDeviceRGB); +} + +static LayoutSize size(HTMLImageElement* image) +{ + if (CachedImage* cachedImage = image->cachedImage()) + return cachedImage->imageSizeForRenderer(image->renderer(), 1.0f); // FIXME: Not sure about this. + return IntSize(); +} + +#if ENABLE(VIDEO) +static IntSize size(HTMLVideoElement* video) +{ + if (MediaPlayer* player = video->player()) + return player->naturalSize(); + return IntSize(); +} +#endif + +static inline FloatRect normalizeRect(const FloatRect& rect) +{ + return FloatRect(min(rect.x(), rect.maxX()), + min(rect.y(), rect.maxY()), + max(rect.width(), -rect.width()), + max(rect.height(), -rect.height())); +} + +void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, float x, float y, ExceptionCode& ec) +{ + if (!image) { + ec = TYPE_MISMATCH_ERR; + return; + } + LayoutSize s = size(image); + drawImage(image, x, y, s.width(), s.height(), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, + float x, float y, float width, float height, ExceptionCode& ec) +{ + if (!image) { + ec = TYPE_MISMATCH_ERR; + return; + } + LayoutSize s = size(image); + drawImage(image, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, + float sx, float sy, float sw, float sh, + float dx, float dy, float dw, float dh, ExceptionCode& ec) +{ + if (!image) { + ec = TYPE_MISMATCH_ERR; + return; + } + drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode& ec) +{ + drawImage(image, srcRect, dstRect, state().m_globalComposite, ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLImageElement* image, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator& op, ExceptionCode& ec) +{ + if (!image) { + ec = TYPE_MISMATCH_ERR; + return; + } + + ec = 0; + + if (!isfinite(dstRect.x()) || !isfinite(dstRect.y()) || !isfinite(dstRect.width()) || !isfinite(dstRect.height()) + || !isfinite(srcRect.x()) || !isfinite(srcRect.y()) || !isfinite(srcRect.width()) || !isfinite(srcRect.height())) + return; + + if (!dstRect.width() || !dstRect.height()) + return; + + if (!image->complete()) + return; + + FloatRect normalizedSrcRect = normalizeRect(srcRect); + FloatRect normalizedDstRect = normalizeRect(dstRect); + + FloatRect imageRect = FloatRect(FloatPoint(), size(image)); + if (!srcRect.width() || !srcRect.height()) { + ec = INDEX_SIZE_ERR; + return; + } + if (!imageRect.contains(normalizedSrcRect)) + return; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + CachedImage* cachedImage = image->cachedImage(); + if (!cachedImage) + return; + + checkOrigin(image); + + if (rectContainsCanvas(normalizedDstRect)) { + c->drawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op); + didDrawEntireCanvas(); + } else if (isFullCanvasCompositeMode(op)) { + fullCanvasCompositedDrawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op); + didDrawEntireCanvas(); + } else if (op == CompositeCopy) { + clearCanvas(); + c->drawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op); + didDrawEntireCanvas(); + } else { + c->drawImage(cachedImage->imageForRenderer(image->renderer()), ColorSpaceDeviceRGB, normalizedDstRect, normalizedSrcRect, op); + didDraw(normalizedDstRect); + } +} + +void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, float x, float y, ExceptionCode& ec) +{ + if (!canvas) { + ec = TYPE_MISMATCH_ERR; + return; + } + + // In order to emulate drawing the result of toDataURL() into the canvas, we + // need to deflate the size of the source rectangle by the source canvas's + // backing store scale factor. + // See https://www.w3.org/Bugs/Public/show_bug.cgi?id=15041 for motivation. + + FloatSize logicalSize = canvas->convertDeviceToLogical(canvas->size()); + + drawImage(canvas, 0, 0, logicalSize.width(), logicalSize.height(), x, y, canvas->width(), canvas->height(), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, + float x, float y, float width, float height, ExceptionCode& ec) +{ + if (!canvas) { + ec = TYPE_MISMATCH_ERR; + return; + } + drawImage(canvas, FloatRect(0, 0, canvas->width(), canvas->height()), FloatRect(x, y, width, height), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* canvas, + float sx, float sy, float sw, float sh, + float dx, float dy, float dw, float dh, ExceptionCode& ec) +{ + drawImage(canvas, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const FloatRect& srcRect, + const FloatRect& dstRect, ExceptionCode& ec) +{ + if (!sourceCanvas) { + ec = TYPE_MISMATCH_ERR; + return; + } + + FloatRect srcCanvasRect = FloatRect(FloatPoint(), sourceCanvas->size()); + + if (!srcCanvasRect.width() || !srcCanvasRect.height()) { + ec = INVALID_STATE_ERR; + return; + } + + if (!srcRect.width() || !srcRect.height()) { + ec = INDEX_SIZE_ERR; + return; + } + + ec = 0; + + if (!srcCanvasRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height()) + return; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + // FIXME: Do this through platform-independent GraphicsContext API. + ImageBuffer* buffer = sourceCanvas->buffer(); + if (!buffer) + return; + + checkOrigin(sourceCanvas); + +#if ENABLE(ACCELERATED_2D_CANVAS) + // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas->makeRenderingResultsAvailable() + // as that will do a readback to software. + CanvasRenderingContext* sourceContext = sourceCanvas->renderingContext(); + // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible. + if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d()) + sourceCanvas->makeRenderingResultsAvailable(); +#else + sourceCanvas->makeRenderingResultsAvailable(); +#endif + + // drawImageBuffer's srcRect is in buffer pixels (backing store pixels, in our case), dstRect is in canvas pixels. + FloatRect bufferSrcRect(sourceCanvas->convertLogicalToDevice(srcRect)); + + if (rectContainsCanvas(dstRect)) { + c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, dstRect, bufferSrcRect, state().m_globalComposite); + didDrawEntireCanvas(); + } else if (isFullCanvasCompositeMode(state().m_globalComposite)) { + fullCanvasCompositedDrawImage(buffer, ColorSpaceDeviceRGB, bufferSrcRect, srcRect, state().m_globalComposite); + didDrawEntireCanvas(); + } else if (state().m_globalComposite == CompositeCopy) { + clearCanvas(); + c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, dstRect, bufferSrcRect, state().m_globalComposite); + didDrawEntireCanvas(); + } else { + c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, dstRect, bufferSrcRect, state().m_globalComposite); + didDraw(dstRect); + } +} + +#if ENABLE(VIDEO) +void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, float x, float y, ExceptionCode& ec) +{ + if (!video) { + ec = TYPE_MISMATCH_ERR; + return; + } + IntSize s = size(video); + drawImage(video, x, y, s.width(), s.height(), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, + float x, float y, float width, float height, ExceptionCode& ec) +{ + if (!video) { + ec = TYPE_MISMATCH_ERR; + return; + } + IntSize s = size(video); + drawImage(video, FloatRect(0, 0, s.width(), s.height()), FloatRect(x, y, width, height), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, + float sx, float sy, float sw, float sh, + float dx, float dy, float dw, float dh, ExceptionCode& ec) +{ + drawImage(video, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), ec); +} + +void CanvasRenderingContext2D::drawImage(HTMLVideoElement* video, const FloatRect& srcRect, const FloatRect& dstRect, + ExceptionCode& ec) +{ + if (!video) { + ec = TYPE_MISMATCH_ERR; + return; + } + + ec = 0; + + if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA) + return; + + FloatRect videoRect = FloatRect(FloatPoint(), size(video)); + if (!srcRect.width() || !srcRect.height()) { + ec = INDEX_SIZE_ERR; + return; + } + + if (!videoRect.contains(normalizeRect(srcRect)) || !dstRect.width() || !dstRect.height()) + return; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + + checkOrigin(video); + + GraphicsContextStateSaver stateSaver(*c); + c->clip(dstRect); + c->translate(dstRect.x(), dstRect.y()); + c->scale(FloatSize(dstRect.width() / srcRect.width(), dstRect.height() / srcRect.height())); + c->translate(-srcRect.x(), -srcRect.y()); + video->paintCurrentFrameInContext(c, IntRect(IntPoint(), size(video))); + stateSaver.restore(); + didDraw(dstRect); +} +#endif + +void CanvasRenderingContext2D::drawImageFromRect(HTMLImageElement* image, + float sx, float sy, float sw, float sh, + float dx, float dy, float dw, float dh, + const String& compositeOperation) +{ + CompositeOperator op; + if (!parseCompositeOperator(compositeOperation, op)) + op = CompositeSourceOver; + + ExceptionCode ec; + drawImage(image, FloatRect(sx, sy, sw, sh), FloatRect(dx, dy, dw, dh), op, ec); +} + +void CanvasRenderingContext2D::setAlpha(float alpha) +{ + setGlobalAlpha(alpha); +} + +void CanvasRenderingContext2D::setCompositeOperation(const String& operation) +{ + setGlobalCompositeOperation(operation); +} + +void CanvasRenderingContext2D::clearCanvas() +{ + FloatRect canvasRect(0, 0, canvas()->width(), canvas()->height()); + GraphicsContext* c = drawingContext(); + if (!c) + return; + + c->save(); + c->setCTM(canvas()->baseTransform()); + c->clearRect(canvasRect); + c->restore(); +} + +Path CanvasRenderingContext2D::transformAreaToDevice(const Path& path) const +{ + Path transformed(path); + transformed.transform(state().m_transform); + transformed.transform(canvas()->baseTransform()); + return transformed; +} + +Path CanvasRenderingContext2D::transformAreaToDevice(const FloatRect& rect) const +{ + Path path; + path.addRect(rect); + return transformAreaToDevice(path); +} + +bool CanvasRenderingContext2D::rectContainsCanvas(const FloatRect& rect) const +{ + FloatQuad quad(rect); + FloatQuad canvasQuad(FloatRect(0, 0, canvas()->width(), canvas()->height())); + return state().m_transform.mapQuad(quad).containsQuad(canvasQuad); +} + +template<class T> IntRect CanvasRenderingContext2D::calculateCompositingBufferRect(const T& area, IntSize* croppedOffset) +{ + IntRect canvasRect(0, 0, canvas()->width(), canvas()->height()); + canvasRect = canvas()->baseTransform().mapRect(canvasRect); + Path path = transformAreaToDevice(area); + IntRect bufferRect = enclosingIntRect(path.fastBoundingRect()); + IntPoint originalLocation = bufferRect.location(); + bufferRect.intersect(canvasRect); + if (croppedOffset) + *croppedOffset = originalLocation - bufferRect.location(); + return bufferRect; +} + +PassOwnPtr<ImageBuffer> CanvasRenderingContext2D::createCompositingBuffer(const IntRect& bufferRect) +{ + RenderingMode renderMode = isAccelerated() ? Accelerated : Unaccelerated; + return ImageBuffer::create(bufferRect.size(), ColorSpaceDeviceRGB, renderMode); +} + +void CanvasRenderingContext2D::compositeBuffer(ImageBuffer* buffer, const IntRect& bufferRect, CompositeOperator op) +{ + IntRect canvasRect(0, 0, canvas()->width(), canvas()->height()); + canvasRect = canvas()->baseTransform().mapRect(canvasRect); + + GraphicsContext* c = drawingContext(); + if (!c) + return; + + c->save(); + c->setCTM(AffineTransform()); + c->setCompositeOperation(op); + + c->save(); + c->clipOut(bufferRect); + c->clearRect(canvasRect); + c->restore(); + + c->drawImageBuffer(buffer, ColorSpaceDeviceRGB, bufferRect.location(), state().m_globalComposite); + c->restore(); +} + +static void drawImageToContext(Image* image, GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op) +{ + context->drawImage(image, styleColorSpace, dest, src, op); +} + +static void drawImageToContext(ImageBuffer* imageBuffer, GraphicsContext* context, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op) +{ + context->drawImageBuffer(imageBuffer, styleColorSpace, dest, src, op); +} + +template<class T> void CanvasRenderingContext2D::fullCanvasCompositedDrawImage(T* image, ColorSpace styleColorSpace, const FloatRect& dest, const FloatRect& src, CompositeOperator op) +{ + ASSERT(isFullCanvasCompositeMode(op)); + + IntSize croppedOffset; + IntRect bufferRect = calculateCompositingBufferRect(dest, &croppedOffset); + if (bufferRect.isEmpty()) { + clearCanvas(); + return; + } + + OwnPtr<ImageBuffer> buffer = createCompositingBuffer(bufferRect); + if (!buffer) + return; + + GraphicsContext* c = drawingContext(); + if (!c) + return; + + FloatRect adjustedDest = dest; + adjustedDest.setLocation(FloatPoint(0, 0)); + AffineTransform effectiveTransform = c->getCTM(); + IntRect transformedAdjustedRect = enclosingIntRect(effectiveTransform.mapRect(adjustedDest)); + buffer->context()->translate(-transformedAdjustedRect.location().x(), -transformedAdjustedRect.location().y()); + buffer->context()->translate(croppedOffset.width(), croppedOffset.height()); + buffer->context()->concatCTM(effectiveTransform); + drawImageToContext(image, buffer->context(), styleColorSpace, adjustedDest, src, CompositeSourceOver); + + compositeBuffer(buffer.get(), bufferRect, op); +} + +template<class T> void CanvasRenderingContext2D::fullCanvasCompositedFill(const T& area) +{ + ASSERT(isFullCanvasCompositeMode(state().m_globalComposite)); + + IntRect bufferRect = calculateCompositingBufferRect(area, 0); + if (bufferRect.isEmpty()) { + clearCanvas(); + return; + } + + OwnPtr<ImageBuffer> buffer = createCompositingBuffer(bufferRect); + if (!buffer) + return; + + Path path = transformAreaToDevice(area); + path.translate(FloatSize(-bufferRect.x(), -bufferRect.y())); + + buffer->context()->setCompositeOperation(CompositeSourceOver); + state().m_fillStyle->applyFillColor(buffer->context()); + buffer->context()->fillPath(path); + + compositeBuffer(buffer.get(), bufferRect, state().m_globalComposite); +} + +void CanvasRenderingContext2D::prepareGradientForDashboard(CanvasGradient* gradient) const +{ +#if ENABLE(DASHBOARD_SUPPORT) + if (m_usesDashboardCompatibilityMode) + gradient->setDashboardCompatibilityMode(); +#else + UNUSED_PARAM(gradient); +#endif +} + +PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode& ec) +{ + if (!isfinite(x0) || !isfinite(y0) || !isfinite(x1) || !isfinite(y1)) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), FloatPoint(x1, y1)); + prepareGradientForDashboard(gradient.get()); + return gradient.release(); +} + +PassRefPtr<CanvasGradient> CanvasRenderingContext2D::createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode& ec) +{ + if (!isfinite(x0) || !isfinite(y0) || !isfinite(r0) || !isfinite(x1) || !isfinite(y1) || !isfinite(r1)) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + if (r0 < 0 || r1 < 0) { + ec = INDEX_SIZE_ERR; + return 0; + } + + RefPtr<CanvasGradient> gradient = CanvasGradient::create(FloatPoint(x0, y0), r0, FloatPoint(x1, y1), r1); + prepareGradientForDashboard(gradient.get()); + return gradient.release(); +} + +PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLImageElement* image, + const String& repetitionType, ExceptionCode& ec) +{ + if (!image) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + bool repeatX, repeatY; + ec = 0; + CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); + if (ec) + return 0; + + if (!image->complete()) + return 0; + + CachedImage* cachedImage = image->cachedImage(); + if (!cachedImage || !image->cachedImage()->imageForRenderer(image->renderer())) + return CanvasPattern::create(Image::nullImage(), repeatX, repeatY, true); + + bool originClean = isOriginClean(cachedImage, canvas()->securityOrigin()); + return CanvasPattern::create(cachedImage->imageForRenderer(image->renderer()), repeatX, repeatY, originClean); +} + +PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElement* canvas, + const String& repetitionType, ExceptionCode& ec) +{ + if (!canvas) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + if (!canvas->width() || !canvas->height()) { + ec = INVALID_STATE_ERR; + return 0; + } + + bool repeatX, repeatY; + ec = 0; + CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); + if (ec) + return 0; + return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean()); +} + +void CanvasRenderingContext2D::didDrawEntireCanvas() +{ + didDraw(FloatRect(FloatPoint::zero(), canvas()->size()), CanvasDidDrawApplyClip); +} + +void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) + // If we are drawing to hardware and we have a composited layer, just call contentChanged(). + if (isAccelerated()) { + RenderBox* renderBox = canvas()->renderBox(); + if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) { + renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); + canvas()->clearCopiedImage(); + return; + } + } +#endif + + FloatRect dirtyRect = r; + if (options & CanvasDidDrawApplyTransform) { + AffineTransform ctm = state().m_transform; + dirtyRect = ctm.mapRect(r); + } + + if (options & CanvasDidDrawApplyShadow && alphaChannel(state().m_shadowColor)) { + // The shadow gets applied after transformation + FloatRect shadowRect(dirtyRect); + shadowRect.move(state().m_shadowOffset); + shadowRect.inflate(state().m_shadowBlur); + dirtyRect.unite(shadowRect); + } + + if (options & CanvasDidDrawApplyClip) { + // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip + // back out of the GraphicsContext, so to take clip into account for incremental painting, + // we'd have to keep the clip path around. + } + + canvas()->didDraw(dirtyRect); +} + +GraphicsContext* CanvasRenderingContext2D::drawingContext() const +{ + return canvas()->drawingContext(); +} + +static PassRefPtr<ImageData> createEmptyImageData(const IntSize& size) +{ + Checked<int, RecordOverflow> dataSize = 4; + dataSize *= size.width(); + dataSize *= size.height(); + if (dataSize.hasOverflowed()) + return 0; + + RefPtr<ImageData> data = ImageData::create(size); + memset(data->data()->data()->data(), 0, data->data()->data()->length()); + return data.release(); +} + +PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(PassRefPtr<ImageData> imageData, ExceptionCode& ec) const +{ + if (!imageData) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + return createEmptyImageData(imageData->size()); +} + +PassRefPtr<ImageData> CanvasRenderingContext2D::createImageData(float sw, float sh, ExceptionCode& ec) const +{ + ec = 0; + if (!sw || !sh) { + ec = INDEX_SIZE_ERR; + return 0; + } + if (!isfinite(sw) || !isfinite(sh)) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + FloatSize logicalSize(fabs(sw), fabs(sh)); + FloatSize deviceSize = canvas()->convertLogicalToDevice(logicalSize); + if (!deviceSize.isExpressibleAsIntSize()) + return 0; + + IntSize size(deviceSize.width(), deviceSize.height()); + if (size.width() < 1) + size.setWidth(1); + if (size.height() < 1) + size.setHeight(1); + + return createEmptyImageData(size); +} + +PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, float sw, float sh, ExceptionCode& ec) const +{ + if (!canvas()->originClean()) { + DEFINE_STATIC_LOCAL(String, consoleMessage, ("Unable to get image data from canvas because the canvas has been tainted by cross-origin data.")); + canvas()->document()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage); + ec = SECURITY_ERR; + return 0; + } + if (!sw || !sh) { + ec = INDEX_SIZE_ERR; + return 0; + } + if (!isfinite(sx) || !isfinite(sy) || !isfinite(sw) || !isfinite(sh)) { + ec = NOT_SUPPORTED_ERR; + return 0; + } + + if (sw < 0) { + sx += sw; + sw = -sw; + } + if (sh < 0) { + sy += sh; + sh = -sh; + } + + FloatRect logicalRect(sx, sy, sw, sh); + FloatRect deviceRect = canvas()->convertLogicalToDevice(logicalRect); + if (deviceRect.width() < 1) + deviceRect.setWidth(1); + if (deviceRect.height() < 1) + deviceRect.setHeight(1); + if (!deviceRect.isExpressibleAsIntRect()) + return 0; + + IntRect imageDataRect(deviceRect); + ImageBuffer* buffer = canvas()->buffer(); + if (!buffer) + return createEmptyImageData(imageDataRect.size()); + + RefPtr<ByteArray> byteArray = buffer->getUnmultipliedImageData(imageDataRect); + if (!byteArray) + return 0; + + return ImageData::create(imageDataRect.size(), byteArray.release()); +} + +void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, ExceptionCode& ec) +{ + if (!data) { + ec = TYPE_MISMATCH_ERR; + return; + } + putImageData(data, dx, dy, 0, 0, data->width(), data->height(), ec); +} + +void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, float dirtyX, float dirtyY, + float dirtyWidth, float dirtyHeight, ExceptionCode& ec) +{ + if (!data) { + ec = TYPE_MISMATCH_ERR; + return; + } + if (!isfinite(dx) || !isfinite(dy) || !isfinite(dirtyX) || !isfinite(dirtyY) || !isfinite(dirtyWidth) || !isfinite(dirtyHeight)) { + ec = NOT_SUPPORTED_ERR; + return; + } + + ImageBuffer* buffer = canvas()->buffer(); + if (!buffer) + return; + + if (dirtyWidth < 0) { + dirtyX += dirtyWidth; + dirtyWidth = -dirtyWidth; + } + + if (dirtyHeight < 0) { + dirtyY += dirtyHeight; + dirtyHeight = -dirtyHeight; + } + + FloatRect clipRect(dirtyX, dirtyY, dirtyWidth, dirtyHeight); + clipRect.intersect(IntRect(0, 0, data->width(), data->height())); + IntSize destOffset(static_cast<int>(dx), static_cast<int>(dy)); + IntRect destRect = enclosingIntRect(clipRect); + destRect.move(destOffset); + destRect.intersect(IntRect(IntPoint(), buffer->size())); + if (destRect.isEmpty()) + return; + IntRect sourceRect(destRect); + sourceRect.move(-destOffset); + + buffer->putUnmultipliedImageData(data->data()->data(), IntSize(data->width(), data->height()), sourceRect, IntPoint(destOffset)); + didDraw(destRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip +} + +String CanvasRenderingContext2D::font() const +{ + return state().m_unparsedFont; +} + +void CanvasRenderingContext2D::setFont(const String& newFont) +{ + RefPtr<CSSMutableStyleDeclaration> tempDecl = CSSMutableStyleDeclaration::create(); + CSSParser parser(!m_usesCSSCompatibilityParseMode); + + String declarationText("font: "); + declarationText += newFont; + parser.parseDeclaration(tempDecl.get(), declarationText); + if (!tempDecl->length()) + return; + + // The parse succeeded. + state().m_unparsedFont = newFont; + + // Map the <canvas> font into the text style. If the font uses keywords like larger/smaller, these will work + // relative to the canvas. + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + if (RenderStyle* computedStyle = canvas()->computedStyle()) + newStyle->setFontDescription(computedStyle->fontDescription()); + newStyle->font().update(newStyle->font().fontSelector()); + + // Now map the font property longhands into the style. + CSSStyleSelector* styleSelector = canvas()->styleSelector(); + styleSelector->applyPropertyToStyle(CSSPropertyFontFamily, tempDecl->getPropertyCSSValue(CSSPropertyFontFamily).get(), newStyle.get()); + styleSelector->applyPropertyToCurrentStyle(CSSPropertyFontStyle, tempDecl->getPropertyCSSValue(CSSPropertyFontStyle).get()); + styleSelector->applyPropertyToCurrentStyle(CSSPropertyFontVariant, tempDecl->getPropertyCSSValue(CSSPropertyFontVariant).get()); + styleSelector->applyPropertyToCurrentStyle(CSSPropertyFontWeight, tempDecl->getPropertyCSSValue(CSSPropertyFontWeight).get()); + + // As described in BUG66291, setting font-size on a font may entail a CSSPrimitiveValue::computeLengthDouble call, + // which assumes the fontMetrics are available for the affected font, otherwise a crash occurs (see http://trac.webkit.org/changeset/96122). + // The updateFont() call below updates the fontMetrics and ensures the proper setting of font-size. + styleSelector->updateFont(); + styleSelector->applyPropertyToCurrentStyle(CSSPropertyFontSize, tempDecl->getPropertyCSSValue(CSSPropertyFontSize).get()); + styleSelector->applyPropertyToCurrentStyle(CSSPropertyLineHeight, tempDecl->getPropertyCSSValue(CSSPropertyLineHeight).get()); + + state().m_font = newStyle->font(); + state().m_font.update(styleSelector->fontSelector()); + state().m_realizedFont = true; + styleSelector->fontSelector()->registerForInvalidationCallbacks(&state()); +} + +String CanvasRenderingContext2D::textAlign() const +{ + return textAlignName(state().m_textAlign); +} + +void CanvasRenderingContext2D::setTextAlign(const String& s) +{ + TextAlign align; + if (!parseTextAlign(s, align)) + return; + state().m_textAlign = align; +} + +String CanvasRenderingContext2D::textBaseline() const +{ + return textBaselineName(state().m_textBaseline); +} + +void CanvasRenderingContext2D::setTextBaseline(const String& s) +{ + TextBaseline baseline; + if (!parseTextBaseline(s, baseline)) + return; + state().m_textBaseline = baseline; +} + +void CanvasRenderingContext2D::fillText(const String& text, float x, float y) +{ + drawTextInternal(text, x, y, true); +} + +void CanvasRenderingContext2D::fillText(const String& text, float x, float y, float maxWidth) +{ + drawTextInternal(text, x, y, true, maxWidth, true); +} + +void CanvasRenderingContext2D::strokeText(const String& text, float x, float y) +{ + drawTextInternal(text, x, y, false); +} + +void CanvasRenderingContext2D::strokeText(const String& text, float x, float y, float maxWidth) +{ + drawTextInternal(text, x, y, false, maxWidth, true); +} + +PassRefPtr<TextMetrics> CanvasRenderingContext2D::measureText(const String& text) +{ + FontCachePurgePreventer fontCachePurgePreventer; + + RefPtr<TextMetrics> metrics = TextMetrics::create(); + +#if PLATFORM(QT) + // We always use complex text shaping since it can't be turned off for QPainterPath::addText(). + Font::CodePath oldCodePath = Font::codePath(); + Font::setCodePath(Font::Complex); +#endif + + metrics->setWidth(accessFont().width(TextRun(text.characters(), text.length()))); + +#if PLATFORM(QT) + Font::setCodePath(oldCodePath); +#endif + + return metrics.release(); +} + +void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth, bool useMaxWidth) +{ + GraphicsContext* c = drawingContext(); + if (!c) + return; + if (!state().m_invertibleCTM) + return; + if (!isfinite(x) | !isfinite(y)) + return; + if (useMaxWidth && !isfinite(maxWidth)) + return; + + FontCachePurgePreventer fontCachePurgePreventer; + + const Font& font = accessFont(); + const FontMetrics& fontMetrics = font.fontMetrics(); + + // FIXME: Need to turn off font smoothing. + + RenderStyle* computedStyle = canvas()->computedStyle(); + TextDirection direction = computedStyle ? computedStyle->direction() : LTR; + bool isRTL = direction == RTL; + bool override = computedStyle ? computedStyle->unicodeBidi() == Override : false; + + unsigned length = text.length(); + const UChar* string = text.characters(); + TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, direction, override, TextRun::NoRounding); + + // Draw the item text at the correct point. + FloatPoint location(x, y); + switch (state().m_textBaseline) { + case TopTextBaseline: + case HangingTextBaseline: + location.setY(y + fontMetrics.ascent()); + break; + case BottomTextBaseline: + case IdeographicTextBaseline: + location.setY(y - fontMetrics.descent()); + break; + case MiddleTextBaseline: + location.setY(y - fontMetrics.descent() + fontMetrics.height() / 2); + break; + case AlphabeticTextBaseline: + default: + // Do nothing. + break; + } + + float fontWidth = font.width(TextRun(text, false, 0, 0, TextRun::AllowTrailingExpansion, direction, override)); + + useMaxWidth = (useMaxWidth && maxWidth < fontWidth); + float width = useMaxWidth ? maxWidth : fontWidth; + + TextAlign align = state().m_textAlign; + if (align == StartTextAlign) + align = isRTL ? RightTextAlign : LeftTextAlign; + else if (align == EndTextAlign) + align = isRTL ? LeftTextAlign : RightTextAlign; + + switch (align) { + case CenterTextAlign: + location.setX(location.x() - width / 2); + break; + case RightTextAlign: + location.setX(location.x() - width); + break; + default: + break; + } + + // The slop built in to this mask rect matches the heuristic used in FontCGWin.cpp for GDI text. + FloatRect textRect = FloatRect(location.x() - fontMetrics.height() / 2, location.y() - fontMetrics.ascent() - fontMetrics.lineGap(), + width + fontMetrics.height(), fontMetrics.lineSpacing()); + if (!fill) + textRect.inflate(c->strokeThickness() / 2); + +#if USE(CG) + CanvasStyle* drawStyle = fill ? state().m_fillStyle.get() : state().m_strokeStyle.get(); + if (drawStyle->canvasGradient() || drawStyle->canvasPattern()) { + // FIXME: The rect is not big enough for miters on stroked text. + IntRect maskRect = enclosingIntRect(textRect); + +#if USE(IOSURFACE_CANVAS_BACKING_STORE) + OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), ColorSpaceDeviceRGB, Accelerated); +#else + OwnPtr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size()); +#endif + + GraphicsContext* maskImageContext = maskImage->context(); + + if (fill) + maskImageContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + else { + maskImageContext->setStrokeColor(Color::black, ColorSpaceDeviceRGB); + maskImageContext->setStrokeThickness(c->strokeThickness()); + } + + maskImageContext->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); + + if (useMaxWidth) { + maskImageContext->translate(location.x() - maskRect.x(), location.y() - maskRect.y()); + // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work. + maskImageContext->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1)); + maskImageContext->drawBidiText(font, textRun, FloatPoint(0, 0)); + } else { + maskImageContext->translate(-maskRect.x(), -maskRect.y()); + maskImageContext->drawBidiText(font, textRun, location); + } + + GraphicsContextStateSaver stateSaver(*c); + c->clipToImageBuffer(maskImage.get(), maskRect); + drawStyle->applyFillColor(c); + c->fillRect(maskRect); + return; + } +#endif + + c->setTextDrawingMode(fill ? TextModeFill : TextModeStroke); + +#if PLATFORM(QT) + // We always use complex text shaping since it can't be turned off for QPainterPath::addText(). + Font::CodePath oldCodePath = Font::codePath(); + Font::setCodePath(Font::Complex); +#endif + + if (useMaxWidth) { + GraphicsContextStateSaver stateSaver(*c); + c->translate(location.x(), location.y()); + // We draw when fontWidth is 0 so compositing operations (eg, a "copy" op) still work. + c->scale(FloatSize((fontWidth > 0 ? (width / fontWidth) : 0), 1)); + c->drawBidiText(font, textRun, FloatPoint(0, 0)); + } else + c->drawBidiText(font, textRun, location); + + if (fill) + didDraw(textRect); + else { + // When stroking text, pointy miters can extend outside of textRect, so we + // punt and dirty the whole canvas. + didDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); + } + +#if PLATFORM(QT) + Font::setCodePath(oldCodePath); +#endif +} + +const Font& CanvasRenderingContext2D::accessFont() +{ + canvas()->document()->updateStyleIfNeeded(); + + if (!state().m_realizedFont) + setFont(state().m_unparsedFont); + return state().m_font; +} + +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) +PlatformLayer* CanvasRenderingContext2D::platformLayer() const +{ + return canvas()->buffer() ? canvas()->buffer()->platformLayer() : 0; +} +#endif + +} // namespace WebCore diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2D.h b/Source/WebCore/html/canvas/CanvasRenderingContext2D.h new file mode 100644 index 000000000..4b3df1592 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasRenderingContext2D.h @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasRenderingContext2D_h +#define CanvasRenderingContext2D_h + +#include "AffineTransform.h" +#include "CanvasRenderingContext.h" +#include "Color.h" +#include "ColorSpace.h" +#include "DashArray.h" +#include "FloatSize.h" +#include "Font.h" +#include "GraphicsTypes.h" +#include "Path.h" +#include "PlatformString.h" +#include <wtf/Vector.h> + +#if USE(ACCELERATED_COMPOSITING) +#include "GraphicsLayer.h" +#endif + +namespace WebCore { + +class CanvasGradient; +class CanvasPattern; +class CanvasStyle; +class FloatRect; +class GraphicsContext; +class HTMLCanvasElement; +class HTMLImageElement; +class HTMLVideoElement; +class ImageData; +class TextMetrics; + +typedef int ExceptionCode; + +class CanvasRenderingContext2D : public CanvasRenderingContext { +public: + static PassOwnPtr<CanvasRenderingContext2D> create(HTMLCanvasElement* canvas, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode) + { + return adoptPtr(new CanvasRenderingContext2D(canvas, usesCSSCompatibilityParseMode, usesDashboardCompatibilityMode)); + } + virtual ~CanvasRenderingContext2D(); + + virtual bool is2d() const { return true; } + virtual bool isAccelerated() const; + virtual bool paintsIntoCanvasBuffer() const; + + CanvasStyle* strokeStyle() const; + void setStrokeStyle(PassRefPtr<CanvasStyle>); + + CanvasStyle* fillStyle() const; + void setFillStyle(PassRefPtr<CanvasStyle>); + + float lineWidth() const; + void setLineWidth(float); + + String lineCap() const; + void setLineCap(const String&); + + String lineJoin() const; + void setLineJoin(const String&); + + float miterLimit() const; + void setMiterLimit(float); + + const DashArray* webkitLineDash() const; + void setWebkitLineDash(const DashArray&); + + float webkitLineDashOffset() const; + void setWebkitLineDashOffset(float); + + float shadowOffsetX() const; + void setShadowOffsetX(float); + + float shadowOffsetY() const; + void setShadowOffsetY(float); + + float shadowBlur() const; + void setShadowBlur(float); + + String shadowColor() const; + void setShadowColor(const String&); + + float globalAlpha() const; + void setGlobalAlpha(float); + + String globalCompositeOperation() const; + void setGlobalCompositeOperation(const String&); + + void save(); + void restore(); + void setAllAttributesToDefault(); + + void scale(float sx, float sy); + void rotate(float angleInRadians); + void translate(float tx, float ty); + void transform(float m11, float m12, float m21, float m22, float dx, float dy); + void setTransform(float m11, float m12, float m21, float m22, float dx, float dy); + + void setStrokeColor(const String& color); + void setStrokeColor(float grayLevel); + void setStrokeColor(const String& color, float alpha); + void setStrokeColor(float grayLevel, float alpha); + void setStrokeColor(float r, float g, float b, float a); + void setStrokeColor(float c, float m, float y, float k, float a); + + void setFillColor(const String& color); + void setFillColor(float grayLevel); + void setFillColor(const String& color, float alpha); + void setFillColor(float grayLevel, float alpha); + void setFillColor(float r, float g, float b, float a); + void setFillColor(float c, float m, float y, float k, float a); + + void beginPath(); + void closePath(); + + void moveTo(float x, float y); + void lineTo(float x, float y); + void quadraticCurveTo(float cpx, float cpy, float x, float y); + void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y); + void arcTo(float x0, float y0, float x1, float y1, float radius, ExceptionCode&); + void arc(float x, float y, float r, float sa, float ea, bool clockwise, ExceptionCode&); + void rect(float x, float y, float width, float height); + + void fill(); + void stroke(); + void clip(); + + bool isPointInPath(const float x, const float y); + + void clearRect(float x, float y, float width, float height); + void fillRect(float x, float y, float width, float height); + void strokeRect(float x, float y, float width, float height); + void strokeRect(float x, float y, float width, float height, float lineWidth); + + void setShadow(float width, float height, float blur); + void setShadow(float width, float height, float blur, const String& color); + void setShadow(float width, float height, float blur, float grayLevel); + void setShadow(float width, float height, float blur, const String& color, float alpha); + void setShadow(float width, float height, float blur, float grayLevel, float alpha); + void setShadow(float width, float height, float blur, float r, float g, float b, float a); + void setShadow(float width, float height, float blur, float c, float m, float y, float k, float a); + + void clearShadow(); + + void drawImage(HTMLImageElement*, float x, float y, ExceptionCode&); + void drawImage(HTMLImageElement*, float x, float y, float width, float height, ExceptionCode&); + void drawImage(HTMLImageElement*, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, ExceptionCode&); + void drawImage(HTMLImageElement*, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode&); + void drawImage(HTMLCanvasElement*, float x, float y, ExceptionCode&); + void drawImage(HTMLCanvasElement*, float x, float y, float width, float height, ExceptionCode&); + void drawImage(HTMLCanvasElement*, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, ExceptionCode&); + void drawImage(HTMLCanvasElement*, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode&); + void drawImage(HTMLImageElement*, const FloatRect& srcRect, const FloatRect& dstRect, const CompositeOperator&, ExceptionCode&); +#if ENABLE(VIDEO) + void drawImage(HTMLVideoElement*, float x, float y, ExceptionCode&); + void drawImage(HTMLVideoElement*, float x, float y, float width, float height, ExceptionCode&); + void drawImage(HTMLVideoElement*, float sx, float sy, float sw, float sh, float dx, float dy, float dw, float dh, ExceptionCode&); + void drawImage(HTMLVideoElement*, const FloatRect& srcRect, const FloatRect& dstRect, ExceptionCode&); +#endif + + void drawImageFromRect(HTMLImageElement*, float sx = 0, float sy = 0, float sw = 0, float sh = 0, + float dx = 0, float dy = 0, float dw = 0, float dh = 0, const String& compositeOperation = emptyString()); + + void setAlpha(float); + + void setCompositeOperation(const String&); + + PassRefPtr<CanvasGradient> createLinearGradient(float x0, float y0, float x1, float y1, ExceptionCode&); + PassRefPtr<CanvasGradient> createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1, ExceptionCode&); + PassRefPtr<CanvasPattern> createPattern(HTMLImageElement*, const String& repetitionType, ExceptionCode&); + PassRefPtr<CanvasPattern> createPattern(HTMLCanvasElement*, const String& repetitionType, ExceptionCode&); + + PassRefPtr<ImageData> createImageData(PassRefPtr<ImageData>, ExceptionCode&) const; + PassRefPtr<ImageData> createImageData(float width, float height, ExceptionCode&) const; + PassRefPtr<ImageData> getImageData(float sx, float sy, float sw, float sh, ExceptionCode&) const; + void putImageData(ImageData*, float dx, float dy, ExceptionCode&); + void putImageData(ImageData*, float dx, float dy, float dirtyX, float dirtyY, float dirtyWidth, float dirtyHeight, ExceptionCode&); + + void reset(); + + String font() const; + void setFont(const String&); + + String textAlign() const; + void setTextAlign(const String&); + + String textBaseline() const; + void setTextBaseline(const String&); + + void fillText(const String& text, float x, float y); + void fillText(const String& text, float x, float y, float maxWidth); + void strokeText(const String& text, float x, float y); + void strokeText(const String& text, float x, float y, float maxWidth); + PassRefPtr<TextMetrics> measureText(const String& text); + + LineCap getLineCap() const { return state().m_lineCap; } + LineJoin getLineJoin() const { return state().m_lineJoin; } + +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const; +#endif + +private: + struct State : FontSelectorClient { + State(); + virtual ~State(); + + State(const State&); + State& operator=(const State&); + + virtual void fontsNeedUpdate(FontSelector*); + + String m_unparsedStrokeColor; + String m_unparsedFillColor; + RefPtr<CanvasStyle> m_strokeStyle; + RefPtr<CanvasStyle> m_fillStyle; + float m_lineWidth; + LineCap m_lineCap; + LineJoin m_lineJoin; + float m_miterLimit; + FloatSize m_shadowOffset; + float m_shadowBlur; + RGBA32 m_shadowColor; + float m_globalAlpha; + CompositeOperator m_globalComposite; + AffineTransform m_transform; + bool m_invertibleCTM; + DashArray m_lineDash; + float m_lineDashOffset; + + // Text state. + TextAlign m_textAlign; + TextBaseline m_textBaseline; + + String m_unparsedFont; + Font m_font; + bool m_realizedFont; + }; + + enum CanvasDidDrawOption { + CanvasDidDrawApplyNone = 0, + CanvasDidDrawApplyTransform = 1, + CanvasDidDrawApplyShadow = 1 << 1, + CanvasDidDrawApplyClip = 1 << 2, + CanvasDidDrawApplyAll = 0xffffffff + }; + + CanvasRenderingContext2D(HTMLCanvasElement*, bool usesCSSCompatibilityParseMode, bool usesDashboardCompatibilityMode); + + Path m_path; + + State& state() { return m_stateStack.last(); } + const State& state() const { return m_stateStack.last(); } + + void applyShadow(); + + void didDraw(const FloatRect&, unsigned options = CanvasDidDrawApplyAll); + void didDrawEntireCanvas(); + + GraphicsContext* drawingContext() const; + + void unwindStateStack(); + + void applyStrokePattern(); + void applyFillPattern(); + + void drawTextInternal(const String& text, float x, float y, bool fill, float maxWidth = 0, bool useMaxWidth = false); + + const Font& accessFont(); + +#if ENABLE(DASHBOARD_SUPPORT) + void clearPathForDashboardBackwardCompatibilityMode(); +#endif + + void clearCanvas(); + Path transformAreaToDevice(const Path&) const; + Path transformAreaToDevice(const FloatRect&) const; + bool rectContainsCanvas(const FloatRect&) const; + + template<class T> IntRect calculateCompositingBufferRect(const T&, IntSize*); + PassOwnPtr<ImageBuffer> createCompositingBuffer(const IntRect&); + void compositeBuffer(ImageBuffer*, const IntRect&, CompositeOperator); + + template<class T> void fullCanvasCompositedFill(const T&); + template<class T> void fullCanvasCompositedDrawImage(T*, ColorSpace, const FloatRect&, const FloatRect&, CompositeOperator); + + void prepareGradientForDashboard(CanvasGradient* gradient) const; + + Vector<State, 1> m_stateStack; + bool m_usesCSSCompatibilityParseMode; +#if ENABLE(DASHBOARD_SUPPORT) + bool m_usesDashboardCompatibilityMode; +#endif +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2D.idl b/Source/WebCore/html/canvas/CanvasRenderingContext2D.idl new file mode 100644 index 000000000..0168e4c66 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasRenderingContext2D.idl @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + InterfaceUUID=98fb48ae-7216-489c-862b-8e1217fc4443, + ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54 + ] CanvasRenderingContext2D : CanvasRenderingContext { + + void save(); + void restore(); + + void scale(in [Optional=CallWithDefaultValue] float sx, + in [Optional=CallWithDefaultValue] float sy); + void rotate(in [Optional=CallWithDefaultValue] float angle); + void translate(in [Optional=CallWithDefaultValue] float tx, + in [Optional=CallWithDefaultValue] float ty); + void transform(in [Optional=CallWithDefaultValue] float m11, + in [Optional=CallWithDefaultValue] float m12, + in [Optional=CallWithDefaultValue] float m21, + in [Optional=CallWithDefaultValue] float m22, + in [Optional=CallWithDefaultValue] float dx, + in [Optional=CallWithDefaultValue] float dy); + void setTransform(in [Optional=CallWithDefaultValue] float m11, + in [Optional=CallWithDefaultValue] float m12, + in [Optional=CallWithDefaultValue] float m21, + in [Optional=CallWithDefaultValue] float m22, + in [Optional=CallWithDefaultValue] float dx, + in [Optional=CallWithDefaultValue] float dy); + + attribute float globalAlpha; + attribute [ConvertNullToNullString] DOMString globalCompositeOperation; + + CanvasGradient createLinearGradient(in [Optional=CallWithDefaultValue] float x0, + in [Optional=CallWithDefaultValue] float y0, + in [Optional=CallWithDefaultValue] float x1, + in [Optional=CallWithDefaultValue] float y1) + raises (DOMException); + CanvasGradient createRadialGradient(in [Optional=CallWithDefaultValue] float x0, + in [Optional=CallWithDefaultValue] float y0, + in [Optional=CallWithDefaultValue] float r0, + in [Optional=CallWithDefaultValue] float x1, + in [Optional=CallWithDefaultValue] float y1, + in [Optional=CallWithDefaultValue] float r1) + raises (DOMException); + + attribute float lineWidth; + attribute [ConvertNullToNullString] DOMString lineCap; + attribute [ConvertNullToNullString] DOMString lineJoin; + attribute float miterLimit; + + attribute float shadowOffsetX; + attribute float shadowOffsetY; + attribute float shadowBlur; + attribute [ConvertNullToNullString] DOMString shadowColor; + + // FIXME: These attributes should also be implemented for V8. +#if !(defined(V8_BINDING) && V8_BINDING) + attribute [Custom] Array webkitLineDash; + attribute float webkitLineDashOffset; +#endif + + void clearRect(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y, + in [Optional=CallWithDefaultValue] float width, + in [Optional=CallWithDefaultValue] float height); + void fillRect(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y, + in [Optional=CallWithDefaultValue] float width, + in [Optional=CallWithDefaultValue] float height); + + void beginPath(); + void closePath(); + void moveTo(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y); + void lineTo(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y); + void quadraticCurveTo(in [Optional=CallWithDefaultValue] float cpx, + in [Optional=CallWithDefaultValue] float cpy, + in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y); + void bezierCurveTo(in [Optional=CallWithDefaultValue] float cp1x, + in [Optional=CallWithDefaultValue] float cp1y, + in [Optional=CallWithDefaultValue] float cp2x, + in [Optional=CallWithDefaultValue] float cp2y, + in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y); + void arcTo(in [Optional=CallWithDefaultValue] float x1, + in [Optional=CallWithDefaultValue] float y1, + in [Optional=CallWithDefaultValue] float x2, + in [Optional=CallWithDefaultValue] float y2, + in [Optional=CallWithDefaultValue] float radius) + raises (DOMException); + void rect(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y, + in [Optional=CallWithDefaultValue] float width, + in [Optional=CallWithDefaultValue] float height); + void arc(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y, + in [Optional=CallWithDefaultValue] float radius, + in [Optional=CallWithDefaultValue] float startAngle, + in [Optional=CallWithDefaultValue] float endAngle, + in [Optional=CallWithDefaultValue] boolean anticlockwise) + raises (DOMException); + void fill(); + void stroke(); + void clip(); + boolean isPointInPath(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y); + + // text + attribute DOMString font; + attribute DOMString textAlign; + attribute DOMString textBaseline; + + TextMetrics measureText(in [Optional=CallWithDefaultValue] DOMString text); + + // other + + void setAlpha(in [Optional=CallWithDefaultValue] float alpha); + void setCompositeOperation(in [Optional=CallWithDefaultValue] DOMString compositeOperation); + +#if !defined(LANGUAGE_CPP) || !LANGUAGE_CPP + void setLineWidth(in [Optional=CallWithDefaultValue] float width); + void setLineCap(in [Optional=CallWithDefaultValue] DOMString cap); + void setLineJoin(in [Optional=CallWithDefaultValue] DOMString join); + void setMiterLimit(in [Optional=CallWithDefaultValue] float limit); +#endif + + void clearShadow(); + + void fillText(in DOMString text, in float x, in float y, in [Optional] float maxWidth); + void strokeText(in DOMString text, in float x, in float y, in [Optional] float maxWidth); + + void setStrokeColor(in DOMString color, in [Optional] float alpha); + void setStrokeColor(in float grayLevel, in [Optional] float alpha); + void setStrokeColor(in float r, in float g, in float b, in float a); + void setStrokeColor(in float c, in float m, in float y, in float k, in float a); + + void setFillColor(in DOMString color, in [Optional] float alpha); + void setFillColor(in float grayLevel, in [Optional] float alpha); + void setFillColor(in float r, in float g, in float b, in float a); + void setFillColor(in float c, in float m, in float y, in float k, in float a); + + void strokeRect(in [Optional=CallWithDefaultValue] float x, + in [Optional=CallWithDefaultValue] float y, + in [Optional=CallWithDefaultValue] float width, + in [Optional=CallWithDefaultValue] float height, + in [Optional] float lineWidth); + + void drawImage(in HTMLImageElement image, in float x, in float y) + raises (DOMException); + void drawImage(in HTMLImageElement image, in float x, in float y, in float width, in float height) + raises (DOMException); + void drawImage(in HTMLImageElement image, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh) + raises (DOMException); + void drawImage(in HTMLCanvasElement canvas, in float x, in float y) + raises (DOMException); + void drawImage(in HTMLCanvasElement canvas, in float x, in float y, in float width, in float height) + raises (DOMException); + void drawImage(in HTMLCanvasElement canvas, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh) + raises (DOMException); +#if defined(ENABLE_VIDEO) && ENABLE_VIDEO + void drawImage(in HTMLVideoElement video, in float x, in float y) + raises (DOMException); + void drawImage(in HTMLVideoElement video, in float x, in float y, in float width, in float height) + raises (DOMException); + void drawImage(in HTMLVideoElement video, in float sx, in float sy, in float sw, in float sh, in float dx, in float dy, in float dw, in float dh) + raises (DOMException); +#endif + + void drawImageFromRect(in HTMLImageElement image, + in [Optional] float sx, in [Optional] float sy, in [Optional] float sw, in [Optional] float sh, + in [Optional] float dx, in [Optional] float dy, in [Optional] float dw, in [Optional] float dh, + in [Optional] DOMString compositeOperation); + + void setShadow(in float width, in float height, in float blur, in [Optional] DOMString color, in [Optional] float alpha); + void setShadow(in float width, in float height, in float blur, in float grayLevel, in [Optional] float alpha); + void setShadow(in float width, in float height, in float blur, in float r, in float g, in float b, in float a); + void setShadow(in float width, in float height, in float blur, in float c, in float m, in float y, in float k, in float a); + + void putImageData(in ImageData imagedata, in float dx, in float dy) + raises(DOMException); + void putImageData(in ImageData imagedata, in float dx, in float dy, in float dirtyX, in float dirtyY, in float dirtyWidth, in float dirtyHeight) + raises(DOMException); + + CanvasPattern createPattern(in HTMLCanvasElement canvas, in [ConvertNullToNullString] DOMString repetitionType) + raises (DOMException); + CanvasPattern createPattern(in HTMLImageElement image, in [ConvertNullToNullString] DOMString repetitionType) + raises (DOMException); + ImageData createImageData(in ImageData imagedata) + raises (DOMException); + ImageData createImageData(in float sw, in float sh) + raises (DOMException); + + attribute [Custom] custom strokeStyle; + attribute [Custom] custom fillStyle; + + // pixel manipulation + ImageData getImageData(in [Optional=CallWithDefaultValue] float sx, in [Optional=CallWithDefaultValue] float sy, + in [Optional=CallWithDefaultValue] float sw, in [Optional=CallWithDefaultValue] float sh) + raises(DOMException); + }; + +} + diff --git a/Source/WebCore/html/canvas/CanvasStyle.cpp b/Source/WebCore/html/canvas/CanvasStyle.cpp new file mode 100644 index 000000000..3aee6e8f1 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasStyle.cpp @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2010 Nokia Corporation and/or its subsidiary(-ies) + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2008 Eric Seidel <eric@webkit.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CanvasStyle.h" + +#include "CSSParser.h" +#include "CSSPropertyNames.h" +#include "CanvasGradient.h" +#include "CanvasPattern.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include <wtf/Assertions.h> +#include <wtf/PassRefPtr.h> + +#if USE(CG) +#include <CoreGraphics/CGContext.h> +#endif + +#if PLATFORM(QT) +#include <QPainter> +#include <QBrush> +#include <QPen> +#include <QColor> +#endif + +namespace WebCore { + +enum ColorParseResult { ParsedRGBA, ParsedCurrentColor, ParsedSystemColor, ParseFailed }; + +static ColorParseResult parseColor(RGBA32& parsedColor, const String& colorString, Document* document = 0) +{ + if (equalIgnoringCase(colorString, "currentcolor")) + return ParsedCurrentColor; + if (CSSParser::parseColor(parsedColor, colorString)) + return ParsedRGBA; + if (CSSParser::parseSystemColor(parsedColor, colorString, document)) + return ParsedSystemColor; + return ParseFailed; +} + +RGBA32 currentColor(HTMLCanvasElement* canvas) +{ + if (!canvas || !canvas->inDocument()) + return Color::black; + RGBA32 rgba = Color::black; + CSSParser::parseColor(rgba, canvas->style()->getPropertyValue(CSSPropertyColor)); + return rgba; +} + +bool parseColorOrCurrentColor(RGBA32& parsedColor, const String& colorString, HTMLCanvasElement* canvas) +{ + ColorParseResult parseResult = parseColor(parsedColor, colorString, canvas ? canvas->document() : 0); + switch (parseResult) { + case ParsedRGBA: + case ParsedSystemColor: + return true; + case ParsedCurrentColor: + parsedColor = currentColor(canvas); + return true; + case ParseFailed: + return false; + default: + ASSERT_NOT_REACHED(); + return false; + } +} + +CanvasStyle::CanvasStyle(Type type, float overrideAlpha) + : m_type(type) + , m_overrideAlpha(overrideAlpha) +{ +} + +CanvasStyle::CanvasStyle(RGBA32 rgba) + : m_type(RGBA) + , m_rgba(rgba) +{ +} + +CanvasStyle::CanvasStyle(float grayLevel, float alpha) + : m_type(RGBA) + , m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha)) +{ +} + +CanvasStyle::CanvasStyle(float r, float g, float b, float a) + : m_type(RGBA) + , m_rgba(makeRGBA32FromFloats(r, g, b, a)) +{ +} + +CanvasStyle::CanvasStyle(float c, float m, float y, float k, float a) + : m_type(CMYKA) + , m_rgba(makeRGBAFromCMYKA(c, m, y, k, a)) + , m_cmyka(c, m, y, k, a) +{ +} + +CanvasStyle::CanvasStyle(PassRefPtr<CanvasGradient> gradient) + : m_type(Gradient) + , m_gradient(gradient) +{ +} + +CanvasStyle::CanvasStyle(PassRefPtr<CanvasPattern> pattern) + : m_type(ImagePattern) + , m_pattern(pattern) +{ +} + +PassRefPtr<CanvasStyle> CanvasStyle::createFromString(const String& color, Document* document) +{ + RGBA32 rgba; + ColorParseResult parseResult = parseColor(rgba, color, document); + switch (parseResult) { + case ParsedRGBA: + case ParsedSystemColor: + return adoptRef(new CanvasStyle(rgba)); + case ParsedCurrentColor: + return adoptRef(new CanvasStyle(CurrentColor)); + case ParseFailed: + return 0; + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +PassRefPtr<CanvasStyle> CanvasStyle::createFromStringWithOverrideAlpha(const String& color, float alpha) +{ + RGBA32 rgba; + ColorParseResult parseResult = parseColor(rgba, color); + switch (parseResult) { + case ParsedRGBA: + return adoptRef(new CanvasStyle(colorWithOverrideAlpha(rgba, alpha))); + case ParsedCurrentColor: + return adoptRef(new CanvasStyle(CurrentColorWithOverrideAlpha, alpha)); + case ParseFailed: + return 0; + default: + ASSERT_NOT_REACHED(); + return 0; + } +} + +PassRefPtr<CanvasStyle> CanvasStyle::createFromGradient(PassRefPtr<CanvasGradient> gradient) +{ + if (!gradient) + return 0; + return adoptRef(new CanvasStyle(gradient)); +} +PassRefPtr<CanvasStyle> CanvasStyle::createFromPattern(PassRefPtr<CanvasPattern> pattern) +{ + if (!pattern) + return 0; + return adoptRef(new CanvasStyle(pattern)); +} + +bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const +{ + if (m_type != other.m_type) + return false; + + switch (m_type) { + case RGBA: + return m_rgba == other.m_rgba; + case CMYKA: + return m_cmyka.c == other.m_cmyka.c + && m_cmyka.m == other.m_cmyka.m + && m_cmyka.y == other.m_cmyka.y + && m_cmyka.k == other.m_cmyka.k + && m_cmyka.a == other.m_cmyka.a; + case Gradient: + case ImagePattern: + case CurrentColor: + case CurrentColorWithOverrideAlpha: + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + +bool CanvasStyle::isEquivalentRGBA(float r, float g, float b, float a) const +{ + if (m_type != RGBA) + return false; + + return m_rgba == makeRGBA32FromFloats(r, g, b, a); +} + +bool CanvasStyle::isEquivalentCMYKA(float c, float m, float y, float k, float a) const +{ + if (m_type != CMYKA) + return false; + + return c == m_cmyka.c + && m == m_cmyka.m + && y == m_cmyka.y + && k == m_cmyka.k + && a == m_cmyka.a; +} + +void CanvasStyle::applyStrokeColor(GraphicsContext* context) +{ + if (!context) + return; + switch (m_type) { + case RGBA: + context->setStrokeColor(m_rgba, ColorSpaceDeviceRGB); + break; + case CMYKA: { + // FIXME: Do this through platform-independent GraphicsContext API. + // We'll need a fancier Color abstraction to support CMYKA correctly +#if USE(CG) + CGContextSetCMYKStrokeColor(context->platformContext(), m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a); +#elif PLATFORM(QT) + QPen currentPen = context->platformContext()->pen(); + QColor clr; + clr.setCmykF(m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a); + currentPen.setColor(clr); + context->platformContext()->setPen(currentPen); +#else + context->setStrokeColor(m_rgba, ColorSpaceDeviceRGB); +#endif + break; + } + case Gradient: + context->setStrokeGradient(canvasGradient()->gradient()); + break; + case ImagePattern: + context->setStrokePattern(canvasPattern()->pattern()); + break; + case CurrentColor: + case CurrentColorWithOverrideAlpha: + ASSERT_NOT_REACHED(); + break; + } +} + +void CanvasStyle::applyFillColor(GraphicsContext* context) +{ + if (!context) + return; + switch (m_type) { + case RGBA: + context->setFillColor(m_rgba, ColorSpaceDeviceRGB); + break; + case CMYKA: { + // FIXME: Do this through platform-independent GraphicsContext API. + // We'll need a fancier Color abstraction to support CMYKA correctly +#if USE(CG) + CGContextSetCMYKFillColor(context->platformContext(), m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a); +#elif PLATFORM(QT) + QBrush currentBrush = context->platformContext()->brush(); + QColor clr; + clr.setCmykF(m_cmyka.c, m_cmyka.m, m_cmyka.y, m_cmyka.k, m_cmyka.a); + currentBrush.setColor(clr); + context->platformContext()->setBrush(currentBrush); +#else + context->setFillColor(m_rgba, ColorSpaceDeviceRGB); +#endif + break; + } + case Gradient: + context->setFillGradient(canvasGradient()->gradient()); + break; + case ImagePattern: + context->setFillPattern(canvasPattern()->pattern()); + break; + case CurrentColor: + case CurrentColorWithOverrideAlpha: + ASSERT_NOT_REACHED(); + break; + } +} + +} diff --git a/Source/WebCore/html/canvas/CanvasStyle.h b/Source/WebCore/html/canvas/CanvasStyle.h new file mode 100644 index 000000000..8f4bfd402 --- /dev/null +++ b/Source/WebCore/html/canvas/CanvasStyle.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CanvasStyle_h +#define CanvasStyle_h + +#include "Color.h" +#include "PlatformString.h" +#include <wtf/Assertions.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + + class CanvasGradient; + class CanvasPattern; + class Document; + class GraphicsContext; + class HTMLCanvasElement; + + class CanvasStyle : public RefCounted<CanvasStyle> { + public: + static PassRefPtr<CanvasStyle> createFromRGBA(RGBA32 rgba) { return adoptRef(new CanvasStyle(rgba)); } + static PassRefPtr<CanvasStyle> createFromString(const String& color, Document* = 0); + static PassRefPtr<CanvasStyle> createFromStringWithOverrideAlpha(const String& color, float alpha); + static PassRefPtr<CanvasStyle> createFromGrayLevelWithAlpha(float grayLevel, float alpha) { return adoptRef(new CanvasStyle(grayLevel, alpha)); } + static PassRefPtr<CanvasStyle> createFromRGBAChannels(float r, float g, float b, float a) { return adoptRef(new CanvasStyle(r, g, b, a)); } + static PassRefPtr<CanvasStyle> createFromCMYKAChannels(float c, float m, float y, float k, float a) { return adoptRef(new CanvasStyle(c, m, y, k, a)); } + static PassRefPtr<CanvasStyle> createFromGradient(PassRefPtr<CanvasGradient>); + static PassRefPtr<CanvasStyle> createFromPattern(PassRefPtr<CanvasPattern>); + + bool isCurrentColor() const { return m_type == CurrentColor || m_type == CurrentColorWithOverrideAlpha; } + bool hasOverrideAlpha() const { return m_type == CurrentColorWithOverrideAlpha; } + float overrideAlpha() const { ASSERT(m_type == CurrentColorWithOverrideAlpha); return m_overrideAlpha; } + + String color() const { ASSERT(m_type == RGBA || m_type == CMYKA); return Color(m_rgba).serialized(); } + CanvasGradient* canvasGradient() const { return m_gradient.get(); } + CanvasPattern* canvasPattern() const { return m_pattern.get(); } + + void applyFillColor(GraphicsContext*); + void applyStrokeColor(GraphicsContext*); + + bool isEquivalentColor(const CanvasStyle&) const; + bool isEquivalentRGBA(float r, float g, float b, float a) const; + bool isEquivalentCMYKA(float c, float m, float y, float k, float a) const; + + private: + enum Type { RGBA, CMYKA, Gradient, ImagePattern, CurrentColor, CurrentColorWithOverrideAlpha }; + + CanvasStyle(Type, float overrideAlpha = 0); + CanvasStyle(RGBA32 rgba); + CanvasStyle(float grayLevel, float alpha); + CanvasStyle(float r, float g, float b, float a); + CanvasStyle(float c, float m, float y, float k, float a); + CanvasStyle(PassRefPtr<CanvasGradient>); + CanvasStyle(PassRefPtr<CanvasPattern>); + + Type m_type; + + union { + RGBA32 m_rgba; + float m_overrideAlpha; + }; + + RefPtr<CanvasGradient> m_gradient; + RefPtr<CanvasPattern> m_pattern; + + struct CMYKAValues { + CMYKAValues() {} + CMYKAValues(float cyan, float magenta, float yellow, float black, float alpha) : c(cyan), m(magenta), y(yellow), k(black), a(alpha) {} + float c; + float m; + float y; + float k; + float a; + } m_cmyka; + }; + + RGBA32 currentColor(HTMLCanvasElement*); + bool parseColorOrCurrentColor(RGBA32& parsedColor, const String& colorString, HTMLCanvasElement*); + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/canvas/CheckedInt.h b/Source/WebCore/html/canvas/CheckedInt.h new file mode 100644 index 000000000..b83ac19ea --- /dev/null +++ b/Source/WebCore/html/canvas/CheckedInt.h @@ -0,0 +1,588 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla code. + * + * The Initial Developer of the Original Code is the Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benoit Jacob <bjacob@mozilla.com> + * Jeff Muizelaar <jmuizelaar@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// Necessary modifications are made to the original CheckedInt.h file to remove +// dependencies on prtypes. +// Also, change define Mozilla_CheckedInt_h to CheckedInt_h, change namespace +// from mozilla to WebCore for easier usage. + +#ifndef CheckedInt_h +#define CheckedInt_h + +#include <climits> + +namespace WebCore { + +namespace CheckedInt_internal { + +/* we don't want to use std::numeric_limits here because int... types may not support it, + * depending on the platform, e.g. on certain platform they use nonstandard built-in types + */ + +/*** Step 1: manually record information for all the types that we want to support + ***/ + +struct unsupported_type {}; + +template<typename T> struct integer_type_manually_recorded_info; + + +#define CHECKEDINT_REGISTER_SUPPORTED_TYPE(T,_twice_bigger_type,_unsigned_type) \ +template<> struct integer_type_manually_recorded_info<T> \ +{ \ + enum { is_supported = 1 }; \ + typedef _twice_bigger_type twice_bigger_type; \ + typedef _unsigned_type unsigned_type; \ +}; + +// Type Twice Bigger Type Unsigned Type +CHECKEDINT_REGISTER_SUPPORTED_TYPE(int8_t, int16_t, uint8_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint8_t, uint16_t, uint8_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(int16_t, int32_t, uint16_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint16_t, uint32_t, uint16_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(int32_t, int64_t, uint32_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint32_t, uint64_t, uint32_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(int64_t, unsupported_type, uint64_t) +CHECKEDINT_REGISTER_SUPPORTED_TYPE(uint64_t, unsupported_type, uint64_t) + +// now implement the fallback for standard types like int, long, ... +// the difficulty is that they may or may not be equal to one of the above types, and/or +// to each other. This is why any attempt to handle at once PRInt8... types and standard types +// is bound to fail. +template<typename T> +struct is_standard_integer_type { enum { value = 0 }; }; + +template<> +struct is_standard_integer_type<char> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<unsigned char> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<short> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<unsigned short> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<int> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<unsigned int> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<long> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<unsigned long> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<long long> { enum { value = 1 }; }; +template<> +struct is_standard_integer_type<unsigned long long> { enum { value = 1 }; }; + +template<int size, bool is_signed> +struct explicitly_sized_integer_type {}; + +template<> +struct explicitly_sized_integer_type<1, true> { typedef int8_t type; }; +template<> +struct explicitly_sized_integer_type<1, false> { typedef uint8_t type; }; +template<> +struct explicitly_sized_integer_type<2, true> { typedef int16_t type; }; +template<> +struct explicitly_sized_integer_type<2, false> { typedef uint16_t type; }; +template<> +struct explicitly_sized_integer_type<4, true> { typedef int32_t type; }; +template<> +struct explicitly_sized_integer_type<4, false> { typedef uint32_t type; }; +template<> +struct explicitly_sized_integer_type<8, true> { typedef int64_t type; }; +template<> +struct explicitly_sized_integer_type<8, false> { typedef uint64_t type; }; + +template<typename T> struct integer_type_manually_recorded_info +{ + enum { + is_supported = is_standard_integer_type<T>::value, + size = sizeof(T), + is_signed = (T(-1) > T(0)) ? 0 : 1 + }; + typedef typename explicitly_sized_integer_type<size, is_signed>::type explicit_sized_type; + typedef integer_type_manually_recorded_info<explicit_sized_type> base; + typedef typename base::twice_bigger_type twice_bigger_type; + typedef typename base::unsigned_type unsigned_type; +}; + +template<typename T, bool is_supported = integer_type_manually_recorded_info<T>::is_supported> +struct TYPE_NOT_SUPPORTED_BY_CheckedInt {}; + +template<typename T> +struct TYPE_NOT_SUPPORTED_BY_CheckedInt<T, true> { static void run() {} }; + + +/*** Step 2: record some info about a given integer type, + *** including whether it is supported, whether a twice bigger integer type + *** is supported, what that twice bigger type is, and some stuff as found + *** in std::numeric_limits (which we don't use because int.. types may + *** not support it, if they are defined directly from compiler built-in types). + ***/ + +template<typename T> struct is_unsupported_type { enum { answer = 0 }; }; +template<> struct is_unsupported_type<unsupported_type> { enum { answer = 1 }; }; + +template<typename T> struct integer_traits +{ + typedef typename integer_type_manually_recorded_info<T>::twice_bigger_type twice_bigger_type; + + enum { + is_supported = integer_type_manually_recorded_info<T>::is_supported, + twice_bigger_type_is_supported + = is_unsupported_type< + typename integer_type_manually_recorded_info<T>::twice_bigger_type + >::answer ? 0 : 1, + size = sizeof(T), + position_of_sign_bit = CHAR_BIT * size - 1, + is_signed = (T(-1) > T(0)) ? 0 : 1 + }; + + static T min() + { + // bitwise ops may return a larger type, that's why we cast explicitly to T + return is_signed ? T(T(1) << position_of_sign_bit) : T(0); + } + + static T max() + { + return ~min(); + } +}; + +/*** Step 3: Implement the actual validity checks --- ideas taken from IntegerLib, code different. + ***/ + +// bitwise ops may return a larger type, so it's good to use these inline helpers guaranteeing that +// the result is really of type T + +template<typename T> inline T has_sign_bit(T x) +{ + return x >> integer_traits<T>::position_of_sign_bit; +} + +template<typename T> inline T binary_complement(T x) +{ + return ~x; +} + +template<typename T, typename U, + bool is_T_signed = integer_traits<T>::is_signed, + bool is_U_signed = integer_traits<U>::is_signed> +struct is_in_range_impl {}; + +template<typename T, typename U> +struct is_in_range_impl<T, U, true, true> +{ + static T run(U x) + { + return (x <= integer_traits<T>::max()) & + (x >= integer_traits<T>::min()); + } +}; + +template<typename T, typename U> +struct is_in_range_impl<T, U, false, false> +{ + static T run(U x) + { + return x <= integer_traits<T>::max(); + } +}; + +template<typename T, typename U> +struct is_in_range_impl<T, U, true, false> +{ + static T run(U x) + { + if (sizeof(T) > sizeof(U)) + return 1; + else + return x <= U(integer_traits<T>::max()); + } +}; + +template<typename T, typename U> +struct is_in_range_impl<T, U, false, true> +{ + static T run(U x) + { + if (sizeof(T) >= sizeof(U)) + return x >= 0; + else + return x >= 0 && x <= U(integer_traits<T>::max()); + } +}; + +template<typename T, typename U> inline T is_in_range(U x) +{ + return is_in_range_impl<T, U>::run(x); +} + +template<typename T> inline T is_add_valid(T x, T y, T result) +{ + return integer_traits<T>::is_signed ? + // addition is valid if the sign of x+y is equal to either that of x or that of y. + // Beware! These bitwise operations can return a larger integer type, if T was a + // small type like int8, so we explicitly cast to T. + has_sign_bit(binary_complement(T((result^x) & (result^y)))) + : + binary_complement(x) >= y; +} + +template<typename T> inline T is_sub_valid(T x, T y, T result) +{ + return integer_traits<T>::is_signed ? + // substraction is valid if either x and y have same sign, or x-y and x have same sign + has_sign_bit(binary_complement(T((result^x) & (x^y)))) + : + x >= y; +} + +template<typename T, + bool is_signed = integer_traits<T>::is_signed, + bool twice_bigger_type_is_supported = integer_traits<T>::twice_bigger_type_is_supported> +struct is_mul_valid_impl {}; + +template<typename T> +struct is_mul_valid_impl<T, true, true> +{ + static T run(T x, T y) + { + typedef typename integer_traits<T>::twice_bigger_type twice_bigger_type; + twice_bigger_type product = twice_bigger_type(x) * twice_bigger_type(y); + return is_in_range<T>(product); + } +}; + +template<typename T> +struct is_mul_valid_impl<T, false, true> +{ + static T run(T x, T y) + { + typedef typename integer_traits<T>::twice_bigger_type twice_bigger_type; + twice_bigger_type product = twice_bigger_type(x) * twice_bigger_type(y); + return is_in_range<T>(product); + } +}; + +template<typename T> +struct is_mul_valid_impl<T, true, false> +{ + static T run(T x, T y) + { + const T max_value = integer_traits<T>::max(); + const T min_value = integer_traits<T>::min(); + + if (x == 0 || y == 0) return true; + + if (x > 0) { + if (y > 0) + return x <= max_value / y; + else + return y >= min_value / x; + } else { + if (y > 0) + return x >= min_value / y; + else + return y >= max_value / x; + } + } +}; + +template<typename T> +struct is_mul_valid_impl<T, false, false> +{ + static T run(T x, T y) + { + const T max_value = integer_traits<T>::max(); + if (x == 0 || y == 0) return true; + return x <= max_value / y; + } +}; + +template<typename T> inline T is_mul_valid(T x, T y, T /*result not used*/) +{ + return is_mul_valid_impl<T>::run(x, y); +} + +template<typename T> inline T is_div_valid(T x, T y) +{ + return integer_traits<T>::is_signed ? + // keep in mind that min/-1 is invalid because abs(min)>max + y != 0 && (x != integer_traits<T>::min() || y != T(-1)) + : + y != 0; +} + +} // end namespace CheckedInt_internal + + +/*** Step 4: Now define the CheckedInt class. + ***/ + +/** \class CheckedInt + * \brief Integer wrapper class checking for integer overflow and other errors + * \param T the integer type to wrap. Can be any of int8_t, uint8_t, int16_t, uint16_t, + * int32_t, uint32_t, int64_t, uint64_t. + * + * This class implements guarded integer arithmetic. Do a computation, then check that + * valid() returns true, you then have a guarantee that no problem, such as integer overflow, + * happened during this computation. + * + * The arithmetic operators in this class are guaranteed not to crash your app + * in case of a division by zero. + * + * For example, suppose that you want to implement a function that computes (x+y)/z, + * that doesn't crash if z==0, and that reports on error (divide by zero or integer overflow). + * You could code it as follows: + \code + bool compute_x_plus_y_over_z(int32_t x, int32_t y, int32_t z, int32_t *result) + { + CheckedInt<int32_t> checked_result = (CheckedInt<int32_t>(x) + y) / z; + *result = checked_result.value(); + return checked_result.valid(); + } + \endcode + * + * Implicit conversion from plain integers to checked integers is allowed. The plain integer + * is checked to be in range before being casted to the destination type. This means that the following + * lines all compile, and the resulting CheckedInts are correctly detected as valid or invalid: + * \code + CheckedInt<uint8_t> x(1); // 1 is of type int, is found to be in range for uint8_t, x is valid + CheckedInt<uint8_t> x(-1); // -1 is of type int, is found not to be in range for uint8_t, x is invalid + CheckedInt<int8_t> x(-1); // -1 is of type int, is found to be in range for int8_t, x is valid + CheckedInt<int8_t> x(int16_t(1000)); // 1000 is of type int16_t, is found not to be in range for int8_t, x is invalid + CheckedInt<int32_t> x(uint32_t(123456789)); // 3123456789 is of type uint32_t, is found not to be in range + // for int32_t, x is invalid + * \endcode + * Implicit conversion from + * checked integers to plain integers is not allowed. As shown in the + * above example, to get the value of a checked integer as a normal integer, call value(). + * + * Arithmetic operations between checked and plain integers is allowed; the result type + * is the type of the checked integer. + * + * Safe integers of different types cannot be used in the same arithmetic expression. + */ +template<typename T> +class CheckedInt +{ +protected: + T mValue; + T mIsValid; // stored as a T to limit the number of integer conversions when + // evaluating nested arithmetic expressions. + + template<typename U> + CheckedInt(const U& value, bool isValid) : mValue(value), mIsValid(isValid) + { + CheckedInt_internal::TYPE_NOT_SUPPORTED_BY_CheckedInt<T>::run(); + } + +public: + /** Constructs a checked integer with given \a value. The checked integer is initialized as valid or invalid + * depending on whether the \a value is in range. + * + * This constructor is not explicit. Instead, the type of its argument is a separate template parameter, + * ensuring that no conversion is performed before this constructor is actually called. + * As explained in the above documentation for class CheckedInt, this constructor checks that its argument is + * valid. + */ + template<typename U> + CheckedInt(const U& value) + : mValue(value), + mIsValid(CheckedInt_internal::is_in_range<T>(value)) + { + CheckedInt_internal::TYPE_NOT_SUPPORTED_BY_CheckedInt<T>::run(); + } + + /** Constructs a valid checked integer with uninitialized value */ + CheckedInt() : mIsValid(1) + { + CheckedInt_internal::TYPE_NOT_SUPPORTED_BY_CheckedInt<T>::run(); + } + + /** \returns the actual value */ + T value() const { return mValue; } + + /** \returns true if the checked integer is valid, i.e. is not the result + * of an invalid operation or of an operation involving an invalid checked integer + */ + bool valid() const { return mIsValid; } + + /** \returns the sum. Checks for overflow. */ + template<typename U> friend CheckedInt<U> operator +(const CheckedInt<U>& lhs, const CheckedInt<U>& rhs); + /** Adds. Checks for overflow. \returns self reference */ + template<typename U> CheckedInt& operator +=(const U &rhs); + /** \returns the difference. Checks for overflow. */ + template<typename U> friend CheckedInt<U> operator -(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs); + /** Substracts. Checks for overflow. \returns self reference */ + template<typename U> CheckedInt& operator -=(const U &rhs); + /** \returns the product. Checks for overflow. */ + template<typename U> friend CheckedInt<U> operator *(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs); + /** Multiplies. Checks for overflow. \returns self reference */ + template<typename U> CheckedInt& operator *=(const U &rhs); + /** \returns the quotient. Checks for overflow and for divide-by-zero. */ + template<typename U> friend CheckedInt<U> operator /(const CheckedInt<U>& lhs, const CheckedInt<U> &rhs); + /** Divides. Checks for overflow and for divide-by-zero. \returns self reference */ + template<typename U> CheckedInt& operator /=(const U &rhs); + + /** \returns the opposite value. Checks for overflow. */ + CheckedInt operator -() const + { + T result = -value(); + /* give the compiler a good chance to perform RVO */ + return CheckedInt(result, + mIsValid & CheckedInt_internal::is_sub_valid(T(0), value(), result)); + } + + /** \returns true if the left and right hand sides are valid and have the same value. */ + bool operator ==(const CheckedInt& other) const + { + return bool(mIsValid & other.mIsValid & T(value() == other.value())); + } + +private: + /** operator!= is disabled. Indeed: (a!=b) should be the same as !(a==b) but that + * would mean that if a or b is invalid, (a!=b) is always true, which is very tricky. + */ + template<typename U> + bool operator !=(const U& other) const { return !(*this == other); } +}; + +#define CHECKEDINT_BASIC_BINARY_OPERATOR(NAME, OP) \ +template<typename T> \ +inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, const CheckedInt<T> &rhs) \ +{ \ + T x = lhs.value(); \ + T y = rhs.value(); \ + T result = x OP y; \ + T is_op_valid \ + = CheckedInt_internal::is_##NAME##_valid(x, y, result); \ + /* give the compiler a good chance to perform RVO */ \ + return CheckedInt<T>(result, \ + lhs.mIsValid & \ + rhs.mIsValid & \ + is_op_valid); \ +} + +CHECKEDINT_BASIC_BINARY_OPERATOR(add, +) +CHECKEDINT_BASIC_BINARY_OPERATOR(sub, -) +CHECKEDINT_BASIC_BINARY_OPERATOR(mul, *) + +// division can't be implemented by CHECKEDINT_BASIC_BINARY_OPERATOR +// because if rhs == 0, we are not allowed to even try to compute the quotient. +template<typename T> +inline CheckedInt<T> operator /(const CheckedInt<T> &lhs, const CheckedInt<T> &rhs) +{ + T x = lhs.value(); + T y = rhs.value(); + T is_op_valid = CheckedInt_internal::is_div_valid(x, y); + T result = is_op_valid ? (x / y) : 0; + /* give the compiler a good chance to perform RVO */ + return CheckedInt<T>(result, + lhs.mIsValid & + rhs.mIsValid & + is_op_valid); +} + +// implement cast_to_CheckedInt<T>(x), making sure that +// - it allows x to be either a CheckedInt<T> or any integer type that can be casted to T +// - if x is already a CheckedInt<T>, we just return a reference to it, instead of copying it (optimization) + +template<typename T, typename U> +struct cast_to_CheckedInt_impl +{ + typedef CheckedInt<T> return_type; + static CheckedInt<T> run(const U& u) { return u; } +}; + +template<typename T> +struct cast_to_CheckedInt_impl<T, CheckedInt<T> > +{ + typedef const CheckedInt<T>& return_type; + static const CheckedInt<T>& run(const CheckedInt<T>& u) { return u; } +}; + +template<typename T, typename U> +inline typename cast_to_CheckedInt_impl<T, U>::return_type +cast_to_CheckedInt(const U& u) +{ + return cast_to_CheckedInt_impl<T, U>::run(u); +} + +#define CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(OP, COMPOUND_OP) \ +template<typename T> \ +template<typename U> \ +CheckedInt<T>& CheckedInt<T>::operator COMPOUND_OP(const U &rhs) \ +{ \ + *this = *this OP cast_to_CheckedInt<T>(rhs); \ + return *this; \ +} \ +template<typename T, typename U> \ +inline CheckedInt<T> operator OP(const CheckedInt<T> &lhs, const U &rhs) \ +{ \ + return lhs OP cast_to_CheckedInt<T>(rhs); \ +} \ +template<typename T, typename U> \ +inline CheckedInt<T> operator OP(const U & lhs, const CheckedInt<T> &rhs) \ +{ \ + return cast_to_CheckedInt<T>(lhs) OP rhs; \ +} + +CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(+, +=) +CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(*, *=) +CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(-, -=) +CHECKEDINT_CONVENIENCE_BINARY_OPERATORS(/, /=) + +template<typename T, typename U> +inline bool operator ==(const CheckedInt<T> &lhs, const U &rhs) +{ + return lhs == cast_to_CheckedInt<T>(rhs); +} + +template<typename T, typename U> +inline bool operator ==(const U & lhs, const CheckedInt<T> &rhs) +{ + return cast_to_CheckedInt<T>(lhs) == rhs; +} + +} // end namespace WebCore + +#endif /* CheckedInt_h */ diff --git a/Source/WebCore/html/canvas/DataView.cpp b/Source/WebCore/html/canvas/DataView.cpp new file mode 100755 index 000000000..3e55d3776 --- /dev/null +++ b/Source/WebCore/html/canvas/DataView.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DataView.h" + +#include "CheckedInt.h" +#include "ExceptionCode.h" + +namespace { + +template<typename T> +union Value { + T data; + char bytes[sizeof(T)]; +}; + +} + +namespace WebCore { + +PassRefPtr<DataView> DataView::create(unsigned length) +{ + RefPtr<ArrayBuffer> buffer = ArrayBuffer::create(length, sizeof(uint8_t)); + if (!buffer.get()) + return 0; + return create(buffer, 0, length); +} + +PassRefPtr<DataView> DataView::create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned byteLength) +{ + if (byteOffset > buffer->byteLength()) + return 0; + CheckedInt<uint32_t> checkedOffset(byteOffset); + CheckedInt<uint32_t> checkedLength(byteLength); + CheckedInt<uint32_t> checkedMax = checkedOffset + checkedLength; + if (!checkedMax.valid() || checkedMax.value() > buffer->byteLength()) + return 0; + return adoptRef(new DataView(buffer, byteOffset, byteLength)); +} + +DataView::DataView(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned byteLength) + : ArrayBufferView(buffer, byteOffset) + , m_byteLength(byteLength) +{ +} + +static bool needToFlipBytes(bool littleEndian) +{ +#if CPU(BIG_ENDIAN) + return littleEndian; +#else + return !littleEndian; +#endif +} + +inline void swapBytes(char* p, char* q) +{ + char temp = *p; + *p = *q; + *q = temp; +} + +static void flipBytesFor16Bits(char* p) +{ + swapBytes(p, p + 1); +} + +static void flipBytesFor32Bits(char* p) +{ + swapBytes(p, p + 3); + swapBytes(p + 1, p + 2); +} + +static void flipBytesFor64Bits(char* p) +{ + swapBytes(p, p + 7); + swapBytes(p + 1, p + 6); + swapBytes(p + 2, p + 5); + swapBytes(p + 3, p + 4); +} + +static void flipBytesIfNeeded(char* value, size_t size, bool littleEndian) +{ + if (!needToFlipBytes(littleEndian)) + return; + + switch (size) { + case 1: + // Nothing to do. + break; + case 2: + flipBytesFor16Bits(value); + break; + case 4: + flipBytesFor32Bits(value); + break; + case 8: + flipBytesFor64Bits(value); + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +template<typename T> +T DataView::getData(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) const +{ + if (beyondRange<T>(byteOffset)) { + ec = INDEX_SIZE_ERR; + return 0; + } + + // We do not want to load the data directly since it would cause a bus error on architectures that don't support unaligned loads. + Value<T> value; + memcpy(value.bytes, static_cast<const char*>(m_baseAddress) + byteOffset, sizeof(T)); + flipBytesIfNeeded(value.bytes, sizeof(T), littleEndian); + return value.data; +} + +template<typename T> +void DataView::setData(unsigned byteOffset, T value, bool littleEndian, ExceptionCode& ec) +{ + if (beyondRange<T>(byteOffset)) { + ec = INDEX_SIZE_ERR; + return; + } + + // We do not want to store the data directly since it would cause a bus error on architectures that don't support unaligned stores. + Value<T> tempValue; + tempValue.data = value; + flipBytesIfNeeded(tempValue.bytes, sizeof(T), littleEndian); + memcpy(static_cast<char*>(m_baseAddress) + byteOffset, tempValue.bytes, sizeof(T)); +} + +int8_t DataView::getInt8(unsigned byteOffset, ExceptionCode& ec) +{ + return getData<int8_t>(byteOffset, false, ec); +} + +uint8_t DataView::getUint8(unsigned byteOffset, ExceptionCode& ec) +{ + return getData<uint8_t>(byteOffset, false, ec); +} + +int16_t DataView::getInt16(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) +{ + return getData<int16_t>(byteOffset, littleEndian, ec); +} + +uint16_t DataView::getUint16(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) +{ + return getData<uint16_t>(byteOffset, littleEndian, ec); +} + +int32_t DataView::getInt32(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) +{ + return getData<int32_t>(byteOffset, littleEndian, ec); +} + +uint32_t DataView::getUint32(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) +{ + return getData<uint32_t>(byteOffset, littleEndian, ec); +} + +float DataView::getFloat32(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) +{ + return getData<float>(byteOffset, littleEndian, ec); +} + +double DataView::getFloat64(unsigned byteOffset, bool littleEndian, ExceptionCode& ec) +{ + return getData<double>(byteOffset, littleEndian, ec); +} + +void DataView::setInt8(unsigned byteOffset, int8_t value, ExceptionCode& ec) +{ + setData<int8_t>(byteOffset, value, false, ec); +} + +void DataView::setUint8(unsigned byteOffset, uint8_t value, ExceptionCode& ec) +{ + setData<uint8_t>(byteOffset, value, false, ec); +} + +void DataView::setInt16(unsigned byteOffset, short value, bool littleEndian, ExceptionCode& ec) +{ + setData<int16_t>(byteOffset, value, littleEndian, ec); +} + +void DataView::setUint16(unsigned byteOffset, uint16_t value, bool littleEndian, ExceptionCode& ec) +{ + setData<uint16_t>(byteOffset, value, littleEndian, ec); +} + +void DataView::setInt32(unsigned byteOffset, int32_t value, bool littleEndian, ExceptionCode& ec) +{ + setData<int32_t>(byteOffset, value, littleEndian, ec); +} + +void DataView::setUint32(unsigned byteOffset, uint32_t value, bool littleEndian, ExceptionCode& ec) +{ + setData<uint32_t>(byteOffset, value, littleEndian, ec); +} + +void DataView::setFloat32(unsigned byteOffset, float value, bool littleEndian, ExceptionCode& ec) +{ + setData<float>(byteOffset, value, littleEndian, ec); +} + +void DataView::setFloat64(unsigned byteOffset, double value, bool littleEndian, ExceptionCode& ec) +{ + setData<double>(byteOffset, value, littleEndian, ec); +} + +void DataView::neuter() +{ + ArrayBufferView::neuter(); + m_byteLength = 0; +} + +} diff --git a/Source/WebCore/html/canvas/DataView.h b/Source/WebCore/html/canvas/DataView.h new file mode 100644 index 000000000..63b156d3d --- /dev/null +++ b/Source/WebCore/html/canvas/DataView.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 DataView_h +#define DataView_h + +#include <wtf/ArrayBufferView.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +typedef int ExceptionCode; + +class DataView : public ArrayBufferView { +public: + static PassRefPtr<DataView> create(unsigned length); + static PassRefPtr<DataView> create(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned byteLength); + + virtual bool isDataView() const { return true; } + virtual unsigned length() const { return m_byteLength; } + virtual unsigned byteLength() const { return m_byteLength; } + virtual PassRefPtr<ArrayBufferView> slice(int, int) const { return 0; } + + int8_t getInt8(unsigned byteOffset, ExceptionCode&); + uint8_t getUint8(unsigned byteOffset, ExceptionCode&); + int16_t getInt16(unsigned byteOffset, ExceptionCode& ec) { return getInt16(byteOffset, false, ec); } + int16_t getInt16(unsigned byteOffset, bool littleEndian, ExceptionCode&); + uint16_t getUint16(unsigned byteOffset, ExceptionCode& ec) { return getUint16(byteOffset, false, ec); } + uint16_t getUint16(unsigned byteOffset, bool littleEndian, ExceptionCode&); + int32_t getInt32(unsigned byteOffset, ExceptionCode& ec) { return getInt32(byteOffset, false, ec); } + int32_t getInt32(unsigned byteOffset, bool littleEndian, ExceptionCode&); + uint32_t getUint32(unsigned byteOffset, ExceptionCode& ec) { return getUint32(byteOffset, false, ec); } + uint32_t getUint32(unsigned byteOffset, bool littleEndian, ExceptionCode&); + float getFloat32(unsigned byteOffset, ExceptionCode& ec) { return getFloat32(byteOffset, false, ec); } + float getFloat32(unsigned byteOffset, bool littleEndian, ExceptionCode&); + double getFloat64(unsigned byteOffset, ExceptionCode& ec) { return getFloat64(byteOffset, false, ec); } + double getFloat64(unsigned byteOffset, bool littleEndian, ExceptionCode&); + + void setInt8(unsigned byteOffset, int8_t value, ExceptionCode&); + void setUint8(unsigned byteOffset, uint8_t value, ExceptionCode&); + void setInt16(unsigned byteOffset, int16_t value, ExceptionCode& ec) { setInt16(byteOffset, value, false, ec); } + void setInt16(unsigned byteOffset, int16_t value, bool littleEndian, ExceptionCode&); + void setUint16(unsigned byteOffset, uint16_t value, ExceptionCode& ec) { setUint16(byteOffset, value, false, ec); } + void setUint16(unsigned byteOffset, uint16_t value, bool littleEndian, ExceptionCode&); + void setInt32(unsigned byteOffset, int32_t value, ExceptionCode& ec) { setInt32(byteOffset, value, false, ec); } + void setInt32(unsigned byteOffset, int32_t value, bool littleEndian, ExceptionCode&); + void setUint32(unsigned byteOffset, uint32_t value, ExceptionCode& ec) { setUint32(byteOffset, value, false, ec); } + void setUint32(unsigned byteOffset, uint32_t value, bool littleEndian, ExceptionCode&); + void setFloat32(unsigned byteOffset, float value, ExceptionCode& ec) { setFloat32(byteOffset, value, false, ec); } + void setFloat32(unsigned byteOffset, float value, bool littleEndian, ExceptionCode&); + void setFloat64(unsigned byteOffset, double value, ExceptionCode& ec) { setFloat64(byteOffset, value, false, ec); } + void setFloat64(unsigned byteOffset, double value, bool littleEndian, ExceptionCode&); + +protected: + virtual void neuter(); + +private: + DataView(PassRefPtr<ArrayBuffer>, unsigned byteOffset, unsigned byteLength); + + template<typename T> + inline bool beyondRange(unsigned byteOffset) const { return byteOffset >= m_byteLength || byteOffset + sizeof(T) > m_byteLength; } + + template<typename T> + T getData(unsigned byteOffset, bool littleEndian, ExceptionCode&) const; + + template<typename T> + void setData(unsigned byteOffset, T value, bool littleEndian, ExceptionCode&); + + unsigned m_byteLength; +}; + + +} // namespace WebCore + +#endif // DataView_h diff --git a/Source/WebCore/html/canvas/DataView.idl b/Source/WebCore/html/canvas/DataView.idl new file mode 100755 index 000000000..bba13c93a --- /dev/null +++ b/Source/WebCore/html/canvas/DataView.idl @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 [ + CustomConstructor, + CustomToJS, + NoStaticTables + ] DataView : ArrayBufferView { + // All these methods raise an exception if they would read or write beyond the end of the view. + + // We have to use custom code because our code generator does not support int8_t type. + // int8_t getInt8(in unsigned long byteOffset); + // uint8_t getUint8(in unsigned long byteOffset); + [Custom] DOMObject getInt8() + raises (DOMException); + [Custom] DOMObject getUint8() + raises (DOMException); + + [StrictTypeChecking] short getInt16(in unsigned long byteOffset, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] unsigned short getUint16(in unsigned long byteOffset, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] long getInt32(in unsigned long byteOffset, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] unsigned long getUint32(in unsigned long byteOffset, in [Optional] boolean littleEndian) + raises (DOMException); + + // Use custom code to handle NaN case for JSC. + [JSCCustom, StrictTypeChecking] float getFloat32(in unsigned long byteOffset, in [Optional] boolean littleEndian) + raises (DOMException); + [JSCCustom, StrictTypeChecking] double getFloat64(in unsigned long byteOffset, in [Optional] boolean littleEndian) + raises (DOMException); + + // We have to use custom code because our code generator does not support uint8_t type. + // void setInt8(in unsigned long byteOffset, in int8_t value); + // void setUint8(in unsigned long byteOffset, in uint8_t value); + [Custom] void setInt8() + raises (DOMException); + [Custom] void setUint8() + raises (DOMException); + + [StrictTypeChecking] void setInt16(in unsigned long byteOffset, in short value, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] void setUint16(in unsigned long byteOffset, in unsigned short value, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] void setInt32(in unsigned long byteOffset, in long value, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] void setUint32(in unsigned long byteOffset, in unsigned long value, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] void setFloat32(in unsigned long byteOffset, in float value, in [Optional] boolean littleEndian) + raises (DOMException); + [StrictTypeChecking] void setFloat64(in unsigned long byteOffset, in double value, in [Optional] boolean littleEndian) + raises (DOMException); + }; + +} diff --git a/Source/WebCore/html/canvas/Float32Array.idl b/Source/WebCore/html/canvas/Float32Array.idl new file mode 100644 index 000000000..ef6c956b7 --- /dev/null +++ b/Source/WebCore/html/canvas/Float32Array.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006, 2010 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Float32Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 4; + + readonly attribute unsigned long length; + Float32Array subarray(in [Optional=CallWithDefaultValue] long start, + in [Optional] long end); + + // void set(in Float32Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/Float64Array.idl b/Source/WebCore/html/canvas/Float64Array.idl new file mode 100644 index 000000000..01495350b --- /dev/null +++ b/Source/WebCore/html/canvas/Float64Array.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Float64Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 8; + + readonly attribute unsigned long length; + Float64Array subarray(in [Optional=CallWithDefaultValue] long start, + in [Optional] long end); + + // void set(in Float64Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/Int16Array.idl b/Source/WebCore/html/canvas/Int16Array.idl new file mode 100644 index 000000000..b1a547180 --- /dev/null +++ b/Source/WebCore/html/canvas/Int16Array.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2006, 2010 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Int16Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 2; + + readonly attribute unsigned long length; + Int16Array subarray(in [Optional=CallWithDefaultValue] long start, + in [Optional] long end); + + // void set(in Int16Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/Int32Array.idl b/Source/WebCore/html/canvas/Int32Array.idl new file mode 100644 index 000000000..bad745d6e --- /dev/null +++ b/Source/WebCore/html/canvas/Int32Array.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Int32Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 4; + + readonly attribute unsigned long length; + Int32Array subarray(in [Optional=CallWithDefaultValue] long start, + in [Optional] long end); + + // void set(in Int32Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/Int8Array.idl b/Source/WebCore/html/canvas/Int8Array.idl new file mode 100644 index 000000000..4ae2eace6 --- /dev/null +++ b/Source/WebCore/html/canvas/Int8Array.idl @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Int8Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 1; + + readonly attribute unsigned long length; + Int8Array subarray(in [Optional=CallWithDefaultValue] long start, + in [Optional] long end); + + // void set(in Int8Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/OESStandardDerivatives.cpp b/Source/WebCore/html/canvas/OESStandardDerivatives.cpp new file mode 100644 index 000000000..a91c193d5 --- /dev/null +++ b/Source/WebCore/html/canvas/OESStandardDerivatives.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 "OESStandardDerivatives.h" + +namespace WebCore { + +OESStandardDerivatives::OESStandardDerivatives(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +OESStandardDerivatives::~OESStandardDerivatives() +{ +} + +WebGLExtension::ExtensionName OESStandardDerivatives::getName() const +{ + return OESStandardDerivativesName; +} + +PassOwnPtr<OESStandardDerivatives> OESStandardDerivatives::create(WebGLRenderingContext* context) +{ + return adoptPtr(new OESStandardDerivatives(context)); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/OESStandardDerivatives.h b/Source/WebCore/html/canvas/OESStandardDerivatives.h new file mode 100644 index 000000000..af2ea14b9 --- /dev/null +++ b/Source/WebCore/html/canvas/OESStandardDerivatives.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 OESStandardDerivatives_h +#define OESStandardDerivatives_h + +#include "WebGLExtension.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class OESStandardDerivatives : public WebGLExtension { +public: + static PassOwnPtr<OESStandardDerivatives> create(WebGLRenderingContext*); + + virtual ~OESStandardDerivatives(); + virtual ExtensionName getName() const; + +private: + OESStandardDerivatives(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // OESStandardDerivatives_h diff --git a/Source/WebCore/html/canvas/OESStandardDerivatives.idl b/Source/WebCore/html/canvas/OESStandardDerivatives.idl new file mode 100644 index 000000000..f1d4740b2 --- /dev/null +++ b/Source/WebCore/html/canvas/OESStandardDerivatives.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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, + GenerateIsReachable=ImplContext, + OmitConstructor, + DontCheckEnums + ] OESStandardDerivatives { + const unsigned int FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B; + }; +} diff --git a/Source/WebCore/html/canvas/OESTextureFloat.cpp b/Source/WebCore/html/canvas/OESTextureFloat.cpp new file mode 100644 index 000000000..3ba150b07 --- /dev/null +++ b/Source/WebCore/html/canvas/OESTextureFloat.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 "OESTextureFloat.h" + +namespace WebCore { + +OESTextureFloat::OESTextureFloat(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +OESTextureFloat::~OESTextureFloat() +{ +} + +WebGLExtension::ExtensionName OESTextureFloat::getName() const +{ + return OESTextureFloatName; +} + +PassOwnPtr<OESTextureFloat> OESTextureFloat::create(WebGLRenderingContext* context) +{ + return adoptPtr(new OESTextureFloat(context)); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/OESTextureFloat.h b/Source/WebCore/html/canvas/OESTextureFloat.h new file mode 100644 index 000000000..21da31c08 --- /dev/null +++ b/Source/WebCore/html/canvas/OESTextureFloat.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 OESTextureFloat_h +#define OESTextureFloat_h + +#include "WebGLExtension.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class OESTextureFloat : public WebGLExtension { +public: + static PassOwnPtr<OESTextureFloat> create(WebGLRenderingContext*); + + virtual ~OESTextureFloat(); + virtual ExtensionName getName() const; + +private: + OESTextureFloat(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // OESTextureFloat_h diff --git a/Source/WebCore/html/canvas/OESTextureFloat.idl b/Source/WebCore/html/canvas/OESTextureFloat.idl new file mode 100644 index 000000000..950c355c3 --- /dev/null +++ b/Source/WebCore/html/canvas/OESTextureFloat.idl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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, + GenerateIsReachable=ImplContext, + OmitConstructor + ] OESTextureFloat { + }; +} diff --git a/Source/WebCore/html/canvas/OESVertexArrayObject.cpp b/Source/WebCore/html/canvas/OESVertexArrayObject.cpp new file mode 100644 index 000000000..a26d7069f --- /dev/null +++ b/Source/WebCore/html/canvas/OESVertexArrayObject.cpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "OESVertexArrayObject.h" + +#include "Extensions3D.h" +#include "WebGLRenderingContext.h" +#include "WebGLVertexArrayObjectOES.h" + +namespace WebCore { + +OESVertexArrayObject::OESVertexArrayObject(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +OESVertexArrayObject::~OESVertexArrayObject() +{ +} + +WebGLExtension::ExtensionName OESVertexArrayObject::getName() const +{ + return OESVertexArrayObjectName; +} + +PassOwnPtr<OESVertexArrayObject> OESVertexArrayObject::create(WebGLRenderingContext* context) +{ + return adoptPtr(new OESVertexArrayObject(context)); +} + +PassRefPtr<WebGLVertexArrayObjectOES> OESVertexArrayObject::createVertexArrayOES() +{ + if (m_context->isContextLost()) + return 0; + + RefPtr<WebGLVertexArrayObjectOES> o = WebGLVertexArrayObjectOES::create(m_context, WebGLVertexArrayObjectOES::VaoTypeUser); + m_context->addObject(o.get()); + return o.release(); +} + +void OESVertexArrayObject::deleteVertexArrayOES(WebGLVertexArrayObjectOES* arrayObject) +{ + if (!arrayObject || m_context->isContextLost()) + return; + + arrayObject->deleteObject(); +} + +GC3Dboolean OESVertexArrayObject::isVertexArrayOES(WebGLVertexArrayObjectOES* arrayObject) +{ + if (!arrayObject || m_context->isContextLost()) + return 0; + + if (!arrayObject->hasEverBeenBound()) + return 0; + + Extensions3D* extensions = m_context->graphicsContext3D()->getExtensions(); + return extensions->isVertexArrayOES(arrayObject->object()); +} + +void OESVertexArrayObject::bindVertexArrayOES(WebGLVertexArrayObjectOES* arrayObject, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (m_context->isContextLost()) + return; + + if (arrayObject && arrayObject->context() != m_context) { + m_context->graphicsContext3D()->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + Extensions3D* extensions = m_context->graphicsContext3D()->getExtensions(); + if (arrayObject && !arrayObject->isDefaultObject() && arrayObject->object()) { + extensions->bindVertexArrayOES(arrayObject->object()); + + arrayObject->setHasEverBeenBound(); + m_context->setBoundVertexArrayObject(arrayObject); + } else { + extensions->bindVertexArrayOES(0); + + m_context->setBoundVertexArrayObject(0); + } + + m_context->cleanupAfterGraphicsCall(false); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/OESVertexArrayObject.h b/Source/WebCore/html/canvas/OESVertexArrayObject.h new file mode 100644 index 000000000..148df3721 --- /dev/null +++ b/Source/WebCore/html/canvas/OESVertexArrayObject.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef OESVertexArrayObject_h +#define OESVertexArrayObject_h + +#include "GraphicsTypes3D.h" +#include "WebGLExtension.h" +#include "WebGLVertexArrayObjectOES.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/UnusedParam.h> + +namespace WebCore { + +class WebGLRenderingContext; +class WebGLVertexArrayObjectOES; + +typedef int ExceptionCode; + +class OESVertexArrayObject : public WebGLExtension { +public: + static PassOwnPtr<OESVertexArrayObject> create(WebGLRenderingContext*); + + virtual ~OESVertexArrayObject(); + virtual ExtensionName getName() const; + + PassRefPtr<WebGLVertexArrayObjectOES> createVertexArrayOES(); + void deleteVertexArrayOES(WebGLVertexArrayObjectOES*); + GC3Dboolean isVertexArrayOES(WebGLVertexArrayObjectOES*); + void bindVertexArrayOES(WebGLVertexArrayObjectOES*, ExceptionCode&); + +private: + OESVertexArrayObject(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // OESVertexArrayObject_h diff --git a/Source/WebCore/html/canvas/OESVertexArrayObject.idl b/Source/WebCore/html/canvas/OESVertexArrayObject.idl new file mode 100644 index 000000000..cfb6506bb --- /dev/null +++ b/Source/WebCore/html/canvas/OESVertexArrayObject.idl @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL, + GenerateIsReachable=ImplContext, + OmitConstructor, + DontCheckEnums + ] OESVertexArrayObject { + const unsigned int VERTEX_ARRAY_BINDING_OES = 0x85B5; + + [StrictTypeChecking] WebGLVertexArrayObjectOES createVertexArrayOES(); + [StrictTypeChecking] void deleteVertexArrayOES(in [Optional=CallWithDefaultValue] WebGLVertexArrayObjectOES arrayObject); + [StrictTypeChecking] boolean isVertexArrayOES(in [Optional=CallWithDefaultValue] WebGLVertexArrayObjectOES arrayObject); + [StrictTypeChecking] void bindVertexArrayOES(in [Optional=CallWithDefaultValue] WebGLVertexArrayObjectOES arrayObject) raises(DOMException); + }; +} diff --git a/Source/WebCore/html/canvas/Uint16Array.idl b/Source/WebCore/html/canvas/Uint16Array.idl new file mode 100644 index 000000000..bb08a4f01 --- /dev/null +++ b/Source/WebCore/html/canvas/Uint16Array.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Uint16Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 2; + + readonly attribute unsigned long length; + Uint16Array subarray(in [Optional=CallWithDefaultValue] long start, in [Optional] long end); + + // void set(in Uint16Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/Uint32Array.idl b/Source/WebCore/html/canvas/Uint32Array.idl new file mode 100644 index 000000000..2a87885a7 --- /dev/null +++ b/Source/WebCore/html/canvas/Uint32Array.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Uint32Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 4; + + readonly attribute unsigned long length; + Uint32Array subarray(in [Optional=CallWithDefaultValue] long start, in [Optional] long end); + + // void set(in Uint32Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/Uint8Array.idl b/Source/WebCore/html/canvas/Uint8Array.idl new file mode 100644 index 000000000..1a6f5889f --- /dev/null +++ b/Source/WebCore/html/canvas/Uint8Array.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + CustomConstructor, + HasNumericIndexGetter, + HasCustomIndexSetter, + GenerateNativeConverter, + NoStaticTables, + CustomToJS, + DontCheckEnums + ] Uint8Array : ArrayBufferView { + const unsigned int BYTES_PER_ELEMENT = 1; + + readonly attribute unsigned long length; + Uint8Array subarray(in [Optional=CallWithDefaultValue] long start, in [Optional] long end); + + // void set(in Uint8Array array, [Optional] in unsigned long offset); + // void set(in sequence<long> array, [Optional] in unsigned long offset); + [Custom] void set(); + }; +} diff --git a/Source/WebCore/html/canvas/WebGLActiveInfo.h b/Source/WebCore/html/canvas/WebGLActiveInfo.h new file mode 100644 index 000000000..e75e1f545 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLActiveInfo.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 WebGLActiveInfo_h +#define WebGLActiveInfo_h + +#include "GraphicsContext3D.h" +#include "PlatformString.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLActiveInfo : public RefCounted<WebGLActiveInfo> { +public: + static PassRefPtr<WebGLActiveInfo> create(const String& name, GC3Denum type, GC3Dint size) + { + return adoptRef(new WebGLActiveInfo(name, type, size)); + } + String name() const { return m_name; } + GC3Denum type() const { return m_type; } + GC3Dint size() const { return m_size; } + +private: + WebGLActiveInfo(const String& name, GC3Denum type, GC3Dint size) + : m_name(name) + , m_type(type) + , m_size(size) + { + ASSERT(name.length()); + ASSERT(type); + ASSERT(size); + } + String m_name; + GC3Denum m_type; + GC3Dint m_size; +}; + +} + +#endif // WebGLActiveInfo_h diff --git a/Source/WebCore/html/canvas/WebGLActiveInfo.idl b/Source/WebCore/html/canvas/WebGLActiveInfo.idl new file mode 100644 index 000000000..20ab8af90 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLActiveInfo.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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, + ] WebGLActiveInfo { + readonly attribute int size; + readonly attribute unsigned int type; + readonly attribute DOMString name; + }; + +} diff --git a/Source/WebCore/html/canvas/WebGLBuffer.cpp b/Source/WebCore/html/canvas/WebGLBuffer.cpp new file mode 100644 index 000000000..997dba500 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLBuffer.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLBuffer.h" + +#include "CheckedInt.h" +#include "WebGLRenderingContext.h" +#include <wtf/ArrayBufferView.h> + +namespace WebCore { + +PassRefPtr<WebGLBuffer> WebGLBuffer::create(WebGLRenderingContext* ctx) +{ + return adoptRef(new WebGLBuffer(ctx)); +} + +WebGLBuffer::WebGLBuffer(WebGLRenderingContext* ctx) + : WebGLObject(ctx) + , m_target(0) + , m_byteLength(0) + , m_nextAvailableCacheEntry(0) +{ + setObject(context()->graphicsContext3D()->createBuffer()); + clearCachedMaxIndices(); +} + +void WebGLBuffer::deleteObjectImpl(Platform3DObject object) +{ + context()->graphicsContext3D()->deleteBuffer(object); +} + +bool WebGLBuffer::associateBufferDataImpl(ArrayBuffer* array, GC3Dintptr byteOffset, GC3Dsizeiptr byteLength) +{ + if (byteLength < 0 || byteOffset < 0) + return false; + + if (array && byteLength) { + CheckedInt<GC3Dintptr> checkedOffset(byteOffset); + CheckedInt<GC3Dsizeiptr> checkedLength(byteLength); + CheckedInt<GC3Dintptr> checkedMax = checkedOffset + checkedLength; + if (!checkedMax.valid() || checkedMax.value() > static_cast<int32_t>(array->byteLength())) + return false; + } + + switch (m_target) { + case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: + m_byteLength = byteLength; + clearCachedMaxIndices(); + if (byteLength) { + m_elementArrayBuffer = ArrayBuffer::create(byteLength, 1); + if (!m_elementArrayBuffer) { + m_byteLength = 0; + return false; + } + if (array) { + // We must always clone the incoming data because client-side + // modifications without calling bufferData or bufferSubData + // must never be able to change the validation results. + memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()), + static_cast<unsigned char*>(array->data()) + byteOffset, + byteLength); + } + } else + m_elementArrayBuffer = 0; + return true; + case GraphicsContext3D::ARRAY_BUFFER: + m_byteLength = byteLength; + return true; + default: + return false; + } +} + +bool WebGLBuffer::associateBufferData(GC3Dsizeiptr size) +{ + if (size < 0) + return false; + return associateBufferDataImpl(0, 0, size); +} + +bool WebGLBuffer::associateBufferData(ArrayBuffer* array) +{ + if (!array) + return false; + return associateBufferDataImpl(array, 0, array->byteLength()); +} + +bool WebGLBuffer::associateBufferData(ArrayBufferView* array) +{ + if (!array) + return false; + return associateBufferDataImpl(array->buffer().get(), array->byteOffset(), array->byteLength()); +} + +bool WebGLBuffer::associateBufferSubDataImpl(GC3Dintptr offset, ArrayBuffer* array, GC3Dintptr arrayByteOffset, GC3Dsizeiptr byteLength) +{ + if (!array || offset < 0 || arrayByteOffset < 0 || byteLength < 0) + return false; + + if (byteLength) { + CheckedInt<GC3Dintptr> checkedBufferOffset(offset); + CheckedInt<GC3Dintptr> checkedArrayOffset(arrayByteOffset); + CheckedInt<GC3Dsizeiptr> checkedLength(byteLength); + CheckedInt<GC3Dintptr> checkedArrayMax = checkedArrayOffset + checkedLength; + CheckedInt<GC3Dintptr> checkedBufferMax = checkedBufferOffset + checkedLength; + if (!checkedArrayMax.valid() || checkedArrayMax.value() > static_cast<int32_t>(array->byteLength()) || !checkedBufferMax.valid() || checkedBufferMax.value() > m_byteLength) + return false; + } + + switch (m_target) { + case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: + clearCachedMaxIndices(); + if (byteLength) { + if (!m_elementArrayBuffer) + return false; + memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()) + offset, + static_cast<unsigned char*>(array->data()) + arrayByteOffset, + byteLength); + } + return true; + case GraphicsContext3D::ARRAY_BUFFER: + return true; + default: + return false; + } +} + +bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBuffer* array) +{ + if (!array) + return false; + return associateBufferSubDataImpl(offset, array, 0, array->byteLength()); +} + +bool WebGLBuffer::associateBufferSubData(GC3Dintptr offset, ArrayBufferView* array) +{ + if (!array) + return false; + return associateBufferSubDataImpl(offset, array->buffer().get(), array->byteOffset(), array->byteLength()); +} + +GC3Dsizeiptr WebGLBuffer::byteLength() const +{ + return m_byteLength; +} + +int WebGLBuffer::getCachedMaxIndex(GC3Denum type) +{ + for (size_t i = 0; i < WTF_ARRAY_LENGTH(m_maxIndexCache); ++i) + if (m_maxIndexCache[i].type == type) + return m_maxIndexCache[i].maxIndex; + return -1; +} + +void WebGLBuffer::setCachedMaxIndex(GC3Denum type, int value) +{ + size_t numEntries = WTF_ARRAY_LENGTH(m_maxIndexCache); + for (size_t i = 0; i < numEntries; ++i) + if (m_maxIndexCache[i].type == type) { + m_maxIndexCache[i].maxIndex = value; + return; + } + m_maxIndexCache[m_nextAvailableCacheEntry].type = type; + m_maxIndexCache[m_nextAvailableCacheEntry].maxIndex = value; + m_nextAvailableCacheEntry = (m_nextAvailableCacheEntry + 1) % numEntries; +} + +void WebGLBuffer::setTarget(GC3Denum target) +{ + // In WebGL, a buffer is bound to one target in its lifetime + if (m_target) + return; + if (target == GraphicsContext3D::ARRAY_BUFFER || target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) + m_target = target; +} + +void WebGLBuffer::clearCachedMaxIndices() +{ + memset(m_maxIndexCache, 0, sizeof(m_maxIndexCache)); +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLBuffer.h b/Source/WebCore/html/canvas/WebGLBuffer.h new file mode 100644 index 000000000..0157ece65 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLBuffer.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLBuffer_h +#define WebGLBuffer_h + +#include "WebGLObject.h" + +#include <wtf/ArrayBuffer.h> +#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLBuffer : public WebGLObject { +public: + virtual ~WebGLBuffer() { deleteObject(); } + + static PassRefPtr<WebGLBuffer> create(WebGLRenderingContext*); + + bool associateBufferData(GC3Dsizeiptr size); + bool associateBufferData(ArrayBuffer*); + bool associateBufferData(ArrayBufferView*); + bool associateBufferSubData(GC3Dintptr offset, ArrayBuffer*); + bool associateBufferSubData(GC3Dintptr offset, ArrayBufferView*); + + GC3Dsizeiptr byteLength() const; + const ArrayBuffer* elementArrayBuffer() const { return m_elementArrayBuffer.get(); } + + // Gets the cached max index for the given type. Returns -1 if + // none has been set. + int getCachedMaxIndex(GC3Denum type); + // Sets the cached max index for the given type. + void setCachedMaxIndex(GC3Denum type, int value); + + GC3Denum getTarget() const { return m_target; } + void setTarget(GC3Denum); + + bool hasEverBeenBound() const { return object() && m_target; } + +protected: + WebGLBuffer(WebGLRenderingContext*); + + virtual void deleteObjectImpl(Platform3DObject o); + +private: + virtual bool isBuffer() const { return true; } + + GC3Denum m_target; + + RefPtr<ArrayBuffer> m_elementArrayBuffer; + GC3Dsizeiptr m_byteLength; + + // Optimization for index validation. For each type of index + // (i.e., UNSIGNED_SHORT), cache the maximum index in the + // entire buffer. + // + // This is sufficient to eliminate a lot of work upon each + // draw call as long as all bound array buffers are at least + // that size. + struct MaxIndexCacheEntry { + GC3Denum type; + int maxIndex; + }; + // OpenGL ES 2.0 only has two valid index types (UNSIGNED_BYTE + // and UNSIGNED_SHORT), but might as well leave open the + // possibility of adding others. + MaxIndexCacheEntry m_maxIndexCache[4]; + unsigned int m_nextAvailableCacheEntry; + + // Clears all of the cached max indices. + void clearCachedMaxIndices(); + + // Helper function called by the three associateBufferData(). + bool associateBufferDataImpl(ArrayBuffer* array, GC3Dintptr byteOffset, GC3Dsizeiptr byteLength); + // Helper function called by the two associateBufferSubData(). + bool associateBufferSubDataImpl(GC3Dintptr offset, ArrayBuffer* array, GC3Dintptr arrayByteOffset, GC3Dsizeiptr byteLength); +}; + +} // namespace WebCore + +#endif // WebGLBuffer_h diff --git a/Source/WebCore/html/canvas/WebGLBuffer.idl b/Source/WebCore/html/canvas/WebGLBuffer.idl new file mode 100644 index 000000000..312b00911 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLBuffer.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL, + ] WebGLBuffer { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLCompressedTextures.cpp b/Source/WebCore/html/canvas/WebGLCompressedTextures.cpp new file mode 100644 index 000000000..1eab78723 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLCompressedTextures.cpp @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 "WebGLCompressedTextures.h" + +#include "Extensions3D.h" +#include "WebGLRenderingContext.h" + +#include <wtf/Int32Array.h> +#include <wtf/OwnArrayPtr.h> + +namespace WebCore { + +WebGLCompressedTextures::WebGLCompressedTextures(WebGLRenderingContext* context) + : WebGLExtension(context) + , m_supportsDxt1(false) + , m_supportsDxt5(false) + , m_supportsEtc1(false) + , m_supportsPvrtc(false) +{ + Extensions3D* extensions = context->graphicsContext3D()->getExtensions(); + if (extensions->supports("GL_EXT_texture_compression_dxt1")) { + extensions->ensureEnabled("GL_EXT_texture_compression_dxt1"); + m_supportsDxt1 = true; + } + if (extensions->supports("GL_EXT_texture_compression_s3tc")) { + extensions->ensureEnabled("GL_EXT_texture_compression_s3tc"); + m_supportsDxt1 = true; + m_supportsDxt5 = true; + } + if (extensions->supports("GL_CHROMIUM_texture_compression_dxt5")) { + extensions->ensureEnabled("GL_CHROMIUM_texture_compression_dxt5"); + m_supportsDxt5 = true; + } + if (extensions->supports("GL_OES_compressed_ETC1_RGB8_texture")) { + extensions->ensureEnabled("GL_OES_compressed_ETC1_RGB8_texture"); + m_supportsEtc1 = true; + } + if (extensions->supports("GL_IMG_texture_compression_pvrtc")) { + extensions->ensureEnabled("GL_IMG_texture_compression_pvrtc"); + m_supportsPvrtc = true; + } + + if (m_supportsDxt1) { + m_formats.append(Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT); + m_formats.append(Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT); + } + + if (m_supportsDxt5) + m_formats.append(Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT); + + if (m_supportsEtc1) + m_formats.append(Extensions3D::ETC1_RGB8_OES); + + if (m_supportsPvrtc) { + m_formats.append(Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG); + m_formats.append(Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG); + } +} + +WebGLCompressedTextures::~WebGLCompressedTextures() +{ +} + +WebGLExtension::ExtensionName WebGLCompressedTextures::getName() const +{ + return WebKitWebGLCompressedTexturesName; +} + +PassOwnPtr<WebGLCompressedTextures> WebGLCompressedTextures::create(WebGLRenderingContext* context) +{ + return adoptPtr(new WebGLCompressedTextures(context)); +} + +bool WebGLCompressedTextures::supported(WebGLRenderingContext* context) +{ + Extensions3D* extensions = context->graphicsContext3D()->getExtensions(); + return extensions->supports("GL_EXT_texture_compression_dxt1") + || extensions->supports("GL_EXT_texture_compression_s3tc") + || extensions->supports("GL_CHROMIUM_texture_compression_dxt5") + || extensions->supports("GL_OES_compressed_ETC1_RGB8_texture") + || extensions->supports("GL_IMG_texture_compression_pvrtc"); +} + +bool WebGLCompressedTextures::validateCompressedTexFormat(GC3Denum format) +{ + switch (format) { + case Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT: + case Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT: + return m_supportsDxt1; + case Extensions3D::ETC1_RGB8_OES: + return m_supportsEtc1; + case Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT: + return m_supportsDxt5; + case Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + case Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + return m_supportsPvrtc; + } + return false; +} + +bool WebGLCompressedTextures::validateCompressedTexFuncData(GC3Dsizei width, GC3Dsizei height, + GC3Denum format, ArrayBufferView* pixels) +{ + GraphicsContext3D* context = m_context->graphicsContext3D(); + if (!pixels) { + context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + if (width < 0 || height < 0) { + context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + + unsigned int bytesRequired = 0; + + switch (format) { + case Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT: + case Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT: + case Extensions3D::ETC1_RGB8_OES: + { + const int kBlockWidth = 4; + const int kBlockHeight = 4; + const int kBlockSize = 8; + int numBlocksAcross = (width + kBlockWidth - 1) / kBlockWidth; + int numBlocksDown = (height + kBlockHeight - 1) / kBlockHeight; + int numBlocks = numBlocksAcross * numBlocksDown; + bytesRequired = numBlocks * kBlockSize; + } + break; + case Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT: + { + const int kBlockWidth = 4; + const int kBlockHeight = 4; + const int kBlockSize = 16; + int numBlocksAcross = (width + kBlockWidth - 1) / kBlockWidth; + int numBlocksDown = (height + kBlockHeight - 1) / kBlockHeight; + int numBlocks = numBlocksAcross * numBlocksDown; + bytesRequired = numBlocks * kBlockSize; + } + break; + case Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + case Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + { + bytesRequired = (std::max(width, 8) * std::max(height, 8) + 7) / 8; + } + break; + default: + context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } + + if (pixels->byteLength() != bytesRequired) { + context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + + return true; +} + +bool WebGLCompressedTextures::validateCompressedTexSubDimensions(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, GC3Denum format, WebGLTexture* tex) +{ + switch (format) { + case Extensions3D::COMPRESSED_RGB_S3TC_DXT1_EXT: + case Extensions3D::COMPRESSED_RGBA_S3TC_DXT1_EXT: + case Extensions3D::COMPRESSED_RGBA_S3TC_DXT5_EXT: + case Extensions3D::ETC1_RGB8_OES: + { + const int kBlockWidth = 4; + const int kBlockHeight = 4; + if ((xoffset % kBlockWidth) || (yoffset % kBlockHeight)) + return false; + if (!xoffset) { + if (width != tex->getWidth(target, level)) + return false; + } else { + if (width % kBlockWidth) + return false; + } + if (!yoffset) { + if (height != tex->getHeight(target, level)) + return false; + } else { + if (height % kBlockHeight) + return false; + } + return true; + } + case Extensions3D::COMPRESSED_RGB_PVRTC_4BPPV1_IMG: + case Extensions3D::COMPRESSED_RGBA_PVRTC_4BPPV1_IMG: + { + if (xoffset || yoffset + || width != tex->getWidth(target, level) || height != tex->getHeight(target, level)) { + return false; + } + return true; + } + } + return false; +} + +void WebGLCompressedTextures::compressedTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, + GC3Dsizei height, GC3Dint border, ArrayBufferView* data) +{ + GraphicsContext3D* context = m_context->graphicsContext3D(); + if (m_context->isContextLost()) + return; + if (!validateCompressedTexFormat(internalformat)) { + context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (border) { + context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!validateCompressedTexFuncData(width, height, internalformat, data)) + return; + WebGLTexture* tex = m_context->validateTextureBinding(target, true); + if (!tex) + return; + if (!m_context->isGLES2NPOTStrict()) { + if (level && WebGLTexture::isNPOT(width, height)) { + context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + context->compressedTexImage2D(target, level, internalformat, width, height, + border, data->byteLength(), data->baseAddress()); + tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); + m_context->cleanupAfterGraphicsCall(false); +} + +void WebGLCompressedTextures::compressedTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, GC3Denum format, ArrayBufferView* data) +{ + GraphicsContext3D* context = m_context->graphicsContext3D(); + if (m_context->isContextLost()) + return; + if (!validateCompressedTexFormat(format)) { + context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (!validateCompressedTexFuncData(width, height, format, data)) + return; + + WebGLTexture* tex = m_context->validateTextureBinding(target, true); + if (!tex) + return; + + if (format != tex->getInternalFormat(target, level)) { + context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + if (!validateCompressedTexSubDimensions(target, level, xoffset, yoffset, width, height, format, tex)) { + context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + context->compressedTexSubImage2D(target, level, xoffset, yoffset, + width, height, format, data->byteLength(), data->baseAddress()); + m_context->cleanupAfterGraphicsCall(false); +} + +WebGLGetInfo WebGLCompressedTextures::getCompressedTextureFormats() +{ + return WebGLGetInfo(Int32Array::create(&m_formats[0], m_formats.size())); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLCompressedTextures.h b/Source/WebCore/html/canvas/WebGLCompressedTextures.h new file mode 100644 index 000000000..699370668 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLCompressedTextures.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 WebGLCompressedTextures_h +#define WebGLCompressedTextures_h + +#include "ExceptionCode.h" +#include "WebGLExtension.h" +#include <wtf/ArrayBufferView.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class WebGLTexture; + +class WebGLCompressedTextures : public WebGLExtension { +public: + static PassOwnPtr<WebGLCompressedTextures> create(WebGLRenderingContext*); + + static bool supported(WebGLRenderingContext*); + + virtual ~WebGLCompressedTextures(); + virtual ExtensionName getName() const; + + void compressedTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, + GC3Dsizei height, GC3Dint border, ArrayBufferView* data); + void compressedTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, GC3Denum format, ArrayBufferView* data); + + WebGLGetInfo getCompressedTextureFormats(); + +private: + WebGLCompressedTextures(WebGLRenderingContext*); + + bool validateCompressedTexFuncData(GC3Dsizei width, GC3Dsizei height, + GC3Denum format, ArrayBufferView* pixels); + + bool validateCompressedTexFormat(GC3Denum format); + + bool validateCompressedTexSubDimensions(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, GC3Denum format, WebGLTexture*); + + bool m_supportsDxt1; + bool m_supportsDxt5; + bool m_supportsEtc1; + bool m_supportsPvrtc; + + Vector<int> m_formats; +}; + +} // namespace WebCore + +#endif // WebGLCompressedTextures_h diff --git a/Source/WebCore/html/canvas/WebGLCompressedTextures.idl b/Source/WebCore/html/canvas/WebGLCompressedTextures.idl new file mode 100644 index 000000000..bbd9a256c --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLCompressedTextures.idl @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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, + GenerateIsReachable=ImplContext, + OmitConstructor, + DontCheckEnums + ] WebGLCompressedTextures { + /* Compressed Texture Formats */ + const unsigned int COMPRESSED_RGB_S3TC_DXT1_EXT = 0x83F0; + const unsigned int COMPRESSED_RGBA_S3TC_DXT1_EXT = 0x83F1; + const unsigned int COMPRESSED_RGBA_S3TC_DXT5_EXT = 0x83F3; + const unsigned int ETC1_RGB8_OES = 0x8D64; + const unsigned int COMPRESSED_RGB_PVRTC_4BPPV1_IMG = 0x8C00; + const unsigned int COMPRESSED_RGBA_PVRTC_4BPPV1_IMG = 0x8C02; + + [StrictTypeChecking] void compressedTexImage2D(in unsigned long target, in long level, in unsigned long internalformat, + in long width, in long height, in long border, in ArrayBufferView data); + [StrictTypeChecking] void compressedTexSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, + in long width, in long height, in unsigned long format, in ArrayBufferView data); + }; +} diff --git a/Source/WebCore/html/canvas/WebGLContextAttributes.cpp b/Source/WebCore/html/canvas/WebGLContextAttributes.cpp new file mode 100644 index 000000000..e36e04ef0 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLContextAttributes.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLContextAttributes.h" + +namespace WebCore { + +PassRefPtr<WebGLContextAttributes> WebGLContextAttributes::create() +{ + return adoptRef(new WebGLContextAttributes()); +} + +PassRefPtr<WebGLContextAttributes> WebGLContextAttributes::create(GraphicsContext3D::Attributes attributes) +{ + return adoptRef(new WebGLContextAttributes(attributes)); +} + +WebGLContextAttributes::WebGLContextAttributes() + : CanvasContextAttributes() +{ +} + +WebGLContextAttributes::WebGLContextAttributes(GraphicsContext3D::Attributes attributes) + : CanvasContextAttributes() + , m_attrs(attributes) +{ +} + +WebGLContextAttributes::~WebGLContextAttributes() +{ +} + +bool WebGLContextAttributes::alpha() const +{ + return m_attrs.alpha; +} + +void WebGLContextAttributes::setAlpha(bool alpha) +{ + m_attrs.alpha = alpha; +} + +bool WebGLContextAttributes::depth() const +{ + return m_attrs.depth; +} + +void WebGLContextAttributes::setDepth(bool depth) +{ + m_attrs.depth = depth; +} + +bool WebGLContextAttributes::stencil() const +{ + return m_attrs.stencil; +} + +void WebGLContextAttributes::setStencil(bool stencil) +{ + m_attrs.stencil = stencil; +} + +bool WebGLContextAttributes::antialias() const +{ + return m_attrs.antialias; +} + +void WebGLContextAttributes::setAntialias(bool antialias) +{ + m_attrs.antialias = antialias; +} + +bool WebGLContextAttributes::premultipliedAlpha() const +{ + return m_attrs.premultipliedAlpha; +} + +void WebGLContextAttributes::setPremultipliedAlpha(bool premultipliedAlpha) +{ + m_attrs.premultipliedAlpha = premultipliedAlpha; +} + +bool WebGLContextAttributes::preserveDrawingBuffer() const +{ + return m_attrs.preserveDrawingBuffer; +} + +void WebGLContextAttributes::setPreserveDrawingBuffer(bool preserveDrawingBuffer) +{ + m_attrs.preserveDrawingBuffer = preserveDrawingBuffer; +} + +GraphicsContext3D::Attributes WebGLContextAttributes::attributes() const +{ + return m_attrs; +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLContextAttributes.h b/Source/WebCore/html/canvas/WebGLContextAttributes.h new file mode 100644 index 000000000..5391a2b7d --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLContextAttributes.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLContextAttributes_h +#define WebGLContextAttributes_h + +#include "CanvasContextAttributes.h" +#include "GraphicsContext3D.h" +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class WebGLContextAttributes : public CanvasContextAttributes { + public: + virtual ~WebGLContextAttributes(); + + // Create a new attributes object + static PassRefPtr<WebGLContextAttributes> create(); + + // Create a new attributes object initialized with preexisting attributes + static PassRefPtr<WebGLContextAttributes> create(GraphicsContext3D::Attributes attributes); + + // Whether or not the drawing buffer has an alpha channel; default=true + bool alpha() const; + void setAlpha(bool alpha); + + // Whether or not the drawing buffer has a depth buffer; default=true + bool depth() const; + void setDepth(bool depth); + + // Whether or not the drawing buffer has a stencil buffer; default=true + bool stencil() const; + void setStencil(bool stencil); + + // Whether or not the drawing buffer is antialiased; default=true + bool antialias() const; + void setAntialias(bool antialias); + + // Whether or not to treat the values in the drawing buffer as + // though their alpha channel has already been multiplied into the + // color channels; default=true + bool premultipliedAlpha() const; + void setPremultipliedAlpha(bool premultipliedAlpha); + + // Whether or not to preserve the drawing buffer after presentation to the + // screen; default=false + bool preserveDrawingBuffer() const; + void setPreserveDrawingBuffer(bool); + + // Fetches a copy of the attributes stored in this object in a + // form that can be used to initialize a GraphicsContext3D. + GraphicsContext3D::Attributes attributes() const; + + protected: + WebGLContextAttributes(); + WebGLContextAttributes(GraphicsContext3D::Attributes attributes); + + private: + GraphicsContext3D::Attributes m_attrs; +}; + +} // namespace WebCore + +#endif // WebGLContextAttributes_h diff --git a/Source/WebCore/html/canvas/WebGLContextAttributes.idl b/Source/WebCore/html/canvas/WebGLContextAttributes.idl new file mode 100644 index 000000000..56da1c61e --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLContextAttributes.idl @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL, + OmitConstructor + ] WebGLContextAttributes { + attribute boolean alpha; + attribute boolean depth; + attribute boolean stencil; + attribute boolean antialias; + attribute boolean premultipliedAlpha; + attribute boolean preserveDrawingBuffer; + }; +} diff --git a/Source/WebCore/html/canvas/WebGLContextEvent.cpp b/Source/WebCore/html/canvas/WebGLContextEvent.cpp new file mode 100644 index 000000000..3351a4b08 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLContextEvent.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WebGLContextEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +WebGLContextEventInit::WebGLContextEventInit() +{ +} + +WebGLContextEvent::WebGLContextEvent() +{ +} + +WebGLContextEvent::WebGLContextEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& statusMessage) + : Event(type, canBubble, cancelable) + , m_statusMessage(statusMessage) +{ +} + +WebGLContextEvent::WebGLContextEvent(const AtomicString& type, const WebGLContextEventInit& initializer) + : Event(type, initializer) + , m_statusMessage(initializer.statusMessage) +{ +} + +WebGLContextEvent::~WebGLContextEvent() +{ +} + +const AtomicString& WebGLContextEvent::interfaceName() const +{ + return eventNames().interfaceForWebGLContextEvent; +} + +} // namespace WebCore diff --git a/Source/WebCore/html/canvas/WebGLContextEvent.h b/Source/WebCore/html/canvas/WebGLContextEvent.h new file mode 100644 index 000000000..036bd9617 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLContextEvent.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 WebGLContextEvent_h +#define WebGLContextEvent_h + +#include "Event.h" + +namespace WebCore { + +struct WebGLContextEventInit : public EventInit { + WebGLContextEventInit(); + + String statusMessage; +}; + +class WebGLContextEvent : public Event { +public: + static PassRefPtr<WebGLContextEvent> create() + { + return adoptRef(new WebGLContextEvent); + } + static PassRefPtr<WebGLContextEvent> create(const AtomicString& type, bool canBubble, bool cancelable, const String& statusMessage) + { + return adoptRef(new WebGLContextEvent(type, canBubble, cancelable, statusMessage)); + } + static PassRefPtr<WebGLContextEvent> create(const AtomicString& type, const WebGLContextEventInit& initializer) + { + return adoptRef(new WebGLContextEvent(type, initializer)); + } + virtual ~WebGLContextEvent(); + + const String& statusMessage() const { return m_statusMessage; } + + virtual const AtomicString& interfaceName() const; + +private: + WebGLContextEvent(); + WebGLContextEvent(const AtomicString& type, bool canBubble, bool cancelable, const String& statusMessage); + WebGLContextEvent(const AtomicString&, const WebGLContextEventInit&); + + String m_statusMessage; +}; + +} // namespace WebCore + +#endif // WebGLContextEvent_h diff --git a/Source/WebCore/html/canvas/WebGLContextEvent.idl b/Source/WebCore/html/canvas/WebGLContextEvent.idl new file mode 100644 index 000000000..8b4bbddbc --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLContextEvent.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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, + ConstructorTemplate=Event + ] WebGLContextEvent : Event { + readonly attribute [InitializedByConstructor] DOMString statusMessage; + }; + +} diff --git a/Source/WebCore/html/canvas/WebGLDebugRendererInfo.cpp b/Source/WebCore/html/canvas/WebGLDebugRendererInfo.cpp new file mode 100644 index 000000000..f5715b681 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDebugRendererInfo.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 "WebGLDebugRendererInfo.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +WebGLDebugRendererInfo::WebGLDebugRendererInfo(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +WebGLDebugRendererInfo::~WebGLDebugRendererInfo() +{ +} + +WebGLExtension::ExtensionName WebGLDebugRendererInfo::getName() const +{ + return WebGLDebugRendererInfoName; +} + +PassOwnPtr<WebGLDebugRendererInfo> WebGLDebugRendererInfo::create(WebGLRenderingContext* context) +{ + return adoptPtr(new WebGLDebugRendererInfo(context)); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLDebugRendererInfo.h b/Source/WebCore/html/canvas/WebGLDebugRendererInfo.h new file mode 100644 index 000000000..b96c02b74 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDebugRendererInfo.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 WebGLDebugRendererInfo_h +#define WebGLDebugRendererInfo_h + +#include "WebGLExtension.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class WebGLDebugRendererInfo : public WebGLExtension { +public: + enum EnumType { + UNMASKED_VENDOR_WEBGL = 0x9245, + UNMASKED_RENDERER_WEBGL = 0x9246 + }; + + static PassOwnPtr<WebGLDebugRendererInfo> create(WebGLRenderingContext*); + + virtual ~WebGLDebugRendererInfo(); + virtual ExtensionName getName() const; + +private: + WebGLDebugRendererInfo(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // WebGLDebugRendererInfo_h diff --git a/Source/WebCore/html/canvas/WebGLDebugRendererInfo.idl b/Source/WebCore/html/canvas/WebGLDebugRendererInfo.idl new file mode 100644 index 000000000..da2082a2c --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDebugRendererInfo.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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, + GenerateIsReachable=ImplContext, + OmitConstructor, + DontCheckEnums + ] WebGLDebugRendererInfo { + const unsigned int UNMASKED_VENDOR_WEBGL = 0x9245; + const unsigned int UNMASKED_RENDERER_WEBGL = 0x9246; + }; +} diff --git a/Source/WebCore/html/canvas/WebGLDebugShaders.cpp b/Source/WebCore/html/canvas/WebGLDebugShaders.cpp new file mode 100644 index 000000000..5d2c3176d --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDebugShaders.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 "WebGLDebugShaders.h" + +#include "Extensions3D.h" +#include "WebGLRenderingContext.h" +#include "WebGLShader.h" + +#include <wtf/OwnArrayPtr.h> + +namespace WebCore { + +WebGLDebugShaders::WebGLDebugShaders(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +WebGLDebugShaders::~WebGLDebugShaders() +{ +} + +WebGLExtension::ExtensionName WebGLDebugShaders::getName() const +{ + return WebGLDebugShadersName; +} + +PassOwnPtr<WebGLDebugShaders> WebGLDebugShaders::create(WebGLRenderingContext* context) +{ + return adoptPtr(new WebGLDebugShaders(context)); +} + +String WebGLDebugShaders::getTranslatedShaderSource(WebGLShader* shader, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (m_context->isContextLost()) + return String(); + if (!m_context->validateWebGLObject(shader)) + return ""; + return m_context->graphicsContext3D()->getExtensions()->getTranslatedShaderSourceANGLE(shader->object()); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLDebugShaders.h b/Source/WebCore/html/canvas/WebGLDebugShaders.h new file mode 100644 index 000000000..2068c6dfd --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDebugShaders.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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 WebGLDebugShaders_h +#define WebGLDebugShaders_h + +#include "WebGLExtension.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class WebGLShader; + +typedef int ExceptionCode; + +class WebGLDebugShaders : public WebGLExtension { +public: + static PassOwnPtr<WebGLDebugShaders> create(WebGLRenderingContext*); + + virtual ~WebGLDebugShaders(); + virtual ExtensionName getName() const; + + String getTranslatedShaderSource(WebGLShader*, ExceptionCode&); + +private: + WebGLDebugShaders(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // WebGLDebugShaders_h diff --git a/Source/WebCore/html/canvas/WebGLDebugShaders.idl b/Source/WebCore/html/canvas/WebGLDebugShaders.idl new file mode 100644 index 000000000..ce120327d --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLDebugShaders.idl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 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, + GenerateIsReachable=ImplContext, + OmitConstructor + ] WebGLDebugShaders { + [StrictTypeChecking, ConvertNullStringTo=Null] DOMString getTranslatedShaderSource(in WebGLShader shader) raises(DOMException); + }; +} diff --git a/Source/WebCore/html/canvas/WebGLExtension.cpp b/Source/WebCore/html/canvas/WebGLExtension.cpp new file mode 100644 index 000000000..bf62eecec --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLExtension.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 "WebGLExtension.h" + +namespace WebCore { + +WebGLExtension::WebGLExtension(WebGLRenderingContext* context) + : m_context(context) +{ +} + +WebGLExtension::~WebGLExtension() +{ +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLExtension.h b/Source/WebCore/html/canvas/WebGLExtension.h new file mode 100644 index 000000000..4ff969086 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLExtension.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 WebGLExtension_h +#define WebGLExtension_h + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +class WebGLExtension { +public: + // Extension names are needed to properly wrap instances in JavaScript objects. + enum ExtensionName { + WebKitWebGLLoseContextName, // WEBKIT_ prefix until extension is official + OESTextureFloatName, + OESStandardDerivativesName, + OESVertexArrayObjectName, + WebGLDebugRendererInfoName, + WebGLDebugShadersName, + WebKitWebGLCompressedTexturesName, // WEBKIT_ prefix until extension is official + }; + + void ref() { m_context->ref(); } + void deref() { m_context->deref(); } + WebGLRenderingContext* context() { return m_context; } + + virtual ~WebGLExtension(); + virtual ExtensionName getName() const = 0; + +protected: + WebGLExtension(WebGLRenderingContext*); + WebGLRenderingContext* m_context; +}; + +} // namespace WebCore + +#endif // WebGLExtension_h diff --git a/Source/WebCore/html/canvas/WebGLFramebuffer.cpp b/Source/WebCore/html/canvas/WebGLFramebuffer.cpp new file mode 100644 index 000000000..d1af518c0 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLFramebuffer.cpp @@ -0,0 +1,497 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLFramebuffer.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +namespace { + + bool isAttachmentComplete(WebGLObject* attachedObject, GC3Denum attachment) + { + ASSERT(attachedObject && attachedObject->object()); + ASSERT(attachedObject->isRenderbuffer()); + WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject); + switch (attachment) { + case GraphicsContext3D::DEPTH_ATTACHMENT: + if (buffer->getInternalFormat() != GraphicsContext3D::DEPTH_COMPONENT16) + return false; + break; + case GraphicsContext3D::STENCIL_ATTACHMENT: + if (buffer->getInternalFormat() != GraphicsContext3D::STENCIL_INDEX8) + return false; + break; + case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: + if (buffer->getInternalFormat() != GraphicsContext3D::DEPTH_STENCIL) + return false; + break; + default: + ASSERT_NOT_REACHED(); + return false; + } + if (!buffer->getWidth() || !buffer->getHeight()) + return false; + return true; + } + + GC3Dsizei getImageWidth(WebGLObject* attachedObject) + { + ASSERT(attachedObject && attachedObject->object()); + ASSERT(attachedObject->isRenderbuffer()); + WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject); + return buffer->getWidth(); + } + + GC3Dsizei getImageHeight(WebGLObject* attachedObject) + { + ASSERT(attachedObject && attachedObject->object()); + ASSERT(attachedObject->isRenderbuffer()); + WebGLRenderbuffer* buffer = reinterpret_cast<WebGLRenderbuffer*>(attachedObject); + return buffer->getHeight(); + } + + bool isUninitialized(WebGLObject* attachedObject) + { + if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer() + && !(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isInitialized()) + return true; + return false; + } + + void setInitialized(WebGLObject* attachedObject) + { + if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) + (reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->setInitialized(); + } + + bool isValid(WebGLObject* attachedObject) + { + if (attachedObject && attachedObject->object() && attachedObject->isRenderbuffer()) { + if (!(reinterpret_cast<WebGLRenderbuffer*>(attachedObject))->isValid()) + return false; + } + return true; + } + +} // anonymous namespace + +PassRefPtr<WebGLFramebuffer> WebGLFramebuffer::create(WebGLRenderingContext* ctx) +{ + return adoptRef(new WebGLFramebuffer(ctx)); +} + +WebGLFramebuffer::WebGLFramebuffer(WebGLRenderingContext* ctx) + : WebGLObject(ctx) + , m_hasEverBeenBound(false) + , m_texTarget(0) + , m_texLevel(-1) +{ + setObject(context()->graphicsContext3D()->createFramebuffer()); +} + +void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, GC3Denum texTarget, WebGLTexture* texture, GC3Dint level) +{ + ASSERT(isBound()); + 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) + texture->onAttached(); +} + +void WebGLFramebuffer::setAttachmentForBoundFramebuffer(GC3Denum attachment, WebGLRenderbuffer* renderbuffer) +{ + ASSERT(isBound()); + 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) + renderbuffer->onAttached(); +} + +WebGLObject* WebGLFramebuffer::getAttachment(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; + } +} + +void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(GC3Denum attachment) +{ + ASSERT(isBound()); + if (!object()) + return; + switch (attachment) { + case GraphicsContext3D::COLOR_ATTACHMENT0: + if (m_colorAttachment) { + m_colorAttachment->onDetached(); + m_colorAttachment = 0; + m_texTarget = 0; + m_texLevel = -1; + } + break; + case GraphicsContext3D::DEPTH_ATTACHMENT: + if (m_depthAttachment) { + m_depthAttachment->onDetached(); + m_depthAttachment = 0; + } + break; + case GraphicsContext3D::STENCIL_ATTACHMENT: + if (m_stencilAttachment) { + m_stencilAttachment->onDetached(); + m_stencilAttachment = 0; + } + break; + case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: + if (m_depthStencilAttachment) { + m_depthStencilAttachment->onDetached(); + m_depthStencilAttachment = 0; + } + break; + default: + ASSERT_NOT_REACHED(); + break; + } +} + +void WebGLFramebuffer::removeAttachmentFromBoundFramebuffer(WebGLObject* attachment) +{ + ASSERT(isBound()); + if (!object()) + return; + 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); + } +} + +GC3Dsizei WebGLFramebuffer::getColorBufferWidth() const +{ + if (!object() || !isColorAttached()) + 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; +} + +GC3Dsizei WebGLFramebuffer::getColorBufferHeight() const +{ + if (!object() || !isColorAttached()) + 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; +} + +GC3Denum WebGLFramebuffer::getColorBufferFormat() const +{ + if (!object() || !isColorAttached()) + 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; + } + return 0; + } + if (m_colorAttachment->isTexture()) + return (reinterpret_cast<WebGLTexture*>(m_colorAttachment.get()))->getInternalFormat(m_texTarget, m_texLevel); + ASSERT_NOT_REACHED(); + return 0; +} + +GC3Denum WebGLFramebuffer::checkStatus() const +{ + unsigned int count = 0; + GC3Dsizei width = 0, height = 0; + if (isDepthAttached()) { + if (!isAttachmentComplete(m_depthAttachment.get(), GraphicsContext3D::DEPTH_ATTACHMENT)) + 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)) + 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())) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + } + count++; + } + if (isDepthStencilAttached()) { + if (!isAttachmentComplete(m_depthStencilAttachment.get(), GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT)) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + if (!isValid(m_depthStencilAttachment.get())) + return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; + if (!count) { + width = getImageWidth(m_depthStencilAttachment.get()); + height = getImageHeight(m_depthStencilAttachment.get()); + } else { + if (width != getImageWidth(m_depthStencilAttachment.get()) || height != getImageHeight(m_depthStencilAttachment.get())) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + } + count++; + } + // WebGL specific: no conflicting DEPTH/STENCIL/DEPTH_STENCIL attachments. + if (count > 1) + return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; + if (isColorAttached()) { + // FIXME: if color buffer is texture, is ALPHA, LUMINANCE or LUMINANCE_ALPHA valid? + if (!getColorBufferFormat()) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + if (!count) { + if (!getColorBufferWidth() || !getColorBufferHeight()) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_ATTACHMENT; + } else { + if (width != getColorBufferWidth() || height != getColorBufferHeight()) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_DIMENSIONS; + } + } else { + if (!count) + return GraphicsContext3D::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; + } + return GraphicsContext3D::FRAMEBUFFER_COMPLETE; +} + +bool WebGLFramebuffer::onAccess(bool needToInitializeRenderbuffers) +{ + if (checkStatus() != GraphicsContext3D::FRAMEBUFFER_COMPLETE) + return false; + if (needToInitializeRenderbuffers) + return initializeRenderbuffers(); + return true; +} + +void WebGLFramebuffer::deleteObjectImpl(Platform3DObject object) +{ + if (m_colorAttachment) + m_colorAttachment->onDetached(); + if (m_depthAttachment) + m_depthAttachment->onDetached(); + if (m_stencilAttachment) + m_stencilAttachment->onDetached(); + if (m_depthStencilAttachment) + m_depthStencilAttachment->onDetached(); + context()->graphicsContext3D()->deleteFramebuffer(object); +} + +bool WebGLFramebuffer::initializeRenderbuffers() +{ + 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); + } + if (!initColor && !initDepth && !initStencil) + return true; + + // We only clear un-initialized renderbuffers when they are ready to be + // read, i.e., when the framebuffer is complete. + GraphicsContext3D* g3d = context()->graphicsContext3D(); + if (g3d->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) + return false; + + GC3Dfloat colorClearValue[] = {0, 0, 0, 0}, depthClearValue = 0; + GC3Dint stencilClearValue = 0; + GC3Dboolean colorMask[] = {0, 0, 0, 0}, depthMask = 0; + GC3Duint stencilMask = 0xffffffff; + GC3Dboolean isScissorEnabled = 0; + GC3Dboolean isDitherEnabled = 0; + if (initColor) { + g3d->getFloatv(GraphicsContext3D::COLOR_CLEAR_VALUE, colorClearValue); + g3d->getBooleanv(GraphicsContext3D::COLOR_WRITEMASK, colorMask); + g3d->clearColor(0, 0, 0, 0); + g3d->colorMask(true, true, true, true); + } + if (initDepth) { + g3d->getFloatv(GraphicsContext3D::DEPTH_CLEAR_VALUE, &depthClearValue); + g3d->getBooleanv(GraphicsContext3D::DEPTH_WRITEMASK, &depthMask); + g3d->clearDepth(0); + g3d->depthMask(true); + } + if (initStencil) { + g3d->getIntegerv(GraphicsContext3D::STENCIL_CLEAR_VALUE, &stencilClearValue); + g3d->getIntegerv(GraphicsContext3D::STENCIL_WRITEMASK, reinterpret_cast<GC3Dint*>(&stencilMask)); + g3d->clearStencil(0); + g3d->stencilMask(0xffffffff); + } + isScissorEnabled = g3d->isEnabled(GraphicsContext3D::SCISSOR_TEST); + g3d->disable(GraphicsContext3D::SCISSOR_TEST); + isDitherEnabled = g3d->isEnabled(GraphicsContext3D::DITHER); + g3d->disable(GraphicsContext3D::DITHER); + + g3d->clear(mask); + + if (initColor) { + g3d->clearColor(colorClearValue[0], colorClearValue[1], colorClearValue[2], colorClearValue[3]); + g3d->colorMask(colorMask[0], colorMask[1], colorMask[2], colorMask[3]); + } + if (initDepth) { + g3d->clearDepth(depthClearValue); + g3d->depthMask(depthMask); + } + if (initStencil) { + g3d->clearStencil(stencilClearValue); + g3d->stencilMask(stencilMask); + } + if (isScissorEnabled) + g3d->enable(GraphicsContext3D::SCISSOR_TEST); + else + g3d->disable(GraphicsContext3D::SCISSOR_TEST); + if (isDitherEnabled) + g3d->enable(GraphicsContext3D::DITHER); + 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()); + } + return true; +} + +bool WebGLFramebuffer::isBound() const +{ + return (context()->m_framebufferBinding.get() == this); +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLFramebuffer.h b/Source/WebCore/html/canvas/WebGLFramebuffer.h new file mode 100644 index 000000000..110f51395 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLFramebuffer.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLFramebuffer_h +#define WebGLFramebuffer_h + +#include "WebGLObject.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLRenderbuffer; +class WebGLTexture; + +class WebGLFramebuffer : public WebGLObject { +public: + virtual ~WebGLFramebuffer() { deleteObject(); } + + static PassRefPtr<WebGLFramebuffer> create(WebGLRenderingContext*); + + void setAttachmentForBoundFramebuffer(GC3Denum attachment, GC3Denum texTarget, WebGLTexture*, GC3Dint level); + void setAttachmentForBoundFramebuffer(GC3Denum attachment, WebGLRenderbuffer*); + // If an object is attached to the currently bound framebuffer, remove it. + void removeAttachmentFromBoundFramebuffer(WebGLObject*); + // If a given attachment point for the currently bound framebuffer is not null, remove the attached object. + void removeAttachmentFromBoundFramebuffer(GC3Denum); + WebGLObject* getAttachment(GC3Denum) const; + + GC3Denum getColorBufferFormat() const; + GC3Dsizei getColorBufferWidth() const; + GC3Dsizei getColorBufferHeight() const; + + // This should always be called before drawArray, drawElements, clear, + // readPixels, copyTexImage2D, copyTexSubImage2D if this framebuffer is + // 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(bool needToInitializeRenderbuffers); + + // Software version of glCheckFramebufferStatus(), except that when + // FRAMEBUFFER_COMPLETE is returned, it is still possible for + // glCheckFramebufferStatus() to return FRAMEBUFFER_UNSUPPORTED, + // depending on hardware implementation. + GC3Denum checkStatus() const; + + bool hasEverBeenBound() const { return object() && m_hasEverBeenBound; } + + void setHasEverBeenBound() { m_hasEverBeenBound = true; } + +protected: + WebGLFramebuffer(WebGLRenderingContext*); + + virtual void deleteObjectImpl(Platform3DObject); + +private: + virtual bool isFramebuffer() const { return true; } + + // Return false if framebuffer is incomplete. + bool initializeRenderbuffers(); + + // 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()); } + + RefPtr<WebGLObject> m_colorAttachment; + RefPtr<WebGLObject> m_depthAttachment; + RefPtr<WebGLObject> m_stencilAttachment; + RefPtr<WebGLObject> m_depthStencilAttachment; + + bool m_hasEverBeenBound; + + GC3Denum m_texTarget; + GC3Dint m_texLevel; +}; + +} // namespace WebCore + +#endif // WebGLFramebuffer_h diff --git a/Source/WebCore/html/canvas/WebGLFramebuffer.idl b/Source/WebCore/html/canvas/WebGLFramebuffer.idl new file mode 100644 index 000000000..d0caa917f --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLFramebuffer.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLFramebuffer { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLGetInfo.cpp b/Source/WebCore/html/canvas/WebGLGetInfo.cpp new file mode 100644 index 000000000..bce383676 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLGetInfo.cpp @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 "WebGLGetInfo.h" + +#include "WebGLBuffer.h" +#include "WebGLFramebuffer.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLTexture.h" +#include "WebGLVertexArrayObjectOES.h" +#include <wtf/Float32Array.h> +#include <wtf/Int32Array.h> +#include <wtf/Uint8Array.h> + +namespace WebCore { + +WebGLGetInfo::WebGLGetInfo(bool value) + : m_type(kTypeBool) + , m_bool(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(const bool* value, int size) + : m_type(kTypeBoolArray) +{ + if (!value || size <=0) + return; + m_boolArray.resize(size); + for (int ii = 0; ii < size; ++ii) + m_boolArray[ii] = value[ii]; +} + +WebGLGetInfo::WebGLGetInfo(float value) + : m_type(kTypeFloat) + , m_float(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(int value) + : m_type(kTypeInt) + , m_int(value) +{ +} + +WebGLGetInfo::WebGLGetInfo() + : m_type(kTypeNull) +{ +} + +WebGLGetInfo::WebGLGetInfo(const String& value) + : m_type(kTypeString) + , m_string(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(unsigned int value) + : m_type(kTypeUnsignedInt) + , m_unsignedInt(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLBuffer> value) + : m_type(kTypeWebGLBuffer) + , m_webglBuffer(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<Float32Array> value) + : m_type(kTypeWebGLFloatArray) + , m_webglFloatArray(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLFramebuffer> value) + : m_type(kTypeWebGLFramebuffer) + , m_webglFramebuffer(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<Int32Array> value) + : m_type(kTypeWebGLIntArray) + , m_webglIntArray(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLProgram> value) + : m_type(kTypeWebGLProgram) + , m_webglProgram(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLRenderbuffer> value) + : m_type(kTypeWebGLRenderbuffer) + , m_webglRenderbuffer(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLTexture> value) + : m_type(kTypeWebGLTexture) + , m_webglTexture(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<Uint8Array> value) + : m_type(kTypeWebGLUnsignedByteArray) + , m_webglUnsignedByteArray(value) +{ +} + +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLVertexArrayObjectOES> value) + : m_type(kTypeWebGLVertexArrayObjectOES) + , m_webglVertexArrayObject(value) +{ +} + +WebGLGetInfo::~WebGLGetInfo() +{ +} + +WebGLGetInfo::Type WebGLGetInfo::getType() const +{ + return m_type; +} + +bool WebGLGetInfo::getBool() const +{ + ASSERT(getType() == kTypeBool); + return m_bool; +} + +const Vector<bool>& WebGLGetInfo::getBoolArray() const +{ + ASSERT(getType() == kTypeBoolArray); + return m_boolArray; +} + +float WebGLGetInfo::getFloat() const +{ + ASSERT(getType() == kTypeFloat); + return m_float; +} + +int WebGLGetInfo::getInt() const +{ + ASSERT(getType() == kTypeInt); + return m_int; +} + +const String& WebGLGetInfo::getString() const +{ + ASSERT(getType() == kTypeString); + return m_string; +} + +unsigned int WebGLGetInfo::getUnsignedInt() const +{ + ASSERT(getType() == kTypeUnsignedInt); + return m_unsignedInt; +} + +PassRefPtr<WebGLBuffer> WebGLGetInfo::getWebGLBuffer() const +{ + ASSERT(getType() == kTypeWebGLBuffer); + return m_webglBuffer; +} + +PassRefPtr<Float32Array> WebGLGetInfo::getWebGLFloatArray() const +{ + ASSERT(getType() == kTypeWebGLFloatArray); + return m_webglFloatArray; +} + +PassRefPtr<WebGLFramebuffer> WebGLGetInfo::getWebGLFramebuffer() const +{ + ASSERT(getType() == kTypeWebGLFramebuffer); + return m_webglFramebuffer; +} + +PassRefPtr<Int32Array> WebGLGetInfo::getWebGLIntArray() const +{ + ASSERT(getType() == kTypeWebGLIntArray); + return m_webglIntArray; +} + +PassRefPtr<WebGLProgram> WebGLGetInfo::getWebGLProgram() const +{ + ASSERT(getType() == kTypeWebGLProgram); + return m_webglProgram; +} + +PassRefPtr<WebGLRenderbuffer> WebGLGetInfo::getWebGLRenderbuffer() const +{ + ASSERT(getType() == kTypeWebGLRenderbuffer); + return m_webglRenderbuffer; +} + +PassRefPtr<WebGLTexture> WebGLGetInfo::getWebGLTexture() const +{ + ASSERT(getType() == kTypeWebGLTexture); + return m_webglTexture; +} + +PassRefPtr<Uint8Array> WebGLGetInfo::getWebGLUnsignedByteArray() const +{ + ASSERT(getType() == kTypeWebGLUnsignedByteArray); + return m_webglUnsignedByteArray; +} + +PassRefPtr<WebGLVertexArrayObjectOES> WebGLGetInfo::getWebGLVertexArrayObjectOES() const +{ + ASSERT(getType() == kTypeWebGLVertexArrayObjectOES); + return m_webglVertexArrayObject; +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLGetInfo.h b/Source/WebCore/html/canvas/WebGLGetInfo.h new file mode 100644 index 000000000..f747a0902 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLGetInfo.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 WebGLGetInfo_h +#define WebGLGetInfo_h + +#include "PlatformString.h" +#include "WebGLBuffer.h" +#include "WebGLFramebuffer.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLTexture.h" +#include "WebGLVertexArrayObjectOES.h" + +#include <wtf/Float32Array.h> +#include <wtf/Int32Array.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Uint8Array.h> + +namespace WebCore { + +// A tagged union representing the result of get queries like +// getParameter (encompassing getBooleanv, getIntegerv, getFloatv) and +// similar variants. For reference counted types, increments and +// decrements the reference count of the target object. + +class WebGLGetInfo { +public: + enum Type { + kTypeBool, + kTypeBoolArray, + kTypeFloat, + kTypeInt, + kTypeNull, + kTypeString, + kTypeUnsignedInt, + kTypeWebGLBuffer, + kTypeWebGLFloatArray, + kTypeWebGLFramebuffer, + kTypeWebGLIntArray, + kTypeWebGLObjectArray, + kTypeWebGLProgram, + kTypeWebGLRenderbuffer, + kTypeWebGLTexture, + kTypeWebGLUnsignedByteArray, + kTypeWebGLVertexArrayObjectOES, + }; + + explicit WebGLGetInfo(bool value); + WebGLGetInfo(const bool* value, int size); + explicit WebGLGetInfo(float value); + explicit WebGLGetInfo(int value); + // Represents the null value and type. + WebGLGetInfo(); + explicit WebGLGetInfo(const String& value); + explicit WebGLGetInfo(unsigned int value); + explicit WebGLGetInfo(PassRefPtr<WebGLBuffer> value); + explicit WebGLGetInfo(PassRefPtr<Float32Array> value); + explicit WebGLGetInfo(PassRefPtr<WebGLFramebuffer> value); + explicit WebGLGetInfo(PassRefPtr<Int32Array> value); + // FIXME: implement WebGLObjectArray + // WebGLGetInfo(PassRefPtr<WebGLObjectArray> value); + explicit WebGLGetInfo(PassRefPtr<WebGLProgram> value); + explicit WebGLGetInfo(PassRefPtr<WebGLRenderbuffer> value); + explicit WebGLGetInfo(PassRefPtr<WebGLTexture> value); + explicit WebGLGetInfo(PassRefPtr<Uint8Array> value); + explicit WebGLGetInfo(PassRefPtr<WebGLVertexArrayObjectOES> value); + + virtual ~WebGLGetInfo(); + + Type getType() const; + + bool getBool() const; + const Vector<bool>& getBoolArray() const; + float getFloat() const; + int getInt() const; + const String& getString() const; + unsigned int getUnsignedInt() const; + PassRefPtr<WebGLBuffer> getWebGLBuffer() const; + PassRefPtr<Float32Array> getWebGLFloatArray() const; + PassRefPtr<WebGLFramebuffer> getWebGLFramebuffer() const; + PassRefPtr<Int32Array> getWebGLIntArray() const; + // FIXME: implement WebGLObjectArray + // PassRefPtr<WebGLObjectArray> getWebGLObjectArray() const; + PassRefPtr<WebGLProgram> getWebGLProgram() const; + PassRefPtr<WebGLRenderbuffer> getWebGLRenderbuffer() const; + PassRefPtr<WebGLTexture> getWebGLTexture() const; + PassRefPtr<Uint8Array> getWebGLUnsignedByteArray() const; + PassRefPtr<WebGLVertexArrayObjectOES> getWebGLVertexArrayObjectOES() const; + +private: + Type m_type; + bool m_bool; + Vector<bool> m_boolArray; + float m_float; + int m_int; + String m_string; + unsigned int m_unsignedInt; + RefPtr<WebGLBuffer> m_webglBuffer; + RefPtr<Float32Array> m_webglFloatArray; + RefPtr<WebGLFramebuffer> m_webglFramebuffer; + RefPtr<Int32Array> m_webglIntArray; + // FIXME: implement WebGLObjectArray + // RefPtr<WebGLObjectArray> m_webglObjectArray; + RefPtr<WebGLProgram> m_webglProgram; + RefPtr<WebGLRenderbuffer> m_webglRenderbuffer; + RefPtr<WebGLTexture> m_webglTexture; + RefPtr<Uint8Array> m_webglUnsignedByteArray; + RefPtr<WebGLVertexArrayObjectOES> m_webglVertexArrayObject; +}; + +} // namespace WebCore + +#endif // WebGLGetInfo_h diff --git a/Source/WebCore/html/canvas/WebGLLoseContext.cpp b/Source/WebCore/html/canvas/WebGLLoseContext.cpp new file mode 100644 index 000000000..244f3dc18 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLLoseContext.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 "WebGLLoseContext.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +WebGLLoseContext::WebGLLoseContext(WebGLRenderingContext* context) + : WebGLExtension(context) +{ +} + +WebGLLoseContext::~WebGLLoseContext() +{ +} + +WebGLExtension::ExtensionName WebGLLoseContext::getName() const +{ + return WebKitWebGLLoseContextName; +} + +PassOwnPtr<WebGLLoseContext> WebGLLoseContext::create(WebGLRenderingContext* context) +{ + return adoptPtr(new WebGLLoseContext(context)); +} + +void WebGLLoseContext::loseContext() +{ + m_context->forceLostContext(WebGLRenderingContext::SyntheticLostContext); +} + +void WebGLLoseContext::restoreContext() +{ + m_context->forceRestoreContext(); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLLoseContext.h b/Source/WebCore/html/canvas/WebGLLoseContext.h new file mode 100644 index 000000000..a326e0852 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLLoseContext.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 WebGLLoseContext_h +#define WebGLLoseContext_h + +#include "WebGLExtension.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class WebGLRenderingContext; + +class WebGLLoseContext : public WebGLExtension { +public: + static PassOwnPtr<WebGLLoseContext> create(WebGLRenderingContext*); + + virtual ~WebGLLoseContext(); + virtual ExtensionName getName() const; + + void loseContext(); + void restoreContext(); + +private: + WebGLLoseContext(WebGLRenderingContext*); +}; + +} // namespace WebCore + +#endif // WebGLLoseContext_h diff --git a/Source/WebCore/html/canvas/WebGLLoseContext.idl b/Source/WebCore/html/canvas/WebGLLoseContext.idl new file mode 100644 index 000000000..50955cbb6 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLLoseContext.idl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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, + GenerateIsReachable=ImplContext, + OmitConstructor + ] WebGLLoseContext { + [StrictTypeChecking] void loseContext(); + [StrictTypeChecking] void restoreContext(); + }; +} diff --git a/Source/WebCore/html/canvas/WebGLObject.cpp b/Source/WebCore/html/canvas/WebGLObject.cpp new file mode 100644 index 000000000..7b629a69c --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLObject.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLObject.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +WebGLObject::WebGLObject(WebGLRenderingContext* context) + : m_object(0) + , m_context(context) + , m_attachmentCount(0) + , m_deleted(false) +{ +} + +WebGLObject::~WebGLObject() +{ + if (m_context) + m_context->removeObject(this); +} + +void WebGLObject::setObject(Platform3DObject object) +{ + // object==0 && m_deleted==false indicating an uninitialized state; + ASSERT(!m_object && !m_deleted); + m_object = object; +} + +void WebGLObject::deleteObject() +{ + m_deleted = true; + if (!m_context || !m_object) + return; + if (!m_attachmentCount) { + m_context->graphicsContext3D()->makeContextCurrent(); + deleteObjectImpl(m_object); + m_object = 0; + } +} + +void WebGLObject::detachContext() +{ + m_attachmentCount = 0; // Make sure OpenGL resource is deleted. + if (m_context) { + deleteObject(); + m_context->removeObject(this); + m_context = 0; + } +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLObject.h b/Source/WebCore/html/canvas/WebGLObject.h new file mode 100644 index 000000000..44cc36f6b --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLObject.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLObject_h +#define WebGLObject_h + +#include "GraphicsContext3D.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLRenderingContext; + +class WebGLObject : public RefCounted<WebGLObject> { +public: + virtual ~WebGLObject(); + + Platform3DObject object() const { return m_object; } + + // deleteObject may not always delete the OpenGL resource. For programs and + // shaders, deletion is delayed until they are no longer attached. + // FIXME: revisit this when resource sharing between contexts are implemented. + void deleteObject(); + + void detachContext(); + + WebGLRenderingContext* context() const { return m_context; } + + virtual bool isBuffer() const { return false; } + virtual bool isFramebuffer() const { return false; } + virtual bool isProgram() const { return false; } + virtual bool isRenderbuffer() const { return false; } + virtual bool isShader() const { return false; } + virtual bool isTexture() const { return false; } + + void onAttached() { ++m_attachmentCount; } + void onDetached() + { + if (m_attachmentCount) + --m_attachmentCount; + if (m_deleted) + deleteObject(); + } + + // This indicates whether the client side issue a delete call already, not + // whether the OpenGL resource is deleted. + // object()==0 indicates the OpenGL resource is deleted. + bool isDeleted() { return m_deleted; } + +protected: + WebGLObject(WebGLRenderingContext*); + + // setObject should be only called once right after creating a WebGLObject. + void setObject(Platform3DObject); + + // deleteObjectImpl should be only called once to delete the OpenGL resource. + virtual void deleteObjectImpl(Platform3DObject) = 0; + +private: + Platform3DObject m_object; + WebGLRenderingContext* m_context; + unsigned m_attachmentCount; + bool m_deleted; +}; + +} // namespace WebCore + +#endif // WebGLObject_h diff --git a/Source/WebCore/html/canvas/WebGLProgram.cpp b/Source/WebCore/html/canvas/WebGLProgram.cpp new file mode 100644 index 000000000..d3efda47d --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLProgram.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLProgram.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +PassRefPtr<WebGLProgram> WebGLProgram::create(WebGLRenderingContext* ctx) +{ + return adoptRef(new WebGLProgram(ctx)); +} + +WebGLProgram::WebGLProgram(WebGLRenderingContext* ctx) + : WebGLObject(ctx) + , m_linkStatus(false) + , m_linkCount(0) +{ + setObject(context()->graphicsContext3D()->createProgram()); +} + +void WebGLProgram::deleteObjectImpl(Platform3DObject obj) +{ + context()->graphicsContext3D()->deleteProgram(obj); + if (m_vertexShader) { + m_vertexShader->onDetached(); + m_vertexShader = 0; + } + if (m_fragmentShader) { + m_fragmentShader->onDetached(); + m_fragmentShader = 0; + } +} + +bool WebGLProgram::cacheActiveAttribLocations() +{ + m_activeAttribLocations.clear(); + if (!object()) + return false; + GraphicsContext3D* context3d = context()->graphicsContext3D(); + + // Assume link status has already been cached. + if (!m_linkStatus) + return false; + + GC3Dint numAttribs = 0; + context3d->getProgramiv(object(), GraphicsContext3D::ACTIVE_ATTRIBUTES, &numAttribs); + m_activeAttribLocations.resize(static_cast<size_t>(numAttribs)); + for (int i = 0; i < numAttribs; ++i) { + ActiveInfo info; + context3d->getActiveAttrib(object(), i, info); + m_activeAttribLocations[i] = context3d->getAttribLocation(object(), info.name.charactersWithNullTermination()); + } + + return true; +} + +unsigned WebGLProgram::numActiveAttribLocations() const +{ + return m_activeAttribLocations.size(); +} + +GC3Dint WebGLProgram::getActiveAttribLocation(GC3Duint index) const +{ + if (index >= numActiveAttribLocations()) + return -1; + return m_activeAttribLocations[index]; +} + +bool WebGLProgram::isUsingVertexAttrib0() const +{ + for (unsigned ii = 0; ii < numActiveAttribLocations(); ++ii) { + if (!getActiveAttribLocation(ii)) + return true; + } + return false; +} + +WebGLShader* WebGLProgram::getAttachedShader(GC3Denum type) +{ + switch (type) { + case GraphicsContext3D::VERTEX_SHADER: + return m_vertexShader.get(); + case GraphicsContext3D::FRAGMENT_SHADER: + return m_fragmentShader.get(); + default: + return 0; + } +} + +bool WebGLProgram::attachShader(WebGLShader* shader) +{ + if (!shader || !shader->object()) + return false; + switch (shader->getType()) { + case GraphicsContext3D::VERTEX_SHADER: + if (m_vertexShader) + return false; + m_vertexShader = shader; + return true; + case GraphicsContext3D::FRAGMENT_SHADER: + if (m_fragmentShader) + return false; + m_fragmentShader = shader; + return true; + default: + return false; + } +} + +bool WebGLProgram::detachShader(WebGLShader* shader) +{ + if (!shader || !shader->object()) + return false; + switch (shader->getType()) { + case GraphicsContext3D::VERTEX_SHADER: + if (m_vertexShader != shader) + return false; + m_vertexShader = 0; + return true; + case GraphicsContext3D::FRAGMENT_SHADER: + if (m_fragmentShader != shader) + return false; + m_fragmentShader = 0; + return true; + default: + return false; + } +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLProgram.h b/Source/WebCore/html/canvas/WebGLProgram.h new file mode 100644 index 000000000..0dd3ba0fa --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLProgram.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLProgram_h +#define WebGLProgram_h + +#include "WebGLObject.h" + +#include "WebGLShader.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class WebGLProgram : public WebGLObject { +public: + virtual ~WebGLProgram() { deleteObject(); } + + static PassRefPtr<WebGLProgram> create(WebGLRenderingContext*); + + // cacheActiveAttribLocation() is only called once after linkProgram() + // succeeds. + bool cacheActiveAttribLocations(); + unsigned numActiveAttribLocations() const; + GC3Dint getActiveAttribLocation(GC3Duint index) const; + + bool isUsingVertexAttrib0() const; + + bool getLinkStatus() const { return m_linkStatus; } + void setLinkStatus(bool status) { m_linkStatus = status; } + + unsigned getLinkCount() const { return m_linkCount; } + + // This is to be called everytime after the program is successfully linked. + // We don't deal with integer overflow here, assuming in reality a program + // will never be linked so many times. + void increaseLinkCount() { ++m_linkCount; } + + WebGLShader* getAttachedShader(GC3Denum); + bool attachShader(WebGLShader*); + bool detachShader(WebGLShader*); + +protected: + WebGLProgram(WebGLRenderingContext*); + + virtual void deleteObjectImpl(Platform3DObject); + +private: + virtual bool isProgram() const { return true; } + + Vector<GC3Dint> m_activeAttribLocations; + + GC3Dint m_linkStatus; + + // This is used to track whether a WebGLUniformLocation belongs to this + // program or not. + unsigned m_linkCount; + + RefPtr<WebGLShader> m_vertexShader; + RefPtr<WebGLShader> m_fragmentShader; +}; + +} // namespace WebCore + +#endif // WebGLProgram_h diff --git a/Source/WebCore/html/canvas/WebGLProgram.idl b/Source/WebCore/html/canvas/WebGLProgram.idl new file mode 100644 index 000000000..326f1c376 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLProgram.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLProgram { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLRenderbuffer.cpp b/Source/WebCore/html/canvas/WebGLRenderbuffer.cpp new file mode 100644 index 000000000..93b9165c9 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLRenderbuffer.cpp @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLRenderbuffer.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +PassRefPtr<WebGLRenderbuffer> WebGLRenderbuffer::create(WebGLRenderingContext* ctx) +{ + return adoptRef(new WebGLRenderbuffer(ctx)); +} + +WebGLRenderbuffer::WebGLRenderbuffer(WebGLRenderingContext* ctx) + : WebGLObject(ctx) + , m_internalFormat(GraphicsContext3D::RGBA4) + , m_initialized(false) + , m_width(0) + , m_height(0) + , m_isValid(true) + , m_hasEverBeenBound(false) +{ + setObject(context()->graphicsContext3D()->createRenderbuffer()); +} + +void WebGLRenderbuffer::deleteObjectImpl(Platform3DObject object) +{ + context()->graphicsContext3D()->deleteRenderbuffer(object); +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLRenderbuffer.h b/Source/WebCore/html/canvas/WebGLRenderbuffer.h new file mode 100644 index 000000000..4b47bf5d3 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLRenderbuffer.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLRenderbuffer_h +#define WebGLRenderbuffer_h + +#include "WebGLObject.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLRenderbuffer : public WebGLObject { +public: + virtual ~WebGLRenderbuffer() { deleteObject(); } + + static PassRefPtr<WebGLRenderbuffer> create(WebGLRenderingContext*); + + void setInternalFormat(GC3Denum internalformat) + { + m_internalFormat = internalformat; + m_initialized = false; + } + GC3Denum getInternalFormat() const { return m_internalFormat; } + + void setSize(GC3Dsizei width, GC3Dsizei height) + { + m_width = width; + m_height = height; + } + GC3Dsizei getWidth() const { return m_width; } + GC3Dsizei getHeight() const { return m_height; } + + void setIsValid(bool isValid) { m_isValid = isValid; } + bool isValid() const { return m_isValid; } + + bool isInitialized() const { return m_initialized; } + void setInitialized() { m_initialized = true; } + + bool hasEverBeenBound() const { return object() && m_hasEverBeenBound; } + + void setHasEverBeenBound() { m_hasEverBeenBound = true; } + +protected: + WebGLRenderbuffer(WebGLRenderingContext*); + + virtual void deleteObjectImpl(Platform3DObject); + +private: + virtual bool isRenderbuffer() const { return true; } + + GC3Denum m_internalFormat; + bool m_initialized; + GC3Dsizei m_width, m_height; + bool m_isValid; // This is only false if internalFormat is DEPTH_STENCIL and packed_depth_stencil is not supported. + + bool m_hasEverBeenBound; +}; + +} // namespace WebCore + +#endif // WebGLRenderbuffer_h diff --git a/Source/WebCore/html/canvas/WebGLRenderbuffer.idl b/Source/WebCore/html/canvas/WebGLRenderbuffer.idl new file mode 100644 index 000000000..a6518ea2e --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLRenderbuffer.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLRenderbuffer { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp new file mode 100644 index 000000000..a7b9cf648 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -0,0 +1,5087 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLRenderingContext.h" + +#include "CachedImage.h" +#include "CanvasPixelArray.h" +#include "CheckedInt.h" +#include "Console.h" +#include "DOMWindow.h" +#include "ExceptionCode.h" +#include "Extensions3D.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLCanvasElement.h" +#include "HTMLImageElement.h" +#include "HTMLVideoElement.h" +#include "ImageBuffer.h" +#include "ImageData.h" +#include "IntSize.h" +#include "NotImplemented.h" +#include "OESStandardDerivatives.h" +#include "OESTextureFloat.h" +#include "OESVertexArrayObject.h" +#include "Page.h" +#include "RenderBox.h" +#include "RenderLayer.h" +#include "Settings.h" +#include "WebGLActiveInfo.h" +#include "WebGLBuffer.h" +#include "WebGLCompressedTextures.h" +#include "WebGLContextAttributes.h" +#include "WebGLContextEvent.h" +#include "WebGLDebugRendererInfo.h" +#include "WebGLDebugShaders.h" +#include "WebGLFramebuffer.h" +#include "WebGLLoseContext.h" +#include "WebGLProgram.h" +#include "WebGLRenderbuffer.h" +#include "WebGLShader.h" +#include "WebGLTexture.h" +#include "WebGLUniformLocation.h" + +#include <wtf/ByteArray.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/PassOwnArrayPtr.h> +#include <wtf/Uint16Array.h> +#include <wtf/text/StringBuilder.h> + +#if PLATFORM(QT) +#undef emit +#endif + +namespace WebCore { + +const double secondsBetweenRestoreAttempts = 1.0; + +namespace { + + class ScopedDrawingBufferBinder { + public: + ScopedDrawingBufferBinder(DrawingBuffer* drawingBuffer, WebGLFramebuffer* framebufferBinding) + : m_drawingBuffer(drawingBuffer) + , m_framebufferBinding(framebufferBinding) + { + // Commit DrawingBuffer if needed (e.g., for multisampling) + if (!m_framebufferBinding && m_drawingBuffer) + m_drawingBuffer->commit(); + } + + ~ScopedDrawingBufferBinder() + { + // Restore DrawingBuffer if needed + if (!m_framebufferBinding && m_drawingBuffer) + m_drawingBuffer->bind(); + } + + private: + DrawingBuffer* m_drawingBuffer; + WebGLFramebuffer* m_framebufferBinding; + }; + + Platform3DObject objectOrZero(WebGLObject* object) + { + return object ? object->object() : 0; + } + + void clip1D(GC3Dint start, GC3Dsizei range, GC3Dsizei sourceRange, GC3Dint* clippedStart, GC3Dsizei* clippedRange) + { + ASSERT(clippedStart && clippedRange); + if (start < 0) { + range += start; + start = 0; + } + GC3Dint end = start + range; + if (end > sourceRange) + range -= end - sourceRange; + *clippedStart = start; + *clippedRange = range; + } + + // Returns false if no clipping is necessary, i.e., x, y, width, height stay the same. + bool clip2D(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, + GC3Dsizei sourceWidth, GC3Dsizei sourceHeight, + GC3Dint* clippedX, GC3Dint* clippedY, GC3Dsizei* clippedWidth, GC3Dsizei*clippedHeight) + { + ASSERT(clippedX && clippedY && clippedWidth && clippedHeight); + clip1D(x, width, sourceWidth, clippedX, clippedWidth); + clip1D(y, height, sourceHeight, clippedY, clippedHeight); + return (*clippedX != x || *clippedY != y || *clippedWidth != width || *clippedHeight != height); + } + + GC3Dint clamp(GC3Dint value, GC3Dint min, GC3Dint max) + { + if (value < min) + value = min; + if (value > max) + value = max; + return value; + } + + // Return true if a character belongs to the ASCII subset as defined in + // GLSL ES 1.0 spec section 3.1. + bool validateCharacter(unsigned char c) + { + // Printing characters are valid except " $ ` @ \ ' DEL. + if (c >= 32 && c <= 126 + && c != '"' && c != '$' && c != '`' && c != '@' && c != '\\' && c != '\'') + return true; + // Horizontal tab, line feed, vertical tab, form feed, carriage return + // are also valid. + if (c >= 9 && c <= 13) + return true; + return false; + } + + // Strips comments from shader text. This allows non-ASCII characters + // to be used in comments without potentially breaking OpenGL + // implementations not expecting characters outside the GLSL ES set. + class StripComments { + public: + StripComments(const String& str) + : m_parseState(BeginningOfLine) + , m_sourceString(str) + , m_length(str.length()) + , m_position(0) + { + parse(); + } + + String result() + { + return m_builder.toString(); + } + + private: + bool hasMoreCharacters() + { + return (m_position < m_length); + } + + void parse() + { + while (hasMoreCharacters()) { + process(current()); + // process() might advance the position. + if (hasMoreCharacters()) + advance(); + } + } + + void process(UChar); + + bool peek(UChar& character) + { + if (m_position + 1 >= m_length) + return false; + character = m_sourceString[m_position + 1]; + return true; + } + + UChar current() + { + ASSERT(m_position < m_length); + return m_sourceString[m_position]; + } + + void advance() + { + ++m_position; + } + + bool isNewline(UChar character) + { + // Don't attempt to canonicalize newline related characters. + return (character == '\n' || character == '\r'); + } + + void emit(UChar character) + { + m_builder.append(character); + } + + enum ParseState { + // Have not seen an ASCII non-whitespace character yet on + // this line. Possible that we might see a preprocessor + // directive. + BeginningOfLine, + + // Have seen at least one ASCII non-whitespace character + // on this line. + MiddleOfLine, + + // Handling a preprocessor directive. Passes through all + // characters up to the end of the line. Disables comment + // processing. + InPreprocessorDirective, + + // Handling a single-line comment. The comment text is + // replaced with a single space. + InSingleLineComment, + + // Handling a multi-line comment. Newlines are passed + // through to preserve line numbers. + InMultiLineComment + }; + + ParseState m_parseState; + String m_sourceString; + unsigned m_length; + unsigned m_position; + StringBuilder m_builder; + }; + + void StripComments::process(UChar c) + { + if (isNewline(c)) { + // No matter what state we are in, pass through newlines + // so we preserve line numbers. + emit(c); + + if (m_parseState != InMultiLineComment) + m_parseState = BeginningOfLine; + + return; + } + + UChar temp = 0; + switch (m_parseState) { + case BeginningOfLine: + if (WTF::isASCIISpace(c)) { + emit(c); + break; + } + + if (c == '#') { + m_parseState = InPreprocessorDirective; + emit(c); + break; + } + + // Transition to normal state and re-handle character. + m_parseState = MiddleOfLine; + process(c); + break; + + case MiddleOfLine: + if (c == '/' && peek(temp)) { + if (temp == '/') { + m_parseState = InSingleLineComment; + emit(' '); + advance(); + break; + } + + if (temp == '*') { + m_parseState = InMultiLineComment; + // Emit the comment start in case the user has + // an unclosed comment and we want to later + // signal an error. + emit('/'); + emit('*'); + advance(); + break; + } + } + + emit(c); + break; + + case InPreprocessorDirective: + // No matter what the character is, just pass it + // through. Do not parse comments in this state. This + // might not be the right thing to do long term, but it + // should handle the #error preprocessor directive. + emit(c); + break; + + case InSingleLineComment: + // The newline code at the top of this function takes care + // of resetting our state when we get out of the + // single-line comment. Swallow all other characters. + break; + + case InMultiLineComment: + if (c == '*' && peek(temp) && temp == '/') { + emit('*'); + emit('/'); + m_parseState = MiddleOfLine; + advance(); + break; + } + + // Swallow all other characters. Unclear whether we may + // want or need to just emit a space per character to try + // to preserve column numbers for debugging purposes. + break; + } + } +} // namespace anonymous + +class WebGLStateRestorer { +public: + WebGLStateRestorer(WebGLRenderingContext* context, + bool changed) + : m_context(context) + , m_changed(changed) + { + } + + ~WebGLStateRestorer() + { + m_context->cleanupAfterGraphicsCall(m_changed); + } + +private: + WebGLRenderingContext* m_context; + bool m_changed; +}; + +class WebGLRenderingContextLostCallback : public GraphicsContext3D::ContextLostCallback { +public: + explicit WebGLRenderingContextLostCallback(WebGLRenderingContext* cb) : m_context(cb) { } + virtual void onContextLost() { m_context->forceLostContext(WebGLRenderingContext::RealLostContext); } + virtual ~WebGLRenderingContextLostCallback() {} +private: + WebGLRenderingContext* m_context; +}; + +PassOwnPtr<WebGLRenderingContext> WebGLRenderingContext::create(HTMLCanvasElement* canvas, WebGLContextAttributes* attrs) +{ + HostWindow* hostWindow = canvas->document()->view()->root()->hostWindow(); + GraphicsContext3D::Attributes attributes = attrs ? attrs->attributes() : GraphicsContext3D::Attributes(); + + if (attributes.antialias) { + Page* p = canvas->document()->page(); + if (p && !p->settings()->openGLMultisamplingEnabled()) + attributes.antialias = false; + } + + attributes.noExtensions = true; +#if PLATFORM(CHROMIUM) + attributes.shareResources = true; +#else + attributes.shareResources = false; +#endif + + + RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(attributes, hostWindow)); + + if (!context) { + canvas->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextcreationerrorEvent, false, true, "Could not create a WebGL context.")); + return nullptr; + } + + return adoptPtr(new WebGLRenderingContext(canvas, context, attributes)); +} + +WebGLRenderingContext::WebGLRenderingContext(HTMLCanvasElement* passedCanvas, PassRefPtr<GraphicsContext3D> context, + GraphicsContext3D::Attributes attributes) + : CanvasRenderingContext(passedCanvas) + , m_context(context) + , m_drawingBuffer(0) + , m_dispatchContextLostEventTimer(this, &WebGLRenderingContext::dispatchContextLostEvent) + , m_restoreAllowed(false) + , m_restoreTimer(this, &WebGLRenderingContext::maybeRestoreContext) + , m_videoCache(4) + , m_contextLost(false) + , m_contextLostMode(SyntheticLostContext) + , m_attributes(attributes) +{ + ASSERT(m_context); + +#if PLATFORM(CHROMIUM) + // Create the DrawingBuffer and initialize the platform layer. + m_drawingBuffer = DrawingBuffer::create(m_context.get(), IntSize(canvas()->width(), canvas()->height()), !m_attributes.preserveDrawingBuffer); +#endif + + if (m_drawingBuffer) + m_drawingBuffer->bind(); + + setupFlags(); + initializeNewContext(); +} + +void WebGLRenderingContext::initializeNewContext() +{ + ASSERT(!m_contextLost); + m_needsUpdate = true; + m_markedCanvasDirty = false; + m_activeTextureUnit = 0; + m_packAlignment = 4; + m_unpackAlignment = 4; + m_unpackFlipY = false; + m_unpackPremultiplyAlpha = false; + m_unpackColorspaceConversion = GraphicsContext3D::BROWSER_DEFAULT_WEBGL; + m_boundArrayBuffer = 0; + m_currentProgram = 0; + m_framebufferBinding = 0; + m_renderbufferBinding = 0; + m_depthMask = true; + m_stencilMask = 0xFFFFFFFF; + m_stencilMaskBack = 0xFFFFFFFF; + m_stencilFuncRef = 0; + m_stencilFuncRefBack = 0; + m_stencilFuncMask = 0xFFFFFFFF; + m_stencilFuncMaskBack = 0xFFFFFFFF; + m_layerCleared = false; + + m_clearColor[0] = m_clearColor[1] = m_clearColor[2] = m_clearColor[3] = 0; + m_scissorEnabled = false; + m_clearDepth = 1; + m_clearStencil = 0; + m_colorMask[0] = m_colorMask[1] = m_colorMask[2] = m_colorMask[3] = true; + + GC3Dint numCombinedTextureImageUnits = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits); + m_textureUnits.clear(); + m_textureUnits.resize(numCombinedTextureImageUnits); + + GC3Dint numVertexAttribs = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_VERTEX_ATTRIBS, &numVertexAttribs); + m_maxVertexAttribs = numVertexAttribs; + + m_maxTextureSize = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &m_maxTextureSize); + m_maxTextureLevel = WebGLTexture::computeLevelCount(m_maxTextureSize, m_maxTextureSize); + m_maxCubeMapTextureSize = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_CUBE_MAP_TEXTURE_SIZE, &m_maxCubeMapTextureSize); + m_maxCubeMapTextureLevel = WebGLTexture::computeLevelCount(m_maxCubeMapTextureSize, m_maxCubeMapTextureSize); + m_maxRenderbufferSize = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_RENDERBUFFER_SIZE, &m_maxRenderbufferSize); + m_maxViewportDims[0] = m_maxViewportDims[1] = 0; + m_context->getIntegerv(GraphicsContext3D::MAX_VIEWPORT_DIMS, m_maxViewportDims); + + m_defaultVertexArrayObject = WebGLVertexArrayObjectOES::create(this, WebGLVertexArrayObjectOES::VaoTypeDefault); + addObject(m_defaultVertexArrayObject.get()); + m_boundVertexArrayObject = m_defaultVertexArrayObject; + + m_vertexAttribValue.resize(m_maxVertexAttribs); + + if (!isGLES2NPOTStrict()) + createFallbackBlackTextures1x1(); + if (!isGLES2Compliant()) + initVertexAttrib0(); + + if (m_drawingBuffer) + m_drawingBuffer->reset(IntSize(canvas()->width(), canvas()->height())); + + m_context->reshape(canvas()->width(), canvas()->height()); + m_context->viewport(0, 0, canvas()->width(), canvas()->height()); + + m_context->setContextLostCallback(adoptPtr(new WebGLRenderingContextLostCallback(this))); +} + +void WebGLRenderingContext::setupFlags() +{ + ASSERT(m_context); + + m_isGLES2Compliant = m_context->isGLES2Compliant(); + m_isErrorGeneratedOnOutOfBoundsAccesses = m_context->getExtensions()->isEnabled("GL_CHROMIUM_strict_attribs"); + m_isResourceSafe = m_context->getExtensions()->isEnabled("GL_CHROMIUM_resource_safe"); + if (m_isGLES2Compliant) { + m_isGLES2NPOTStrict = !m_context->getExtensions()->isEnabled("GL_OES_texture_npot"); + m_isDepthStencilSupported = m_context->getExtensions()->isEnabled("GL_OES_packed_depth_stencil"); + } else { + m_isGLES2NPOTStrict = !m_context->getExtensions()->isEnabled("GL_ARB_texture_non_power_of_two"); + m_isDepthStencilSupported = m_context->getExtensions()->isEnabled("GL_EXT_packed_depth_stencil"); + } +} + +bool WebGLRenderingContext::allowPrivilegedExtensions() const +{ + Page* p = canvas()->document()->page(); + if (p && p->settings()) + return p->settings()->privilegedWebGLExtensionsEnabled(); + return false; +} + +WebGLRenderingContext::~WebGLRenderingContext() +{ + detachAndRemoveAllObjects(); + m_context->setContextLostCallback(nullptr); +} + +void WebGLRenderingContext::markContextChanged() +{ + if (m_framebufferBinding) + return; + + m_context->markContextChanged(); + + m_layerCleared = false; +#if USE(ACCELERATED_COMPOSITING) + RenderBox* renderBox = canvas()->renderBox(); + if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) { + m_markedCanvasDirty = true; + renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); + } else { +#endif + if (!m_markedCanvasDirty) { + m_markedCanvasDirty = true; + canvas()->didDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); + } +#if USE(ACCELERATED_COMPOSITING) + } +#endif +} + +bool WebGLRenderingContext::clearIfComposited(GC3Dbitfield mask) +{ + if (isContextLost()) + return false; + + if (!m_context->layerComposited() || m_layerCleared + || m_attributes.preserveDrawingBuffer || (mask && m_framebufferBinding)) + return false; + + RefPtr<WebGLContextAttributes> contextAttributes = getContextAttributes(); + + // Determine if it's possible to combine the clear the user asked for and this clear. + bool combinedClear = mask && !m_scissorEnabled; + + if (m_framebufferBinding) { + if (m_drawingBuffer) + m_drawingBuffer->bind(); + else + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0); + } + m_context->disable(GraphicsContext3D::SCISSOR_TEST); + if (combinedClear && (mask & GraphicsContext3D::COLOR_BUFFER_BIT)) + m_context->clearColor(m_colorMask[0] ? m_clearColor[0] : 0, + m_colorMask[1] ? m_clearColor[1] : 0, + m_colorMask[2] ? m_clearColor[2] : 0, + m_colorMask[3] ? m_clearColor[3] : 0); + else + m_context->clearColor(0, 0, 0, 0); + m_context->colorMask(true, true, true, true); + GC3Dbitfield clearMask = GraphicsContext3D::COLOR_BUFFER_BIT; + if (contextAttributes->depth()) { + if (!combinedClear || !m_depthMask || !(mask & GraphicsContext3D::DEPTH_BUFFER_BIT)) + m_context->clearDepth(1.0f); + clearMask |= GraphicsContext3D::DEPTH_BUFFER_BIT; + m_context->depthMask(true); + } + if (contextAttributes->stencil()) { + if (combinedClear && (mask & GraphicsContext3D::STENCIL_BUFFER_BIT)) + m_context->clearStencil(m_clearStencil & m_stencilMask); + else + m_context->clearStencil(0); + clearMask |= GraphicsContext3D::STENCIL_BUFFER_BIT; + m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, 0xFFFFFFFF); + } + m_context->clear(clearMask); + + // Restore the state that the context set. + if (m_scissorEnabled) + m_context->enable(GraphicsContext3D::SCISSOR_TEST); + m_context->clearColor(m_clearColor[0], m_clearColor[1], + m_clearColor[2], m_clearColor[3]); + m_context->colorMask(m_colorMask[0], m_colorMask[1], + m_colorMask[2], m_colorMask[3]); + m_context->clearDepth(m_clearDepth); + m_context->clearStencil(m_clearStencil); + m_context->stencilMaskSeparate(GraphicsContext3D::FRONT, m_stencilMask); + m_context->depthMask(m_depthMask); + if (m_framebufferBinding) + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, objectOrZero(m_framebufferBinding.get())); + m_layerCleared = true; + + return combinedClear; +} + +void WebGLRenderingContext::markLayerComposited() +{ + m_context->markLayerComposited(); +} + +void WebGLRenderingContext::paintRenderingResultsToCanvas() +{ + // Until the canvas is written to by the application, the clear that + // happened after it was composited should be ignored by the compositor. + if (m_context->layerComposited() && !m_attributes.preserveDrawingBuffer) { + m_context->paintCompositedResultsToCanvas(this); + +#if USE(ACCELERATED_COMPOSITING) && PLATFORM(CHROMIUM) + if (m_drawingBuffer) + m_drawingBuffer->paintCompositedResultsToCanvas(this); +#endif + + canvas()->makePresentationCopy(); + } else + canvas()->clearPresentationCopy(); + clearIfComposited(); + + if (!m_markedCanvasDirty && !m_layerCleared) + return; + + canvas()->clearCopiedImage(); + m_markedCanvasDirty = false; + + if (m_drawingBuffer) + m_drawingBuffer->commit(); + m_context->paintRenderingResultsToCanvas(this, m_drawingBuffer.get()); + + if (m_drawingBuffer) { + if (m_framebufferBinding) + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, objectOrZero(m_framebufferBinding.get())); + else + m_drawingBuffer->bind(); + } +} + +PassRefPtr<ImageData> WebGLRenderingContext::paintRenderingResultsToImageData() +{ + clearIfComposited(); + if (m_drawingBuffer) + m_drawingBuffer->commit(); + RefPtr<ImageData> imageData = m_context->paintRenderingResultsToImageData(m_drawingBuffer.get()); + + if (m_drawingBuffer) { + if (m_framebufferBinding) + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, objectOrZero(m_framebufferBinding.get())); + else + m_drawingBuffer->bind(); + } + + return imageData; +} + +bool WebGLRenderingContext::paintsIntoCanvasBuffer() const +{ + return m_context->paintsIntoCanvasBuffer(); +} + +void WebGLRenderingContext::reshape(int width, int height) +{ + // This is an approximation because at WebGLRenderingContext level we don't + // know if the underlying FBO uses textures or renderbuffers. + GC3Dint maxSize = std::min(m_maxTextureSize, m_maxRenderbufferSize); + // Limit drawing buffer size to 4k to avoid memory exhaustion. + const int sizeUpperLimit = 4096; + maxSize = std::min(maxSize, sizeUpperLimit); + GC3Dint maxWidth = std::min(maxSize, m_maxViewportDims[0]); + GC3Dint maxHeight = std::min(maxSize, m_maxViewportDims[1]); + width = clamp(width, 1, maxWidth); + height = clamp(height, 1, maxHeight); + + if (m_needsUpdate) { +#if USE(ACCELERATED_COMPOSITING) + RenderBox* renderBox = canvas()->renderBox(); + if (renderBox && renderBox->hasLayer()) + renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); +#endif + m_needsUpdate = false; + } + + // We don't have to mark the canvas as dirty, since the newly created image buffer will also start off + // clear (and this matches what reshape will do). + if (m_drawingBuffer) + m_drawingBuffer->reset(IntSize(width, height)); + else + m_context->reshape(width, height); +} + +int WebGLRenderingContext::drawingBufferWidth() const +{ + if (m_drawingBuffer) + return m_drawingBuffer->size().width(); + + return m_context->getInternalFramebufferSize().width(); +} + +int WebGLRenderingContext::drawingBufferHeight() const +{ + if (m_drawingBuffer) + return m_drawingBuffer->size().height(); + + return m_context->getInternalFramebufferSize().height(); +} + +unsigned int WebGLRenderingContext::sizeInBytes(GC3Denum type) +{ + switch (type) { + case GraphicsContext3D::BYTE: + return sizeof(GC3Dbyte); + case GraphicsContext3D::UNSIGNED_BYTE: + return sizeof(GC3Dubyte); + case GraphicsContext3D::SHORT: + return sizeof(GC3Dshort); + case GraphicsContext3D::UNSIGNED_SHORT: + return sizeof(GC3Dushort); + case GraphicsContext3D::INT: + return sizeof(GC3Dint); + case GraphicsContext3D::UNSIGNED_INT: + return sizeof(GC3Duint); + case GraphicsContext3D::FLOAT: + return sizeof(GC3Dfloat); + } + ASSERT_NOT_REACHED(); + return 0; +} + +void WebGLRenderingContext::activeTexture(GC3Denum texture, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + if (texture - GraphicsContext3D::TEXTURE0 >= m_textureUnits.size()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_activeTextureUnit = texture - GraphicsContext3D::TEXTURE0; + m_context->activeTexture(texture); + + if (m_drawingBuffer) + m_drawingBuffer->setActiveTextureUnit(texture); + + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::attachShader(WebGLProgram* program, WebGLShader* shader, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program) || !validateWebGLObject(shader)) + return; + if (!program->attachShader(shader)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + m_context->attachShader(objectOrZero(program), objectOrZero(shader)); + shader->onAttached(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bindAttribLocation(WebGLProgram* program, GC3Duint index, const String& name, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return; + if (!validateLocationLength(name)) + return; + if (!validateString(name)) + return; + m_context->bindAttribLocation(objectOrZero(program), index, name); + cleanupAfterGraphicsCall(false); +} + +bool WebGLRenderingContext::checkObjectToBeBound(WebGLObject* object, bool& deleted) +{ + deleted = false; + if (isContextLost()) + return false; + if (object) { + if (object->context() != this) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + deleted = !object->object(); + } + return true; +} + +void WebGLRenderingContext::bindBuffer(GC3Denum target, WebGLBuffer* buffer, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + bool deleted; + if (!checkObjectToBeBound(buffer, deleted)) + return; + if (deleted) + buffer = 0; + if (buffer && buffer->getTarget() && buffer->getTarget() != target) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (target == GraphicsContext3D::ARRAY_BUFFER) + m_boundArrayBuffer = buffer; + else if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) + m_boundVertexArrayObject->setElementArrayBuffer(buffer); + else { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + + m_context->bindBuffer(target, objectOrZero(buffer)); + if (buffer) + buffer->setTarget(target); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bindFramebuffer(GC3Denum target, WebGLFramebuffer* buffer, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + bool deleted; + if (!checkObjectToBeBound(buffer, deleted)) + return; + if (deleted) + buffer = 0; + if (target != GraphicsContext3D::FRAMEBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_framebufferBinding = buffer; + if (!m_framebufferBinding && m_drawingBuffer) { + // Instead of binding fb 0, bind the drawing buffer. + m_drawingBuffer->bind(); + } else + m_context->bindFramebuffer(target, objectOrZero(buffer)); + if (buffer) + buffer->setHasEverBeenBound(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bindRenderbuffer(GC3Denum target, WebGLRenderbuffer* renderBuffer, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + bool deleted; + if (!checkObjectToBeBound(renderBuffer, deleted)) + return; + if (deleted) + renderBuffer = 0; + if (target != GraphicsContext3D::RENDERBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_renderbufferBinding = renderBuffer; + m_context->bindRenderbuffer(target, objectOrZero(renderBuffer)); + if (renderBuffer) + renderBuffer->setHasEverBeenBound(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bindTexture(GC3Denum target, WebGLTexture* texture, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + bool deleted; + if (!checkObjectToBeBound(texture, deleted)) + return; + if (deleted) + texture = 0; + if (texture && texture->getTarget() && texture->getTarget() != target) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + GC3Dint maxLevel = 0; + if (target == GraphicsContext3D::TEXTURE_2D) { + m_textureUnits[m_activeTextureUnit].m_texture2DBinding = texture; + maxLevel = m_maxTextureLevel; + + if (m_drawingBuffer && !m_activeTextureUnit) + m_drawingBuffer->setTexture2DBinding(objectOrZero(texture)); + + } else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) { + m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding = texture; + maxLevel = m_maxCubeMapTextureLevel; + } else { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_context->bindTexture(target, objectOrZero(texture)); + if (texture) + texture->setTarget(target, maxLevel); + + // Note: previously we used to automatically set the TEXTURE_WRAP_R + // repeat mode to CLAMP_TO_EDGE for cube map textures, because OpenGL + // ES 2.0 doesn't expose this flag (a bug in the specification) and + // otherwise the application has no control over the seams in this + // dimension. However, it appears that supporting this properly on all + // platforms is fairly involved (will require a HashMap from texture ID + // in all ports), and we have not had any complaints, so the logic has + // been removed. + + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::blendColor(GC3Dfloat red, GC3Dfloat green, GC3Dfloat blue, GC3Dfloat alpha) +{ + if (isContextLost()) + return; + m_context->blendColor(red, green, blue, alpha); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::blendEquation(GC3Denum mode) +{ + if (isContextLost() || !validateBlendEquation(mode)) + return; + m_context->blendEquation(mode); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::blendEquationSeparate(GC3Denum modeRGB, GC3Denum modeAlpha) +{ + if (isContextLost() || !validateBlendEquation(modeRGB) || !validateBlendEquation(modeAlpha)) + return; + m_context->blendEquationSeparate(modeRGB, modeAlpha); + cleanupAfterGraphicsCall(false); +} + + +void WebGLRenderingContext::blendFunc(GC3Denum sfactor, GC3Denum dfactor) +{ + if (isContextLost() || !validateBlendFuncFactors(sfactor, dfactor)) + return; + m_context->blendFunc(sfactor, dfactor); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, GC3Denum srcAlpha, GC3Denum dstAlpha) +{ + if (isContextLost() || !validateBlendFuncFactors(srcRGB, dstRGB)) + return; + m_context->blendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferData(GC3Denum target, GC3Dsizeiptr size, GC3Denum usage, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (size < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + if (!buffer->associateBufferData(size)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + + m_context->bufferData(target, size, usage); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferData(GC3Denum target, ArrayBuffer* data, GC3Denum usage, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!data) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + if (!buffer->associateBufferData(data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + + m_context->bufferData(target, data->byteLength(), data->data(), usage); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferData(GC3Denum target, ArrayBufferView* data, GC3Denum usage, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!data) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + if (!buffer->associateBufferData(data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + + m_context->bufferData(target, data->byteLength(), data->baseAddress(), usage); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferSubData(GC3Denum target, GC3Dintptr offset, ArrayBuffer* data, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + WebGLBuffer* buffer = validateBufferDataParameters(target, GraphicsContext3D::STATIC_DRAW); + if (!buffer) + return; + if (offset < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!data) + return; + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + if (!buffer->associateBufferSubData(offset, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + + m_context->bufferSubData(target, offset, data->byteLength(), data->data()); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferSubData(GC3Denum target, GC3Dintptr offset, ArrayBufferView* data, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + WebGLBuffer* buffer = validateBufferDataParameters(target, GraphicsContext3D::STATIC_DRAW); + if (!buffer) + return; + if (offset < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!data) + return; + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + if (!buffer->associateBufferSubData(offset, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + + m_context->bufferSubData(target, offset, data->byteLength(), data->baseAddress()); + cleanupAfterGraphicsCall(false); +} + +GC3Denum WebGLRenderingContext::checkFramebufferStatus(GC3Denum target) +{ + if (isContextLost()) + return GraphicsContext3D::FRAMEBUFFER_UNSUPPORTED; + if (target != GraphicsContext3D::FRAMEBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + if (!m_framebufferBinding || !m_framebufferBinding->object()) + return GraphicsContext3D::FRAMEBUFFER_COMPLETE; + GC3Denum result = m_framebufferBinding->checkStatus(); + if (result != GraphicsContext3D::FRAMEBUFFER_COMPLETE) + return result; + result = m_context->checkFramebufferStatus(target); + cleanupAfterGraphicsCall(false); + return result; +} + +void WebGLRenderingContext::clear(GC3Dbitfield mask) +{ + if (isContextLost()) + return; + if (mask & ~(GraphicsContext3D::COLOR_BUFFER_BIT | GraphicsContext3D::DEPTH_BUFFER_BIT | GraphicsContext3D::STENCIL_BUFFER_BIT)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (m_framebufferBinding && !m_framebufferBinding->onAccess(!isResourceSafe())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION); + return; + } + if (!clearIfComposited(mask)) + m_context->clear(mask); + cleanupAfterGraphicsCall(true); +} + +void WebGLRenderingContext::clearColor(GC3Dfloat r, GC3Dfloat g, GC3Dfloat b, GC3Dfloat a) +{ + if (isContextLost()) + return; + if (isnan(r)) + r = 0; + if (isnan(g)) + g = 0; + if (isnan(b)) + b = 0; + if (isnan(a)) + a = 1; + m_clearColor[0] = r; + m_clearColor[1] = g; + m_clearColor[2] = b; + m_clearColor[3] = a; + m_context->clearColor(r, g, b, a); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::clearDepth(GC3Dfloat depth) +{ + if (isContextLost()) + return; + m_clearDepth = depth; + m_context->clearDepth(depth); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::clearStencil(GC3Dint s) +{ + if (isContextLost()) + return; + m_clearStencil = s; + m_context->clearStencil(s); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::colorMask(GC3Dboolean red, GC3Dboolean green, GC3Dboolean blue, GC3Dboolean alpha) +{ + if (isContextLost()) + return; + m_colorMask[0] = red; + m_colorMask[1] = green; + m_colorMask[2] = blue; + m_colorMask[3] = alpha; + m_context->colorMask(red, green, blue, alpha); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::compileShader(WebGLShader* shader, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(shader)) + return; + m_context->compileShader(objectOrZero(shader)); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border) +{ + if (isContextLost()) + return; + if (!validateTexFuncParameters(target, level, internalformat, width, height, border, internalformat, GraphicsContext3D::UNSIGNED_BYTE)) + return; + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; + if (!isTexInternalFormatColorBufferCombinationValid(internalformat, getBoundFramebufferColorFormat())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (!isGLES2NPOTStrict() && level && WebGLTexture::isNPOT(width, height)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (m_framebufferBinding && !m_framebufferBinding->onAccess(!isResourceSafe())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION); + return; + } + clearIfComposited(); + if (isResourceSafe()) { + ScopedDrawingBufferBinder binder(m_drawingBuffer.get(), m_framebufferBinding.get()); + m_context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); + } else { + ScopedDrawingBufferBinder binder(m_drawingBuffer.get(), m_framebufferBinding.get()); + GC3Dint clippedX, clippedY; + GC3Dsizei clippedWidth, clippedHeight; + if (clip2D(x, y, width, height, getBoundFramebufferWidth(), getBoundFramebufferHeight(), &clippedX, &clippedY, &clippedWidth, &clippedHeight)) { + m_context->texImage2DResourceSafe(target, level, internalformat, width, height, border, + internalformat, GraphicsContext3D::UNSIGNED_BYTE, m_unpackAlignment); + if (clippedWidth > 0 && clippedHeight > 0) { + m_context->copyTexSubImage2D(target, level, clippedX - x, clippedY - y, + clippedX, clippedY, clippedWidth, clippedHeight); + } + } else + m_context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); + } + // FIXME: if the framebuffer is not complete, none of the below should be executed. + tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::copyTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) +{ + if (isContextLost()) + return; + if (!validateTexFuncLevel(target, level)) + return; + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; + if (!validateSize(xoffset, yoffset) || !validateSize(width, height)) + return; + if (xoffset + width > tex->getWidth(target, level) || yoffset + height > tex->getHeight(target, level)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!isTexInternalFormatColorBufferCombinationValid(tex->getInternalFormat(target, level), getBoundFramebufferColorFormat())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (m_framebufferBinding && !m_framebufferBinding->onAccess(!isResourceSafe())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION); + return; + } + clearIfComposited(); + if (isResourceSafe()) { + ScopedDrawingBufferBinder binder(m_drawingBuffer.get(), m_framebufferBinding.get()); + m_context->copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } else { + GC3Dint clippedX, clippedY; + GC3Dsizei clippedWidth, clippedHeight; + if (clip2D(x, y, width, height, getBoundFramebufferWidth(), getBoundFramebufferHeight(), &clippedX, &clippedY, &clippedWidth, &clippedHeight)) { + GC3Denum format = tex->getInternalFormat(target, level); + GC3Denum type = tex->getType(target, level); + OwnArrayPtr<unsigned char> zero; + if (width && height) { + unsigned int size; + GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &size, 0); + if (error != GraphicsContext3D::NO_ERROR) { + m_context->synthesizeGLError(error); + return; + } + zero = adoptArrayPtr(new unsigned char[size]); + if (!zero) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + memset(zero.get(), 0, size); + } + m_context->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, zero.get()); + if (clippedWidth > 0 && clippedHeight > 0) { + ScopedDrawingBufferBinder binder(m_drawingBuffer.get(), m_framebufferBinding.get()); + m_context->copyTexSubImage2D(target, level, xoffset + clippedX - x, yoffset + clippedY - y, + clippedX, clippedY, clippedWidth, clippedHeight); + } + } else { + ScopedDrawingBufferBinder binder(m_drawingBuffer.get(), m_framebufferBinding.get()); + m_context->copyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); + } + } + cleanupAfterGraphicsCall(false); +} + +PassRefPtr<WebGLBuffer> WebGLRenderingContext::createBuffer() +{ + if (isContextLost()) + return 0; + RefPtr<WebGLBuffer> o = WebGLBuffer::create(this); + addObject(o.get()); + return o; +} + +PassRefPtr<WebGLFramebuffer> WebGLRenderingContext::createFramebuffer() +{ + if (isContextLost()) + return 0; + RefPtr<WebGLFramebuffer> o = WebGLFramebuffer::create(this); + addObject(o.get()); + return o; +} + +PassRefPtr<WebGLTexture> WebGLRenderingContext::createTexture() +{ + if (isContextLost()) + return 0; + RefPtr<WebGLTexture> o = WebGLTexture::create(this); + addObject(o.get()); + return o; +} + +PassRefPtr<WebGLProgram> WebGLRenderingContext::createProgram() +{ + if (isContextLost()) + return 0; + RefPtr<WebGLProgram> o = WebGLProgram::create(this); + addObject(o.get()); + return o; +} + +PassRefPtr<WebGLRenderbuffer> WebGLRenderingContext::createRenderbuffer() +{ + if (isContextLost()) + return 0; + RefPtr<WebGLRenderbuffer> o = WebGLRenderbuffer::create(this); + addObject(o.get()); + return o; +} + +PassRefPtr<WebGLShader> WebGLRenderingContext::createShader(GC3Denum type, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return 0; + if (type != GraphicsContext3D::VERTEX_SHADER && type != GraphicsContext3D::FRAGMENT_SHADER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + + RefPtr<WebGLShader> o = WebGLShader::create(this, type); + addObject(o.get()); + return o; +} + +void WebGLRenderingContext::cullFace(GC3Denum mode) +{ + if (isContextLost()) + return; + m_context->cullFace(mode); + cleanupAfterGraphicsCall(false); +} + +bool WebGLRenderingContext::deleteObject(WebGLObject* object) +{ + if (isContextLost() || !object) + return false; + if (object->context() != this) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + if (object->object()) + object->deleteObject(); + return true; +} + +void WebGLRenderingContext::deleteBuffer(WebGLBuffer* buffer) +{ + if (!deleteObject(buffer)) + return; + if (m_boundArrayBuffer == buffer) + m_boundArrayBuffer = 0; + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); + if (elementArrayBuffer == buffer) + m_boundVertexArrayObject->setElementArrayBuffer(0); + if (!isGLES2Compliant()) { + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0); + if (buffer == state.bufferBinding) { + state.bufferBinding = m_vertexAttrib0Buffer; + state.bytesPerElement = 0; + state.size = 4; + state.type = GraphicsContext3D::FLOAT; + state.normalized = false; + state.stride = 16; + state.originalStride = 0; + state.offset = 0; + } + } +} + +void WebGLRenderingContext::deleteFramebuffer(WebGLFramebuffer* framebuffer) +{ + if (!deleteObject(framebuffer)) + return; + if (framebuffer == m_framebufferBinding) { + m_framebufferBinding = 0; + // Have to call bindFramebuffer here to bind back to internal fbo. + if (m_drawingBuffer) + m_drawingBuffer->bind(); + else + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, 0); + } +} + +void WebGLRenderingContext::deleteProgram(WebGLProgram* program) +{ + deleteObject(program); + // We don't reset m_currentProgram to 0 here because the deletion of the + // current program is delayed. +} + +void WebGLRenderingContext::deleteRenderbuffer(WebGLRenderbuffer* renderbuffer) +{ + if (!deleteObject(renderbuffer)) + return; + if (renderbuffer == m_renderbufferBinding) + m_renderbufferBinding = 0; + if (m_framebufferBinding) + m_framebufferBinding->removeAttachmentFromBoundFramebuffer(renderbuffer); +} + +void WebGLRenderingContext::deleteShader(WebGLShader* shader) +{ + deleteObject(shader); +} + +void WebGLRenderingContext::deleteTexture(WebGLTexture* texture) +{ + if (!deleteObject(texture)) + return; + for (size_t i = 0; i < m_textureUnits.size(); ++i) { + if (texture == m_textureUnits[i].m_texture2DBinding) + m_textureUnits[i].m_texture2DBinding = 0; + if (texture == m_textureUnits[i].m_textureCubeMapBinding) + m_textureUnits[i].m_textureCubeMapBinding = 0; + } + if (m_framebufferBinding) + m_framebufferBinding->removeAttachmentFromBoundFramebuffer(texture); +} + +void WebGLRenderingContext::depthFunc(GC3Denum func) +{ + if (isContextLost()) + return; + m_context->depthFunc(func); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::depthMask(GC3Dboolean flag) +{ + if (isContextLost()) + return; + m_depthMask = flag; + m_context->depthMask(flag); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::depthRange(GC3Dfloat zNear, GC3Dfloat zFar) +{ + if (isContextLost()) + return; + if (zNear > zFar) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + m_context->depthRange(zNear, zFar); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::detachShader(WebGLProgram* program, WebGLShader* shader, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program) || !validateWebGLObject(shader)) + return; + if (!program->detachShader(shader)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + m_context->detachShader(objectOrZero(program), objectOrZero(shader)); + shader->onDetached(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::disable(GC3Denum cap) +{ + if (isContextLost() || !validateCapability(cap)) + return; + if (cap == GraphicsContext3D::SCISSOR_TEST) { + m_scissorEnabled = false; + if (m_drawingBuffer) + m_drawingBuffer->setScissorEnabled(m_scissorEnabled); + } + m_context->disable(cap); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::disableVertexAttribArray(GC3Duint index, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + if (index >= m_maxVertexAttribs) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index); + state.enabled = false; + + if (index > 0 || isGLES2Compliant()) { + m_context->disableVertexAttribArray(index); + cleanupAfterGraphicsCall(false); + } +} + +bool WebGLRenderingContext::validateElementArraySize(GC3Dsizei count, GC3Denum type, GC3Dintptr offset) +{ + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); + + if (!elementArrayBuffer) + return false; + + if (offset < 0) + return false; + + if (type == GraphicsContext3D::UNSIGNED_SHORT) { + // For an unsigned short array, offset must be divisible by 2 for alignment reasons. + if (offset % 2) + return false; + + // Make uoffset an element offset. + offset /= 2; + + GC3Dsizeiptr n = elementArrayBuffer->byteLength() / 2; + if (offset > n || count > n - offset) + return false; + } else if (type == GraphicsContext3D::UNSIGNED_BYTE) { + GC3Dsizeiptr n = elementArrayBuffer->byteLength(); + if (offset > n || count > n - offset) + return false; + } + return true; +} + +bool WebGLRenderingContext::validateIndexArrayConservative(GC3Denum type, int& numElementsRequired) +{ + // Performs conservative validation by caching a maximum index of + // the given type per element array buffer. If all of the bound + // array buffers have enough elements to satisfy that maximum + // index, skips the expensive per-draw-call iteration in + // validateIndexArrayPrecise. + + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); + + if (!elementArrayBuffer) + return false; + + GC3Dsizeiptr numElements = elementArrayBuffer->byteLength(); + // The case count==0 is already dealt with in drawElements before validateIndexArrayConservative. + if (!numElements) + return false; + const ArrayBuffer* buffer = elementArrayBuffer->elementArrayBuffer(); + ASSERT(buffer); + + int maxIndex = elementArrayBuffer->getCachedMaxIndex(type); + if (maxIndex < 0) { + // Compute the maximum index in the entire buffer for the given type of index. + switch (type) { + case GraphicsContext3D::UNSIGNED_BYTE: { + const GC3Dubyte* p = static_cast<const GC3Dubyte*>(buffer->data()); + for (GC3Dsizeiptr i = 0; i < numElements; i++) + maxIndex = max(maxIndex, static_cast<int>(p[i])); + break; + } + case GraphicsContext3D::UNSIGNED_SHORT: { + numElements /= sizeof(GC3Dushort); + const GC3Dushort* p = static_cast<const GC3Dushort*>(buffer->data()); + for (GC3Dsizeiptr i = 0; i < numElements; i++) + maxIndex = max(maxIndex, static_cast<int>(p[i])); + break; + } + default: + return false; + } + elementArrayBuffer->setCachedMaxIndex(type, maxIndex); + } + + if (maxIndex >= 0) { + // The number of required elements is one more than the maximum + // index that will be accessed. + numElementsRequired = maxIndex + 1; + return true; + } + + return false; +} + +bool WebGLRenderingContext::validateIndexArrayPrecise(GC3Dsizei count, GC3Denum type, GC3Dintptr offset, int& numElementsRequired) +{ + ASSERT(count >= 0 && offset >= 0); + int lastIndex = -1; + + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); + + if (!elementArrayBuffer) + return false; + + if (!count) { + numElementsRequired = 0; + return true; + } + + if (!elementArrayBuffer->elementArrayBuffer()) + return false; + + unsigned long uoffset = offset; + unsigned long n = count; + + if (type == GraphicsContext3D::UNSIGNED_SHORT) { + // Make uoffset an element offset. + uoffset /= sizeof(GC3Dushort); + const GC3Dushort* p = static_cast<const GC3Dushort*>(elementArrayBuffer->elementArrayBuffer()->data()) + uoffset; + while (n-- > 0) { + if (*p > lastIndex) + lastIndex = *p; + ++p; + } + } else if (type == GraphicsContext3D::UNSIGNED_BYTE) { + const GC3Dubyte* p = static_cast<const GC3Dubyte*>(elementArrayBuffer->elementArrayBuffer()->data()) + uoffset; + while (n-- > 0) { + if (*p > lastIndex) + lastIndex = *p; + ++p; + } + } + + // Then set the last index in the index array and make sure it is valid. + numElementsRequired = lastIndex + 1; + return numElementsRequired > 0; +} + +bool WebGLRenderingContext::validateRenderingState(int numElementsRequired) +{ + if (!m_currentProgram) + return false; + + // Look in each enabled vertex attrib and check if they've been bound to a buffer. + for (unsigned i = 0; i < m_maxVertexAttribs; ++i) { + const WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(i); + if (state.enabled + && (!state.bufferBinding || !state.bufferBinding->object())) + return false; + } + + if (numElementsRequired <= 0) + return true; + + // Look in each consumed vertex attrib (by the current program) and find the smallest buffer size + int smallestNumElements = INT_MAX; + int numActiveAttribLocations = m_currentProgram->numActiveAttribLocations(); + for (int i = 0; i < numActiveAttribLocations; ++i) { + int loc = m_currentProgram->getActiveAttribLocation(i); + if (loc >= 0 && loc < static_cast<int>(m_maxVertexAttribs)) { + const WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(loc); + if (state.enabled) { + // Avoid off-by-one errors in numElements computation. + // For the last element, we will only touch the data for the + // element and nothing beyond it. + int bytesRemaining = static_cast<int>(state.bufferBinding->byteLength() - state.offset); + int numElements = 0; + ASSERT(state.stride > 0); + if (bytesRemaining >= state.bytesPerElement) + numElements = 1 + (bytesRemaining - state.bytesPerElement) / state.stride; + if (numElements < smallestNumElements) + smallestNumElements = numElements; + } + } + } + + if (smallestNumElements == INT_MAX) + smallestNumElements = 0; + + return numElementsRequired <= smallestNumElements; +} + +bool WebGLRenderingContext::validateWebGLObject(WebGLObject* object) +{ + if (!object || !object->object()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + if (object->context() != this) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + return true; +} + +void WebGLRenderingContext::drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + + if (isContextLost() || !validateDrawMode(mode)) + return; + + if (!validateStencilSettings()) + return; + + if (first < 0 || count < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + if (!count) + return; + + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + // Ensure we have a valid rendering state + CheckedInt<GC3Dint> checkedFirst(first); + CheckedInt<GC3Dint> checkedCount(count); + CheckedInt<GC3Dint> checkedSum = checkedFirst + checkedCount; + if (!checkedSum.valid() || !validateRenderingState(checkedSum.value())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + } else { + if (!validateRenderingState(0)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + } + + if (m_framebufferBinding && !m_framebufferBinding->onAccess(!isResourceSafe())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION); + return; + } + + clearIfComposited(); + + bool vertexAttrib0Simulated = false; + if (!isGLES2Compliant()) + vertexAttrib0Simulated = simulateVertexAttrib0(first + count - 1); + if (!isGLES2NPOTStrict()) + handleNPOTTextures(true); + m_context->drawArrays(mode, first, count); + if (!isGLES2Compliant() && vertexAttrib0Simulated) + restoreStatesAfterVertexAttrib0Simulation(); + if (!isGLES2NPOTStrict()) + handleNPOTTextures(false); + cleanupAfterGraphicsCall(true); +} + +void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + + if (isContextLost() || !validateDrawMode(mode)) + return; + + if (!validateStencilSettings()) + return; + + switch (type) { + case GraphicsContext3D::UNSIGNED_BYTE: + case GraphicsContext3D::UNSIGNED_SHORT: + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + + if (count < 0 || offset < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + if (!count) + return; + + if (!m_boundVertexArrayObject->getElementArrayBuffer()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + int numElements = 0; + if (!isErrorGeneratedOnOutOfBoundsAccesses()) { + // Ensure we have a valid rendering state + if (!validateElementArraySize(count, type, offset)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (!count) + return; + if (!validateIndexArrayConservative(type, numElements) || !validateRenderingState(numElements)) { + if (!validateIndexArrayPrecise(count, type, offset, numElements) || !validateRenderingState(numElements)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + } + } else { + if (!validateRenderingState(0)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + } + + if (m_framebufferBinding && !m_framebufferBinding->onAccess(!isResourceSafe())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION); + return; + } + clearIfComposited(); + + bool vertexAttrib0Simulated = false; + if (!isGLES2Compliant()) { + if (!numElements) + validateIndexArrayPrecise(count, type, offset, numElements); + vertexAttrib0Simulated = simulateVertexAttrib0(numElements); + } + if (!isGLES2NPOTStrict()) + handleNPOTTextures(true); + m_context->drawElements(mode, count, type, offset); + if (!isGLES2Compliant() && vertexAttrib0Simulated) + restoreStatesAfterVertexAttrib0Simulation(); + if (!isGLES2NPOTStrict()) + handleNPOTTextures(false); + cleanupAfterGraphicsCall(true); +} + +void WebGLRenderingContext::enable(GC3Denum cap) +{ + if (isContextLost() || !validateCapability(cap)) + return; + if (cap == GraphicsContext3D::SCISSOR_TEST) { + m_scissorEnabled = true; + if (m_drawingBuffer) + m_drawingBuffer->setScissorEnabled(m_scissorEnabled); + } + m_context->enable(cap); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::enableVertexAttribArray(GC3Duint index, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + if (index >= m_maxVertexAttribs) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index); + state.enabled = true; + + m_context->enableVertexAttribArray(index); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::finish() +{ + if (isContextLost()) + return; + m_context->finish(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::flush() +{ + if (isContextLost()) + return; + m_context->flush(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::framebufferRenderbuffer(GC3Denum target, GC3Denum attachment, GC3Denum renderbuffertarget, WebGLRenderbuffer* buffer, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateFramebufferFuncParameters(target, attachment)) + return; + if (renderbuffertarget != GraphicsContext3D::RENDERBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (buffer && buffer->context() != this) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + // Don't allow the default framebuffer to be mutated; all current + // implementations use an FBO internally in place of the default + // FBO. + if (!m_framebufferBinding || !m_framebufferBinding->object()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + 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; + break; + case GraphicsContext3D::STENCIL_ATTACHMENT: + m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, objectOrZero(buffer)); + if (!bufferObject) + reattachDepthStencilStencil = true; + break; + default: + m_context->framebufferRenderbuffer(target, attachment, renderbuffertarget, objectOrZero(buffer)); + } + 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); + } + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::framebufferTexture2D(GC3Denum target, GC3Denum attachment, GC3Denum textarget, WebGLTexture* texture, GC3Dint level, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateFramebufferFuncParameters(target, attachment)) + return; + if (level) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (texture && texture->context() != this) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + // Don't allow the default framebuffer to be mutated; all current + // implementations use an FBO internally in place of the default + // FBO. + if (!m_framebufferBinding || !m_framebufferBinding->object()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + m_context->framebufferTexture2D(target, attachment, textarget, objectOrZero(texture), level); + m_framebufferBinding->setAttachmentForBoundFramebuffer(attachment, textarget, texture, level); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::frontFace(GC3Denum mode) +{ + if (isContextLost()) + return; + m_context->frontFace(mode); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::generateMipmap(GC3Denum target) +{ + if (isContextLost()) + return; + WebGLTexture* tex = validateTextureBinding(target, false); + if (!tex) + return; + if (!tex->canGenerateMipmaps()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + 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) + bool needToResetMinFilter = false; + if (tex->getMinFilter() != GraphicsContext3D::NEAREST_MIPMAP_LINEAR) { + m_context->texParameteri(target, GraphicsContext3D::TEXTURE_MIN_FILTER, GraphicsContext3D::NEAREST_MIPMAP_LINEAR); + needToResetMinFilter = true; + } +#endif + m_context->generateMipmap(target); +#if OS(DARWIN) + if (needToResetMinFilter) + m_context->texParameteri(target, GraphicsContext3D::TEXTURE_MIN_FILTER, tex->getMinFilter()); +#endif + tex->generateMipmapLevelInfo(); + cleanupAfterGraphicsCall(false); +} + +PassRefPtr<WebGLActiveInfo> WebGLRenderingContext::getActiveAttrib(WebGLProgram* program, GC3Duint index, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return 0; + ActiveInfo info; + if (!m_context->getActiveAttrib(objectOrZero(program), index, info)) + return 0; + return WebGLActiveInfo::create(info.name, info.type, info.size); +} + +PassRefPtr<WebGLActiveInfo> WebGLRenderingContext::getActiveUniform(WebGLProgram* program, GC3Duint index, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return 0; + ActiveInfo info; + if (!m_context->getActiveUniform(objectOrZero(program), index, info)) + return 0; + if (!isGLES2Compliant()) + if (info.size > 1 && !info.name.endsWith("[0]")) + info.name.append("[0]"); + return WebGLActiveInfo::create(info.name, info.type, info.size); +} + +bool WebGLRenderingContext::getAttachedShaders(WebGLProgram* program, Vector<RefPtr<WebGLShader> >& shaderObjects, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + shaderObjects.clear(); + if (isContextLost() || !validateWebGLObject(program)) + return false; + + const GC3Denum shaderType[] = { + GraphicsContext3D::VERTEX_SHADER, + GraphicsContext3D::FRAGMENT_SHADER + }; + for (unsigned i = 0; i < sizeof(shaderType) / sizeof(GC3Denum); ++i) { + WebGLShader* shader = program->getAttachedShader(shaderType[i]); + if (shader) + shaderObjects.append(shader); + } + return true; +} + +GC3Dint WebGLRenderingContext::getAttribLocation(WebGLProgram* program, const String& name) +{ + if (isContextLost()) + return -1; + if (!validateLocationLength(name)) + return -1; + if (!validateString(name)) + return -1; + return m_context->getAttribLocation(objectOrZero(program), name); +} + +WebGLGetInfo WebGLRenderingContext::getBufferParameter(GC3Denum target, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return WebGLGetInfo(); + if (target != GraphicsContext3D::ARRAY_BUFFER && target != GraphicsContext3D::ELEMENT_ARRAY_BUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + + if (pname != GraphicsContext3D::BUFFER_SIZE && pname != GraphicsContext3D::BUFFER_USAGE) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + + WebGLStateRestorer(this, false); + GC3Dint value = 0; + m_context->getBufferParameteriv(target, pname, &value); + if (pname == GraphicsContext3D::BUFFER_SIZE) + return WebGLGetInfo(value); + return WebGLGetInfo(static_cast<unsigned int>(value)); +} + +PassRefPtr<WebGLContextAttributes> WebGLRenderingContext::getContextAttributes() +{ + if (isContextLost()) + return 0; + // We always need to return a new WebGLContextAttributes object to + // prevent the user from mutating any cached version. + return WebGLContextAttributes::create(m_context->getContextAttributes()); +} + +GC3Denum WebGLRenderingContext::getError() +{ + return m_context->getError(); +} + +WebGLExtension* WebGLRenderingContext::getExtension(const String& name) +{ + if (isContextLost()) + return 0; + + if (equalIgnoringCase(name, "OES_standard_derivatives") + && m_context->getExtensions()->supports("GL_OES_standard_derivatives")) { + if (!m_oesStandardDerivatives) { + m_context->getExtensions()->ensureEnabled("GL_OES_standard_derivatives"); + m_oesStandardDerivatives = OESStandardDerivatives::create(this); + } + return m_oesStandardDerivatives.get(); + } + if (equalIgnoringCase(name, "OES_texture_float") + && m_context->getExtensions()->supports("GL_OES_texture_float")) { + if (!m_oesTextureFloat) { + m_context->getExtensions()->ensureEnabled("GL_OES_texture_float"); + m_oesTextureFloat = OESTextureFloat::create(this); + } + return m_oesTextureFloat.get(); + } + if (equalIgnoringCase(name, "OES_vertex_array_object") + && m_context->getExtensions()->supports("GL_OES_vertex_array_object")) { + if (!m_oesVertexArrayObject) { + m_context->getExtensions()->ensureEnabled("GL_OES_vertex_array_object"); + m_oesVertexArrayObject = OESVertexArrayObject::create(this); + } + return m_oesVertexArrayObject.get(); + } + if (equalIgnoringCase(name, "WEBKIT_WEBGL_lose_context") + // FIXME: remove this after a certain grace period. + || equalIgnoringCase(name, "WEBKIT_lose_context")) { + if (!m_webglLoseContext) + m_webglLoseContext = WebGLLoseContext::create(this); + return m_webglLoseContext.get(); + } + if (equalIgnoringCase(name, "WEBKIT_WEBGL_compressed_textures")) { + // Use WEBKIT_ prefix until extension is official. + if (!m_webglCompressedTextures) + m_webglCompressedTextures = WebGLCompressedTextures::create(this); + return m_webglCompressedTextures.get(); + } + + if (allowPrivilegedExtensions()) { + if (equalIgnoringCase(name, "WEBGL_debug_renderer_info")) { + if (!m_webglDebugRendererInfo) + m_webglDebugRendererInfo = WebGLDebugRendererInfo::create(this); + return m_webglDebugRendererInfo.get(); + } + if (equalIgnoringCase(name, "WEBGL_debug_shaders") + && m_context->getExtensions()->supports("GL_ANGLE_translated_shader_source")) { + if (!m_webglDebugShaders) + m_webglDebugShaders = WebGLDebugShaders::create(this); + return m_webglDebugShaders.get(); + } + } + + return 0; +} + +WebGLGetInfo WebGLRenderingContext::getFramebufferAttachmentParameter(GC3Denum target, GC3Denum attachment, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateFramebufferFuncParameters(target, attachment)) + return WebGLGetInfo(); + + if (!m_framebufferBinding || !m_framebufferBinding->object()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return WebGLGetInfo(); + } + + WebGLObject* object = m_framebufferBinding->getAttachment(attachment); + if (!object) { + if (pname == GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE) + return WebGLGetInfo(GraphicsContext3D::NONE); + // OpenGL ES 2.0 specifies INVALID_ENUM in this case, while desktop GL + // specifies INVALID_OPERATION. + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + + ASSERT(object->isTexture() || object->isRenderbuffer()); + if (object->isTexture()) { + switch (pname) { + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return WebGLGetInfo(GraphicsContext3D::TEXTURE); + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return WebGLGetInfo(PassRefPtr<WebGLTexture>(reinterpret_cast<WebGLTexture*>(object))); + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL: + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE: + { + WebGLStateRestorer(this, false); + GC3Dint value = 0; + m_context->getFramebufferAttachmentParameteriv(target, attachment, pname, &value); + return WebGLGetInfo(value); + } + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + } else { + switch (pname) { + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE: + return WebGLGetInfo(GraphicsContext3D::RENDERBUFFER); + case GraphicsContext3D::FRAMEBUFFER_ATTACHMENT_OBJECT_NAME: + return WebGLGetInfo(PassRefPtr<WebGLRenderbuffer>(reinterpret_cast<WebGLRenderbuffer*>(object))); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + } +} + +WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return WebGLGetInfo(); + WebGLStateRestorer(this, false); + switch (pname) { + case GraphicsContext3D::ACTIVE_TEXTURE: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::ALIASED_LINE_WIDTH_RANGE: + return getWebGLFloatArrayParameter(pname); + case GraphicsContext3D::ALIASED_POINT_SIZE_RANGE: + return getWebGLFloatArrayParameter(pname); + case GraphicsContext3D::ALPHA_BITS: + return getIntParameter(pname); + case GraphicsContext3D::ARRAY_BUFFER_BINDING: + return WebGLGetInfo(PassRefPtr<WebGLBuffer>(m_boundArrayBuffer)); + case GraphicsContext3D::BLEND: + return getBooleanParameter(pname); + case GraphicsContext3D::BLEND_COLOR: + return getWebGLFloatArrayParameter(pname); + case GraphicsContext3D::BLEND_DST_ALPHA: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::BLEND_DST_RGB: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::BLEND_EQUATION_ALPHA: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::BLEND_EQUATION_RGB: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::BLEND_SRC_ALPHA: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::BLEND_SRC_RGB: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::BLUE_BITS: + return getIntParameter(pname); + case GraphicsContext3D::COLOR_CLEAR_VALUE: + return getWebGLFloatArrayParameter(pname); + case GraphicsContext3D::COLOR_WRITEMASK: + return getBooleanArrayParameter(pname); + case GraphicsContext3D::COMPRESSED_TEXTURE_FORMATS: + if (m_webglCompressedTextures) + return m_webglCompressedTextures->getCompressedTextureFormats(); + // Defined as null in the spec + return WebGLGetInfo(); + case GraphicsContext3D::CULL_FACE: + return getBooleanParameter(pname); + case GraphicsContext3D::CULL_FACE_MODE: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::CURRENT_PROGRAM: + return WebGLGetInfo(PassRefPtr<WebGLProgram>(m_currentProgram)); + case GraphicsContext3D::DEPTH_BITS: + return getIntParameter(pname); + case GraphicsContext3D::DEPTH_CLEAR_VALUE: + return getFloatParameter(pname); + case GraphicsContext3D::DEPTH_FUNC: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::DEPTH_RANGE: + return getWebGLFloatArrayParameter(pname); + case GraphicsContext3D::DEPTH_TEST: + return getBooleanParameter(pname); + case GraphicsContext3D::DEPTH_WRITEMASK: + return getBooleanParameter(pname); + case GraphicsContext3D::DITHER: + return getBooleanParameter(pname); + case GraphicsContext3D::ELEMENT_ARRAY_BUFFER_BINDING: + return WebGLGetInfo(PassRefPtr<WebGLBuffer>(m_boundVertexArrayObject->getElementArrayBuffer())); + case GraphicsContext3D::FRAMEBUFFER_BINDING: + return WebGLGetInfo(PassRefPtr<WebGLFramebuffer>(m_framebufferBinding)); + case GraphicsContext3D::FRONT_FACE: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::GENERATE_MIPMAP_HINT: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::GREEN_BITS: + return getIntParameter(pname); + case GraphicsContext3D::LINE_WIDTH: + return getFloatParameter(pname); + case GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_CUBE_MAP_TEXTURE_SIZE: + return getIntParameter(pname); + case GraphicsContext3D::MAX_FRAGMENT_UNIFORM_VECTORS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_RENDERBUFFER_SIZE: + return getIntParameter(pname); + case GraphicsContext3D::MAX_TEXTURE_IMAGE_UNITS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_TEXTURE_SIZE: + return getIntParameter(pname); + case GraphicsContext3D::MAX_VARYING_VECTORS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_VERTEX_ATTRIBS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_VERTEX_TEXTURE_IMAGE_UNITS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_VERTEX_UNIFORM_VECTORS: + return getIntParameter(pname); + case GraphicsContext3D::MAX_VIEWPORT_DIMS: + return getWebGLIntArrayParameter(pname); + case GraphicsContext3D::NUM_COMPRESSED_TEXTURE_FORMATS: + // WebGL 1.0 specifies that there are no compressed texture formats. + return WebGLGetInfo(static_cast<int>(0)); + case GraphicsContext3D::NUM_SHADER_BINARY_FORMATS: + // FIXME: should we always return 0 for this? + return getIntParameter(pname); + case GraphicsContext3D::PACK_ALIGNMENT: + return getIntParameter(pname); + case GraphicsContext3D::POLYGON_OFFSET_FACTOR: + return getFloatParameter(pname); + case GraphicsContext3D::POLYGON_OFFSET_FILL: + return getBooleanParameter(pname); + case GraphicsContext3D::POLYGON_OFFSET_UNITS: + return getFloatParameter(pname); + case GraphicsContext3D::RED_BITS: + return getIntParameter(pname); + case GraphicsContext3D::RENDERBUFFER_BINDING: + return WebGLGetInfo(PassRefPtr<WebGLRenderbuffer>(m_renderbufferBinding)); + case GraphicsContext3D::RENDERER: + return WebGLGetInfo(String("WebKit WebGL")); + case GraphicsContext3D::SAMPLE_BUFFERS: + return getIntParameter(pname); + case GraphicsContext3D::SAMPLE_COVERAGE_INVERT: + return getBooleanParameter(pname); + case GraphicsContext3D::SAMPLE_COVERAGE_VALUE: + return getFloatParameter(pname); + case GraphicsContext3D::SAMPLES: + return getIntParameter(pname); + case GraphicsContext3D::SCISSOR_BOX: + return getWebGLIntArrayParameter(pname); + case GraphicsContext3D::SCISSOR_TEST: + return getBooleanParameter(pname); + case GraphicsContext3D::SHADING_LANGUAGE_VERSION: + return WebGLGetInfo("WebGL GLSL ES 1.0 (" + m_context->getString(GraphicsContext3D::SHADING_LANGUAGE_VERSION) + ")"); + case GraphicsContext3D::STENCIL_BACK_FAIL: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_BACK_FUNC: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_BACK_PASS_DEPTH_FAIL: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_BACK_PASS_DEPTH_PASS: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_BACK_REF: + return getIntParameter(pname); + case GraphicsContext3D::STENCIL_BACK_VALUE_MASK: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_BACK_WRITEMASK: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_BITS: + return getIntParameter(pname); + case GraphicsContext3D::STENCIL_CLEAR_VALUE: + return getIntParameter(pname); + case GraphicsContext3D::STENCIL_FAIL: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_FUNC: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_PASS_DEPTH_FAIL: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_PASS_DEPTH_PASS: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_REF: + return getIntParameter(pname); + case GraphicsContext3D::STENCIL_TEST: + return getBooleanParameter(pname); + case GraphicsContext3D::STENCIL_VALUE_MASK: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::STENCIL_WRITEMASK: + return getUnsignedIntParameter(pname); + case GraphicsContext3D::SUBPIXEL_BITS: + return getIntParameter(pname); + case GraphicsContext3D::TEXTURE_BINDING_2D: + return WebGLGetInfo(PassRefPtr<WebGLTexture>(m_textureUnits[m_activeTextureUnit].m_texture2DBinding)); + case GraphicsContext3D::TEXTURE_BINDING_CUBE_MAP: + return WebGLGetInfo(PassRefPtr<WebGLTexture>(m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding)); + case GraphicsContext3D::UNPACK_ALIGNMENT: + return getIntParameter(pname); + case GraphicsContext3D::UNPACK_FLIP_Y_WEBGL: + return WebGLGetInfo(m_unpackFlipY); + case GraphicsContext3D::UNPACK_PREMULTIPLY_ALPHA_WEBGL: + return WebGLGetInfo(m_unpackPremultiplyAlpha); + case GraphicsContext3D::UNPACK_COLORSPACE_CONVERSION_WEBGL: + return WebGLGetInfo(m_unpackColorspaceConversion); + case GraphicsContext3D::VENDOR: + return WebGLGetInfo(String("WebKit")); + case GraphicsContext3D::VERSION: + return WebGLGetInfo("WebGL 1.0 (" + m_context->getString(GraphicsContext3D::VERSION) + ")"); + case GraphicsContext3D::VIEWPORT: + return getWebGLIntArrayParameter(pname); + case Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES: // OES_standard_derivatives + if (m_oesStandardDerivatives) + return getUnsignedIntParameter(Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES); + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + case WebGLDebugRendererInfo::UNMASKED_RENDERER_WEBGL: + if (m_webglDebugRendererInfo) + return WebGLGetInfo(m_context->getString(GraphicsContext3D::RENDERER)); + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + case WebGLDebugRendererInfo::UNMASKED_VENDOR_WEBGL: + if (m_webglDebugRendererInfo) + return WebGLGetInfo(m_context->getString(GraphicsContext3D::VENDOR)); + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + case Extensions3D::VERTEX_ARRAY_BINDING_OES: // OES_vertex_array_object + if (m_oesVertexArrayObject) { + if (!m_boundVertexArrayObject->isDefaultObject()) + return WebGLGetInfo(PassRefPtr<WebGLVertexArrayObjectOES>(m_boundVertexArrayObject)); + return WebGLGetInfo(); + } + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } +} + +WebGLGetInfo WebGLRenderingContext::getProgramParameter(WebGLProgram* program, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return WebGLGetInfo(); + + WebGLStateRestorer(this, false); + GC3Dint value = 0; + switch (pname) { + case GraphicsContext3D::DELETE_STATUS: + return WebGLGetInfo(program->isDeleted()); + case GraphicsContext3D::VALIDATE_STATUS: + m_context->getProgramiv(objectOrZero(program), pname, &value); + return WebGLGetInfo(static_cast<bool>(value)); + case GraphicsContext3D::LINK_STATUS: + return WebGLGetInfo(program->getLinkStatus()); + case GraphicsContext3D::ATTACHED_SHADERS: + case GraphicsContext3D::ACTIVE_ATTRIBUTES: + case GraphicsContext3D::ACTIVE_UNIFORMS: + m_context->getProgramiv(objectOrZero(program), pname, &value); + return WebGLGetInfo(value); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } +} + +String WebGLRenderingContext::getProgramInfoLog(WebGLProgram* program, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return String(); + if (!validateWebGLObject(program)) + return ""; + WebGLStateRestorer(this, false); + return m_context->getProgramInfoLog(objectOrZero(program)); +} + +WebGLGetInfo WebGLRenderingContext::getRenderbufferParameter(GC3Denum target, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return WebGLGetInfo(); + if (target != GraphicsContext3D::RENDERBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + if (!m_renderbufferBinding || !m_renderbufferBinding->object()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return WebGLGetInfo(); + } + + if (m_renderbufferBinding->getInternalFormat() == GraphicsContext3D::DEPTH_STENCIL + && !m_renderbufferBinding->isValid()) { + ASSERT(!isDepthStencilSupported()); + int value = 0; + switch (pname) { + case GraphicsContext3D::RENDERBUFFER_WIDTH: + value = m_renderbufferBinding->getWidth(); + break; + case GraphicsContext3D::RENDERBUFFER_HEIGHT: + value = m_renderbufferBinding->getHeight(); + break; + case GraphicsContext3D::RENDERBUFFER_RED_SIZE: + case GraphicsContext3D::RENDERBUFFER_GREEN_SIZE: + case GraphicsContext3D::RENDERBUFFER_BLUE_SIZE: + case GraphicsContext3D::RENDERBUFFER_ALPHA_SIZE: + value = 0; + break; + case GraphicsContext3D::RENDERBUFFER_DEPTH_SIZE: + value = 24; + break; + case GraphicsContext3D::RENDERBUFFER_STENCIL_SIZE: + value = 8; + break; + case GraphicsContext3D::RENDERBUFFER_INTERNAL_FORMAT: + return WebGLGetInfo(m_renderbufferBinding->getInternalFormat()); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } + return WebGLGetInfo(value); + } + + WebGLStateRestorer(this, false); + GC3Dint value = 0; + switch (pname) { + case GraphicsContext3D::RENDERBUFFER_WIDTH: + case GraphicsContext3D::RENDERBUFFER_HEIGHT: + case GraphicsContext3D::RENDERBUFFER_RED_SIZE: + case GraphicsContext3D::RENDERBUFFER_GREEN_SIZE: + case GraphicsContext3D::RENDERBUFFER_BLUE_SIZE: + case GraphicsContext3D::RENDERBUFFER_ALPHA_SIZE: + case GraphicsContext3D::RENDERBUFFER_DEPTH_SIZE: + case GraphicsContext3D::RENDERBUFFER_STENCIL_SIZE: + m_context->getRenderbufferParameteriv(target, pname, &value); + return WebGLGetInfo(value); + case GraphicsContext3D::RENDERBUFFER_INTERNAL_FORMAT: + return WebGLGetInfo(m_renderbufferBinding->getInternalFormat()); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } +} + +WebGLGetInfo WebGLRenderingContext::getShaderParameter(WebGLShader* shader, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(shader)) + return WebGLGetInfo(); + WebGLStateRestorer(this, false); + GC3Dint value = 0; + switch (pname) { + case GraphicsContext3D::DELETE_STATUS: + return WebGLGetInfo(shader->isDeleted()); + case GraphicsContext3D::COMPILE_STATUS: + m_context->getShaderiv(objectOrZero(shader), pname, &value); + return WebGLGetInfo(static_cast<bool>(value)); + case GraphicsContext3D::SHADER_TYPE: + m_context->getShaderiv(objectOrZero(shader), pname, &value); + return WebGLGetInfo(static_cast<unsigned int>(value)); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } +} + +String WebGLRenderingContext::getShaderInfoLog(WebGLShader* shader, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return String(); + if (!validateWebGLObject(shader)) + return ""; + WebGLStateRestorer(this, false); + return m_context->getShaderInfoLog(objectOrZero(shader)); +} + +String WebGLRenderingContext::getShaderSource(WebGLShader* shader, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return String(); + if (!validateWebGLObject(shader)) + return ""; + return shader->getSource(); +} + +Vector<String> WebGLRenderingContext::getSupportedExtensions() +{ + Vector<String> result; + if (m_context->getExtensions()->supports("GL_OES_texture_float")) + result.append("OES_texture_float"); + if (m_context->getExtensions()->supports("GL_OES_standard_derivatives")) + result.append("OES_standard_derivatives"); + if (m_context->getExtensions()->supports("GL_OES_vertex_array_object")) + result.append("OES_vertex_array_object"); + result.append("WEBKIT_WEBGL_lose_context"); + if (WebGLCompressedTextures::supported(this)) + result.append("WEBKIT_WEBGL_compressed_textures"); + + if (allowPrivilegedExtensions()) { + if (m_context->getExtensions()->supports("GL_ANGLE_translated_shader_source")) + result.append("WEBGL_debug_shaders"); + result.append("WEBGL_debug_renderer_info"); + } + + return result; +} + +WebGLGetInfo WebGLRenderingContext::getTexParameter(GC3Denum target, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return WebGLGetInfo(); + WebGLTexture* tex = validateTextureBinding(target, false); + if (!tex) + return WebGLGetInfo(); + WebGLStateRestorer(this, false); + GC3Dint value = 0; + switch (pname) { + case GraphicsContext3D::TEXTURE_MAG_FILTER: + case GraphicsContext3D::TEXTURE_MIN_FILTER: + case GraphicsContext3D::TEXTURE_WRAP_S: + case GraphicsContext3D::TEXTURE_WRAP_T: + m_context->getTexParameteriv(target, pname, &value); + return WebGLGetInfo(static_cast<unsigned int>(value)); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } +} + +WebGLGetInfo WebGLRenderingContext::getUniform(WebGLProgram* program, const WebGLUniformLocation* uniformLocation, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return WebGLGetInfo(); + if (!uniformLocation || uniformLocation->program() != program) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return WebGLGetInfo(); + } + GC3Dint location = uniformLocation->location(); + + WebGLStateRestorer(this, false); + // FIXME: make this more efficient using WebGLUniformLocation and caching types in it + GC3Dint activeUniforms = 0; + m_context->getProgramiv(objectOrZero(program), GraphicsContext3D::ACTIVE_UNIFORMS, &activeUniforms); + for (GC3Dint i = 0; i < activeUniforms; i++) { + ActiveInfo info; + if (!m_context->getActiveUniform(objectOrZero(program), i, info)) + return WebGLGetInfo(); + // Strip "[0]" from the name if it's an array. + if (info.size > 1 && info.name.endsWith("[0]")) + info.name = info.name.left(info.name.length() - 3); + // If it's an array, we need to iterate through each element, appending "[index]" to the name. + for (GC3Dint index = 0; index < info.size; ++index) { + String name = info.name; + if (info.size > 1 && index >= 1) { + name.append('['); + name.append(String::number(index)); + name.append(']'); + } + // Now need to look this up by name again to find its location + GC3Dint loc = m_context->getUniformLocation(objectOrZero(program), name); + if (loc == location) { + // Found it. Use the type in the ActiveInfo to determine the return type. + GC3Denum baseType; + unsigned int length; + switch (info.type) { + case GraphicsContext3D::BOOL: + baseType = GraphicsContext3D::BOOL; + length = 1; + break; + case GraphicsContext3D::BOOL_VEC2: + baseType = GraphicsContext3D::BOOL; + length = 2; + break; + case GraphicsContext3D::BOOL_VEC3: + baseType = GraphicsContext3D::BOOL; + length = 3; + break; + case GraphicsContext3D::BOOL_VEC4: + baseType = GraphicsContext3D::BOOL; + length = 4; + break; + case GraphicsContext3D::INT: + baseType = GraphicsContext3D::INT; + length = 1; + break; + case GraphicsContext3D::INT_VEC2: + baseType = GraphicsContext3D::INT; + length = 2; + break; + case GraphicsContext3D::INT_VEC3: + baseType = GraphicsContext3D::INT; + length = 3; + break; + case GraphicsContext3D::INT_VEC4: + baseType = GraphicsContext3D::INT; + length = 4; + break; + case GraphicsContext3D::FLOAT: + baseType = GraphicsContext3D::FLOAT; + length = 1; + break; + case GraphicsContext3D::FLOAT_VEC2: + baseType = GraphicsContext3D::FLOAT; + length = 2; + break; + case GraphicsContext3D::FLOAT_VEC3: + baseType = GraphicsContext3D::FLOAT; + length = 3; + break; + case GraphicsContext3D::FLOAT_VEC4: + baseType = GraphicsContext3D::FLOAT; + length = 4; + break; + case GraphicsContext3D::FLOAT_MAT2: + baseType = GraphicsContext3D::FLOAT; + length = 4; + break; + case GraphicsContext3D::FLOAT_MAT3: + baseType = GraphicsContext3D::FLOAT; + length = 9; + break; + case GraphicsContext3D::FLOAT_MAT4: + baseType = GraphicsContext3D::FLOAT; + length = 16; + break; + case GraphicsContext3D::SAMPLER_2D: + case GraphicsContext3D::SAMPLER_CUBE: + baseType = GraphicsContext3D::INT; + length = 1; + break; + default: + // Can't handle this type + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return WebGLGetInfo(); + } + switch (baseType) { + case GraphicsContext3D::FLOAT: { + GC3Dfloat value[16] = {0}; + m_context->getUniformfv(objectOrZero(program), location, value); + if (length == 1) + return WebGLGetInfo(value[0]); + return WebGLGetInfo(Float32Array::create(value, length)); + } + case GraphicsContext3D::INT: { + GC3Dint value[4] = {0}; + m_context->getUniformiv(objectOrZero(program), location, value); + if (length == 1) + return WebGLGetInfo(value[0]); + return WebGLGetInfo(Int32Array::create(value, length)); + } + case GraphicsContext3D::BOOL: { + GC3Dint value[4] = {0}; + m_context->getUniformiv(objectOrZero(program), location, value); + if (length > 1) { + bool boolValue[16] = {0}; + for (unsigned j = 0; j < length; j++) + boolValue[j] = static_cast<bool>(value[j]); + return WebGLGetInfo(boolValue, length); + } + return WebGLGetInfo(static_cast<bool>(value[0])); + } + default: + notImplemented(); + } + } + } + } + // If we get here, something went wrong in our unfortunately complex logic above + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return WebGLGetInfo(); +} + +PassRefPtr<WebGLUniformLocation> WebGLRenderingContext::getUniformLocation(WebGLProgram* program, const String& name, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return 0; + if (!validateLocationLength(name)) + return 0; + if (!validateString(name)) + return 0; + WebGLStateRestorer(this, false); + GC3Dint uniformLocation = m_context->getUniformLocation(objectOrZero(program), name); + if (uniformLocation == -1) + return 0; + return WebGLUniformLocation::create(program, uniformLocation); +} + +WebGLGetInfo WebGLRenderingContext::getVertexAttrib(GC3Duint index, GC3Denum pname, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return WebGLGetInfo(); + WebGLStateRestorer(this, false); + if (index >= m_maxVertexAttribs) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return WebGLGetInfo(); + } + const WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index); + switch (pname) { + case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_BUFFER_BINDING: + if ((!isGLES2Compliant() && !index && m_boundVertexArrayObject->getVertexAttribState(0).bufferBinding == m_vertexAttrib0Buffer) + || !state.bufferBinding + || !state.bufferBinding->object()) + return WebGLGetInfo(); + return WebGLGetInfo(PassRefPtr<WebGLBuffer>(state.bufferBinding)); + case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_ENABLED: + return WebGLGetInfo(state.enabled); + case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_NORMALIZED: + return WebGLGetInfo(state.normalized); + case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_SIZE: + return WebGLGetInfo(state.size); + case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_STRIDE: + return WebGLGetInfo(state.originalStride); + case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_TYPE: + return WebGLGetInfo(state.type); + case GraphicsContext3D::CURRENT_VERTEX_ATTRIB: + return WebGLGetInfo(Float32Array::create(m_vertexAttribValue[index].value, 4)); + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return WebGLGetInfo(); + } +} + +GC3Dsizeiptr WebGLRenderingContext::getVertexAttribOffset(GC3Duint index, GC3Denum pname) +{ + if (isContextLost()) + return 0; + GC3Dsizeiptr result = m_context->getVertexAttribOffset(index, pname); + cleanupAfterGraphicsCall(false); + return result; +} + +void WebGLRenderingContext::hint(GC3Denum target, GC3Denum mode) +{ + if (isContextLost()) + return; + bool isValid = false; + switch (target) { + case GraphicsContext3D::GENERATE_MIPMAP_HINT: + isValid = true; + break; + case Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES: // OES_standard_derivatives + if (m_oesStandardDerivatives) + isValid = true; + break; + } + if (!isValid) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_context->hint(target, mode); + cleanupAfterGraphicsCall(false); +} + +GC3Dboolean WebGLRenderingContext::isBuffer(WebGLBuffer* buffer) +{ + if (!buffer || isContextLost()) + return 0; + + if (!buffer->hasEverBeenBound()) + return 0; + + return m_context->isBuffer(buffer->object()); +} + +bool WebGLRenderingContext::isContextLost() +{ + return m_contextLost; +} + +GC3Dboolean WebGLRenderingContext::isEnabled(GC3Denum cap) +{ + if (!validateCapability(cap) || isContextLost()) + return 0; + return m_context->isEnabled(cap); +} + +GC3Dboolean WebGLRenderingContext::isFramebuffer(WebGLFramebuffer* framebuffer) +{ + if (!framebuffer || isContextLost()) + return 0; + + if (!framebuffer->hasEverBeenBound()) + return 0; + + return m_context->isFramebuffer(framebuffer->object()); +} + +GC3Dboolean WebGLRenderingContext::isProgram(WebGLProgram* program) +{ + if (!program || isContextLost()) + return 0; + + return m_context->isProgram(program->object()); +} + +GC3Dboolean WebGLRenderingContext::isRenderbuffer(WebGLRenderbuffer* renderbuffer) +{ + if (!renderbuffer || isContextLost()) + return 0; + + if (!renderbuffer->hasEverBeenBound()) + return 0; + + return m_context->isRenderbuffer(renderbuffer->object()); +} + +GC3Dboolean WebGLRenderingContext::isShader(WebGLShader* shader) +{ + if (!shader || isContextLost()) + return 0; + + return m_context->isShader(shader->object()); +} + +GC3Dboolean WebGLRenderingContext::isTexture(WebGLTexture* texture) +{ + if (!texture || isContextLost()) + return 0; + + if (!texture->hasEverBeenBound()) + return 0; + + return m_context->isTexture(texture->object()); +} + +void WebGLRenderingContext::lineWidth(GC3Dfloat width) +{ + if (isContextLost()) + return; + m_context->lineWidth(width); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::linkProgram(WebGLProgram* program, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return; + if (!isGLES2Compliant()) { + if (!program->getAttachedShader(GraphicsContext3D::VERTEX_SHADER) || !program->getAttachedShader(GraphicsContext3D::FRAGMENT_SHADER)) { + program->setLinkStatus(false); + return; + } + } + + m_context->linkProgram(objectOrZero(program)); + program->increaseLinkCount(); + // cache link status + GC3Dint value = 0; + m_context->getProgramiv(objectOrZero(program), GraphicsContext3D::LINK_STATUS, &value); + program->setLinkStatus(static_cast<bool>(value)); + // Need to cache link status before caching active attribute locations. + program->cacheActiveAttribLocations(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::pixelStorei(GC3Denum pname, GC3Dint param) +{ + if (isContextLost()) + return; + switch (pname) { + case GraphicsContext3D::UNPACK_FLIP_Y_WEBGL: + m_unpackFlipY = param; + break; + case GraphicsContext3D::UNPACK_PREMULTIPLY_ALPHA_WEBGL: + m_unpackPremultiplyAlpha = param; + break; + case GraphicsContext3D::UNPACK_COLORSPACE_CONVERSION_WEBGL: + if (param == GraphicsContext3D::BROWSER_DEFAULT_WEBGL || param == GraphicsContext3D::NONE) + m_unpackColorspaceConversion = static_cast<GC3Denum>(param); + else { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + break; + case GraphicsContext3D::PACK_ALIGNMENT: + case GraphicsContext3D::UNPACK_ALIGNMENT: + if (param == 1 || param == 2 || param == 4 || param == 8) { + if (pname == GraphicsContext3D::PACK_ALIGNMENT) + m_packAlignment = param; + else // GraphicsContext3D::UNPACK_ALIGNMENT: + m_unpackAlignment = param; + m_context->pixelStorei(pname, param); + cleanupAfterGraphicsCall(false); + } else { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } +} + +void WebGLRenderingContext::polygonOffset(GC3Dfloat factor, GC3Dfloat units) +{ + if (isContextLost()) + return; + m_context->polygonOffset(factor, units); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode&) +{ + if (isContextLost()) + return; + // Due to WebGL's same-origin restrictions, it is not possible to + // taint the origin using the WebGL API. + ASSERT(canvas()->originClean()); + // Validate input parameters. + if (!pixels) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + switch (format) { + case GraphicsContext3D::ALPHA: + case GraphicsContext3D::RGB: + case GraphicsContext3D::RGBA: + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + switch (type) { + case GraphicsContext3D::UNSIGNED_BYTE: + case GraphicsContext3D::UNSIGNED_SHORT_5_6_5: + case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4: + case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1: + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (format != GraphicsContext3D::RGBA || type != GraphicsContext3D::UNSIGNED_BYTE) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + // Validate array type against pixel type. + if (!pixels->isUnsignedByteArray()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (m_framebufferBinding && !m_framebufferBinding->onAccess(!isResourceSafe())) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_FRAMEBUFFER_OPERATION); + return; + } + // Calculate array size, taking into consideration of PACK_ALIGNMENT. + unsigned int totalBytesRequired; + unsigned int padding; + GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_packAlignment, &totalBytesRequired, &padding); + if (error != GraphicsContext3D::NO_ERROR) { + m_context->synthesizeGLError(error); + return; + } + if (pixels->byteLength() < totalBytesRequired) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + clearIfComposited(); + void* data = pixels->baseAddress(); + + { + ScopedDrawingBufferBinder binder(m_drawingBuffer.get(), m_framebufferBinding.get()); + m_context->readPixels(x, y, width, height, format, type, data); + } + +#if OS(DARWIN) + // FIXME: remove this section when GL driver bug on Mac is fixed, i.e., + // when alpha is off, readPixels should set alpha to 255 instead of 0. + if (!m_context->getContextAttributes().alpha) { + unsigned char* pixels = reinterpret_cast<unsigned char*>(data); + for (GC3Dsizei iy = 0; iy < height; ++iy) { + for (GC3Dsizei ix = 0; ix < width; ++ix) { + pixels[3] = 255; + pixels += 4; + } + pixels += padding; + } + } +#endif + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::releaseShaderCompiler() +{ + if (isContextLost()) + return; + m_context->releaseShaderCompiler(); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::renderbufferStorage(GC3Denum target, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height) +{ + if (isContextLost()) + return; + if (target != GraphicsContext3D::RENDERBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (!m_renderbufferBinding || !m_renderbufferBinding->object()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (!validateSize(width, height)) + return; + switch (internalformat) { + case GraphicsContext3D::DEPTH_COMPONENT16: + case GraphicsContext3D::RGBA4: + case GraphicsContext3D::RGB5_A1: + case GraphicsContext3D::RGB565: + case GraphicsContext3D::STENCIL_INDEX8: + m_context->renderbufferStorage(target, internalformat, width, height); + m_renderbufferBinding->setInternalFormat(internalformat); + m_renderbufferBinding->setIsValid(true); + m_renderbufferBinding->setSize(width, height); + cleanupAfterGraphicsCall(false); + break; + case GraphicsContext3D::DEPTH_STENCIL: + if (isDepthStencilSupported()) { + m_context->renderbufferStorage(target, Extensions3D::DEPTH24_STENCIL8, width, height); + cleanupAfterGraphicsCall(false); + } + m_renderbufferBinding->setSize(width, height); + m_renderbufferBinding->setIsValid(isDepthStencilSupported()); + m_renderbufferBinding->setInternalFormat(internalformat); + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + } +} + +void WebGLRenderingContext::sampleCoverage(GC3Dfloat value, GC3Dboolean invert) +{ + if (isContextLost()) + return; + m_context->sampleCoverage(value, invert); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::scissor(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) +{ + if (isContextLost()) + return; + if (!validateSize(width, height)) + return; + m_context->scissor(x, y, width, height); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::shaderSource(WebGLShader* shader, const String& string, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(shader)) + return; + String stringWithoutComments = StripComments(string).result(); + if (!validateString(stringWithoutComments)) + return; + shader->setSource(string); + m_context->shaderSource(objectOrZero(shader), stringWithoutComments); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::stencilFunc(GC3Denum func, GC3Dint ref, GC3Duint mask) +{ + if (isContextLost()) + return; + if (!validateStencilFunc(func)) + return; + m_stencilFuncRef = ref; + m_stencilFuncRefBack = ref; + m_stencilFuncMask = mask; + m_stencilFuncMaskBack = mask; + m_context->stencilFunc(func, ref, mask); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::stencilFuncSeparate(GC3Denum face, GC3Denum func, GC3Dint ref, GC3Duint mask) +{ + if (isContextLost()) + return; + if (!validateStencilFunc(func)) + return; + switch (face) { + case GraphicsContext3D::FRONT_AND_BACK: + m_stencilFuncRef = ref; + m_stencilFuncRefBack = ref; + m_stencilFuncMask = mask; + m_stencilFuncMaskBack = mask; + break; + case GraphicsContext3D::FRONT: + m_stencilFuncRef = ref; + m_stencilFuncMask = mask; + break; + case GraphicsContext3D::BACK: + m_stencilFuncRefBack = ref; + m_stencilFuncMaskBack = mask; + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_context->stencilFuncSeparate(face, func, ref, mask); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::stencilMask(GC3Duint mask) +{ + if (isContextLost()) + return; + m_stencilMask = mask; + m_stencilMaskBack = mask; + m_context->stencilMask(mask); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::stencilMaskSeparate(GC3Denum face, GC3Duint mask) +{ + if (isContextLost()) + return; + switch (face) { + case GraphicsContext3D::FRONT_AND_BACK: + m_stencilMask = mask; + m_stencilMaskBack = mask; + break; + case GraphicsContext3D::FRONT: + m_stencilMask = mask; + break; + case GraphicsContext3D::BACK: + m_stencilMaskBack = mask; + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + m_context->stencilMaskSeparate(face, mask); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::stencilOp(GC3Denum fail, GC3Denum zfail, GC3Denum zpass) +{ + if (isContextLost()) + return; + m_context->stencilOp(fail, zfail, zpass); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::stencilOpSeparate(GC3Denum face, GC3Denum fail, GC3Denum zfail, GC3Denum zpass) +{ + if (isContextLost()) + return; + m_context->stencilOpSeparate(face, fail, zfail, zpass); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::texImage2DBase(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Dsizei width, GC3Dsizei height, GC3Dint border, + GC3Denum format, GC3Denum type, void* pixels, ExceptionCode& ec) +{ + // FIXME: For now we ignore any errors returned + ec = 0; + if (!validateTexFuncParameters(target, level, internalformat, width, height, border, format, type)) + return; + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; + if (!isGLES2NPOTStrict()) { + if (level && WebGLTexture::isNPOT(width, height)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + } + if (!pixels) { + bool succeed = m_context->texImage2DResourceSafe(target, level, internalformat, width, height, + border, format, type, m_unpackAlignment); + if (!succeed) + return; + } else { + m_context->texImage2D(target, level, internalformat, width, height, + border, format, type, pixels); + } + tex->setLevelInfo(target, level, internalformat, width, height, type); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::texImage2DImpl(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, Image* image, + bool flipY, bool premultiplyAlpha, ExceptionCode& ec) +{ + ec = 0; + Vector<uint8_t> data; + if (!m_context->extractImageData(image, format, type, flipY, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (m_unpackAlignment != 1) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1); + texImage2DBase(target, level, internalformat, image->width(), image->height(), 0, + format, type, data.data(), ec); + if (m_unpackAlignment != 1) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment); +} + +void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Dsizei width, GC3Dsizei height, GC3Dint border, + GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode& ec) +{ + if (isContextLost() || !validateTexFuncData(width, height, format, type, pixels)) + return; + void* data = pixels ? pixels->baseAddress() : 0; + Vector<uint8_t> tempData; + bool changeUnpackAlignment = false; + if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) { + if (!m_context->extractTextureData(width, height, format, type, + m_unpackAlignment, + m_unpackFlipY, m_unpackPremultiplyAlpha, + data, + tempData)) + return; + data = tempData.data(); + changeUnpackAlignment = true; + } + if (changeUnpackAlignment) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1); + texImage2DBase(target, level, internalformat, width, height, border, + format, type, data, ec); + if (changeUnpackAlignment) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment); +} + +void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, ImageData* pixels, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + Vector<uint8_t> data; + if (!m_context->extractImageData(pixels, format, type, m_unpackFlipY, m_unpackPremultiplyAlpha, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (m_unpackAlignment != 1) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1); + texImage2DBase(target, level, internalformat, pixels->width(), pixels->height(), 0, + format, type, data.data(), ec); + if (m_unpackAlignment != 1) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment); +} + +void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, HTMLImageElement* image, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + if (!validateHTMLImageElement(image)) + return; + if (wouldTaintOrigin(image)) { + ec = SECURITY_ERR; + return; + } + + texImage2DImpl(target, level, internalformat, format, type, image->cachedImage()->imageForRenderer(image->renderer()), + m_unpackFlipY, m_unpackPremultiplyAlpha, ec); +} + +void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, HTMLCanvasElement* canvas, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + if (!canvas || !canvas->buffer()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (wouldTaintOrigin(canvas)) { + ec = SECURITY_ERR; + return; + } + RefPtr<ImageData> imageData = canvas->getImageData(); + if (imageData) + texImage2D(target, level, internalformat, format, type, imageData.get(), ec); + else + texImage2DImpl(target, level, internalformat, format, type, canvas->copiedImage(), + m_unpackFlipY, m_unpackPremultiplyAlpha, ec); +} + +#if ENABLE(VIDEO) +PassRefPtr<Image> WebGLRenderingContext::videoFrameToImage(HTMLVideoElement* video, ExceptionCode& ec) +{ + if (!video || !video->videoWidth() || !video->videoHeight()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return 0; + } + IntSize size(video->videoWidth(), video->videoHeight()); + ImageBuffer* buf = m_videoCache.imageBuffer(size); + if (!buf) { + m_context->synthesizeGLError(GraphicsContext3D::OUT_OF_MEMORY); + return 0; + } + if (wouldTaintOrigin(video)) { + ec = SECURITY_ERR; + return 0; + } + IntRect destRect(0, 0, size.width(), size.height()); + // FIXME: Turn this into a GPU-GPU texture copy instead of CPU readback. + video->paintCurrentFrameInContext(buf->context(), destRect); + return buf->copyImage(CopyBackingStore); +} + +void WebGLRenderingContext::texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, HTMLVideoElement* video, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + RefPtr<Image> image = videoFrameToImage(video, ec); + if (!image) + return; + texImage2DImpl(target, level, internalformat, format, type, image.get(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); +} +#endif + +void WebGLRenderingContext::texParameter(GC3Denum target, GC3Denum pname, GC3Dfloat paramf, GC3Dint parami, bool isFloat) +{ + if (isContextLost()) + return; + WebGLTexture* tex = validateTextureBinding(target, false); + if (!tex) + return; + switch (pname) { + case GraphicsContext3D::TEXTURE_MIN_FILTER: + case GraphicsContext3D::TEXTURE_MAG_FILTER: + break; + case GraphicsContext3D::TEXTURE_WRAP_S: + case GraphicsContext3D::TEXTURE_WRAP_T: + if ((isFloat && paramf != GraphicsContext3D::CLAMP_TO_EDGE && paramf != GraphicsContext3D::MIRRORED_REPEAT && paramf != GraphicsContext3D::REPEAT) + || (!isFloat && parami != GraphicsContext3D::CLAMP_TO_EDGE && parami != GraphicsContext3D::MIRRORED_REPEAT && parami != GraphicsContext3D::REPEAT)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (isFloat) { + tex->setParameterf(pname, paramf); + m_context->texParameterf(target, pname, paramf); + } else { + tex->setParameteri(pname, parami); + m_context->texParameteri(target, pname, parami); + } + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::texParameterf(GC3Denum target, GC3Denum pname, GC3Dfloat param) +{ + texParameter(target, pname, param, 0, true); +} + +void WebGLRenderingContext::texParameteri(GC3Denum target, GC3Denum pname, GC3Dint param) +{ + texParameter(target, pname, 0, param, false); +} + +void WebGLRenderingContext::texSubImage2DBase(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, + GC3Denum format, GC3Denum type, void* pixels, ExceptionCode& ec) +{ + // FIXME: For now we ignore any errors returned + ec = 0; + if (isContextLost()) + return; + if (!validateTexFuncParameters(target, level, format, width, height, 0, format, type)) + return; + if (!validateSize(xoffset, yoffset)) + return; + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; + if (xoffset + width > tex->getWidth(target, level) || yoffset + height > tex->getHeight(target, level)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (tex->getInternalFormat(target, level) != format || tex->getType(target, level) != type) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + m_context->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::texSubImage2DImpl(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, + Image* image, bool flipY, bool premultiplyAlpha, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + Vector<uint8_t> data; + if (!m_context->extractImageData(image, format, type, flipY, premultiplyAlpha, m_unpackColorspaceConversion == GraphicsContext3D::NONE, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + texSubImage2DBase(target, level, xoffset, yoffset, image->width(), image->height(), + format, type, data.data(), ec); +} + +void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, + GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode& ec) +{ + if (isContextLost() || !validateTexFuncData(width, height, format, type, pixels)) + return; + void* data = pixels ? pixels->baseAddress() : 0; + Vector<uint8_t> tempData; + bool changeUnpackAlignment = false; + if (data && (m_unpackFlipY || m_unpackPremultiplyAlpha)) { + if (!m_context->extractTextureData(width, height, format, type, + m_unpackAlignment, + m_unpackFlipY, m_unpackPremultiplyAlpha, + data, + tempData)) + return; + data = tempData.data(); + changeUnpackAlignment = true; + } + if (changeUnpackAlignment) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, 1); + texSubImage2DBase(target, level, xoffset, yoffset, width, height, format, type, data, ec); + if (changeUnpackAlignment) + m_context->pixelStorei(GraphicsContext3D::UNPACK_ALIGNMENT, m_unpackAlignment); +} + +void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, ImageData* pixels, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + Vector<uint8_t> data; + if (!m_context->extractImageData(pixels, format, type, m_unpackFlipY, m_unpackPremultiplyAlpha, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + texSubImage2DBase(target, level, xoffset, yoffset, pixels->width(), pixels->height(), + format, type, data.data(), ec); +} + +void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, HTMLImageElement* image, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + if (!validateHTMLImageElement(image)) + return; + if (wouldTaintOrigin(image)) { + ec = SECURITY_ERR; + return; + } + texSubImage2DImpl(target, level, xoffset, yoffset, format, type, image->cachedImage()->imageForRenderer(image->renderer()), + m_unpackFlipY, m_unpackPremultiplyAlpha, ec); +} + +void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, HTMLCanvasElement* canvas, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + if (!canvas || !canvas->buffer()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (wouldTaintOrigin(canvas)) { + ec = SECURITY_ERR; + return; + } + RefPtr<ImageData> imageData = canvas->getImageData(); + if (imageData) + texSubImage2D(target, level, xoffset, yoffset, format, type, imageData.get(), ec); + else + texSubImage2DImpl(target, level, xoffset, yoffset, format, type, canvas->copiedImage(), + m_unpackFlipY, m_unpackPremultiplyAlpha, ec); +} + +#if ENABLE(VIDEO) +void WebGLRenderingContext::texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, HTMLVideoElement* video, ExceptionCode& ec) +{ + ec = 0; + if (isContextLost()) + return; + RefPtr<Image> image = videoFrameToImage(video, ec); + if (!image) + return; + texSubImage2DImpl(target, level, xoffset, yoffset, format, type, image.get(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); +} +#endif + +void WebGLRenderingContext::uniform1f(const WebGLUniformLocation* location, GC3Dfloat x, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform1f(location->location(), x); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform1fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 1)) + return; + + m_context->uniform1fv(location->location(), v->data(), v->length()); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform1fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 1)) + return; + + m_context->uniform1fv(location->location(), v, size); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform1i(const WebGLUniformLocation* location, GC3Dint x, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform1i(location->location(), x); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform1iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 1)) + return; + + m_context->uniform1iv(location->location(), v->data(), v->length()); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform1iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 1)) + return; + + m_context->uniform1iv(location->location(), v, size); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform2f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform2f(location->location(), x, y); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform2fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 2)) + return; + + m_context->uniform2fv(location->location(), v->data(), v->length() / 2); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform2fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 2)) + return; + + m_context->uniform2fv(location->location(), v, size / 2); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform2i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform2i(location->location(), x, y); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform2iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 2)) + return; + + m_context->uniform2iv(location->location(), v->data(), v->length() / 2); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform2iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 2)) + return; + + m_context->uniform2iv(location->location(), v, size / 2); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform3f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform3f(location->location(), x, y, z); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform3fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 3)) + return; + + m_context->uniform3fv(location->location(), v->data(), v->length() / 3); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform3fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 3)) + return; + + m_context->uniform3fv(location->location(), v, size / 3); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform3i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, GC3Dint z, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform3i(location->location(), x, y, z); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform3iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 3)) + return; + + m_context->uniform3iv(location->location(), v->data(), v->length() / 3); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform3iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 3)) + return; + + m_context->uniform3iv(location->location(), v, size / 3); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform4f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z, GC3Dfloat w, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform4f(location->location(), x, y, z, w); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform4fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 4)) + return; + + m_context->uniform4fv(location->location(), v->data(), v->length() / 4); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform4fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 4)) + return; + + m_context->uniform4fv(location->location(), v, size / 4); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform4i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, GC3Dint z, GC3Dint w, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !location) + return; + + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_context->uniform4i(location->location(), x, y, z, w); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform4iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, 4)) + return; + + m_context->uniform4iv(location->location(), v->data(), v->length() / 4); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniform4iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformParameters(location, v, size, 4)) + return; + + m_context->uniform4iv(location->location(), v, size / 4); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniformMatrix2fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformMatrixParameters(location, transpose, v, 4)) + return; + m_context->uniformMatrix2fv(location->location(), transpose, v->data(), v->length() / 4); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniformMatrix2fv(const WebGLUniformLocation* location, GC3Dboolean transpose, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformMatrixParameters(location, transpose, v, size, 4)) + return; + m_context->uniformMatrix2fv(location->location(), transpose, v, size / 4); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniformMatrix3fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformMatrixParameters(location, transpose, v, 9)) + return; + m_context->uniformMatrix3fv(location->location(), transpose, v->data(), v->length() / 9); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniformMatrix3fv(const WebGLUniformLocation* location, GC3Dboolean transpose, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformMatrixParameters(location, transpose, v, size, 9)) + return; + m_context->uniformMatrix3fv(location->location(), transpose, v, size / 9); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniformMatrix4fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* v, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformMatrixParameters(location, transpose, v, 16)) + return; + m_context->uniformMatrix4fv(location->location(), transpose, v->data(), v->length() / 16); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::uniformMatrix4fv(const WebGLUniformLocation* location, GC3Dboolean transpose, GC3Dfloat* v, GC3Dsizei size, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateUniformMatrixParameters(location, transpose, v, size, 16)) + return; + m_context->uniformMatrix4fv(location->location(), transpose, v, size / 16); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::useProgram(WebGLProgram* program, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + bool deleted; + if (!checkObjectToBeBound(program, deleted)) + return; + if (deleted) + program = 0; + if (program && !program->getLinkStatus()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + cleanupAfterGraphicsCall(false); + return; + } + if (m_currentProgram != program) { + if (m_currentProgram) + m_currentProgram->onDetached(); + m_currentProgram = program; + m_context->useProgram(objectOrZero(program)); + if (program) + program->onAttached(); + } + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::validateProgram(WebGLProgram* program, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost() || !validateWebGLObject(program)) + return; + m_context->validateProgram(objectOrZero(program)); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::vertexAttrib1f(GC3Duint index, GC3Dfloat v0) +{ + vertexAttribfImpl(index, 1, v0, 0.0f, 0.0f, 1.0f); +} + +void WebGLRenderingContext::vertexAttrib1fv(GC3Duint index, Float32Array* v) +{ + vertexAttribfvImpl(index, v, 1); +} + +void WebGLRenderingContext::vertexAttrib1fv(GC3Duint index, GC3Dfloat* v, GC3Dsizei size) +{ + vertexAttribfvImpl(index, v, size, 1); +} + +void WebGLRenderingContext::vertexAttrib2f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1) +{ + vertexAttribfImpl(index, 2, v0, v1, 0.0f, 1.0f); +} + +void WebGLRenderingContext::vertexAttrib2fv(GC3Duint index, Float32Array* v) +{ + vertexAttribfvImpl(index, v, 2); +} + +void WebGLRenderingContext::vertexAttrib2fv(GC3Duint index, GC3Dfloat* v, GC3Dsizei size) +{ + vertexAttribfvImpl(index, v, size, 2); +} + +void WebGLRenderingContext::vertexAttrib3f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2) +{ + vertexAttribfImpl(index, 3, v0, v1, v2, 1.0f); +} + +void WebGLRenderingContext::vertexAttrib3fv(GC3Duint index, Float32Array* v) +{ + vertexAttribfvImpl(index, v, 3); +} + +void WebGLRenderingContext::vertexAttrib3fv(GC3Duint index, GC3Dfloat* v, GC3Dsizei size) +{ + vertexAttribfvImpl(index, v, size, 3); +} + +void WebGLRenderingContext::vertexAttrib4f(GC3Duint index, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3) +{ + vertexAttribfImpl(index, 4, v0, v1, v2, v3); +} + +void WebGLRenderingContext::vertexAttrib4fv(GC3Duint index, Float32Array* v) +{ + vertexAttribfvImpl(index, v, 4); +} + +void WebGLRenderingContext::vertexAttrib4fv(GC3Duint index, GC3Dfloat* v, GC3Dsizei size) +{ + vertexAttribfvImpl(index, v, size, 4); +} + +void WebGLRenderingContext::vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, GC3Dsizei stride, GC3Dintptr offset, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + if (isContextLost()) + return; + switch (type) { + case GraphicsContext3D::BYTE: + case GraphicsContext3D::UNSIGNED_BYTE: + case GraphicsContext3D::SHORT: + case GraphicsContext3D::UNSIGNED_SHORT: + case GraphicsContext3D::FLOAT: + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if (index >= m_maxVertexAttribs) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (size < 1 || size > 4 || stride < 0 || stride > 255 || offset < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (!m_boundArrayBuffer) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + // Determine the number of elements the bound buffer can hold, given the offset, size, type and stride + unsigned int typeSize = sizeInBytes(type); + if (!typeSize) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return; + } + if ((stride % typeSize) || (offset % typeSize)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + GC3Dsizei bytesPerElement = size * typeSize; + + GC3Dsizei validatedStride = stride ? stride : bytesPerElement; + + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index); + state.bufferBinding = m_boundArrayBuffer; + state.bytesPerElement = bytesPerElement; + state.size = size; + state.type = type; + state.normalized = normalized; + state.stride = validatedStride; + state.originalStride = stride; + state.offset = offset; + m_context->vertexAttribPointer(index, size, type, normalized, stride, offset); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height) +{ + if (isContextLost()) + return; + if (!validateSize(width, height)) + return; + m_context->viewport(x, y, width, height); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::forceLostContext(WebGLRenderingContext::LostContextMode mode) +{ + if (isContextLost()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + m_contextLost = true; + m_contextLostMode = mode; + + detachAndRemoveAllObjects(); + + // There is no direct way to clear errors from a GL implementation and + // looping until getError() becomes NO_ERROR might cause an infinite loop if + // the driver or context implementation had a bug. So, loop a reasonably + // large number of times to clear any existing errors. + for (int i = 0; i < 100; ++i) { + if (m_context->getError() == GraphicsContext3D::NO_ERROR) + break; + } + m_context->synthesizeGLError(GraphicsContext3D::CONTEXT_LOST_WEBGL); + + // Don't allow restoration unless the context lost event has both been + // dispatched and its default behavior prevented. + m_restoreAllowed = false; + + // Always defer the dispatch of the context lost event, to implement + // the spec behavior of queueing a task. + m_dispatchContextLostEventTimer.startOneShot(0); +} + +void WebGLRenderingContext::forceRestoreContext() +{ + if (!isContextLost()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + if (!m_restoreAllowed) { + if (m_contextLostMode == SyntheticLostContext) + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + if (!m_restoreTimer.isActive()) + m_restoreTimer.startOneShot(0); +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* WebGLRenderingContext::platformLayer() const +{ +#if PLATFORM(CHROMIUM) + if (m_drawingBuffer) + return m_drawingBuffer->platformLayer(); +#endif + + return m_context->platformLayer(); +} +#endif + +void WebGLRenderingContext::removeObject(WebGLObject* object) +{ + m_canvasObjects.remove(object); +} + +void WebGLRenderingContext::addObject(WebGLObject* object) +{ + ASSERT(!isContextLost()); + removeObject(object); + m_canvasObjects.add(object); +} + +void WebGLRenderingContext::detachAndRemoveAllObjects() +{ + while (m_canvasObjects.size() > 0) { + HashSet<WebGLObject*>::iterator it = m_canvasObjects.begin(); + (*it)->detachContext(); + } +} + +WebGLGetInfo WebGLRenderingContext::getBooleanParameter(GC3Denum pname) +{ + GC3Dboolean value = 0; + m_context->getBooleanv(pname, &value); + return WebGLGetInfo(static_cast<bool>(value)); +} + +WebGLGetInfo WebGLRenderingContext::getBooleanArrayParameter(GC3Denum pname) +{ + if (pname != GraphicsContext3D::COLOR_WRITEMASK) { + notImplemented(); + return WebGLGetInfo(0, 0); + } + GC3Dboolean value[4] = {0}; + m_context->getBooleanv(pname, value); + bool boolValue[4]; + for (int ii = 0; ii < 4; ++ii) + boolValue[ii] = static_cast<bool>(value[ii]); + return WebGLGetInfo(boolValue, 4); +} + +WebGLGetInfo WebGLRenderingContext::getFloatParameter(GC3Denum pname) +{ + GC3Dfloat value = 0; + m_context->getFloatv(pname, &value); + return WebGLGetInfo(value); +} + +WebGLGetInfo WebGLRenderingContext::getIntParameter(GC3Denum pname) +{ + GC3Dint value = 0; + m_context->getIntegerv(pname, &value); + return WebGLGetInfo(value); +} + +WebGLGetInfo WebGLRenderingContext::getUnsignedIntParameter(GC3Denum pname) +{ + GC3Dint value = 0; + m_context->getIntegerv(pname, &value); + return WebGLGetInfo(static_cast<unsigned int>(value)); +} + +WebGLGetInfo WebGLRenderingContext::getWebGLFloatArrayParameter(GC3Denum pname) +{ + GC3Dfloat value[4] = {0}; + m_context->getFloatv(pname, value); + unsigned length = 0; + switch (pname) { + case GraphicsContext3D::ALIASED_POINT_SIZE_RANGE: + case GraphicsContext3D::ALIASED_LINE_WIDTH_RANGE: + case GraphicsContext3D::DEPTH_RANGE: + length = 2; + break; + case GraphicsContext3D::BLEND_COLOR: + case GraphicsContext3D::COLOR_CLEAR_VALUE: + length = 4; + break; + default: + notImplemented(); + } + return WebGLGetInfo(Float32Array::create(value, length)); +} + +WebGLGetInfo WebGLRenderingContext::getWebGLIntArrayParameter(GC3Denum pname) +{ + GC3Dint value[4] = {0}; + m_context->getIntegerv(pname, value); + unsigned length = 0; + switch (pname) { + case GraphicsContext3D::MAX_VIEWPORT_DIMS: + length = 2; + break; + case GraphicsContext3D::SCISSOR_BOX: + case GraphicsContext3D::VIEWPORT: + length = 4; + break; + default: + notImplemented(); + } + return WebGLGetInfo(Int32Array::create(value, length)); +} + +void WebGLRenderingContext::handleNPOTTextures(bool prepareToDraw) +{ + bool resetActiveUnit = false; + for (unsigned ii = 0; ii < m_textureUnits.size(); ++ii) { + if ((m_textureUnits[ii].m_texture2DBinding && m_textureUnits[ii].m_texture2DBinding->needToUseBlackTexture()) + || (m_textureUnits[ii].m_textureCubeMapBinding && m_textureUnits[ii].m_textureCubeMapBinding->needToUseBlackTexture())) { + if (ii != m_activeTextureUnit) { + m_context->activeTexture(ii); + resetActiveUnit = true; + } else if (resetActiveUnit) { + m_context->activeTexture(ii); + resetActiveUnit = false; + } + WebGLTexture* tex2D; + WebGLTexture* texCubeMap; + if (prepareToDraw) { + tex2D = m_blackTexture2D.get(); + texCubeMap = m_blackTextureCubeMap.get(); + } else { + tex2D = m_textureUnits[ii].m_texture2DBinding.get(); + texCubeMap = m_textureUnits[ii].m_textureCubeMapBinding.get(); + } + if (m_textureUnits[ii].m_texture2DBinding && m_textureUnits[ii].m_texture2DBinding->needToUseBlackTexture()) + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, objectOrZero(tex2D)); + if (m_textureUnits[ii].m_textureCubeMapBinding && m_textureUnits[ii].m_textureCubeMapBinding->needToUseBlackTexture()) + m_context->bindTexture(GraphicsContext3D::TEXTURE_CUBE_MAP, objectOrZero(texCubeMap)); + } + } + if (resetActiveUnit) + m_context->activeTexture(m_activeTextureUnit); +} + +void WebGLRenderingContext::createFallbackBlackTextures1x1() +{ + unsigned char black[] = {0, 0, 0, 255}; + m_blackTexture2D = createTexture(); + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_blackTexture2D->object()); + m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); + m_blackTextureCubeMap = createTexture(); + m_context->bindTexture(GraphicsContext3D::TEXTURE_CUBE_MAP, m_blackTextureCubeMap->object()); + m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->texImage2D(GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GraphicsContext3D::RGBA, 1, 1, + 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, black); + m_context->bindTexture(GraphicsContext3D::TEXTURE_CUBE_MAP, 0); +} + +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; +} + +GC3Denum WebGLRenderingContext::getBoundFramebufferColorFormat() +{ + if (m_framebufferBinding && m_framebufferBinding->object()) + return m_framebufferBinding->getColorBufferFormat(); + if (m_attributes.alpha) + return GraphicsContext3D::RGBA; + return GraphicsContext3D::RGB; +} + +int WebGLRenderingContext::getBoundFramebufferWidth() +{ + if (m_framebufferBinding && m_framebufferBinding->object()) + return m_framebufferBinding->getColorBufferWidth(); + return m_drawingBuffer ? m_drawingBuffer->size().width() : m_context->getInternalFramebufferSize().width(); +} + +int WebGLRenderingContext::getBoundFramebufferHeight() +{ + if (m_framebufferBinding && m_framebufferBinding->object()) + return m_framebufferBinding->getColorBufferHeight(); + return m_drawingBuffer ? m_drawingBuffer->size().height() : m_context->getInternalFramebufferSize().height(); +} + +WebGLTexture* WebGLRenderingContext::validateTextureBinding(GC3Denum target, bool useSixEnumsForCubeMap) +{ + WebGLTexture* tex = 0; + switch (target) { + case GraphicsContext3D::TEXTURE_2D: + tex = m_textureUnits[m_activeTextureUnit].m_texture2DBinding.get(); + break; + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: + if (!useSixEnumsForCubeMap) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding.get(); + break; + case GraphicsContext3D::TEXTURE_CUBE_MAP: + if (useSixEnumsForCubeMap) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding.get(); + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + if (!tex) + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return tex; +} + +bool WebGLRenderingContext::validateLocationLength(const String& string) +{ + const unsigned maxWebGLLocationLength = 256; + if (string.length() > maxWebGLLocationLength) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return true; +} + +bool WebGLRenderingContext::validateSize(GC3Dint x, GC3Dint y) +{ + if (x < 0 || y < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return true; +} + +bool WebGLRenderingContext::validateString(const String& string) +{ + for (size_t i = 0; i < string.length(); ++i) { + if (!validateCharacter(string[i])) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + } + return true; +} + +bool WebGLRenderingContext::validateTexFuncFormatAndType(GC3Denum format, GC3Denum type) +{ + switch (format) { + case GraphicsContext3D::ALPHA: + case GraphicsContext3D::LUMINANCE: + case GraphicsContext3D::LUMINANCE_ALPHA: + case GraphicsContext3D::RGB: + case GraphicsContext3D::RGBA: + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } + + switch (type) { + case GraphicsContext3D::UNSIGNED_BYTE: + case GraphicsContext3D::UNSIGNED_SHORT_5_6_5: + case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4: + case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1: + break; + case GraphicsContext3D::FLOAT: + if (m_oesTextureFloat) + break; + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } + + // Verify that the combination of format and type is supported. + switch (format) { + case GraphicsContext3D::ALPHA: + case GraphicsContext3D::LUMINANCE: + case GraphicsContext3D::LUMINANCE_ALPHA: + if (type != GraphicsContext3D::UNSIGNED_BYTE + && type != GraphicsContext3D::FLOAT) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + break; + case GraphicsContext3D::RGB: + if (type != GraphicsContext3D::UNSIGNED_BYTE + && type != GraphicsContext3D::UNSIGNED_SHORT_5_6_5 + && type != GraphicsContext3D::FLOAT) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + break; + case GraphicsContext3D::RGBA: + if (type != GraphicsContext3D::UNSIGNED_BYTE + && type != GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4 + && type != GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1 + && type != GraphicsContext3D::FLOAT) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + break; + default: + ASSERT_NOT_REACHED(); + } + + return true; +} + +bool WebGLRenderingContext::validateTexFuncLevel(GC3Denum target, GC3Dint level) +{ + if (level < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + switch (target) { + case GraphicsContext3D::TEXTURE_2D: + if (level > m_maxTextureLevel) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + break; + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: + if (level > m_maxCubeMapTextureLevel) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + break; + } + // This function only checks if level is legal, so we return true and don't + // generate INVALID_ENUM if target is illegal. + return true; +} + +bool WebGLRenderingContext::validateTexFuncParameters(GC3Denum target, GC3Dint level, + GC3Denum internalformat, + GC3Dsizei width, GC3Dsizei height, GC3Dint border, + GC3Denum format, GC3Denum type) +{ + // 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(format, type) || !validateTexFuncLevel(target, level)) + return false; + + if (width < 0 || height < 0) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + + switch (target) { + case GraphicsContext3D::TEXTURE_2D: + if (width > m_maxTextureSize || height > m_maxTextureSize) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + break; + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: + if (width != height || width > m_maxCubeMapTextureSize) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } + + if (format != internalformat) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + + if (border) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + + return true; +} + +bool WebGLRenderingContext::validateTexFuncData(GC3Dsizei width, GC3Dsizei height, + GC3Denum format, GC3Denum type, + ArrayBufferView* pixels) +{ + if (!pixels) + return true; + + if (!validateTexFuncFormatAndType(format, type)) + return false; + + switch (type) { + case GraphicsContext3D::UNSIGNED_BYTE: + if (!pixels->isUnsignedByteArray()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + break; + case GraphicsContext3D::UNSIGNED_SHORT_5_6_5: + case GraphicsContext3D::UNSIGNED_SHORT_4_4_4_4: + case GraphicsContext3D::UNSIGNED_SHORT_5_5_5_1: + if (!pixels->isUnsignedShortArray()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + break; + case GraphicsContext3D::FLOAT: // OES_texture_float + if (!pixels->isFloatArray()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + break; + default: + ASSERT_NOT_REACHED(); + } + + unsigned int totalBytesRequired; + GC3Denum error = m_context->computeImageSizeInBytes(format, type, width, height, m_unpackAlignment, &totalBytesRequired, 0); + if (error != GraphicsContext3D::NO_ERROR) { + m_context->synthesizeGLError(error); + return false; + } + if (pixels->byteLength() < totalBytesRequired) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + return true; +} + +bool WebGLRenderingContext::validateDrawMode(GC3Denum mode) +{ + switch (mode) { + case GraphicsContext3D::POINTS: + case GraphicsContext3D::LINE_STRIP: + case GraphicsContext3D::LINE_LOOP: + case GraphicsContext3D::LINES: + case GraphicsContext3D::TRIANGLE_STRIP: + case GraphicsContext3D::TRIANGLE_FAN: + case GraphicsContext3D::TRIANGLES: + return true; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } +} + +bool WebGLRenderingContext::validateStencilSettings() +{ + if (m_stencilMask != m_stencilMaskBack || m_stencilFuncRef != m_stencilFuncRefBack || m_stencilFuncMask != m_stencilFuncMaskBack) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + return true; +} + +bool WebGLRenderingContext::validateStencilFunc(GC3Denum func) +{ + switch (func) { + case GraphicsContext3D::NEVER: + case GraphicsContext3D::LESS: + case GraphicsContext3D::LEQUAL: + case GraphicsContext3D::GREATER: + case GraphicsContext3D::GEQUAL: + case GraphicsContext3D::EQUAL: + case GraphicsContext3D::NOTEQUAL: + case GraphicsContext3D::ALWAYS: + return true; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } +} + +void WebGLRenderingContext::printWarningToConsole(const String& message) +{ + canvas()->document()->frame()->domWindow()->console()->addMessage(HTMLMessageSource, LogMessageType, WarningMessageLevel, + message, 0, canvas()->document()->url().string()); +} + +bool WebGLRenderingContext::validateFramebufferFuncParameters(GC3Denum target, GC3Denum attachment) +{ + if (target != GraphicsContext3D::FRAMEBUFFER) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } + switch (attachment) { + case GraphicsContext3D::COLOR_ATTACHMENT0: + case GraphicsContext3D::DEPTH_ATTACHMENT: + case GraphicsContext3D::STENCIL_ATTACHMENT: + case GraphicsContext3D::DEPTH_STENCIL_ATTACHMENT: + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } + return true; +} + +bool WebGLRenderingContext::validateBlendEquation(GC3Denum mode) +{ + switch (mode) { + case GraphicsContext3D::FUNC_ADD: + case GraphicsContext3D::FUNC_SUBTRACT: + case GraphicsContext3D::FUNC_REVERSE_SUBTRACT: + return true; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } +} + +bool WebGLRenderingContext::validateBlendFuncFactors(GC3Denum src, GC3Denum dst) +{ + if (((src == GraphicsContext3D::CONSTANT_COLOR || src == GraphicsContext3D::ONE_MINUS_CONSTANT_COLOR) + && (dst == GraphicsContext3D::CONSTANT_ALPHA || dst == GraphicsContext3D::ONE_MINUS_CONSTANT_ALPHA)) + || ((dst == GraphicsContext3D::CONSTANT_COLOR || dst == GraphicsContext3D::ONE_MINUS_CONSTANT_COLOR) + && (src == GraphicsContext3D::CONSTANT_ALPHA || src == GraphicsContext3D::ONE_MINUS_CONSTANT_ALPHA))) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + return true; +} + +bool WebGLRenderingContext::validateCapability(GC3Denum cap) +{ + switch (cap) { + case GraphicsContext3D::BLEND: + case GraphicsContext3D::CULL_FACE: + case GraphicsContext3D::DEPTH_TEST: + case GraphicsContext3D::DITHER: + case GraphicsContext3D::POLYGON_OFFSET_FILL: + case GraphicsContext3D::SAMPLE_ALPHA_TO_COVERAGE: + case GraphicsContext3D::SAMPLE_COVERAGE: + case GraphicsContext3D::SCISSOR_TEST: + case GraphicsContext3D::STENCIL_TEST: + return true; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return false; + } +} + +bool WebGLRenderingContext::validateUniformParameters(const WebGLUniformLocation* location, Float32Array* v, GC3Dsizei requiredMinSize) +{ + if (!v) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return validateUniformMatrixParameters(location, false, v->data(), v->length(), requiredMinSize); +} + +bool WebGLRenderingContext::validateUniformParameters(const WebGLUniformLocation* location, Int32Array* v, GC3Dsizei requiredMinSize) +{ + if (!v) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return validateUniformMatrixParameters(location, false, v->data(), v->length(), requiredMinSize); +} + +bool WebGLRenderingContext::validateUniformParameters(const WebGLUniformLocation* location, void* v, GC3Dsizei size, GC3Dsizei requiredMinSize) +{ + return validateUniformMatrixParameters(location, false, v, size, requiredMinSize); +} + +bool WebGLRenderingContext::validateUniformMatrixParameters(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* v, GC3Dsizei requiredMinSize) +{ + if (!v) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return validateUniformMatrixParameters(location, transpose, v->data(), v->length(), requiredMinSize); +} + +bool WebGLRenderingContext::validateUniformMatrixParameters(const WebGLUniformLocation* location, GC3Dboolean transpose, void* v, GC3Dsizei size, GC3Dsizei requiredMinSize) +{ + if (!location) + return false; + if (location->program() != m_currentProgram) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return false; + } + if (!v) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + if (transpose) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + if (size < requiredMinSize || (size % requiredMinSize)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return true; +} + +WebGLBuffer* WebGLRenderingContext::validateBufferDataParameters(GC3Denum target, GC3Denum usage) +{ + WebGLBuffer* buffer = 0; + switch (target) { + case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: + buffer = m_boundVertexArrayObject->getElementArrayBuffer().get(); + break; + case GraphicsContext3D::ARRAY_BUFFER: + buffer = m_boundArrayBuffer.get(); + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + if (!buffer) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return 0; + } + switch (usage) { + case GraphicsContext3D::STREAM_DRAW: + case GraphicsContext3D::STATIC_DRAW: + case GraphicsContext3D::DYNAMIC_DRAW: + return buffer; + } + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; +} + +bool WebGLRenderingContext::validateHTMLImageElement(HTMLImageElement* image) +{ + if (!image || !image->cachedImage()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + const KURL& url = image->cachedImage()->response().url(); + if (url.isNull() || url.isEmpty() || !url.isValid()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return false; + } + return true; +} + +void WebGLRenderingContext::vertexAttribfImpl(GC3Duint index, GC3Dsizei expectedSize, GC3Dfloat v0, GC3Dfloat v1, GC3Dfloat v2, GC3Dfloat v3) +{ + if (isContextLost()) + return; + if (index >= m_maxVertexAttribs) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + // In GL, we skip setting vertexAttrib0 values. + if (index || isGLES2Compliant()) { + switch (expectedSize) { + case 1: + m_context->vertexAttrib1f(index, v0); + break; + case 2: + m_context->vertexAttrib2f(index, v0, v1); + break; + case 3: + m_context->vertexAttrib3f(index, v0, v1, v2); + break; + case 4: + m_context->vertexAttrib4f(index, v0, v1, v2, v3); + break; + } + cleanupAfterGraphicsCall(false); + } + VertexAttribValue& attribValue = m_vertexAttribValue[index]; + attribValue.value[0] = v0; + attribValue.value[1] = v1; + attribValue.value[2] = v2; + attribValue.value[3] = v3; +} + +void WebGLRenderingContext::vertexAttribfvImpl(GC3Duint index, Float32Array* v, GC3Dsizei expectedSize) +{ + if (isContextLost()) + return; + if (!v) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + vertexAttribfvImpl(index, v->data(), v->length(), expectedSize); +} + +void WebGLRenderingContext::vertexAttribfvImpl(GC3Duint index, GC3Dfloat* v, GC3Dsizei size, GC3Dsizei expectedSize) +{ + if (isContextLost()) + return; + if (!v) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (size < expectedSize) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + if (index >= m_maxVertexAttribs) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + // In GL, we skip setting vertexAttrib0 values. + if (index || isGLES2Compliant()) { + switch (expectedSize) { + case 1: + m_context->vertexAttrib1fv(index, v); + break; + case 2: + m_context->vertexAttrib2fv(index, v); + break; + case 3: + m_context->vertexAttrib3fv(index, v); + break; + case 4: + m_context->vertexAttrib4fv(index, v); + break; + } + cleanupAfterGraphicsCall(false); + } + VertexAttribValue& attribValue = m_vertexAttribValue[index]; + attribValue.initValue(); + for (int ii = 0; ii < expectedSize; ++ii) + attribValue.value[ii] = v[ii]; +} + +void WebGLRenderingContext::initVertexAttrib0() +{ + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0); + + m_vertexAttrib0Buffer = createBuffer(); + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vertexAttrib0Buffer->object()); + m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, 0, GraphicsContext3D::DYNAMIC_DRAW); + m_context->vertexAttribPointer(0, 4, GraphicsContext3D::FLOAT, false, 0, 0); + state.bufferBinding = m_vertexAttrib0Buffer; + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0); + m_context->enableVertexAttribArray(0); + m_vertexAttrib0BufferSize = 0; + m_vertexAttrib0BufferValue[0] = 0.0f; + m_vertexAttrib0BufferValue[1] = 0.0f; + m_vertexAttrib0BufferValue[2] = 0.0f; + m_vertexAttrib0BufferValue[3] = 1.0f; + m_forceAttrib0BufferRefill = false; + m_vertexAttrib0UsedBefore = false; +} + +bool WebGLRenderingContext::simulateVertexAttrib0(GC3Dsizei numVertex) +{ + const WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0); + const VertexAttribValue& attribValue = m_vertexAttribValue[0]; + if (!m_currentProgram) + return false; + bool usingVertexAttrib0 = m_currentProgram->isUsingVertexAttrib0(); + if (usingVertexAttrib0) + m_vertexAttrib0UsedBefore = true; + if (state.enabled && usingVertexAttrib0) + return false; + if (!usingVertexAttrib0 && !m_vertexAttrib0UsedBefore) + return false; + m_vertexAttrib0UsedBefore = true; + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, m_vertexAttrib0Buffer->object()); + GC3Dsizeiptr bufferDataSize = (numVertex + 1) * 4 * sizeof(GC3Dfloat); + if (bufferDataSize > m_vertexAttrib0BufferSize) { + m_context->bufferData(GraphicsContext3D::ARRAY_BUFFER, bufferDataSize, 0, GraphicsContext3D::DYNAMIC_DRAW); + m_vertexAttrib0BufferSize = bufferDataSize; + m_forceAttrib0BufferRefill = true; + } + if (usingVertexAttrib0 + && (m_forceAttrib0BufferRefill + || attribValue.value[0] != m_vertexAttrib0BufferValue[0] + || attribValue.value[1] != m_vertexAttrib0BufferValue[1] + || attribValue.value[2] != m_vertexAttrib0BufferValue[2] + || attribValue.value[3] != m_vertexAttrib0BufferValue[3])) { + OwnArrayPtr<GC3Dfloat> bufferData = adoptArrayPtr(new GC3Dfloat[(numVertex + 1) * 4]); + for (GC3Dsizei ii = 0; ii < numVertex + 1; ++ii) { + bufferData[ii * 4] = attribValue.value[0]; + bufferData[ii * 4 + 1] = attribValue.value[1]; + bufferData[ii * 4 + 2] = attribValue.value[2]; + bufferData[ii * 4 + 3] = attribValue.value[3]; + } + m_vertexAttrib0BufferValue[0] = attribValue.value[0]; + m_vertexAttrib0BufferValue[1] = attribValue.value[1]; + m_vertexAttrib0BufferValue[2] = attribValue.value[2]; + m_vertexAttrib0BufferValue[3] = attribValue.value[3]; + m_forceAttrib0BufferRefill = false; + m_context->bufferSubData(GraphicsContext3D::ARRAY_BUFFER, 0, bufferDataSize, bufferData.get()); + } + m_context->vertexAttribPointer(0, 4, GraphicsContext3D::FLOAT, 0, 0, 0); + return true; +} + +void WebGLRenderingContext::restoreStatesAfterVertexAttrib0Simulation() +{ + const WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0); + if (state.bufferBinding != m_vertexAttrib0Buffer) { + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, objectOrZero(state.bufferBinding.get())); + m_context->vertexAttribPointer(0, state.size, state.type, state.normalized, state.originalStride, state.offset); + } + m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, objectOrZero(m_boundArrayBuffer.get())); +} + +void WebGLRenderingContext::dispatchContextLostEvent(Timer<WebGLRenderingContext>*) +{ + RefPtr<WebGLContextEvent> event = WebGLContextEvent::create(eventNames().webglcontextlostEvent, false, true, ""); + canvas()->dispatchEvent(event); + m_restoreAllowed = event->defaultPrevented(); + if (m_contextLostMode == RealLostContext && m_restoreAllowed) + m_restoreTimer.startOneShot(0); +} + +void WebGLRenderingContext::maybeRestoreContext(Timer<WebGLRenderingContext>*) +{ + ASSERT(m_contextLost); + if (!m_contextLost) + return; + + // The rendering context is not restored unless the default behavior of the + // webglcontextlost event was prevented earlier. + // + // Because of the way m_restoreTimer is set up for real vs. synthetic lost + // context events, we don't have to worry about this test short-circuiting + // the retry loop for real context lost events. + if (!m_restoreAllowed) + return; + + int contextLostReason = m_context->getExtensions()->getGraphicsResetStatusARB(); + + switch (contextLostReason) { + case GraphicsContext3D::NO_ERROR: + // The GraphicsContext3D implementation might not fully + // support GL_ARB_robustness semantics yet. Alternatively, the + // WEBGL_lose_context extension might have been used to force + // a lost context. + break; + case Extensions3D::GUILTY_CONTEXT_RESET_ARB: + // The rendering context is not restored if this context was + // guilty of causing the graphics reset. + printWarningToConsole("WARNING: WebGL content on the page caused the graphics card to reset; not restoring the context"); + return; + case Extensions3D::INNOCENT_CONTEXT_RESET_ARB: + // Always allow the context to be restored. + break; + case Extensions3D::UNKNOWN_CONTEXT_RESET_ARB: + // Warn. Ideally, prompt the user telling them that WebGL + // content on the page might have caused the graphics card to + // reset and ask them whether they want to continue running + // the content. Only if they say "yes" should we start + // attempting to restore the context. + printWarningToConsole("WARNING: WebGL content on the page might have caused the graphics card to reset"); + break; + } + + RefPtr<GraphicsContext3D> context(GraphicsContext3D::create(m_attributes, canvas()->document()->view()->root()->hostWindow())); + if (!context) { + if (m_contextLostMode == RealLostContext) + m_restoreTimer.startOneShot(secondsBetweenRestoreAttempts); + else + // This likely shouldn't happen but is the best way to report it to the WebGL app. + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + + // Construct a new drawing buffer with the new GraphicsContext3D. + if (m_drawingBuffer) { + m_drawingBuffer->discardResources(); + m_drawingBuffer = DrawingBuffer::create(m_context.get(), m_drawingBuffer->size(), !m_attributes.preserveDrawingBuffer); + } + + m_context = context; + m_contextLost = false; + initializeNewContext(); + canvas()->dispatchEvent(WebGLContextEvent::create(eventNames().webglcontextrestoredEvent, false, true, "")); +} + +WebGLRenderingContext::LRUImageBufferCache::LRUImageBufferCache(int capacity) + : m_buffers(adoptArrayPtr(new OwnPtr<ImageBuffer>[capacity])) + , m_capacity(capacity) +{ +} + +ImageBuffer* WebGLRenderingContext::LRUImageBufferCache::imageBuffer(const IntSize& size) +{ + int i; + for (i = 0; i < m_capacity; ++i) { + ImageBuffer* buf = m_buffers[i].get(); + if (!buf) + break; + if (buf->size() != size) + continue; + bubbleToFront(i); + return buf; + } + + OwnPtr<ImageBuffer> temp = ImageBuffer::create(size); + if (!temp) + return 0; + i = std::min(m_capacity - 1, i); + m_buffers[i] = temp.release(); + + ImageBuffer* buf = m_buffers[i].get(); + bubbleToFront(i); + return buf; +} + +void WebGLRenderingContext::LRUImageBufferCache::bubbleToFront(int idx) +{ + for (int i = idx; i > 0; --i) + m_buffers[i].swap(m_buffers[i-1]); +} + +} // namespace WebCore + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.h b/Source/WebCore/html/canvas/WebGLRenderingContext.h new file mode 100644 index 000000000..8f1efd9b0 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.h @@ -0,0 +1,651 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLRenderingContext_h +#define WebGLRenderingContext_h + +#include "CanvasRenderingContext.h" +#include "DrawingBuffer.h" +#include "GraphicsContext3D.h" +#include "PlatformString.h" +#include "Timer.h" +#include "WebGLGetInfo.h" + +#include <wtf/Float32Array.h> +#include <wtf/Int32Array.h> +#include <wtf/OwnArrayPtr.h> +#include <wtf/Uint8Array.h> + +namespace WebCore { + +class HTMLImageElement; +class HTMLVideoElement; +class ImageBuffer; +class ImageData; +class IntSize; +class OESStandardDerivatives; +class OESTextureFloat; +class OESVertexArrayObject; +class WebGLActiveInfo; +class WebGLBuffer; +class WebGLCompressedTextures; +class WebGLContextAttributes; +class WebGLDebugRendererInfo; +class WebGLDebugShaders; +class WebGLExtension; +class WebGLFramebuffer; +class WebGLLoseContext; +class WebGLObject; +class WebGLProgram; +class WebGLRenderbuffer; +class WebGLShader; +class WebGLTexture; +class WebGLUniformLocation; +class WebGLVertexArrayObjectOES; + +typedef int ExceptionCode; + +class WebGLRenderingContext : public CanvasRenderingContext { +public: + static PassOwnPtr<WebGLRenderingContext> create(HTMLCanvasElement*, WebGLContextAttributes*); + virtual ~WebGLRenderingContext(); + + virtual bool is3d() const { return true; } + virtual bool isAccelerated() const { return true; } + virtual bool paintsIntoCanvasBuffer() const; + + int drawingBufferWidth() const; + int drawingBufferHeight() const; + + void activeTexture(GC3Denum texture, ExceptionCode&); + void attachShader(WebGLProgram*, WebGLShader*, ExceptionCode&); + void bindAttribLocation(WebGLProgram*, GC3Duint index, const String& name, ExceptionCode&); + void bindBuffer(GC3Denum target, WebGLBuffer*, ExceptionCode&); + void bindFramebuffer(GC3Denum target, WebGLFramebuffer*, ExceptionCode&); + void bindRenderbuffer(GC3Denum target, WebGLRenderbuffer*, ExceptionCode&); + void bindTexture(GC3Denum target, WebGLTexture*, ExceptionCode&); + void blendColor(GC3Dfloat red, GC3Dfloat green, GC3Dfloat blue, GC3Dfloat alpha); + void blendEquation(GC3Denum mode); + void blendEquationSeparate(GC3Denum modeRGB, GC3Denum modeAlpha); + void blendFunc(GC3Denum sfactor, GC3Denum dfactor); + void blendFuncSeparate(GC3Denum srcRGB, GC3Denum dstRGB, GC3Denum srcAlpha, GC3Denum dstAlpha); + + void bufferData(GC3Denum target, GC3Dsizeiptr size, GC3Denum usage, ExceptionCode&); + void bufferData(GC3Denum target, ArrayBuffer* data, GC3Denum usage, ExceptionCode&); + void bufferData(GC3Denum target, ArrayBufferView* data, GC3Denum usage, ExceptionCode&); + void bufferSubData(GC3Denum target, GC3Dintptr offset, ArrayBuffer* data, ExceptionCode&); + void bufferSubData(GC3Denum target, GC3Dintptr offset, ArrayBufferView* data, ExceptionCode&); + + GC3Denum checkFramebufferStatus(GC3Denum target); + void clear(GC3Dbitfield mask); + void clearColor(GC3Dfloat red, GC3Dfloat green, GC3Dfloat blue, GC3Dfloat alpha); + void clearDepth(GC3Dfloat); + void clearStencil(GC3Dint); + void colorMask(GC3Dboolean red, GC3Dboolean green, GC3Dboolean blue, GC3Dboolean alpha); + void compileShader(WebGLShader*, ExceptionCode&); + + // void compressedTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height, GC3Dint border, GC3Dsizei imageSize, const void* data); + // void compressedTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dsizei width, GC3Dsizei GC3Dsizei height, GC3Denum format, GC3Dsizei imageSize, const void* data); + + void copyTexImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Dint border); + void copyTexSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height); + + PassRefPtr<WebGLBuffer> createBuffer(); + PassRefPtr<WebGLFramebuffer> createFramebuffer(); + PassRefPtr<WebGLProgram> createProgram(); + PassRefPtr<WebGLRenderbuffer> createRenderbuffer(); + PassRefPtr<WebGLShader> createShader(GC3Denum type, ExceptionCode&); + PassRefPtr<WebGLTexture> createTexture(); + + void cullFace(GC3Denum mode); + + void deleteBuffer(WebGLBuffer*); + void deleteFramebuffer(WebGLFramebuffer*); + void deleteProgram(WebGLProgram*); + void deleteRenderbuffer(WebGLRenderbuffer*); + void deleteShader(WebGLShader*); + void deleteTexture(WebGLTexture*); + + void depthFunc(GC3Denum); + void depthMask(GC3Dboolean); + void depthRange(GC3Dfloat zNear, GC3Dfloat zFar); + void detachShader(WebGLProgram*, WebGLShader*, ExceptionCode&); + void disable(GC3Denum cap); + void disableVertexAttribArray(GC3Duint index, ExceptionCode&); + void drawArrays(GC3Denum mode, GC3Dint first, GC3Dsizei count, ExceptionCode&); + void drawElements(GC3Denum mode, GC3Dsizei count, GC3Denum type, GC3Dintptr offset, ExceptionCode&); + + void enable(GC3Denum cap); + void enableVertexAttribArray(GC3Duint index, ExceptionCode&); + void finish(); + void flush(); + void framebufferRenderbuffer(GC3Denum target, GC3Denum attachment, GC3Denum renderbuffertarget, WebGLRenderbuffer*, ExceptionCode&); + void framebufferTexture2D(GC3Denum target, GC3Denum attachment, GC3Denum textarget, WebGLTexture*, GC3Dint level, ExceptionCode&); + void frontFace(GC3Denum mode); + void generateMipmap(GC3Denum target); + + PassRefPtr<WebGLActiveInfo> getActiveAttrib(WebGLProgram*, GC3Duint index, ExceptionCode&); + PassRefPtr<WebGLActiveInfo> getActiveUniform(WebGLProgram*, GC3Duint index, ExceptionCode&); + bool getAttachedShaders(WebGLProgram*, Vector<RefPtr<WebGLShader> >&, ExceptionCode&); + GC3Dint getAttribLocation(WebGLProgram*, const String& name); + WebGLGetInfo getBufferParameter(GC3Denum target, GC3Denum pname, ExceptionCode&); + PassRefPtr<WebGLContextAttributes> getContextAttributes(); + GC3Denum getError(); + WebGLExtension* getExtension(const String& name); + WebGLGetInfo getFramebufferAttachmentParameter(GC3Denum target, GC3Denum attachment, GC3Denum pname, ExceptionCode&); + WebGLGetInfo getParameter(GC3Denum pname, ExceptionCode&); + WebGLGetInfo getProgramParameter(WebGLProgram*, GC3Denum pname, ExceptionCode&); + String getProgramInfoLog(WebGLProgram*, ExceptionCode&); + WebGLGetInfo getRenderbufferParameter(GC3Denum target, GC3Denum pname, ExceptionCode&); + WebGLGetInfo getShaderParameter(WebGLShader*, GC3Denum pname, ExceptionCode&); + String getShaderInfoLog(WebGLShader*, ExceptionCode&); + + // TBD + // void glGetShaderPrecisionFormat (GC3Denum shadertype, GC3Denum precisiontype, GC3Dint* range, GC3Dint* precision); + + String getShaderSource(WebGLShader*, ExceptionCode&); + Vector<String> getSupportedExtensions(); + WebGLGetInfo getTexParameter(GC3Denum target, GC3Denum pname, ExceptionCode&); + WebGLGetInfo getUniform(WebGLProgram*, const WebGLUniformLocation*, ExceptionCode&); + PassRefPtr<WebGLUniformLocation> getUniformLocation(WebGLProgram*, const String&, ExceptionCode&); + WebGLGetInfo getVertexAttrib(GC3Duint index, GC3Denum pname, ExceptionCode&); + GC3Dsizeiptr getVertexAttribOffset(GC3Duint index, GC3Denum pname); + + void hint(GC3Denum target, GC3Denum mode); + GC3Dboolean isBuffer(WebGLBuffer*); + bool isContextLost(); + GC3Dboolean isEnabled(GC3Denum cap); + GC3Dboolean isFramebuffer(WebGLFramebuffer*); + GC3Dboolean isProgram(WebGLProgram*); + GC3Dboolean isRenderbuffer(WebGLRenderbuffer*); + GC3Dboolean isShader(WebGLShader*); + GC3Dboolean isTexture(WebGLTexture*); + + void lineWidth(GC3Dfloat); + void linkProgram(WebGLProgram*, ExceptionCode&); + void pixelStorei(GC3Denum pname, GC3Dint param); + void polygonOffset(GC3Dfloat factor, GC3Dfloat units); + void readPixels(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height, GC3Denum format, GC3Denum type, ArrayBufferView* pixels, ExceptionCode&); + void releaseShaderCompiler(); + void renderbufferStorage(GC3Denum target, GC3Denum internalformat, GC3Dsizei width, GC3Dsizei height); + void sampleCoverage(GC3Dfloat value, GC3Dboolean invert); + void scissor(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height); + void shaderSource(WebGLShader*, const String&, ExceptionCode&); + void stencilFunc(GC3Denum func, GC3Dint ref, GC3Duint mask); + void stencilFuncSeparate(GC3Denum face, GC3Denum func, GC3Dint ref, GC3Duint mask); + void stencilMask(GC3Duint); + void stencilMaskSeparate(GC3Denum face, GC3Duint mask); + void stencilOp(GC3Denum fail, GC3Denum zfail, GC3Denum zpass); + void stencilOpSeparate(GC3Denum face, GC3Denum fail, GC3Denum zfail, GC3Denum zpass); + + void texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Dsizei width, GC3Dsizei height, GC3Dint border, + GC3Denum format, GC3Denum type, ArrayBufferView*, ExceptionCode&); + void texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, ImageData*, ExceptionCode&); + void texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, HTMLImageElement*, ExceptionCode&); + void texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, HTMLCanvasElement*, ExceptionCode&); +#if ENABLE(VIDEO) + void texImage2D(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, HTMLVideoElement*, ExceptionCode&); +#endif + + void texParameterf(GC3Denum target, GC3Denum pname, GC3Dfloat param); + void texParameteri(GC3Denum target, GC3Denum pname, GC3Dint param); + + void texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, + GC3Denum format, GC3Denum type, ArrayBufferView*, ExceptionCode&); + void texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, ImageData*, ExceptionCode&); + void texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, HTMLImageElement*, ExceptionCode&); + void texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, HTMLCanvasElement*, ExceptionCode&); +#if ENABLE(VIDEO) + void texSubImage2D(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, HTMLVideoElement*, ExceptionCode&); +#endif + + void uniform1f(const WebGLUniformLocation* location, GC3Dfloat x, ExceptionCode&); + void uniform1fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode&); + void uniform1fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode&); + void uniform1i(const WebGLUniformLocation* location, GC3Dint x, ExceptionCode&); + void uniform1iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode&); + void uniform1iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode&); + void uniform2f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, ExceptionCode&); + void uniform2fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode&); + void uniform2fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode&); + void uniform2i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, ExceptionCode&); + void uniform2iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode&); + void uniform2iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode&); + void uniform3f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z, ExceptionCode&); + void uniform3fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode&); + void uniform3fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode&); + void uniform3i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, GC3Dint z, ExceptionCode&); + void uniform3iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode&); + void uniform3iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode&); + void uniform4f(const WebGLUniformLocation* location, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z, GC3Dfloat w, ExceptionCode&); + void uniform4fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode&); + void uniform4fv(const WebGLUniformLocation* location, GC3Dfloat* v, GC3Dsizei size, ExceptionCode&); + void uniform4i(const WebGLUniformLocation* location, GC3Dint x, GC3Dint y, GC3Dint z, GC3Dint w, ExceptionCode&); + void uniform4iv(const WebGLUniformLocation* location, Int32Array* v, ExceptionCode&); + void uniform4iv(const WebGLUniformLocation* location, GC3Dint* v, GC3Dsizei size, ExceptionCode&); + void uniformMatrix2fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* value, ExceptionCode&); + void uniformMatrix2fv(const WebGLUniformLocation* location, GC3Dboolean transpose, GC3Dfloat* value, GC3Dsizei size, ExceptionCode&); + void uniformMatrix3fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* value, ExceptionCode&); + void uniformMatrix3fv(const WebGLUniformLocation* location, GC3Dboolean transpose, GC3Dfloat* value, GC3Dsizei size, ExceptionCode&); + void uniformMatrix4fv(const WebGLUniformLocation* location, GC3Dboolean transpose, Float32Array* value, ExceptionCode&); + void uniformMatrix4fv(const WebGLUniformLocation* location, GC3Dboolean transpose, GC3Dfloat* value, GC3Dsizei size, ExceptionCode&); + + void useProgram(WebGLProgram*, ExceptionCode&); + void validateProgram(WebGLProgram*, ExceptionCode&); + + void vertexAttrib1f(GC3Duint index, GC3Dfloat x); + void vertexAttrib1fv(GC3Duint index, Float32Array* values); + void vertexAttrib1fv(GC3Duint index, GC3Dfloat* values, GC3Dsizei size); + void vertexAttrib2f(GC3Duint index, GC3Dfloat x, GC3Dfloat y); + void vertexAttrib2fv(GC3Duint index, Float32Array* values); + void vertexAttrib2fv(GC3Duint index, GC3Dfloat* values, GC3Dsizei size); + void vertexAttrib3f(GC3Duint index, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z); + void vertexAttrib3fv(GC3Duint index, Float32Array* values); + void vertexAttrib3fv(GC3Duint index, GC3Dfloat* values, GC3Dsizei size); + void vertexAttrib4f(GC3Duint index, GC3Dfloat x, GC3Dfloat y, GC3Dfloat z, GC3Dfloat w); + void vertexAttrib4fv(GC3Duint index, Float32Array* values); + void vertexAttrib4fv(GC3Duint index, GC3Dfloat* values, GC3Dsizei size); + void vertexAttribPointer(GC3Duint index, GC3Dint size, GC3Denum type, GC3Dboolean normalized, + GC3Dsizei stride, GC3Dintptr offset, ExceptionCode&); + + void viewport(GC3Dint x, GC3Dint y, GC3Dsizei width, GC3Dsizei height); + + // WEBKIT_lose_context support + enum LostContextMode { + // Lost context occurred at the graphics system level. + RealLostContext, + + // Lost context provoked by WEBKIT_lose_context. + SyntheticLostContext + }; + void forceLostContext(LostContextMode); + void forceRestoreContext(); + + GraphicsContext3D* graphicsContext3D() const { return m_context.get(); } +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const; +#endif + + void reshape(int width, int height); + + void markLayerComposited(); + virtual void paintRenderingResultsToCanvas(); + virtual PassRefPtr<ImageData> paintRenderingResultsToImageData(); + + void removeObject(WebGLObject*); + + unsigned getMaxVertexAttribs() const { return m_maxVertexAttribs; } + + private: + friend class WebGLFramebuffer; + friend class WebGLObject; + friend class OESVertexArrayObject; + friend class WebGLDebugShaders; + friend class WebGLCompressedTextures; + + WebGLRenderingContext(HTMLCanvasElement*, PassRefPtr<GraphicsContext3D>, GraphicsContext3D::Attributes); + void initializeNewContext(); + void setupFlags(); + + void addObject(WebGLObject*); + void detachAndRemoveAllObjects(); + + void markContextChanged(); + void cleanupAfterGraphicsCall(bool changed) + { + if (changed) + markContextChanged(); + } + + // Query whether it is built on top of compliant GLES2 implementation. + bool isGLES2Compliant() { return m_isGLES2Compliant; } + // Query if the GL implementation is NPOT strict. + bool isGLES2NPOTStrict() { return m_isGLES2NPOTStrict; } + // Query if the GL implementation generates errors on out-of-bounds buffer accesses. + bool isErrorGeneratedOnOutOfBoundsAccesses() { return m_isErrorGeneratedOnOutOfBoundsAccesses; } + // Query if the GL implementation initializes textures/renderbuffers to 0. + bool isResourceSafe() { return m_isResourceSafe; } + // Query if depth_stencil buffer is supported. + bool isDepthStencilSupported() { return m_isDepthStencilSupported; } + + // Helper to return the size in bytes of OpenGL data types + // like GL_FLOAT, GL_INT, etc. + unsigned int sizeInBytes(GC3Denum type); + + // Basic validation of count and offset against number of elements in element array buffer + bool validateElementArraySize(GC3Dsizei count, GC3Denum type, GC3Dintptr offset); + + // Conservative but quick index validation + bool validateIndexArrayConservative(GC3Denum type, int& numElementsRequired); + + // Precise but slow index validation -- only done if conservative checks fail + bool validateIndexArrayPrecise(GC3Dsizei count, GC3Denum type, GC3Dintptr offset, int& numElementsRequired); + // If numElements <= 0, we only check if each enabled vertex attribute is bound to a buffer. + bool validateRenderingState(int numElements); + + bool validateWebGLObject(WebGLObject*); + +#if ENABLE(VIDEO) + PassRefPtr<Image> videoFrameToImage(HTMLVideoElement*, ExceptionCode&); +#endif + + RefPtr<GraphicsContext3D> m_context; + + // Optional structure for rendering to a DrawingBuffer, instead of directly + // to the back-buffer of m_context. + RefPtr<DrawingBuffer> m_drawingBuffer; + + // Dispatches a context lost event once it is determined that one is needed. + // This is used both for synthetic and real context losses. For real ones, it's + // likely that there's no JavaScript on the stack, but that might be dependent + // on how exactly the platform discovers that the context was lost. For better + // portability we always defer the dispatch of the event. + Timer<WebGLRenderingContext> m_dispatchContextLostEventTimer; + bool m_restoreAllowed; + Timer<WebGLRenderingContext> m_restoreTimer; + + bool m_needsUpdate; + bool m_markedCanvasDirty; + HashSet<WebGLObject*> m_canvasObjects; + + // List of bound VBO's. Used to maintain info about sizes for ARRAY_BUFFER and stored values for ELEMENT_ARRAY_BUFFER + RefPtr<WebGLBuffer> m_boundArrayBuffer; + + RefPtr<WebGLVertexArrayObjectOES> m_defaultVertexArrayObject; + RefPtr<WebGLVertexArrayObjectOES> m_boundVertexArrayObject; + void setBoundVertexArrayObject(PassRefPtr<WebGLVertexArrayObjectOES> arrayObject) + { + if (arrayObject) + m_boundVertexArrayObject = arrayObject; + else + m_boundVertexArrayObject = m_defaultVertexArrayObject; + } + + class VertexAttribValue { + public: + VertexAttribValue() + { + initValue(); + } + + void initValue() + { + value[0] = 0.0f; + value[1] = 0.0f; + value[2] = 0.0f; + value[3] = 1.0f; + } + + GC3Dfloat value[4]; + }; + Vector<VertexAttribValue> m_vertexAttribValue; + unsigned m_maxVertexAttribs; + RefPtr<WebGLBuffer> m_vertexAttrib0Buffer; + long m_vertexAttrib0BufferSize; + GC3Dfloat m_vertexAttrib0BufferValue[4]; + bool m_forceAttrib0BufferRefill; + bool m_vertexAttrib0UsedBefore; + + RefPtr<WebGLProgram> m_currentProgram; + RefPtr<WebGLFramebuffer> m_framebufferBinding; + RefPtr<WebGLRenderbuffer> m_renderbufferBinding; + class TextureUnitState { + public: + RefPtr<WebGLTexture> m_texture2DBinding; + RefPtr<WebGLTexture> m_textureCubeMapBinding; + }; + Vector<TextureUnitState> m_textureUnits; + unsigned long m_activeTextureUnit; + + RefPtr<WebGLTexture> m_blackTexture2D; + RefPtr<WebGLTexture> m_blackTextureCubeMap; + + // Fixed-size cache of reusable image buffers for video texImage2D calls. + class LRUImageBufferCache { + public: + LRUImageBufferCache(int capacity); + // The pointer returned is owned by the image buffer map. + ImageBuffer* imageBuffer(const IntSize& size); + private: + void bubbleToFront(int idx); + OwnArrayPtr<OwnPtr<ImageBuffer> > m_buffers; + int m_capacity; + }; + LRUImageBufferCache m_videoCache; + + GC3Dint m_maxTextureSize; + GC3Dint m_maxCubeMapTextureSize; + GC3Dint m_maxRenderbufferSize; + GC3Dint m_maxViewportDims[2]; + GC3Dint m_maxTextureLevel; + GC3Dint m_maxCubeMapTextureLevel; + + GC3Dint m_packAlignment; + GC3Dint m_unpackAlignment; + bool m_unpackFlipY; + bool m_unpackPremultiplyAlpha; + GC3Denum m_unpackColorspaceConversion; + bool m_contextLost; + LostContextMode m_contextLostMode; + GraphicsContext3D::Attributes m_attributes; + + bool m_layerCleared; + GC3Dfloat m_clearColor[4]; + bool m_scissorEnabled; + GC3Dfloat m_clearDepth; + GC3Dint m_clearStencil; + GC3Dboolean m_colorMask[4]; + GC3Dboolean m_depthMask; + + long m_stencilBits; + GC3Duint m_stencilMask, m_stencilMaskBack; + GC3Dint m_stencilFuncRef, m_stencilFuncRefBack; // Note that these are the user specified values, not the internal clamped value. + GC3Duint m_stencilFuncMask, m_stencilFuncMaskBack; + + bool m_isGLES2Compliant; + bool m_isGLES2NPOTStrict; + bool m_isErrorGeneratedOnOutOfBoundsAccesses; + bool m_isResourceSafe; + bool m_isDepthStencilSupported; + + // Enabled extension objects. + OwnPtr<OESTextureFloat> m_oesTextureFloat; + OwnPtr<OESStandardDerivatives> m_oesStandardDerivatives; + OwnPtr<OESVertexArrayObject> m_oesVertexArrayObject; + OwnPtr<WebGLLoseContext> m_webglLoseContext; + OwnPtr<WebGLDebugRendererInfo> m_webglDebugRendererInfo; + OwnPtr<WebGLDebugShaders> m_webglDebugShaders; + OwnPtr<WebGLCompressedTextures> m_webglCompressedTextures; + + // Helpers for getParameter and others + WebGLGetInfo getBooleanParameter(GC3Denum); + WebGLGetInfo getBooleanArrayParameter(GC3Denum); + WebGLGetInfo getFloatParameter(GC3Denum); + WebGLGetInfo getIntParameter(GC3Denum); + WebGLGetInfo getUnsignedIntParameter(GC3Denum); + WebGLGetInfo getWebGLFloatArrayParameter(GC3Denum); + WebGLGetInfo getWebGLIntArrayParameter(GC3Denum); + + // Clear the backbuffer if it was composited since the last operation. + // clearMask is set to the bitfield of any clear that would happen anyway at this time + // and the function returns true if that clear is now unnecessary. + bool clearIfComposited(GC3Dbitfield clearMask = 0); + + void texImage2DBase(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Dsizei width, GC3Dsizei height, GC3Dint border, + GC3Denum format, GC3Denum type, void* pixels, ExceptionCode&); + void texImage2DImpl(GC3Denum target, GC3Dint level, GC3Denum internalformat, + GC3Denum format, GC3Denum type, Image*, + bool flipY, bool premultiplyAlpha, ExceptionCode&); + void texSubImage2DBase(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Dsizei width, GC3Dsizei height, + GC3Denum format, GC3Denum type, void* pixels, ExceptionCode&); + void texSubImage2DImpl(GC3Denum target, GC3Dint level, GC3Dint xoffset, GC3Dint yoffset, + GC3Denum format, GC3Denum type, + Image* image, bool flipY, bool premultiplyAlpha, ExceptionCode&); + + void handleNPOTTextures(bool prepareToDraw); + + void createFallbackBlackTextures1x1(); + + // Helper function for copyTex{Sub}Image, check whether the internalformat + // and the color buffer format of the current bound framebuffer combination + // is valid. + bool isTexInternalFormatColorBufferCombinationValid(GC3Denum texInternalFormat, + GC3Denum colorBufferFormat); + + // Helper function to get the bound framebuffer's color buffer format. + GC3Denum getBoundFramebufferColorFormat(); + + // Helper function to get the bound framebuffer's width. + int getBoundFramebufferWidth(); + + // Helper function to get the bound framebuffer's height. + int getBoundFramebufferHeight(); + + // Helper function to verify limits on the length of uniform and attribute locations. + bool validateLocationLength(const String&); + + // Helper function to check if size is non-negative. + // Generate GL error and return false for negative inputs; otherwise, return true. + bool validateSize(GC3Dint x, GC3Dint y); + + // Helper function to check if all characters in the string belong to the + // ASCII subset as defined in GLSL ES 1.0 spec section 3.1. + bool validateString(const String&); + + // Helper function to check target and texture bound to the target. + // Generate GL errors and return 0 if target is invalid or texture bound is + // null. Otherwise, return the texture bound to the target. + WebGLTexture* validateTextureBinding(GC3Denum target, bool useSixEnumsForCubeMap); + + // 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(GC3Denum format, GC3Denum type); + + // Helper function to check input level for functions {copy}Tex{Sub}Image. + // Generates GL error and returns false if level is invalid. + bool validateTexFuncLevel(GC3Denum target, GC3Dint level); + + // Helper function to check input parameters for functions {copy}Tex{Sub}Image. + // Generates GL error and returns false if parameters are invalid. + bool validateTexFuncParameters(GC3Denum target, GC3Dint level, + GC3Denum internalformat, + GC3Dsizei width, GC3Dsizei height, GC3Dint border, + GC3Denum format, GC3Denum type); + + // 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(GC3Dsizei width, GC3Dsizei height, + GC3Denum format, GC3Denum type, + ArrayBufferView* pixels); + + // Helper function to validate mode for draw{Arrays/Elements}. + bool validateDrawMode(GC3Denum); + + // Helper function to validate if front/back stencilMask and stencilFunc settings are the same. + bool validateStencilSettings(); + + // Helper function to validate stencil func. + bool validateStencilFunc(GC3Denum); + + // Helper function for texParameterf and texParameteri. + void texParameter(GC3Denum target, GC3Denum pname, GC3Dfloat parami, GC3Dint paramf, bool isFloat); + + // Helper function to print warnings to console. Currently + // used only to warn about use of obsolete functions. + void printWarningToConsole(const String&); + + // Helper function to validate input parameters for framebuffer functions. + // Generate GL error if parameters are illegal. + bool validateFramebufferFuncParameters(GC3Denum target, GC3Denum attachment); + + // Helper function to validate blend equation mode. + bool validateBlendEquation(GC3Denum); + + // Helper function to validate blend func factors. + bool validateBlendFuncFactors(GC3Denum src, GC3Denum dst); + + // Helper function to validate a GL capability. + bool validateCapability(GC3Denum); + + // Helper function to validate input parameters for uniform functions. + bool validateUniformParameters(const WebGLUniformLocation*, Float32Array*, GC3Dsizei mod); + bool validateUniformParameters(const WebGLUniformLocation*, Int32Array*, GC3Dsizei mod); + bool validateUniformParameters(const WebGLUniformLocation*, void*, GC3Dsizei size, GC3Dsizei mod); + bool validateUniformMatrixParameters(const WebGLUniformLocation*, GC3Dboolean transpose, Float32Array*, GC3Dsizei mod); + bool validateUniformMatrixParameters(const WebGLUniformLocation*, GC3Dboolean transpose, void*, GC3Dsizei size, GC3Dsizei mod); + + // Helper function to validate parameters for bufferData. + // Return the current bound buffer to target, or 0 if parameters are invalid. + WebGLBuffer* validateBufferDataParameters(GC3Denum target, GC3Denum usage); + + // Helper function for tex{Sub}Image2D to make sure image is ready. + bool validateHTMLImageElement(HTMLImageElement*); + + // Helper functions for vertexAttribNf{v}. + void vertexAttribfImpl(GC3Duint index, GC3Dsizei expectedSize, GC3Dfloat, GC3Dfloat, GC3Dfloat, GC3Dfloat); + void vertexAttribfvImpl(GC3Duint index, Float32Array*, GC3Dsizei expectedSize); + void vertexAttribfvImpl(GC3Duint index, GC3Dfloat*, GC3Dsizei size, GC3Dsizei expectedSize); + + // Helper function for delete* (deleteBuffer, deleteProgram, etc) functions. + // Return false if caller should return without further processing. + bool deleteObject(WebGLObject*); + + // Helper function for bind* (bindBuffer, bindTexture, etc) and useProgram. + // If the object has already been deleted, set deleted to true upon return. + // Return false if caller should return without further processing. + bool checkObjectToBeBound(WebGLObject*, bool& deleted); + + // Helpers for simulating vertexAttrib0 + void initVertexAttrib0(); + bool simulateVertexAttrib0(GC3Dsizei numVertex); + void restoreStatesAfterVertexAttrib0Simulation(); + + void dispatchContextLostEvent(Timer<WebGLRenderingContext>*); + // Helper for restoration after context lost. + void maybeRestoreContext(Timer<WebGLRenderingContext>*); + + // Determine if we are running privileged code in the browser, for example, + // a Safari or Chrome extension. + bool allowPrivilegedExtensions() const; + + friend class WebGLStateRestorer; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.idl b/Source/WebCore/html/canvas/WebGLRenderingContext.idl new file mode 100644 index 000000000..5103af01a --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.idl @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=WEBGL, + InterfaceUUID=98fb48ae-7216-489c-862b-8e1217fc4443, + ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54, + CustomMarkFunction, + DontCheckEnums + ] WebGLRenderingContext : CanvasRenderingContext { + + /* ClearBufferMask */ + const unsigned int DEPTH_BUFFER_BIT = 0x00000100; + const unsigned int STENCIL_BUFFER_BIT = 0x00000400; + const unsigned int COLOR_BUFFER_BIT = 0x00004000; + + /* BeginMode */ + const unsigned int POINTS = 0x0000; + const unsigned int LINES = 0x0001; + const unsigned int LINE_LOOP = 0x0002; + const unsigned int LINE_STRIP = 0x0003; + const unsigned int TRIANGLES = 0x0004; + const unsigned int TRIANGLE_STRIP = 0x0005; + const unsigned int TRIANGLE_FAN = 0x0006; + + /* AlphaFunction (not supported in ES20) */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* BlendingFactorDest */ + const unsigned int ZERO = 0; + const unsigned int ONE = 1; + const unsigned int SRC_COLOR = 0x0300; + const unsigned int ONE_MINUS_SRC_COLOR = 0x0301; + const unsigned int SRC_ALPHA = 0x0302; + const unsigned int ONE_MINUS_SRC_ALPHA = 0x0303; + const unsigned int DST_ALPHA = 0x0304; + const unsigned int ONE_MINUS_DST_ALPHA = 0x0305; + + /* BlendingFactorSrc */ + /* ZERO */ + /* ONE */ + const unsigned int DST_COLOR = 0x0306; + const unsigned int ONE_MINUS_DST_COLOR = 0x0307; + const unsigned int SRC_ALPHA_SATURATE = 0x0308; + /* SRC_ALPHA */ + /* ONE_MINUS_SRC_ALPHA */ + /* DST_ALPHA */ + /* ONE_MINUS_DST_ALPHA */ + + /* BlendEquationSeparate */ + const unsigned int FUNC_ADD = 0x8006; + const unsigned int BLEND_EQUATION = 0x8009; + const unsigned int BLEND_EQUATION_RGB = 0x8009; /* same as BLEND_EQUATION */ + const unsigned int BLEND_EQUATION_ALPHA = 0x883D; + + /* BlendSubtract */ + const unsigned int FUNC_SUBTRACT = 0x800A; + const unsigned int FUNC_REVERSE_SUBTRACT = 0x800B; + + /* Separate Blend Functions */ + const unsigned int BLEND_DST_RGB = 0x80C8; + const unsigned int BLEND_SRC_RGB = 0x80C9; + const unsigned int BLEND_DST_ALPHA = 0x80CA; + const unsigned int BLEND_SRC_ALPHA = 0x80CB; + const unsigned int CONSTANT_COLOR = 0x8001; + const unsigned int ONE_MINUS_CONSTANT_COLOR = 0x8002; + const unsigned int CONSTANT_ALPHA = 0x8003; + const unsigned int ONE_MINUS_CONSTANT_ALPHA = 0x8004; + const unsigned int BLEND_COLOR = 0x8005; + + /* Buffer Objects */ + const unsigned int ARRAY_BUFFER = 0x8892; + const unsigned int ELEMENT_ARRAY_BUFFER = 0x8893; + const unsigned int ARRAY_BUFFER_BINDING = 0x8894; + const unsigned int ELEMENT_ARRAY_BUFFER_BINDING = 0x8895; + + const unsigned int STREAM_DRAW = 0x88E0; + const unsigned int STATIC_DRAW = 0x88E4; + const unsigned int DYNAMIC_DRAW = 0x88E8; + + const unsigned int BUFFER_SIZE = 0x8764; + const unsigned int BUFFER_USAGE = 0x8765; + + const unsigned int CURRENT_VERTEX_ATTRIB = 0x8626; + + /* CullFaceMode */ + const unsigned int FRONT = 0x0404; + const unsigned int BACK = 0x0405; + const unsigned int FRONT_AND_BACK = 0x0408; + + /* DepthFunction */ + /* NEVER */ + /* LESS */ + /* EQUAL */ + /* LEQUAL */ + /* GREATER */ + /* NOTEQUAL */ + /* GEQUAL */ + /* ALWAYS */ + + /* EnableCap */ + const unsigned int TEXTURE_2D = 0x0DE1; + const unsigned int CULL_FACE = 0x0B44; + const unsigned int BLEND = 0x0BE2; + const unsigned int DITHER = 0x0BD0; + const unsigned int STENCIL_TEST = 0x0B90; + const unsigned int DEPTH_TEST = 0x0B71; + const unsigned int SCISSOR_TEST = 0x0C11; + const unsigned int POLYGON_OFFSET_FILL = 0x8037; + const unsigned int SAMPLE_ALPHA_TO_COVERAGE = 0x809E; + const unsigned int SAMPLE_COVERAGE = 0x80A0; + + /* ErrorCode */ + const unsigned int NO_ERROR = 0; + const unsigned int INVALID_ENUM = 0x0500; + const unsigned int INVALID_VALUE = 0x0501; + const unsigned int INVALID_OPERATION = 0x0502; + const unsigned int OUT_OF_MEMORY = 0x0505; + + /* FrontFaceDirection */ + const unsigned int CW = 0x0900; + const unsigned int CCW = 0x0901; + + /* GetPName */ + const unsigned int LINE_WIDTH = 0x0B21; + const unsigned int ALIASED_POINT_SIZE_RANGE = 0x846D; + const unsigned int ALIASED_LINE_WIDTH_RANGE = 0x846E; + const unsigned int CULL_FACE_MODE = 0x0B45; + const unsigned int FRONT_FACE = 0x0B46; + const unsigned int DEPTH_RANGE = 0x0B70; + const unsigned int DEPTH_WRITEMASK = 0x0B72; + const unsigned int DEPTH_CLEAR_VALUE = 0x0B73; + const unsigned int DEPTH_FUNC = 0x0B74; + const unsigned int STENCIL_CLEAR_VALUE = 0x0B91; + const unsigned int STENCIL_FUNC = 0x0B92; + const unsigned int STENCIL_FAIL = 0x0B94; + const unsigned int STENCIL_PASS_DEPTH_FAIL = 0x0B95; + const unsigned int STENCIL_PASS_DEPTH_PASS = 0x0B96; + const unsigned int STENCIL_REF = 0x0B97; + const unsigned int STENCIL_VALUE_MASK = 0x0B93; + const unsigned int STENCIL_WRITEMASK = 0x0B98; + const unsigned int STENCIL_BACK_FUNC = 0x8800; + const unsigned int STENCIL_BACK_FAIL = 0x8801; + const unsigned int STENCIL_BACK_PASS_DEPTH_FAIL = 0x8802; + const unsigned int STENCIL_BACK_PASS_DEPTH_PASS = 0x8803; + const unsigned int STENCIL_BACK_REF = 0x8CA3; + const unsigned int STENCIL_BACK_VALUE_MASK = 0x8CA4; + const unsigned int STENCIL_BACK_WRITEMASK = 0x8CA5; + const unsigned int VIEWPORT = 0x0BA2; + const unsigned int SCISSOR_BOX = 0x0C10; + /* SCISSOR_TEST */ + const unsigned int COLOR_CLEAR_VALUE = 0x0C22; + const unsigned int COLOR_WRITEMASK = 0x0C23; + const unsigned int UNPACK_ALIGNMENT = 0x0CF5; + const unsigned int PACK_ALIGNMENT = 0x0D05; + const unsigned int MAX_TEXTURE_SIZE = 0x0D33; + const unsigned int MAX_VIEWPORT_DIMS = 0x0D3A; + const unsigned int SUBPIXEL_BITS = 0x0D50; + const unsigned int RED_BITS = 0x0D52; + const unsigned int GREEN_BITS = 0x0D53; + const unsigned int BLUE_BITS = 0x0D54; + const unsigned int ALPHA_BITS = 0x0D55; + const unsigned int DEPTH_BITS = 0x0D56; + const unsigned int STENCIL_BITS = 0x0D57; + const unsigned int POLYGON_OFFSET_UNITS = 0x2A00; + /* POLYGON_OFFSET_FILL */ + const unsigned int POLYGON_OFFSET_FACTOR = 0x8038; + const unsigned int TEXTURE_BINDING_2D = 0x8069; + const unsigned int SAMPLE_BUFFERS = 0x80A8; + const unsigned int SAMPLES = 0x80A9; + const unsigned int SAMPLE_COVERAGE_VALUE = 0x80AA; + const unsigned int SAMPLE_COVERAGE_INVERT = 0x80AB; + + /* GetTextureParameter */ + /* TEXTURE_MAG_FILTER */ + /* TEXTURE_MIN_FILTER */ + /* TEXTURE_WRAP_S */ + /* TEXTURE_WRAP_T */ + + const unsigned int NUM_COMPRESSED_TEXTURE_FORMATS = 0x86A2; + const unsigned int COMPRESSED_TEXTURE_FORMATS = 0x86A3; + + /* HintMode */ + const unsigned int DONT_CARE = 0x1100; + const unsigned int FASTEST = 0x1101; + const unsigned int NICEST = 0x1102; + + /* HintTarget */ + const unsigned int GENERATE_MIPMAP_HINT = 0x8192; + + /* DataType */ + const unsigned int BYTE = 0x1400; + const unsigned int UNSIGNED_BYTE = 0x1401; + const unsigned int SHORT = 0x1402; + const unsigned int UNSIGNED_SHORT = 0x1403; + const unsigned int INT = 0x1404; + const unsigned int UNSIGNED_INT = 0x1405; + const unsigned int FLOAT = 0x1406; + + /* PixelFormat */ + const unsigned int DEPTH_COMPONENT = 0x1902; + const unsigned int ALPHA = 0x1906; + const unsigned int RGB = 0x1907; + const unsigned int RGBA = 0x1908; + const unsigned int LUMINANCE = 0x1909; + const unsigned int LUMINANCE_ALPHA = 0x190A; + + /* PixelType */ + /* UNSIGNED_BYTE */ + const unsigned int UNSIGNED_SHORT_4_4_4_4 = 0x8033; + const unsigned int UNSIGNED_SHORT_5_5_5_1 = 0x8034; + const unsigned int UNSIGNED_SHORT_5_6_5 = 0x8363; + + /* Shaders */ + const unsigned int FRAGMENT_SHADER = 0x8B30; + const unsigned int VERTEX_SHADER = 0x8B31; + const unsigned int MAX_VERTEX_ATTRIBS = 0x8869; + const unsigned int MAX_VERTEX_UNIFORM_VECTORS = 0x8DFB; + const unsigned int MAX_VARYING_VECTORS = 0x8DFC; + const unsigned int MAX_COMBINED_TEXTURE_IMAGE_UNITS = 0x8B4D; + const unsigned int MAX_VERTEX_TEXTURE_IMAGE_UNITS = 0x8B4C; + const unsigned int MAX_TEXTURE_IMAGE_UNITS = 0x8872; + const unsigned int MAX_FRAGMENT_UNIFORM_VECTORS = 0x8DFD; + const unsigned int SHADER_TYPE = 0x8B4F; + const unsigned int DELETE_STATUS = 0x8B80; + const unsigned int LINK_STATUS = 0x8B82; + const unsigned int VALIDATE_STATUS = 0x8B83; + const unsigned int ATTACHED_SHADERS = 0x8B85; + const unsigned int ACTIVE_UNIFORMS = 0x8B86; + const unsigned int ACTIVE_ATTRIBUTES = 0x8B89; + const unsigned int SHADING_LANGUAGE_VERSION = 0x8B8C; + const unsigned int CURRENT_PROGRAM = 0x8B8D; + + /* StencilFunction */ + const unsigned int NEVER = 0x0200; + const unsigned int LESS = 0x0201; + const unsigned int EQUAL = 0x0202; + const unsigned int LEQUAL = 0x0203; + const unsigned int GREATER = 0x0204; + const unsigned int NOTEQUAL = 0x0205; + const unsigned int GEQUAL = 0x0206; + const unsigned int ALWAYS = 0x0207; + + /* StencilOp */ + /* ZERO */ + const unsigned int KEEP = 0x1E00; + const unsigned int REPLACE = 0x1E01; + const unsigned int INCR = 0x1E02; + const unsigned int DECR = 0x1E03; + const unsigned int INVERT = 0x150A; + const unsigned int INCR_WRAP = 0x8507; + const unsigned int DECR_WRAP = 0x8508; + + /* StringName */ + const unsigned int VENDOR = 0x1F00; + const unsigned int RENDERER = 0x1F01; + const unsigned int VERSION = 0x1F02; + + /* TextureMagFilter */ + const unsigned int NEAREST = 0x2600; + const unsigned int LINEAR = 0x2601; + + /* TextureMinFilter */ + /* NEAREST */ + /* LINEAR */ + const unsigned int NEAREST_MIPMAP_NEAREST = 0x2700; + const unsigned int LINEAR_MIPMAP_NEAREST = 0x2701; + const unsigned int NEAREST_MIPMAP_LINEAR = 0x2702; + const unsigned int LINEAR_MIPMAP_LINEAR = 0x2703; + + /* TextureParameterName */ + const unsigned int TEXTURE_MAG_FILTER = 0x2800; + const unsigned int TEXTURE_MIN_FILTER = 0x2801; + const unsigned int TEXTURE_WRAP_S = 0x2802; + const unsigned int TEXTURE_WRAP_T = 0x2803; + + /* TextureTarget */ + /* TEXTURE_2D */ + const unsigned int TEXTURE = 0x1702; + + const unsigned int TEXTURE_CUBE_MAP = 0x8513; + const unsigned int TEXTURE_BINDING_CUBE_MAP = 0x8514; + const unsigned int TEXTURE_CUBE_MAP_POSITIVE_X = 0x8515; + const unsigned int TEXTURE_CUBE_MAP_NEGATIVE_X = 0x8516; + const unsigned int TEXTURE_CUBE_MAP_POSITIVE_Y = 0x8517; + const unsigned int TEXTURE_CUBE_MAP_NEGATIVE_Y = 0x8518; + const unsigned int TEXTURE_CUBE_MAP_POSITIVE_Z = 0x8519; + const unsigned int TEXTURE_CUBE_MAP_NEGATIVE_Z = 0x851A; + const unsigned int MAX_CUBE_MAP_TEXTURE_SIZE = 0x851C; + + /* TextureUnit */ + const unsigned int TEXTURE0 = 0x84C0; + const unsigned int TEXTURE1 = 0x84C1; + const unsigned int TEXTURE2 = 0x84C2; + const unsigned int TEXTURE3 = 0x84C3; + const unsigned int TEXTURE4 = 0x84C4; + const unsigned int TEXTURE5 = 0x84C5; + const unsigned int TEXTURE6 = 0x84C6; + const unsigned int TEXTURE7 = 0x84C7; + const unsigned int TEXTURE8 = 0x84C8; + const unsigned int TEXTURE9 = 0x84C9; + const unsigned int TEXTURE10 = 0x84CA; + const unsigned int TEXTURE11 = 0x84CB; + const unsigned int TEXTURE12 = 0x84CC; + const unsigned int TEXTURE13 = 0x84CD; + const unsigned int TEXTURE14 = 0x84CE; + const unsigned int TEXTURE15 = 0x84CF; + const unsigned int TEXTURE16 = 0x84D0; + const unsigned int TEXTURE17 = 0x84D1; + const unsigned int TEXTURE18 = 0x84D2; + const unsigned int TEXTURE19 = 0x84D3; + const unsigned int TEXTURE20 = 0x84D4; + const unsigned int TEXTURE21 = 0x84D5; + const unsigned int TEXTURE22 = 0x84D6; + const unsigned int TEXTURE23 = 0x84D7; + const unsigned int TEXTURE24 = 0x84D8; + const unsigned int TEXTURE25 = 0x84D9; + const unsigned int TEXTURE26 = 0x84DA; + const unsigned int TEXTURE27 = 0x84DB; + const unsigned int TEXTURE28 = 0x84DC; + const unsigned int TEXTURE29 = 0x84DD; + const unsigned int TEXTURE30 = 0x84DE; + const unsigned int TEXTURE31 = 0x84DF; + const unsigned int ACTIVE_TEXTURE = 0x84E0; + + /* TextureWrapMode */ + const unsigned int REPEAT = 0x2901; + const unsigned int CLAMP_TO_EDGE = 0x812F; + const unsigned int MIRRORED_REPEAT = 0x8370; + + /* Uniform Types */ + const unsigned int FLOAT_VEC2 = 0x8B50; + const unsigned int FLOAT_VEC3 = 0x8B51; + const unsigned int FLOAT_VEC4 = 0x8B52; + const unsigned int INT_VEC2 = 0x8B53; + const unsigned int INT_VEC3 = 0x8B54; + const unsigned int INT_VEC4 = 0x8B55; + const unsigned int BOOL = 0x8B56; + const unsigned int BOOL_VEC2 = 0x8B57; + const unsigned int BOOL_VEC3 = 0x8B58; + const unsigned int BOOL_VEC4 = 0x8B59; + const unsigned int FLOAT_MAT2 = 0x8B5A; + const unsigned int FLOAT_MAT3 = 0x8B5B; + const unsigned int FLOAT_MAT4 = 0x8B5C; + const unsigned int SAMPLER_2D = 0x8B5E; + const unsigned int SAMPLER_CUBE = 0x8B60; + + /* Vertex Arrays */ + const unsigned int VERTEX_ATTRIB_ARRAY_ENABLED = 0x8622; + const unsigned int VERTEX_ATTRIB_ARRAY_SIZE = 0x8623; + const unsigned int VERTEX_ATTRIB_ARRAY_STRIDE = 0x8624; + const unsigned int VERTEX_ATTRIB_ARRAY_TYPE = 0x8625; + const unsigned int VERTEX_ATTRIB_ARRAY_NORMALIZED = 0x886A; + const unsigned int VERTEX_ATTRIB_ARRAY_POINTER = 0x8645; + const unsigned int VERTEX_ATTRIB_ARRAY_BUFFER_BINDING = 0x889F; + + /* Shader Source */ + const unsigned int COMPILE_STATUS = 0x8B81; + const unsigned int SHADER_COMPILER = 0x8DFA; + + /* Shader Precision-Specified Types */ + const unsigned int LOW_FLOAT = 0x8DF0; + const unsigned int MEDIUM_FLOAT = 0x8DF1; + const unsigned int HIGH_FLOAT = 0x8DF2; + const unsigned int LOW_INT = 0x8DF3; + const unsigned int MEDIUM_INT = 0x8DF4; + const unsigned int HIGH_INT = 0x8DF5; + + /* Framebuffer Object. */ + const unsigned int FRAMEBUFFER = 0x8D40; + const unsigned int RENDERBUFFER = 0x8D41; + + const unsigned int RGBA4 = 0x8056; + const unsigned int RGB5_A1 = 0x8057; + const unsigned int RGB565 = 0x8D62; + const unsigned int DEPTH_COMPONENT16 = 0x81A5; + const unsigned int STENCIL_INDEX = 0x1901; + const unsigned int STENCIL_INDEX8 = 0x8D48; + const unsigned int DEPTH_STENCIL = 0x84F9; + + const unsigned int RENDERBUFFER_WIDTH = 0x8D42; + const unsigned int RENDERBUFFER_HEIGHT = 0x8D43; + const unsigned int RENDERBUFFER_INTERNAL_FORMAT = 0x8D44; + const unsigned int RENDERBUFFER_RED_SIZE = 0x8D50; + const unsigned int RENDERBUFFER_GREEN_SIZE = 0x8D51; + const unsigned int RENDERBUFFER_BLUE_SIZE = 0x8D52; + const unsigned int RENDERBUFFER_ALPHA_SIZE = 0x8D53; + const unsigned int RENDERBUFFER_DEPTH_SIZE = 0x8D54; + const unsigned int RENDERBUFFER_STENCIL_SIZE = 0x8D55; + + const unsigned int FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE = 0x8CD0; + const unsigned int FRAMEBUFFER_ATTACHMENT_OBJECT_NAME = 0x8CD1; + const unsigned int FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL = 0x8CD2; + const unsigned int FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE = 0x8CD3; + + const unsigned int COLOR_ATTACHMENT0 = 0x8CE0; + const unsigned int DEPTH_ATTACHMENT = 0x8D00; + const unsigned int STENCIL_ATTACHMENT = 0x8D20; + const unsigned int DEPTH_STENCIL_ATTACHMENT = 0x821A; + + const unsigned int NONE = 0; + + const unsigned int FRAMEBUFFER_COMPLETE = 0x8CD5; + const unsigned int FRAMEBUFFER_INCOMPLETE_ATTACHMENT = 0x8CD6; + const unsigned int FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT = 0x8CD7; + const unsigned int FRAMEBUFFER_INCOMPLETE_DIMENSIONS = 0x8CD9; + const unsigned int FRAMEBUFFER_UNSUPPORTED = 0x8CDD; + + const unsigned int FRAMEBUFFER_BINDING = 0x8CA6; + const unsigned int RENDERBUFFER_BINDING = 0x8CA7; + const unsigned int MAX_RENDERBUFFER_SIZE = 0x84E8; + + const unsigned int INVALID_FRAMEBUFFER_OPERATION = 0x0506; + + /* WebGL-specific enums */ + const unsigned int UNPACK_FLIP_Y_WEBGL = 0x9240; + const unsigned int UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241; + const unsigned int CONTEXT_LOST_WEBGL = 0x9242; + const unsigned int UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243; + const unsigned int BROWSER_DEFAULT_WEBGL = 0x9244; + + readonly attribute long drawingBufferWidth; + readonly attribute long drawingBufferHeight; + + [StrictTypeChecking] void activeTexture(in unsigned long texture) raises(DOMException); + [StrictTypeChecking] void attachShader(in WebGLProgram program, in WebGLShader shader) raises(DOMException); + [StrictTypeChecking] void bindAttribLocation(in WebGLProgram program, in unsigned long index, in DOMString name) raises(DOMException); + [StrictTypeChecking] void bindBuffer(in unsigned long target, in WebGLBuffer buffer) raises(DOMException); + [StrictTypeChecking] void bindFramebuffer(in unsigned long target, in WebGLFramebuffer framebuffer) raises(DOMException); + [StrictTypeChecking] void bindRenderbuffer(in unsigned long target, in WebGLRenderbuffer renderbuffer) raises(DOMException); + [StrictTypeChecking] void bindTexture(in unsigned long target, in WebGLTexture texture) raises(DOMException); + [StrictTypeChecking] void blendColor(in float red, in float green, in float blue, in float alpha); + [StrictTypeChecking] void blendEquation( in unsigned long mode ); + [StrictTypeChecking] void blendEquationSeparate(in unsigned long modeRGB, in unsigned long modeAlpha); + [StrictTypeChecking] void blendFunc(in unsigned long sfactor, in unsigned long dfactor); + [StrictTypeChecking] void blendFuncSeparate(in unsigned long srcRGB, in unsigned long dstRGB, in unsigned long srcAlpha, in unsigned long dstAlpha); + [StrictTypeChecking] void bufferData(in unsigned long target, in ArrayBuffer data, in unsigned long usage) raises (DOMException); + [StrictTypeChecking] void bufferData(in unsigned long target, in ArrayBufferView data, in unsigned long usage) raises (DOMException); + [StrictTypeChecking] void bufferData(in unsigned long target, in long size, in unsigned long usage) raises (DOMException); + [StrictTypeChecking] void bufferSubData(in unsigned long target, in long offset, in ArrayBuffer data) raises (DOMException); + [StrictTypeChecking] void bufferSubData(in unsigned long target, in long offset, in ArrayBufferView data) raises (DOMException); + + [StrictTypeChecking] unsigned long checkFramebufferStatus(in unsigned long target); + [StrictTypeChecking] void clear(in unsigned long mask); + [StrictTypeChecking] void clearColor(in float red, in float green, in float blue, in float alpha); + [StrictTypeChecking] void clearDepth(in float depth); + [StrictTypeChecking] void clearStencil(in long s); + [StrictTypeChecking] void colorMask(in boolean red, in boolean green, in boolean blue, in boolean alpha); + [StrictTypeChecking] void compileShader(in WebGLShader shader) raises(DOMException); + + //void compressedTexImage2D(in unsigned long target, in long level, in unsigned long internalformat, in unsigned long width, in unsigned long height, in long border, in unsigned long imageSize, const void* data); + //void compressedTexSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, in unsigned long width, in unsigned long height, in unsigned long format, in unsigned long imageSize, const void* data); + + [StrictTypeChecking] void copyTexImage2D(in unsigned long target, in long level, in unsigned long internalformat, in long x, in long y, in long width, in long height, in long border); + [StrictTypeChecking] void copyTexSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, in long x, in long y, in long width, in long height); + + [StrictTypeChecking] WebGLBuffer createBuffer(); + [StrictTypeChecking] WebGLFramebuffer createFramebuffer(); + [StrictTypeChecking] WebGLProgram createProgram(); + [StrictTypeChecking] WebGLRenderbuffer createRenderbuffer(); + [StrictTypeChecking] WebGLShader createShader(in unsigned long type) raises(DOMException); + [StrictTypeChecking] WebGLTexture createTexture(); + + [StrictTypeChecking] void cullFace(in unsigned long mode); + + [StrictTypeChecking] void deleteBuffer(in WebGLBuffer buffer); + [StrictTypeChecking] void deleteFramebuffer(in WebGLFramebuffer framebuffer); + [StrictTypeChecking] void deleteProgram(in WebGLProgram program); + [StrictTypeChecking] void deleteRenderbuffer(in WebGLRenderbuffer renderbuffer); + [StrictTypeChecking] void deleteShader(in WebGLShader shader); + [StrictTypeChecking] void deleteTexture(in WebGLTexture texture); + + [StrictTypeChecking] void depthFunc(in unsigned long func); + [StrictTypeChecking] void depthMask(in boolean flag); + // FIXME: this differs from the current WebGL spec (depthRangef) + [StrictTypeChecking] void depthRange(in float zNear, in float zFar); + [StrictTypeChecking] void detachShader(in WebGLProgram program, in WebGLShader shader) raises(DOMException); + [StrictTypeChecking] void disable(in unsigned long cap); + [StrictTypeChecking] void disableVertexAttribArray(in unsigned long index) raises(DOMException); + [StrictTypeChecking] void drawArrays(in unsigned long mode, in long first, in long count) raises(DOMException); + [StrictTypeChecking] void drawElements(in unsigned long mode, in long count, in unsigned long type, in long offset) raises(DOMException); + + [StrictTypeChecking] void enable(in unsigned long cap); + [StrictTypeChecking] void enableVertexAttribArray(in unsigned long index) raises(DOMException); + [StrictTypeChecking] void finish(); + [StrictTypeChecking] void flush(); + [StrictTypeChecking] void framebufferRenderbuffer(in unsigned long target, in unsigned long attachment, in unsigned long renderbuffertarget, in WebGLRenderbuffer renderbuffer) raises(DOMException); + [StrictTypeChecking] void framebufferTexture2D(in unsigned long target, in unsigned long attachment, in unsigned long textarget, in WebGLTexture texture, in long level) raises(DOMException); + [StrictTypeChecking] void frontFace(in unsigned long mode); + [StrictTypeChecking] void generateMipmap(in unsigned long target); + + [StrictTypeChecking] WebGLActiveInfo getActiveAttrib(in WebGLProgram program, in unsigned long index) raises (DOMException); + [StrictTypeChecking] WebGLActiveInfo getActiveUniform(in WebGLProgram program, in unsigned long index) raises (DOMException); + + [StrictTypeChecking, Custom] void getAttachedShaders(in WebGLProgram program) raises (DOMException); + + [StrictTypeChecking] int getAttribLocation(in WebGLProgram program, in DOMString name); + + // any getBufferParameter(in unsigned long target, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getBufferParameter(); + + [StrictTypeChecking] WebGLContextAttributes getContextAttributes(); + + [StrictTypeChecking] unsigned long getError(); + + // object getExtension(in DOMString name); + [StrictTypeChecking, Custom] void getExtension(in DOMString name); + + // any getFramebufferAttachmentParameter(in unsigned long target, in unsigned long attachment, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getFramebufferAttachmentParameter(); + // any getParameter(in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getParameter(); + // any getProgramParameter(in WebGLProgram program, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getProgramParameter(); + [StrictTypeChecking, ConvertNullStringTo=Null] DOMString getProgramInfoLog(in WebGLProgram program) raises(DOMException); + // any getRenderbufferParameter(in unsigned long target, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getRenderbufferParameter(); + // any getShaderParameter(in WebGLShader shader, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getShaderParameter() raises(DOMException); + + [StrictTypeChecking, ConvertNullStringTo=Null] DOMString getShaderInfoLog(in WebGLShader shader) raises(DOMException); + + // TBD + // void glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision); + + [StrictTypeChecking, ConvertNullStringTo=Null] DOMString getShaderSource(in WebGLShader shader) raises(DOMException); + + // DOMString[] getSupportedExtensions() + [StrictTypeChecking, Custom] void getSupportedExtensions(); + + // any getTexParameter(in unsigned long target, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getTexParameter(); + + // any getUniform(in WebGLProgram program, in WebGLUniformLocation location) raises(DOMException); + [StrictTypeChecking, Custom] void getUniform(); + + [StrictTypeChecking] WebGLUniformLocation getUniformLocation(in WebGLProgram program, in DOMString name) raises(DOMException); + + // any getVertexAttrib(in unsigned long index, in unsigned long pname) raises(DOMException); + [StrictTypeChecking, Custom] void getVertexAttrib(); + + [StrictTypeChecking] long getVertexAttribOffset(in unsigned long index, in unsigned long pname); + + [StrictTypeChecking] void hint(in unsigned long target, in unsigned long mode); + [StrictTypeChecking] boolean isBuffer(in WebGLBuffer buffer); + [StrictTypeChecking] boolean isContextLost(); + [StrictTypeChecking] boolean isEnabled(in unsigned long cap); + [StrictTypeChecking] boolean isFramebuffer(in WebGLFramebuffer framebuffer); + [StrictTypeChecking] boolean isProgram(in WebGLProgram program); + [StrictTypeChecking] boolean isRenderbuffer(in WebGLRenderbuffer renderbuffer); + [StrictTypeChecking] boolean isShader(in WebGLShader shader); + [StrictTypeChecking] boolean isTexture(in WebGLTexture texture); + [StrictTypeChecking] void lineWidth(in float width); + [StrictTypeChecking] void linkProgram(in WebGLProgram program) raises(DOMException); + [StrictTypeChecking] void pixelStorei(in unsigned long pname, in long param); + [StrictTypeChecking] void polygonOffset(in float factor, in float units); + + [StrictTypeChecking] void readPixels(in long x, in long y, in long width, in long height, in unsigned long format, in unsigned long type, in ArrayBufferView pixels) raises(DOMException); + + [StrictTypeChecking] void releaseShaderCompiler(); + [StrictTypeChecking] void renderbufferStorage(in unsigned long target, in unsigned long internalformat, in long width, in long height); + [StrictTypeChecking] void sampleCoverage(in float value, in boolean invert); + [StrictTypeChecking] void scissor(in long x, in long y, in long width, in long height); + [StrictTypeChecking] void shaderSource(in WebGLShader shader, in DOMString string) raises(DOMException); + [StrictTypeChecking] void stencilFunc(in unsigned long func, in long ref, in unsigned long mask); + [StrictTypeChecking] void stencilFuncSeparate(in unsigned long face, in unsigned long func, in long ref, in unsigned long mask); + [StrictTypeChecking] void stencilMask(in unsigned long mask); + [StrictTypeChecking] void stencilMaskSeparate(in unsigned long face, in unsigned long mask); + [StrictTypeChecking] void stencilOp(in unsigned long fail, in unsigned long zfail, in unsigned long zpass); + [StrictTypeChecking] void stencilOpSeparate(in unsigned long face, in unsigned long fail, in unsigned long zfail, in unsigned long zpass); + + [StrictTypeChecking] void texParameterf(in unsigned long target, in unsigned long pname, in float param); + [StrictTypeChecking] void texParameteri(in unsigned long target, in unsigned long pname, in long param); + + // Supported forms: + [StrictTypeChecking] void texImage2D(in unsigned long target, in long level, in unsigned long internalformat, in long width, in long height, + in long border, in unsigned long format, in unsigned long type, in ArrayBufferView pixels) raises (DOMException); + [StrictTypeChecking] void texImage2D(in unsigned long target, in long level, in unsigned long internalformat, + in unsigned long format, in unsigned long type, in ImageData pixels) raises (DOMException); + [StrictTypeChecking] void texImage2D(in unsigned long target, in long level, in unsigned long internalformat, + in unsigned long format, in unsigned long type, in HTMLImageElement image) raises (DOMException); + [StrictTypeChecking] void texImage2D(in unsigned long target, in long level, in unsigned long internalformat, + in unsigned long format, in unsigned long type, in HTMLCanvasElement canvas) raises (DOMException); +#if defined(ENABLE_VIDEO) && ENABLE_VIDEO + [StrictTypeChecking] void texImage2D(in unsigned long target, in long level, in unsigned long internalformat, + in unsigned long format, in unsigned long type, in HTMLVideoElement video) raises (DOMException); +#endif + + [StrictTypeChecking] void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, + in long width, in long height, + in unsigned long format, in unsigned long type, in ArrayBufferView pixels) raises (DOMException); + [StrictTypeChecking] void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, + in unsigned long format, in unsigned long type, in ImageData pixels) raises (DOMException); + [StrictTypeChecking] void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, + in unsigned long format, in unsigned long type, in HTMLImageElement image) raises (DOMException); + [StrictTypeChecking] void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, + in unsigned long format, in unsigned long type, in HTMLCanvasElement canvas) raises (DOMException); +#if defined(ENABLE_VIDEO) && ENABLE_VIDEO + [StrictTypeChecking] void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, + in unsigned long format, in unsigned long type, in HTMLVideoElement video) raises (DOMException); +#endif + + [StrictTypeChecking] void uniform1f(in WebGLUniformLocation location, in float x) raises(DOMException); + [StrictTypeChecking, Custom] void uniform1fv(in WebGLUniformLocation location, in Float32Array v) raises(DOMException); + [StrictTypeChecking] void uniform1i(in WebGLUniformLocation location, in long x) raises(DOMException); + [StrictTypeChecking, Custom] void uniform1iv(in WebGLUniformLocation location, in Int32Array v) raises(DOMException); + [StrictTypeChecking] void uniform2f(in WebGLUniformLocation location, in float x, in float y) raises(DOMException); + [StrictTypeChecking, Custom] void uniform2fv(in WebGLUniformLocation location, in Float32Array v) raises(DOMException); + [StrictTypeChecking] void uniform2i(in WebGLUniformLocation location, in long x, in long y) raises(DOMException); + [StrictTypeChecking, Custom] void uniform2iv(in WebGLUniformLocation location, in Int32Array v) raises(DOMException); + [StrictTypeChecking] void uniform3f(in WebGLUniformLocation location, in float x, in float y, in float z) raises(DOMException); + [StrictTypeChecking, Custom] void uniform3fv(in WebGLUniformLocation location, in Float32Array v) raises(DOMException); + [StrictTypeChecking] void uniform3i(in WebGLUniformLocation location, in long x, in long y, in long z) raises(DOMException); + [StrictTypeChecking, Custom] void uniform3iv(in WebGLUniformLocation location, in Int32Array v) raises(DOMException); + [StrictTypeChecking] void uniform4f(in WebGLUniformLocation location, in float x, in float y, in float z, in float w) raises(DOMException); + [StrictTypeChecking, Custom] void uniform4fv(in WebGLUniformLocation location, in Float32Array v) raises(DOMException); + [StrictTypeChecking] void uniform4i(in WebGLUniformLocation location, in long x, in long y, in long z, in long w) raises(DOMException); + [StrictTypeChecking, Custom] void uniform4iv(in WebGLUniformLocation location, in Int32Array v) raises(DOMException); + + [StrictTypeChecking, Custom] void uniformMatrix2fv(in WebGLUniformLocation location, in boolean transpose, in Float32Array array) raises(DOMException); + [StrictTypeChecking, Custom] void uniformMatrix3fv(in WebGLUniformLocation location, in boolean transpose, in Float32Array array) raises(DOMException); + [StrictTypeChecking, Custom] void uniformMatrix4fv(in WebGLUniformLocation location, in boolean transpose, in Float32Array array) raises(DOMException); + + [StrictTypeChecking] void useProgram(in WebGLProgram program) raises(DOMException); + [StrictTypeChecking] void validateProgram(in WebGLProgram program) raises(DOMException); + + [StrictTypeChecking] void vertexAttrib1f(in unsigned long indx, in float x); + [StrictTypeChecking, Custom] void vertexAttrib1fv(in unsigned long indx, in Float32Array values); + [StrictTypeChecking] void vertexAttrib2f(in unsigned long indx, in float x, in float y); + [StrictTypeChecking, Custom] void vertexAttrib2fv(in unsigned long indx, in Float32Array values); + [StrictTypeChecking] void vertexAttrib3f(in unsigned long indx, in float x, in float y, in float z); + [StrictTypeChecking, Custom] void vertexAttrib3fv(in unsigned long indx, in Float32Array values); + [StrictTypeChecking] void vertexAttrib4f(in unsigned long indx, in float x, in float y, in float z, in float w); + [StrictTypeChecking, Custom] void vertexAttrib4fv(in unsigned long indx, in Float32Array values); + [StrictTypeChecking] void vertexAttribPointer(in unsigned long indx, in long size, in unsigned long type, in boolean normalized, + in long stride, in long offset) raises(DOMException); + + [StrictTypeChecking] void viewport(in long x, in long y, in long width, in long height); + }; +} + diff --git a/Source/WebCore/html/canvas/WebGLShader.cpp b/Source/WebCore/html/canvas/WebGLShader.cpp new file mode 100644 index 000000000..59695e4c9 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLShader.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLShader.h" + +#include "WebGLRenderingContext.h" + +namespace WebCore { + +PassRefPtr<WebGLShader> WebGLShader::create(WebGLRenderingContext* ctx, GC3Denum type) +{ + return adoptRef(new WebGLShader(ctx, type)); +} + +WebGLShader::WebGLShader(WebGLRenderingContext* ctx, GC3Denum type) + : WebGLObject(ctx) + , m_type(type) + , m_source("") +{ + setObject(context()->graphicsContext3D()->createShader(type)); +} + +void WebGLShader::deleteObjectImpl(Platform3DObject object) +{ + context()->graphicsContext3D()->deleteShader(object); +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLShader.h b/Source/WebCore/html/canvas/WebGLShader.h new file mode 100644 index 000000000..1d7a10c1c --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLShader.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLShader_h +#define WebGLShader_h + +#include "WebGLObject.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLShader : public WebGLObject { +public: + virtual ~WebGLShader() { deleteObject(); } + + static PassRefPtr<WebGLShader> create(WebGLRenderingContext*, GC3Denum); + + GC3Denum getType() const { return m_type; } + const String& getSource() const { return m_source; } + + void setSource(const String& source) { m_source = source; } + +private: + WebGLShader(WebGLRenderingContext*, GC3Denum); + + virtual void deleteObjectImpl(Platform3DObject); + + virtual bool isShader() const { return true; } + + GC3Denum m_type; + String m_source; +}; + +} // namespace WebCore + +#endif // WebGLShader_h diff --git a/Source/WebCore/html/canvas/WebGLShader.idl b/Source/WebCore/html/canvas/WebGLShader.idl new file mode 100644 index 000000000..2aeb704d6 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLShader.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLShader { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLTexture.cpp b/Source/WebCore/html/canvas/WebGLTexture.cpp new file mode 100644 index 000000000..e8e8bf824 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLTexture.cpp @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLTexture.h" + +#include "WebGLFramebuffer.h" +#include "WebGLRenderingContext.h" + +namespace WebCore { + +PassRefPtr<WebGLTexture> WebGLTexture::create(WebGLRenderingContext* ctx) +{ + return adoptRef(new WebGLTexture(ctx)); +} + +WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx) + : WebGLObject(ctx) + , m_target(0) + , m_minFilter(GraphicsContext3D::NEAREST_MIPMAP_LINEAR) + , m_magFilter(GraphicsContext3D::LINEAR) + , m_wrapS(GraphicsContext3D::REPEAT) + , m_wrapT(GraphicsContext3D::REPEAT) + , m_isNPOT(false) + , m_isComplete(false) + , m_needToUseBlackTexture(false) +{ + setObject(context()->graphicsContext3D()->createTexture()); +} + +void WebGLTexture::setTarget(GC3Denum target, GC3Dint maxLevel) +{ + if (!object()) + return; + // Target is finalized the first time bindTexture() is called. + if (m_target) + return; + switch (target) { + case GraphicsContext3D::TEXTURE_2D: + m_target = target; + m_info.resize(1); + m_info[0].resize(maxLevel); + break; + case GraphicsContext3D::TEXTURE_CUBE_MAP: + m_target = target; + m_info.resize(6); + for (int ii = 0; ii < 6; ++ii) + m_info[ii].resize(maxLevel); + break; + } +} + +void WebGLTexture::setParameteri(GC3Denum pname, GC3Dint param) +{ + if (!object() || !m_target) + return; + switch (pname) { + case GraphicsContext3D::TEXTURE_MIN_FILTER: + switch (param) { + case GraphicsContext3D::NEAREST: + case GraphicsContext3D::LINEAR: + case GraphicsContext3D::NEAREST_MIPMAP_NEAREST: + case GraphicsContext3D::LINEAR_MIPMAP_NEAREST: + case GraphicsContext3D::NEAREST_MIPMAP_LINEAR: + case GraphicsContext3D::LINEAR_MIPMAP_LINEAR: + m_minFilter = param; + break; + } + break; + case GraphicsContext3D::TEXTURE_MAG_FILTER: + switch (param) { + case GraphicsContext3D::NEAREST: + case GraphicsContext3D::LINEAR: + m_magFilter = param; + break; + } + break; + case GraphicsContext3D::TEXTURE_WRAP_S: + switch (param) { + case GraphicsContext3D::CLAMP_TO_EDGE: + case GraphicsContext3D::MIRRORED_REPEAT: + case GraphicsContext3D::REPEAT: + m_wrapS = param; + break; + } + break; + case GraphicsContext3D::TEXTURE_WRAP_T: + switch (param) { + case GraphicsContext3D::CLAMP_TO_EDGE: + case GraphicsContext3D::MIRRORED_REPEAT: + case GraphicsContext3D::REPEAT: + m_wrapT = param; + break; + } + break; + default: + return; + } + update(); +} + +void WebGLTexture::setParameterf(GC3Denum pname, GC3Dfloat param) +{ + if (!object() || !m_target) + return; + GC3Dint iparam = static_cast<GC3Dint>(param); + setParameteri(pname, iparam); +} + +void WebGLTexture::setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type) +{ + if (!object() || !m_target) + return; + // We assume level, internalFormat, width, height, and type have all been + // validated already. + int index = mapTargetToIndex(target); + if (index < 0) + return; + m_info[index][level].setInfo(internalFormat, width, height, type); + update(); +} + +void WebGLTexture::generateMipmapLevelInfo() +{ + if (!object() || !m_target) + return; + if (!canGenerateMipmaps()) + return; + if (!m_isComplete) { + for (size_t ii = 0; ii < m_info.size(); ++ii) { + const LevelInfo& info0 = m_info[ii][0]; + GC3Dsizei width = info0.width; + GC3Dsizei height = info0.height; + GC3Dint levelCount = computeLevelCount(width, height); + for (GC3Dint level = 1; level < levelCount; ++level) { + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + LevelInfo& info = m_info[ii][level]; + info.setInfo(info0.internalFormat, width, height, info0.type); + } + } + m_isComplete = true; + } + m_needToUseBlackTexture = false; +} + +GC3Denum WebGLTexture::getInternalFormat(GC3Denum target, GC3Dint level) const +{ + const LevelInfo* info = getLevelInfo(target, level); + if (!info) + return 0; + return info->internalFormat; +} + +GC3Denum WebGLTexture::getType(GC3Denum target, GC3Dint level) const +{ + const LevelInfo* info = getLevelInfo(target, level); + if (!info) + return 0; + return info->type; +} + +GC3Dsizei WebGLTexture::getWidth(GC3Denum target, GC3Dint level) const +{ + const LevelInfo* info = getLevelInfo(target, level); + if (!info) + return 0; + return info->width; +} + +GC3Dsizei WebGLTexture::getHeight(GC3Denum target, GC3Dint level) const +{ + const LevelInfo* info = getLevelInfo(target, level); + if (!info) + return 0; + return info->height; +} + +bool WebGLTexture::isNPOT(GC3Dsizei width, GC3Dsizei height) +{ + ASSERT(width >= 0 && height >= 0); + if (!width || !height) + return false; + if ((width & (width - 1)) || (height & (height - 1))) + return true; + return false; +} + +bool WebGLTexture::isNPOT() const +{ + if (!object()) + return false; + return m_isNPOT; +} + +bool WebGLTexture::needToUseBlackTexture() const +{ + if (!object()) + return false; + return m_needToUseBlackTexture; +} + +void WebGLTexture::deleteObjectImpl(Platform3DObject object) +{ + context()->graphicsContext3D()->deleteTexture(object); +} + +int WebGLTexture::mapTargetToIndex(GC3Denum target) const +{ + if (m_target == GraphicsContext3D::TEXTURE_2D) { + if (target == GraphicsContext3D::TEXTURE_2D) + return 0; + } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) { + switch (target) { + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: + return 0; + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: + return 1; + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: + return 2; + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: + return 3; + case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: + return 4; + case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: + return 5; + } + } + return -1; +} + +bool WebGLTexture::canGenerateMipmaps() +{ + if (isNPOT()) + return false; + const LevelInfo& first = m_info[0][0]; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + const LevelInfo& info = m_info[ii][0]; + if (!info.valid + || info.width != first.width || info.height != first.height + || info.internalFormat != first.internalFormat || info.type != first.type) + return false; + } + return true; +} + +GC3Dint WebGLTexture::computeLevelCount(GC3Dsizei width, GC3Dsizei height) +{ + // return 1 + log2Floor(std::max(width, height)); + GC3Dsizei n = std::max(width, height); + if (n <= 0) + return 0; + GC3Dint log = 0; + GC3Dsizei value = n; + for (int ii = 4; ii >= 0; --ii) { + int shift = (1 << ii); + GC3Dsizei x = (value >> shift); + if (x) { + value = x; + log += shift; + } + } + ASSERT(value == 1); + return log + 1; +} + +void WebGLTexture::update() +{ + m_isNPOT = false; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) { + m_isNPOT = true; + break; + } + } + m_isComplete = true; + const LevelInfo& first = m_info[0][0]; + GC3Dint levelCount = computeLevelCount(first.width, first.height); + if (levelCount < 1) + m_isComplete = false; + else { + for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) { + const LevelInfo& info0 = m_info[ii][0]; + if (!info0.valid + || info0.width != first.width || info0.height != first.height + || info0.internalFormat != first.internalFormat || info0.type != first.type) { + m_isComplete = false; + break; + } + GC3Dsizei width = info0.width; + GC3Dsizei height = info0.height; + for (GC3Dint level = 1; level < levelCount; ++level) { + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + const LevelInfo& info = m_info[ii][level]; + if (!info.valid + || info.width != width || info.height != height + || info.internalFormat != info0.internalFormat || info.type != info0.type) { + m_isComplete = false; + break; + } + + } + } + } + + m_needToUseBlackTexture = false; + // NPOT + if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) + || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE)) + m_needToUseBlackTexture = true; + // Completeness + if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) + m_needToUseBlackTexture = true; +} + +const WebGLTexture::LevelInfo* WebGLTexture::getLevelInfo(GC3Denum target, GC3Dint level) const +{ + if (!object() || !m_target) + return 0; + int targetIndex = mapTargetToIndex(target); + if (targetIndex < 0 || targetIndex >= static_cast<int>(m_info.size())) + return 0; + if (level < 0 || level >= static_cast<GC3Dint>(m_info[targetIndex].size())) + return 0; + return &(m_info[targetIndex][level]); +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLTexture.h b/Source/WebCore/html/canvas/WebGLTexture.h new file mode 100644 index 000000000..6bd12723c --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLTexture.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLTexture_h +#define WebGLTexture_h + +#include "WebGLObject.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class WebGLTexture : public WebGLObject { +public: + virtual ~WebGLTexture() { deleteObject(); } + + static PassRefPtr<WebGLTexture> create(WebGLRenderingContext*); + + void setTarget(GC3Denum target, GC3Dint maxLevel); + void setParameteri(GC3Denum pname, GC3Dint param); + void setParameterf(GC3Denum pname, GC3Dfloat param); + + GC3Denum getTarget() const { return m_target; } + + int getMinFilter() const { return m_minFilter; } + + void setLevelInfo(GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Dsizei width, GC3Dsizei height, GC3Denum type); + + bool canGenerateMipmaps(); + // Generate all level information. + void generateMipmapLevelInfo(); + + GC3Denum getInternalFormat(GC3Denum target, GC3Dint level) const; + GC3Denum getType(GC3Denum target, GC3Dint level) const; + GC3Dsizei getWidth(GC3Denum target, GC3Dint level) const; + GC3Dsizei getHeight(GC3Denum target, GC3Dint level) const; + + // Whether width/height is NotPowerOfTwo. + static bool isNPOT(GC3Dsizei, GC3Dsizei); + + bool isNPOT() const; + // Determine if texture sampling should always return [0, 0, 0, 1] (OpenGL ES 2.0 Sec 3.8.2). + bool needToUseBlackTexture() const; + + bool hasEverBeenBound() const { return object() && m_target; } + + static GC3Dint computeLevelCount(GC3Dsizei width, GC3Dsizei height); + +protected: + WebGLTexture(WebGLRenderingContext*); + + virtual void deleteObjectImpl(Platform3DObject); + +private: + class LevelInfo { + public: + LevelInfo() + : valid(false) + , internalFormat(0) + , width(0) + , height(0) + , type(0) + { + } + + void setInfo(GC3Denum internalFmt, GC3Dsizei w, GC3Dsizei h, GC3Denum tp) + { + valid = true; + internalFormat = internalFmt; + width = w; + height = h; + type = tp; + } + + bool valid; + GC3Denum internalFormat; + GC3Dsizei width; + GC3Dsizei height; + GC3Denum type; + }; + + virtual bool isTexture() const { return true; } + + void update(); + + int mapTargetToIndex(GC3Denum) const; + + const LevelInfo* getLevelInfo(GC3Denum target, GC3Dint level) const; + + GC3Denum m_target; + + GC3Denum m_minFilter; + GC3Denum m_magFilter; + GC3Denum m_wrapS; + GC3Denum m_wrapT; + + Vector<Vector<LevelInfo> > m_info; + + bool m_isNPOT; + bool m_isComplete; + bool m_needToUseBlackTexture; +}; + +} // namespace WebCore + +#endif // WebGLTexture_h diff --git a/Source/WebCore/html/canvas/WebGLTexture.idl b/Source/WebCore/html/canvas/WebGLTexture.idl new file mode 100644 index 000000000..8e72dd34d --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLTexture.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLTexture { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLUniformLocation.cpp b/Source/WebCore/html/canvas/WebGLUniformLocation.cpp new file mode 100644 index 000000000..1aeddb568 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLUniformLocation.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLUniformLocation.h" + +namespace WebCore { + +PassRefPtr<WebGLUniformLocation> WebGLUniformLocation::create(WebGLProgram* program, GC3Dint location) +{ + return adoptRef(new WebGLUniformLocation(program, location)); +} + +WebGLUniformLocation::WebGLUniformLocation(WebGLProgram* program, GC3Dint location) + : m_program(program) + , m_location(location) +{ + ASSERT(m_program); + m_linkCount = m_program->getLinkCount(); +} + +WebGLProgram* WebGLUniformLocation::program() const +{ + // If the program has been linked again, then this UniformLocation is no + // longer valid. + if (m_program->getLinkCount() != m_linkCount) + return 0; + return m_program.get(); +} + +GC3Dint WebGLUniformLocation::location() const +{ + // If the program has been linked again, then this UniformLocation is no + // longer valid. + ASSERT(m_program->getLinkCount() == m_linkCount); + return m_location; +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLUniformLocation.h b/Source/WebCore/html/canvas/WebGLUniformLocation.h new file mode 100644 index 000000000..6e05c7a4c --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLUniformLocation.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLUniformLocation_h +#define WebGLUniformLocation_h + +#include "WebGLObject.h" +#include "WebGLProgram.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLUniformLocation : public RefCounted<WebGLUniformLocation> { +public: + virtual ~WebGLUniformLocation() { } + + static PassRefPtr<WebGLUniformLocation> create(WebGLProgram*, GC3Dint location); + + WebGLProgram* program() const; + + GC3Dint location() const; + +protected: + WebGLUniformLocation(WebGLProgram*, GC3Dint location); + +private: + RefPtr<WebGLProgram> m_program; + GC3Dint m_location; + unsigned m_linkCount; +}; + +} // namespace WebCore + +#endif // WebGLUniformLocation_h diff --git a/Source/WebCore/html/canvas/WebGLUniformLocation.idl b/Source/WebCore/html/canvas/WebGLUniformLocation.idl new file mode 100644 index 000000000..eb3167cd9 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLUniformLocation.idl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLUniformLocation { + }; +} diff --git a/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.cpp b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.cpp new file mode 100644 index 000000000..d14c96c01 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(WEBGL) + +#include "WebGLVertexArrayObjectOES.h" + +#include "Extensions3D.h" +#include "WebGLRenderingContext.h" + +namespace WebCore { + +PassRefPtr<WebGLVertexArrayObjectOES> WebGLVertexArrayObjectOES::create(WebGLRenderingContext* ctx, VaoType type) +{ + return adoptRef(new WebGLVertexArrayObjectOES(ctx, type)); +} + +WebGLVertexArrayObjectOES::WebGLVertexArrayObjectOES(WebGLRenderingContext* ctx, VaoType type) + : WebGLObject(ctx) + , m_type(type) + , m_hasEverBeenBound(false) + , m_boundElementArrayBuffer(0) +{ + m_vertexAttribState.resize(ctx->getMaxVertexAttribs()); + + Extensions3D* extensions = context()->graphicsContext3D()->getExtensions(); + switch (m_type) { + case VaoTypeDefault: + break; + default: + setObject(extensions->createVertexArrayOES()); + break; + } +} + +void WebGLVertexArrayObjectOES::deleteObjectImpl(Platform3DObject object) +{ + Extensions3D* extensions = context()->graphicsContext3D()->getExtensions(); + switch (m_type) { + case VaoTypeDefault: + break; + default: + extensions->deleteVertexArrayOES(object); + break; + } +} + +} + +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.h b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.h new file mode 100644 index 000000000..f49a78069 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebGLVertexArrayObjectOES_h +#define WebGLVertexArrayObjectOES_h + +#include "WebGLBuffer.h" +#include "WebGLObject.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class WebGLVertexArrayObjectOES : public WebGLObject { +public: + enum VaoType { + VaoTypeDefault, + VaoTypeUser, + }; + + virtual ~WebGLVertexArrayObjectOES() { deleteObject(); } + + static PassRefPtr<WebGLVertexArrayObjectOES> create(WebGLRenderingContext*, VaoType); + + // Cached values for vertex attrib range checks + struct VertexAttribState { + VertexAttribState() + : enabled(false) + , bytesPerElement(0) + , size(4) + , type(GraphicsContext3D::FLOAT) + , normalized(false) + , stride(16) + , originalStride(0) + , offset(0) + { + } + + bool enabled; + RefPtr<WebGLBuffer> bufferBinding; + GC3Dsizei bytesPerElement; + GC3Dint size; + GC3Denum type; + bool normalized; + GC3Dsizei stride; + GC3Dsizei originalStride; + GC3Dintptr offset; + }; + + bool isDefaultObject() const { return m_type == VaoTypeDefault; } + + bool hasEverBeenBound() const { return object() && m_hasEverBeenBound; } + void setHasEverBeenBound() { m_hasEverBeenBound = true; } + + PassRefPtr<WebGLBuffer> getElementArrayBuffer() const { return m_boundElementArrayBuffer; } + void setElementArrayBuffer(PassRefPtr<WebGLBuffer> buffer) { m_boundElementArrayBuffer = buffer; } + + VertexAttribState& getVertexAttribState(int index) { return m_vertexAttribState[index]; } + +private: + WebGLVertexArrayObjectOES(WebGLRenderingContext*, VaoType); + + virtual void deleteObjectImpl(Platform3DObject); + + virtual bool isVertexArray() const { return true; } + + VaoType m_type; + bool m_hasEverBeenBound; + RefPtr<WebGLBuffer> m_boundElementArrayBuffer; + Vector<VertexAttribState> m_vertexAttribState; +}; + +} // namespace WebCore + +#endif // WebGLVertexArrayObjectOES_h diff --git a/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.idl b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.idl new file mode 100644 index 000000000..0abbe0746 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.idl @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + interface [ + Conditional=WEBGL + ] WebGLVertexArrayObjectOES { + }; +} diff --git a/Source/WebCore/html/parser/CSSPreloadScanner.cpp b/Source/WebCore/html/parser/CSSPreloadScanner.cpp new file mode 100644 index 000000000..f91190d91 --- /dev/null +++ b/Source/WebCore/html/parser/CSSPreloadScanner.cpp @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2008, 2010 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ + * Copyright (C) 2010 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "CSSPreloadScanner.h" + +#include "CachedCSSStyleSheet.h" +#include "CachedResourceLoader.h" +#include "Document.h" +#include "HTMLParserIdioms.h" +#include "HTMLToken.h" + +namespace WebCore { + +CSSPreloadScanner::CSSPreloadScanner(Document* document) + : m_state(Initial) + , m_document(document) +{ +} + +void CSSPreloadScanner::reset() +{ + m_state = Initial; + m_rule.clear(); + m_ruleValue.clear(); +} + +void CSSPreloadScanner::scan(const HTMLToken& token, bool scanningBody) +{ + m_scanningBody = scanningBody; + + const HTMLToken::DataVector& characters = token.characters(); + for (HTMLToken::DataVector::const_iterator iter = characters.begin(); iter != characters.end() && m_state != DoneParsingImportRules; ++iter) + tokenize(*iter); +} + +inline void CSSPreloadScanner::tokenize(UChar c) +{ + // We are just interested in @import rules, no need for real tokenization here + // Searching for other types of resources is probably low payoff. + switch (m_state) { + case Initial: + if (isHTMLSpace(c)) + break; + if (c == '@') + m_state = RuleStart; + else if (c == '/') + m_state = MaybeComment; + else + m_state = DoneParsingImportRules; + break; + case MaybeComment: + if (c == '*') + m_state = Comment; + else + m_state = Initial; + break; + case Comment: + if (c == '*') + m_state = MaybeCommentEnd; + break; + case MaybeCommentEnd: + if (c == '*') + break; + if (c == '/') + m_state = Initial; + else + m_state = Comment; + break; + case RuleStart: + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { + m_rule.clear(); + m_ruleValue.clear(); + m_rule.append(c); + m_state = Rule; + } else + m_state = Initial; + break; + case Rule: + if (isHTMLSpace(c)) + m_state = AfterRule; + else if (c == ';') + m_state = Initial; + else + m_rule.append(c); + break; + case AfterRule: + if (isHTMLSpace(c)) + break; + if (c == ';') + m_state = Initial; + else if (c == '{') + m_state = DoneParsingImportRules; + else { + m_state = RuleValue; + m_ruleValue.append(c); + } + break; + case RuleValue: + if (isHTMLSpace(c)) + m_state = AfterRuleValue; + else if (c == ';') + emitRule(); + else + m_ruleValue.append(c); + break; + case AfterRuleValue: + if (isHTMLSpace(c)) + break; + if (c == ';') + emitRule(); + else if (c == '{') + m_state = DoneParsingImportRules; + else { + // FIXME: media rules + m_state = Initial; + } + break; + case DoneParsingImportRules: + ASSERT_NOT_REACHED(); + break; + } +} + +static String parseCSSStringOrURL(const UChar* characters, size_t length) +{ + size_t offset = 0; + size_t reducedLength = length; + + while (reducedLength && isHTMLSpace(characters[offset])) { + ++offset; + --reducedLength; + } + while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1])) + --reducedLength; + + if (reducedLength >= 5 + && (characters[offset] == 'u' || characters[offset] == 'U') + && (characters[offset + 1] == 'r' || characters[offset + 1] == 'R') + && (characters[offset + 2] == 'l' || characters[offset + 2] == 'L') + && characters[offset + 3] == '(' + && characters[offset + reducedLength - 1] == ')') { + offset += 4; + reducedLength -= 5; + } + + while (reducedLength && isHTMLSpace(characters[offset])) { + ++offset; + --reducedLength; + } + while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1])) + --reducedLength; + + if (reducedLength < 2 || characters[offset] != characters[offset + reducedLength - 1] || !(characters[offset] == '\'' || characters[offset] == '"')) + return String(); + offset++; + reducedLength -= 2; + + while (reducedLength && isHTMLSpace(characters[offset])) { + ++offset; + --reducedLength; + } + while (reducedLength && isHTMLSpace(characters[offset + reducedLength - 1])) + --reducedLength; + + return String(characters + offset, reducedLength); +} + +void CSSPreloadScanner::emitRule() +{ + if (equalIgnoringCase("import", m_rule.characters(), m_rule.length())) { + String value = parseCSSStringOrURL(m_ruleValue.characters(), m_ruleValue.length()); + if (!value.isEmpty()) { + ResourceRequest request(m_document->completeURL(value)); + m_document->cachedResourceLoader()->preload(CachedResource::CSSStyleSheet, request, String(), m_scanningBody); + } + m_state = Initial; + } else if (equalIgnoringCase("charset", m_rule.characters(), m_rule.length())) + m_state = Initial; + else + m_state = DoneParsingImportRules; + m_rule.clear(); + m_ruleValue.clear(); +} + +} diff --git a/Source/WebCore/html/parser/CSSPreloadScanner.h b/Source/WebCore/html/parser/CSSPreloadScanner.h new file mode 100644 index 000000000..386ab6fea --- /dev/null +++ b/Source/WebCore/html/parser/CSSPreloadScanner.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2010 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CSSPreloadScanner_h +#define CSSPreloadScanner_h + +#include "PlatformString.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +class Document; +class HTMLToken; + +class CSSPreloadScanner { + WTF_MAKE_NONCOPYABLE(CSSPreloadScanner); +public: + CSSPreloadScanner(Document*); + + void reset(); + void scan(const HTMLToken&, bool scanningBody); + +private: + enum State { + Initial, + MaybeComment, + Comment, + MaybeCommentEnd, + RuleStart, + Rule, + AfterRule, + RuleValue, + AfterRuleValue, + DoneParsingImportRules, + }; + + inline void tokenize(UChar c); + void emitRule(); + + State m_state; + StringBuilder m_rule; + StringBuilder m_ruleValue; + + bool m_scanningBody; + Document* m_document; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLConstructionSite.cpp b/Source/WebCore/html/parser/HTMLConstructionSite.cpp new file mode 100644 index 000000000..8cd8f40fd --- /dev/null +++ b/Source/WebCore/html/parser/HTMLConstructionSite.cpp @@ -0,0 +1,533 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011 Apple 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 GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLTreeBuilder.h" + +#include "Comment.h" +#include "DocumentFragment.h" +#include "DocumentType.h" +#include "Element.h" +#include "Frame.h" +#include "HTMLDocument.h" +#include "HTMLElementFactory.h" +#include "HTMLFormElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLScriptElement.h" +#include "HTMLToken.h" +#include "HTMLTokenizer.h" +#include "LocalizedStrings.h" +#if ENABLE(MATHML) +#include "MathMLNames.h" +#endif +#include "NotImplemented.h" +#if ENABLE(SVG) +#include "SVGNames.h" +#endif +#include "Settings.h" +#include "Text.h" +#include <wtf/UnusedParam.h> + +namespace WebCore { + +using namespace HTMLNames; + +namespace { + +bool hasImpliedEndTag(ContainerNode* node) +{ + return node->hasTagName(ddTag) + || node->hasTagName(dtTag) + || node->hasTagName(liTag) + || node->hasTagName(optionTag) + || node->hasTagName(optgroupTag) + || node->hasTagName(pTag) + || node->hasTagName(rpTag) + || node->hasTagName(rtTag); +} + +bool causesFosterParenting(const QualifiedName& tagName) +{ + return tagName == tableTag + || tagName == tbodyTag + || tagName == tfootTag + || tagName == theadTag + || tagName == trTag; +} + +inline bool isAllWhitespace(const String& string) +{ + return string.isAllSpecialCharacters<isHTMLSpace>(); +} + +} // namespace + +static inline void executeTask(HTMLConstructionSiteTask& task) +{ + if (task.nextChild) + task.parent->parserInsertBefore(task.child.get(), task.nextChild.get()); + else + task.parent->parserAddChild(task.child.get()); + + // JavaScript run from beforeload (or DOM Mutation or event handlers) + // might have removed the child, in which case we should not attach it. + + if (task.child->parentNode() && task.parent->attached() && !task.child->attached()) + task.child->attach(); + + task.child->beginParsingChildren(); + + if (task.selfClosing) + task.child->finishParsingChildren(); +} + +void HTMLConstructionSite::attachLater(ContainerNode* parent, PassRefPtr<Node> prpChild) +{ + HTMLConstructionSiteTask task; + task.parent = parent; + task.child = prpChild; + + if (shouldFosterParent()) { + fosterParent(task.child); + return; + } + + // Add as a sibling of the parent if we have reached the maximum depth allowed. + if (m_openElements.stackDepth() > m_maximumDOMTreeDepth) + task.parent = task.parent->parentNode(); + + m_attachmentQueue.append(task); +} + +void HTMLConstructionSite::executeQueuedTasks() +{ + const size_t size = m_attachmentQueue.size(); + if (!size) + return; + + // Copy the task queue into a local variable in case executeTask + // re-enters the parser. + AttachmentQueue queue; + queue.swap(m_attachmentQueue); + + for (size_t i = 0; i < size; ++i) + executeTask(queue[i]); + + // We might be detached now. +} + +HTMLConstructionSite::HTMLConstructionSite(Document* document, unsigned maximumDOMTreeDepth) + : m_document(document) + , m_attachmentRoot(document) + , m_fragmentScriptingPermission(FragmentScriptingAllowed) + , m_isParsingFragment(false) + , m_redirectAttachToFosterParent(false) + , m_maximumDOMTreeDepth(maximumDOMTreeDepth) +{ +} + +HTMLConstructionSite::HTMLConstructionSite(DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission, unsigned maximumDOMTreeDepth) + : m_document(fragment->document()) + , m_attachmentRoot(fragment) + , m_fragmentScriptingPermission(scriptingPermission) + , m_isParsingFragment(true) + , m_redirectAttachToFosterParent(false) + , m_maximumDOMTreeDepth(maximumDOMTreeDepth) +{ +} + +HTMLConstructionSite::~HTMLConstructionSite() +{ +} + +void HTMLConstructionSite::detach() +{ + m_document = 0; + m_attachmentRoot = 0; +} + +void HTMLConstructionSite::setForm(HTMLFormElement* form) +{ + // This method should only be needed for HTMLTreeBuilder in the fragment case. + ASSERT(!m_form); + m_form = form; +} + +PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm() +{ + return m_form.release(); +} + +void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded() +{ + ASSERT(m_document); + if (m_document->frame() && !m_isParsingFragment) + m_document->frame()->loader()->dispatchDocumentElementAvailable(); +} + +void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token) +{ + RefPtr<HTMLHtmlElement> element = HTMLHtmlElement::create(m_document); + element->parserSetAttributeMap(token.takeAttributes(), m_fragmentScriptingPermission); + attachLater(m_attachmentRoot, element); + m_openElements.pushHTMLHtmlElement(element); + // FIXME: We probably need to call executeQueuedTasks() before calling these methods. + element->insertedByParser(); + dispatchDocumentElementAvailableIfNeeded(); +} + +void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element) +{ + if (!token.attributes()) + return; + + NamedNodeMap* attributes = element->attributes(false); + for (unsigned i = 0; i < token.attributes()->length(); ++i) { + Attribute* attribute = token.attributes()->attributeItem(i); + if (!attributes->getAttributeItem(attribute->name())) + element->setAttribute(attribute->name(), attribute->value()); + } +} + +void HTMLConstructionSite::insertHTMLHtmlStartTagInBody(AtomicHTMLToken& token) +{ + // FIXME: parse error + + // Fragments do not have a root HTML element, so any additional HTML elements + // encountered during fragment parsing should be ignored. + if (m_isParsingFragment) + return; + + mergeAttributesFromTokenIntoElement(token, m_openElements.htmlElement()); +} + +void HTMLConstructionSite::insertHTMLBodyStartTagInBody(AtomicHTMLToken& token) +{ + // FIXME: parse error + mergeAttributesFromTokenIntoElement(token, m_openElements.bodyElement()); +} + +void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::DOCTYPE); + + RefPtr<DocumentType> doctype = DocumentType::create(m_document, token.name(), String::adopt(token.publicIdentifier()), String::adopt(token.systemIdentifier())); + attachLater(m_attachmentRoot, doctype.release()); + + // DOCTYPE nodes are only processed when parsing fragments w/o contextElements, which + // never occurs. However, if we ever chose to support such, this code is subtly wrong, + // because context-less fragments can determine their own quirks mode, and thus change + // parsing rules (like <p> inside <table>). For now we ASSERT that we never hit this code + // in a fragment, as changing the owning document's compatibility mode would be wrong. + ASSERT(!m_isParsingFragment); + if (m_isParsingFragment) + return; + + if (token.forceQuirks()) + m_document->setCompatibilityMode(Document::QuirksMode); + else { + // We need to actually add the Doctype node to the DOM. + executeQueuedTasks(); + m_document->setCompatibilityModeFromDoctype(); + } +} + +void HTMLConstructionSite::insertComment(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::Comment); + attachLater(currentNode(), Comment::create(currentNode()->document(), token.comment())); +} + +void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::Comment); + attachLater(m_attachmentRoot, Comment::create(m_document, token.comment())); +} + +void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::Comment); + ContainerNode* parent = m_openElements.rootNode(); + attachLater(parent, Comment::create(parent->document(), token.comment())); +} + +void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token) +{ + ASSERT(!shouldFosterParent()); + m_head = createHTMLElement(token); + attachLater(currentNode(), m_head); + m_openElements.pushHTMLHeadElement(m_head); +} + +void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token) +{ + ASSERT(!shouldFosterParent()); + RefPtr<Element> body = createHTMLElement(token); + attachLater(currentNode(), body); + m_openElements.pushHTMLBodyElement(body.release()); +} + +void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token, bool isDemoted) +{ + RefPtr<Element> element = createHTMLElement(token); + ASSERT(element->hasTagName(formTag)); + m_form = static_pointer_cast<HTMLFormElement>(element.release()); + m_form->setDemoted(isDemoted); + attachLater(currentNode(), m_form); + m_openElements.push(m_form); +} + +void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token) +{ + RefPtr<Element> element = createHTMLElement(token); + attachLater(currentNode(), element); + m_openElements.push(element.release()); +} + +void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + attachLater(currentNode(), createHTMLElement(token)); + // Normally HTMLElementStack is responsible for calling finishParsingChildren, + // but self-closing elements are never in the element stack so the stack + // doesn't get a chance to tell them that we're done parsing their children. + m_attachmentQueue.last().selfClosing = true; + // FIXME: Do we want to acknowledge the token's self-closing flag? + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag +} + +void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token) +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#the-stack-of-open-elements + // Possible active formatting elements include: + // a, b, big, code, em, font, i, nobr, s, small, strike, strong, tt, and u. + insertHTMLElement(token); + m_activeFormattingElements.append(currentElement()); +} + +void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token) +{ + RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, currentNode()->document(), true); + if (m_fragmentScriptingPermission == FragmentScriptingAllowed) + element->parserSetAttributeMap(token.takeAttributes(), m_fragmentScriptingPermission); + attachLater(currentNode(), element); + m_openElements.push(element.release()); +} + +void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + notImplemented(); // parseError when xmlns or xmlns:xlink are wrong. + + RefPtr<Element> element = createElement(token, namespaceURI); + attachLater(currentNode(), element); + // FIXME: Don't we need to set the selfClosing flag on the task if we're + // not going to push the element on to the stack of open elements? + if (!token.selfClosing()) + m_openElements.push(element.release()); +} + +void HTMLConstructionSite::insertTextNode(const String& characters, WhitespaceMode whitespaceMode) +{ + HTMLConstructionSiteTask task; + task.parent = currentNode(); + + if (shouldFosterParent()) + findFosterSite(task); + + // Strings composed entirely of whitespace are likely to be repeated. + // Turn them into AtomicString so we share a single string for each. + bool shouldUseAtomicString = whitespaceMode == AllWhitespace + || (whitespaceMode == WhitespaceUnknown && isAllWhitespace(characters)); + + unsigned currentPosition = 0; + + // FIXME: Splitting text nodes into smaller chunks contradicts HTML5 spec, but is currently necessary + // for performance, see <https://bugs.webkit.org/show_bug.cgi?id=55898>. + + Node* previousChild = task.nextChild ? task.nextChild->previousSibling() : task.parent->lastChild(); + if (previousChild && previousChild->isTextNode()) { + // FIXME: We're only supposed to append to this text node if it + // was the last text node inserted by the parser. + CharacterData* textNode = static_cast<CharacterData*>(previousChild); + currentPosition = textNode->parserAppendData(characters.characters(), characters.length(), Text::defaultLengthLimit); + } + + while (currentPosition < characters.length()) { + RefPtr<Text> textNode = Text::createWithLengthLimit(task.parent->document(), shouldUseAtomicString ? AtomicString(characters).string() : characters, currentPosition); + // If we have a whole string of unbreakable characters the above could lead to an infinite loop. Exceeding the length limit is the lesser evil. + if (!textNode->length()) { + String substring = characters.substring(currentPosition); + textNode = Text::create(task.parent->document(), shouldUseAtomicString ? AtomicString(substring).string() : substring); + } + + currentPosition += textNode->length(); + ASSERT(currentPosition <= characters.length()); + task.child = textNode.release(); + executeTask(task); + } +} + +PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI) +{ + QualifiedName tagName(nullAtom, token.name(), namespaceURI); + RefPtr<Element> element = currentNode()->document()->createElement(tagName, true); + element->parserSetAttributeMap(token.takeAttributes(), m_fragmentScriptingPermission); + return element.release(); +} + +PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token) +{ + QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI); + // FIXME: This can't use HTMLConstructionSite::createElement because we + // have to pass the current form element. We should rework form association + // to occur after construction to allow better code sharing here. + RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, currentNode()->document(), form(), true); + element->parserSetAttributeMap(token.takeAttributes(), m_fragmentScriptingPermission); + ASSERT(element->isHTMLElement()); + return element.release(); +} + +PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record) +{ + return createHTMLElementFromSavedElement(record->element()); +} + +namespace { + +// FIXME: Move this function to the top of the file. +inline PassOwnPtr<NamedNodeMap> cloneAttributes(Element* element) +{ + NamedNodeMap* attributes = element->attributes(true); + if (!attributes) + return nullptr; + + OwnPtr<NamedNodeMap> newAttributes = NamedNodeMap::create(); + for (size_t i = 0; i < attributes->length(); ++i) { + Attribute* attribute = attributes->attributeItem(i); + RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value()); + newAttributes->addAttribute(clone); + } + return newAttributes.release(); +} + +} // namespace + +PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element) +{ + // FIXME: This method is wrong. We should be using the original token. + // Using an Element* causes us to fail examples like this: + // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b> + // When reconstructTheActiveFormattingElements calls this method to open + // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5 + // spec implies it should be "1". Minefield matches the HTML5 spec here. + + ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong. + AtomicHTMLToken fakeToken(HTMLTokenTypes::StartTag, element->localName(), cloneAttributes(element)); + return createHTMLElement(fakeToken); +} + +bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const +{ + if (m_activeFormattingElements.isEmpty()) + return false; + unsigned index = m_activeFormattingElements.size(); + do { + --index; + const HTMLFormattingElementList::Entry& entry = m_activeFormattingElements.at(index); + if (entry.isMarker() || m_openElements.contains(entry.element())) { + firstUnopenElementIndex = index + 1; + return firstUnopenElementIndex < m_activeFormattingElements.size(); + } + } while (index); + firstUnopenElementIndex = index; + return true; +} + +void HTMLConstructionSite::reconstructTheActiveFormattingElements() +{ + unsigned firstUnopenElementIndex; + if (!indexOfFirstUnopenFormattingElement(firstUnopenElementIndex)) + return; + + unsigned unopenEntryIndex = firstUnopenElementIndex; + ASSERT(unopenEntryIndex < m_activeFormattingElements.size()); + for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) { + HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex); + RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element()); + attachLater(currentNode(), reconstructed); + m_openElements.push(reconstructed.release()); + unopenedEntry.replaceElement(currentElement()); + } +} + +void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName) +{ + while (hasImpliedEndTag(currentNode()) && !currentNode()->hasLocalName(tagName)) + m_openElements.pop(); +} + +void HTMLConstructionSite::generateImpliedEndTags() +{ + while (hasImpliedEndTag(currentNode())) + m_openElements.pop(); +} + +void HTMLConstructionSite::findFosterSite(HTMLConstructionSiteTask& task) +{ + HTMLElementStack::ElementRecord* lastTableElementRecord = m_openElements.topmost(tableTag.localName()); + if (lastTableElementRecord) { + Element* lastTableElement = lastTableElementRecord->element(); + if (ContainerNode* parent = lastTableElement->parentNode()) { + task.parent = parent; + task.nextChild = lastTableElement; + return; + } + task.parent = lastTableElementRecord->next()->element(); + return; + } + // Fragment case + task.parent = m_openElements.rootNode(); // DocumentFragment +} + +bool HTMLConstructionSite::shouldFosterParent() const +{ + return m_redirectAttachToFosterParent + && currentNode()->isElementNode() + && causesFosterParenting(currentElement()->tagQName()); +} + +void HTMLConstructionSite::fosterParent(PassRefPtr<Node> node) +{ + HTMLConstructionSiteTask task; + findFosterSite(task); + task.child = node; + m_attachmentQueue.append(task); +} + +} diff --git a/Source/WebCore/html/parser/HTMLConstructionSite.h b/Source/WebCore/html/parser/HTMLConstructionSite.h new file mode 100644 index 000000000..6e28d3a9b --- /dev/null +++ b/Source/WebCore/html/parser/HTMLConstructionSite.h @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011 Apple 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 GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLConstructionSite_h +#define HTMLConstructionSite_h + +#include "FragmentScriptingPermission.h" +#include "HTMLElementStack.h" +#include "HTMLFormattingElementList.h" +#include "NotImplemented.h" +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +struct HTMLConstructionSiteTask { + HTMLConstructionSiteTask() + : selfClosing(false) + { + } + + void take(HTMLConstructionSiteTask& other) + { + parent = other.parent.release(); + nextChild = other.nextChild.release(); + child = other.child.release(); + selfClosing = other.selfClosing; + } + + RefPtr<ContainerNode> parent; + RefPtr<Node> nextChild; + RefPtr<Node> child; + bool selfClosing; +}; + +} // namespace WebCore + +namespace WTF { +template<> struct VectorTraits<WebCore::HTMLConstructionSiteTask> : SimpleClassVectorTraits { }; +} // namespace WTF + +namespace WebCore { + +enum WhitespaceMode { + AllWhitespace, + NotAllWhitespace, + WhitespaceUnknown +}; + +class AtomicHTMLToken; +class Document; +class Element; + +class HTMLConstructionSite { + WTF_MAKE_NONCOPYABLE(HTMLConstructionSite); +public: + HTMLConstructionSite(Document*, unsigned maximumDOMTreeDepth); + HTMLConstructionSite(DocumentFragment*, FragmentScriptingPermission, unsigned maximumDOMTreeDepth); + ~HTMLConstructionSite(); + + void detach(); + void executeQueuedTasks(); + + void insertDoctype(AtomicHTMLToken&); + void insertComment(AtomicHTMLToken&); + void insertCommentOnDocument(AtomicHTMLToken&); + void insertCommentOnHTMLHtmlElement(AtomicHTMLToken&); + void insertHTMLElement(AtomicHTMLToken&); + void insertSelfClosingHTMLElement(AtomicHTMLToken&); + void insertFormattingElement(AtomicHTMLToken&); + void insertHTMLHeadElement(AtomicHTMLToken&); + void insertHTMLBodyElement(AtomicHTMLToken&); + void insertHTMLFormElement(AtomicHTMLToken&, bool isDemoted = false); + void insertScriptElement(AtomicHTMLToken&); + void insertTextNode(const String&, WhitespaceMode = WhitespaceUnknown); + void insertForeignElement(AtomicHTMLToken&, const AtomicString& namespaceURI); + + void insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken&); + void insertHTMLHtmlStartTagInBody(AtomicHTMLToken&); + void insertHTMLBodyStartTagInBody(AtomicHTMLToken&); + + PassRefPtr<Element> createHTMLElement(AtomicHTMLToken&); + PassRefPtr<Element> createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord*); + + bool shouldFosterParent() const; + void fosterParent(PassRefPtr<Node>); + + bool indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const; + void reconstructTheActiveFormattingElements(); + + void generateImpliedEndTags(); + void generateImpliedEndTagsWithExclusion(const AtomicString& tagName); + + bool isEmpty() const { return !m_openElements.stackDepth(); } + Element* currentElement() const { return m_openElements.top(); } + ContainerNode* currentNode() const { return m_openElements.topNode(); } + Element* oneBelowTop() const { return m_openElements.oneBelowTop(); } + + HTMLElementStack* openElements() const { return &m_openElements; } + HTMLFormattingElementList* activeFormattingElements() const { return &m_activeFormattingElements; } + + Element* head() const { return m_head.get(); } + + void setForm(HTMLFormElement*); + HTMLFormElement* form() const { return m_form.get(); } + PassRefPtr<HTMLFormElement> takeForm(); + + class RedirectToFosterParentGuard { + WTF_MAKE_NONCOPYABLE(RedirectToFosterParentGuard); + public: + RedirectToFosterParentGuard(HTMLConstructionSite& tree) + : m_tree(tree) + , m_wasRedirectingBefore(tree.m_redirectAttachToFosterParent) + { + m_tree.m_redirectAttachToFosterParent = true; + } + + ~RedirectToFosterParentGuard() + { + m_tree.m_redirectAttachToFosterParent = m_wasRedirectingBefore; + } + + private: + HTMLConstructionSite& m_tree; + bool m_wasRedirectingBefore; + }; + +private: + // In the common case, this queue will have only one task because most + // tokens produce only one DOM mutation. + typedef Vector<HTMLConstructionSiteTask, 1> AttachmentQueue; + + void attachLater(ContainerNode* parent, PassRefPtr<Node> child); + + void findFosterSite(HTMLConstructionSiteTask&); + + PassRefPtr<Element> createHTMLElementFromSavedElement(Element*); + PassRefPtr<Element> createElement(AtomicHTMLToken&, const AtomicString& namespaceURI); + + void mergeAttributesFromTokenIntoElement(AtomicHTMLToken&, Element*); + void dispatchDocumentElementAvailableIfNeeded(); + + Document* m_document; + + // This is the root ContainerNode to which the parser attaches all newly + // constructed nodes. It points to a DocumentFragment when parsing fragments + // and a Document in all other cases. + ContainerNode* m_attachmentRoot; + + RefPtr<Element> m_head; + RefPtr<HTMLFormElement> m_form; + mutable HTMLElementStack m_openElements; + mutable HTMLFormattingElementList m_activeFormattingElements; + + AttachmentQueue m_attachmentQueue; + + FragmentScriptingPermission m_fragmentScriptingPermission; + bool m_isParsingFragment; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-intable + // In the "in table" insertion mode, we sometimes get into a state where + // "whenever a node would be inserted into the current node, it must instead + // be foster parented." This flag tracks whether we're in that state. + bool m_redirectAttachToFosterParent; + + unsigned m_maximumDOMTreeDepth; +}; + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/parser/HTMLDocumentParser.cpp b/Source/WebCore/html/parser/HTMLDocumentParser.cpp new file mode 100644 index 000000000..7a06b4239 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLDocumentParser.cpp @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLDocumentParser.h" + +#include "ContentSecurityPolicy.h" +#include "DocumentFragment.h" +#include "Element.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "HTMLParserScheduler.h" +#include "HTMLTokenizer.h" +#include "HTMLPreloadScanner.h" +#include "HTMLScriptRunner.h" +#include "HTMLTreeBuilder.h" +#include "HTMLDocument.h" +#include "InspectorInstrumentation.h" +#include "NestingLevelIncrementer.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +namespace { + +// This is a direct transcription of step 4 from: +// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case +HTMLTokenizerState::State tokenizerStateForContextElement(Element* contextElement, bool reportErrors) +{ + if (!contextElement) + return HTMLTokenizerState::DataState; + + const QualifiedName& contextTag = contextElement->tagQName(); + + if (contextTag.matches(titleTag) || contextTag.matches(textareaTag)) + return HTMLTokenizerState::RCDATAState; + if (contextTag.matches(styleTag) + || contextTag.matches(xmpTag) + || contextTag.matches(iframeTag) + || (contextTag.matches(noembedTag) && HTMLTreeBuilder::pluginsEnabled(contextElement->document()->frame())) + || (contextTag.matches(noscriptTag) && HTMLTreeBuilder::scriptEnabled(contextElement->document()->frame())) + || contextTag.matches(noframesTag)) + return reportErrors ? HTMLTokenizerState::RAWTEXTState : HTMLTokenizerState::PLAINTEXTState; + if (contextTag.matches(scriptTag)) + return reportErrors ? HTMLTokenizerState::ScriptDataState : HTMLTokenizerState::PLAINTEXTState; + if (contextTag.matches(plaintextTag)) + return HTMLTokenizerState::PLAINTEXTState; + return HTMLTokenizerState::DataState; +} + +} // namespace + +HTMLDocumentParser::HTMLDocumentParser(HTMLDocument* document, bool reportErrors) + : ScriptableDocumentParser(document) + , m_tokenizer(HTMLTokenizer::create(usePreHTML5ParserQuirks(document))) + , m_scriptRunner(HTMLScriptRunner::create(document, this)) + , m_treeBuilder(HTMLTreeBuilder::create(this, document, reportErrors, usePreHTML5ParserQuirks(document), maximumDOMTreeDepth(document))) + , m_parserScheduler(HTMLParserScheduler::create(this)) + , m_xssAuditor(this) + , m_endWasDelayed(false) + , m_pumpSessionNestingLevel(0) +{ +} + +// FIXME: Member variables should be grouped into self-initializing structs to +// minimize code duplication between these constructors. +HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) + : ScriptableDocumentParser(fragment->document()) + , m_tokenizer(HTMLTokenizer::create(usePreHTML5ParserQuirks(fragment->document()))) + , m_treeBuilder(HTMLTreeBuilder::create(this, fragment, contextElement, scriptingPermission, usePreHTML5ParserQuirks(fragment->document()), maximumDOMTreeDepth(fragment->document()))) + , m_xssAuditor(this) + , m_endWasDelayed(false) + , m_pumpSessionNestingLevel(0) +{ + bool reportErrors = false; // For now document fragment parsing never reports errors. + m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors)); +} + +HTMLDocumentParser::~HTMLDocumentParser() +{ + ASSERT(!m_parserScheduler); + ASSERT(!m_pumpSessionNestingLevel); + ASSERT(!m_preloadScanner); + ASSERT(!m_insertionPreloadScanner); +} + +void HTMLDocumentParser::detach() +{ + DocumentParser::detach(); + if (m_scriptRunner) + m_scriptRunner->detach(); + m_treeBuilder->detach(); + // FIXME: It seems wrong that we would have a preload scanner here. + // Yet during fast/dom/HTMLScriptElement/script-load-events.html we do. + m_preloadScanner.clear(); + m_insertionPreloadScanner.clear(); + m_parserScheduler.clear(); // Deleting the scheduler will clear any timers. +} + +void HTMLDocumentParser::stopParsing() +{ + DocumentParser::stopParsing(); + m_parserScheduler.clear(); // Deleting the scheduler will clear any timers. +} + +// This kicks off "Once the user agent stops parsing" as described by: +// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end +void HTMLDocumentParser::prepareToStopParsing() +{ + ASSERT(!hasInsertionPoint()); + + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + // NOTE: This pump should only ever emit buffered character tokens, + // so ForceSynchronous vs. AllowYield should be meaningless. + pumpTokenizerIfPossible(ForceSynchronous); + + if (isStopped()) + return; + + DocumentParser::prepareToStopParsing(); + + // We will not have a scriptRunner when parsing a DocumentFragment. + if (m_scriptRunner) + document()->setReadyState(Document::Interactive); + + attemptToRunDeferredScriptsAndEnd(); +} + +bool HTMLDocumentParser::isParsingFragment() const +{ + return m_treeBuilder->isParsingFragment(); +} + +bool HTMLDocumentParser::processingData() const +{ + return isScheduledForResume() || inPumpSession(); +} + +void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) +{ + if (isStopped() || m_treeBuilder->isPaused()) + return; + + // Once a resume is scheduled, HTMLParserScheduler controls when we next pump. + if (isScheduledForResume()) { + ASSERT(mode == AllowYield); + return; + } + + pumpTokenizer(mode); +} + +bool HTMLDocumentParser::isScheduledForResume() const +{ + return m_parserScheduler && m_parserScheduler->isScheduledForResume(); +} + +// Used by HTMLParserScheduler +void HTMLDocumentParser::resumeParsingAfterYield() +{ + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + // We should never be here unless we can pump immediately. Call pumpTokenizer() + // directly so that ASSERTS will fire if we're wrong. + pumpTokenizer(AllowYield); + endIfDelayed(); +} + +bool HTMLDocumentParser::runScriptsForPausedTreeBuilder() +{ + ASSERT(m_treeBuilder->isPaused()); + + TextPosition scriptStartPosition = TextPosition::belowRangePosition(); + RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition); + // We will not have a scriptRunner when parsing a DocumentFragment. + if (!m_scriptRunner) + return true; + return m_scriptRunner->execute(scriptElement.release(), scriptStartPosition); +} + +bool HTMLDocumentParser::canTakeNextToken(SynchronousMode mode, PumpSession& session) +{ + if (isStopped()) + return false; + + // The parser will pause itself when waiting on a script to load or run. + if (m_treeBuilder->isPaused()) { + if (mode == AllowYield) + m_parserScheduler->checkForYieldBeforeScript(session); + + // If we don't run the script, we cannot allow the next token to be taken. + if (session.needsYield) + return false; + + // If we're paused waiting for a script, we try to execute scripts before continuing. + bool shouldContinueParsing = runScriptsForPausedTreeBuilder(); + m_treeBuilder->setPaused(!shouldContinueParsing); + if (!shouldContinueParsing || isStopped()) + return false; + } + + // FIXME: It's wrong for the HTMLDocumentParser to reach back to the + // Frame, but this approach is how the old parser handled + // stopping when the page assigns window.location. What really + // should happen is that assigning window.location causes the + // parser to stop parsing cleanly. The problem is we're not + // perpared to do that at every point where we run JavaScript. + if (!isParsingFragment() + && document()->frame() && document()->frame()->navigationScheduler()->locationChangePending()) + return false; + + if (mode == AllowYield) + m_parserScheduler->checkForYieldBeforeToken(session); + + return true; +} + +void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) +{ + ASSERT(!isStopped()); + ASSERT(!isScheduledForResume()); + // ASSERT that this object is both attached to the Document and protected. + ASSERT(refCount() >= 2); + + PumpSession session(m_pumpSessionNestingLevel); + + // We tell the InspectorInstrumentation about every pump, even if we + // end up pumping nothing. It can filter out empty pumps itself. + // FIXME: m_input.current().length() is only accurate if we + // end up parsing the whole buffer in this pump. We should pass how + // much we parsed as part of didWriteHTML instead of willWriteHTML. + InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().length(), m_tokenizer->lineNumber().zeroBasedInt()); + + while (canTakeNextToken(mode, session) && !session.needsYield) { + if (!isParsingFragment()) + m_sourceTracker.start(m_input, m_tokenizer.get(), m_token); + + if (!m_tokenizer->nextToken(m_input.current(), m_token)) + break; + + if (!isParsingFragment()) { + m_sourceTracker.end(m_input, m_tokenizer.get(), m_token); + + // We do not XSS filter innerHTML, which means we (intentionally) fail + // http/tests/security/xssAuditor/dom-write-innerHTML.html + m_xssAuditor.filterToken(m_token); + } + + m_treeBuilder->constructTreeFromToken(m_token); + ASSERT(m_token.isUninitialized()); + } + + // Ensure we haven't been totally deref'ed after pumping. Any caller of this + // function should be holding a RefPtr to this to ensure we weren't deleted. + ASSERT(refCount() >= 1); + + if (isStopped()) + return; + + if (session.needsYield) + m_parserScheduler->scheduleForResume(); + + if (isWaitingForScripts()) { + ASSERT(m_tokenizer->state() == HTMLTokenizerState::DataState); + if (!m_preloadScanner) { + m_preloadScanner = adoptPtr(new HTMLPreloadScanner(document())); + m_preloadScanner->appendToEnd(m_input.current()); + } + m_preloadScanner->scan(); + } + + InspectorInstrumentation::didWriteHTML(cookie, m_tokenizer->lineNumber().zeroBasedInt()); +} + +bool HTMLDocumentParser::hasInsertionPoint() +{ + // FIXME: The wasCreatedByScript() branch here might not be fully correct. + // Our model of the EOF character differs slightly from the one in + // the spec because our treatment is uniform between network-sourced + // and script-sourced input streams whereas the spec treats them + // differently. + return m_input.hasInsertionPoint() || (wasCreatedByScript() && !m_input.haveSeenEndOfFile()); +} + +void HTMLDocumentParser::insert(const SegmentedString& source) +{ + if (isStopped()) + return; + + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + SegmentedString excludedLineNumberSource(source); + excludedLineNumberSource.setExcludeLineNumbers(); + m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); + pumpTokenizerIfPossible(ForceSynchronous); + + if (isWaitingForScripts()) { + // Check the document.write() output with a separate preload scanner as + // the main scanner can't deal with insertions. + if (!m_insertionPreloadScanner) + m_insertionPreloadScanner = adoptPtr(new HTMLPreloadScanner(document())); + m_insertionPreloadScanner->appendToEnd(source); + m_insertionPreloadScanner->scan(); + } + + endIfDelayed(); +} + +void HTMLDocumentParser::append(const SegmentedString& source) +{ + if (isStopped()) + return; + + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + if (m_preloadScanner) { + if (m_input.current().isEmpty() && !isWaitingForScripts()) { + // We have parsed until the end of the current input and so are now moving ahead of the preload scanner. + // Clear the scanner so we know to scan starting from the current input point if we block again. + m_preloadScanner.clear(); + } else { + m_preloadScanner->appendToEnd(source); + if (isWaitingForScripts()) + m_preloadScanner->scan(); + } + } + + m_input.appendToEnd(source); + + if (inPumpSession()) { + // We've gotten data off the network in a nested write. + // We don't want to consume any more of the input stream now. Do + // not worry. We'll consume this data in a less-nested write(). + return; + } + + pumpTokenizerIfPossible(AllowYield); + + endIfDelayed(); +} + +void HTMLDocumentParser::end() +{ + ASSERT(!isDetached()); + ASSERT(!isScheduledForResume()); + + // Informs the the rest of WebCore that parsing is really finished (and deletes this). + m_treeBuilder->finished(); +} + +void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd() +{ + ASSERT(isStopping()); + ASSERT(!hasInsertionPoint()); + if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing()) + return; + end(); +} + +void HTMLDocumentParser::attemptToEnd() +{ + // finish() indicates we will not receive any more data. If we are waiting on + // an external script to load, we can't finish parsing quite yet. + + if (shouldDelayEnd()) { + m_endWasDelayed = true; + return; + } + prepareToStopParsing(); +} + +void HTMLDocumentParser::endIfDelayed() +{ + // If we've already been detached, don't bother ending. + if (isDetached()) + return; + + if (!m_endWasDelayed || shouldDelayEnd()) + return; + + m_endWasDelayed = false; + prepareToStopParsing(); +} + +void HTMLDocumentParser::finish() +{ + // FIXME: We should ASSERT(!m_parserStopped) here, since it does not + // makes sense to call any methods on DocumentParser once it's been stopped. + // However, FrameLoader::stop calls DocumentParser::finish unconditionally. + + // We're not going to get any more data off the network, so we tell the + // input stream we've reached the end of file. finish() can be called more + // than once, if the first time does not call end(). + if (!m_input.haveSeenEndOfFile()) + m_input.markEndOfFile(); + attemptToEnd(); +} + +bool HTMLDocumentParser::finishWasCalled() +{ + return m_input.haveSeenEndOfFile(); +} + +bool HTMLDocumentParser::isExecutingScript() const +{ + if (!m_scriptRunner) + return false; + return m_scriptRunner->isExecutingScript(); +} + +String HTMLDocumentParser::sourceForToken(const HTMLToken& token) +{ + return m_sourceTracker.sourceForToken(token); +} + +OrdinalNumber HTMLDocumentParser::lineNumber() const +{ + return m_tokenizer->lineNumber(); +} + +TextPosition HTMLDocumentParser::textPosition() const +{ + const SegmentedString& currentString = m_input.current(); + OrdinalNumber line = currentString.currentLine(); + OrdinalNumber column = currentString.currentColumn(); + ASSERT(m_tokenizer->lineNumber() == line); + + return TextPosition(line, column); +} + +bool HTMLDocumentParser::isWaitingForScripts() const +{ + return m_treeBuilder->isPaused(); +} + +void HTMLDocumentParser::resumeParsingAfterScriptExecution() +{ + ASSERT(!isExecutingScript()); + ASSERT(!m_treeBuilder->isPaused()); + + m_insertionPreloadScanner.clear(); + pumpTokenizerIfPossible(AllowYield); + endIfDelayed(); +} + +void HTMLDocumentParser::watchForLoad(CachedResource* cachedScript) +{ + ASSERT(!cachedScript->isLoaded()); + // addClient would call notifyFinished if the load were complete. + // Callers do not expect to be re-entered from this call, so they should + // not an already-loaded CachedResource. + cachedScript->addClient(this); +} + +void HTMLDocumentParser::stopWatchingForLoad(CachedResource* cachedScript) +{ + cachedScript->removeClient(this); +} + +void HTMLDocumentParser::appendCurrentInputStreamToPreloadScannerAndScan() +{ + ASSERT(m_preloadScanner); + m_preloadScanner->appendToEnd(m_input.current()); + m_preloadScanner->scan(); +} + +void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource) +{ + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + ASSERT(m_scriptRunner); + ASSERT(!isExecutingScript()); + if (isStopping()) { + attemptToRunDeferredScriptsAndEnd(); + return; + } + + ASSERT(m_treeBuilder->isPaused()); + // Note: We only ever wait on one script at a time, so we always know this + // is the one we were waiting on and can un-pause the tree builder. + m_treeBuilder->setPaused(false); + bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource); + m_treeBuilder->setPaused(!shouldContinueParsing); + if (shouldContinueParsing) + resumeParsingAfterScriptExecution(); +} + +void HTMLDocumentParser::executeScriptsWaitingForStylesheets() +{ + // Document only calls this when the Document owns the DocumentParser + // so this will not be called in the DocumentFragment case. + ASSERT(m_scriptRunner); + // Ignore calls unless we have a script blocking the parser waiting on a + // stylesheet load. Otherwise we are currently parsing and this + // is a re-entrant call from encountering a </ style> tag. + if (!m_scriptRunner->hasScriptsWaitingForStylesheets()) + return; + + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + ASSERT(!m_scriptRunner->isExecutingScript()); + ASSERT(m_treeBuilder->isPaused()); + // Note: We only ever wait on one script at a time, so we always know this + // is the one we were waiting on and can un-pause the tree builder. + m_treeBuilder->setPaused(false); + bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForStylesheets(); + m_treeBuilder->setPaused(!shouldContinueParsing); + if (shouldContinueParsing) + resumeParsingAfterScriptExecution(); +} + +ScriptController* HTMLDocumentParser::script() const +{ + return document()->frame() ? document()->frame()->script() : 0; +} + +void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) +{ + RefPtr<HTMLDocumentParser> parser = HTMLDocumentParser::create(fragment, contextElement, scriptingPermission); + parser->insert(source); // Use insert() so that the parser will not yield. + parser->finish(); + ASSERT(!parser->processingData()); // Make sure we're done. <rdar://problem/3963151> + parser->detach(); // Allows ~DocumentParser to assert it was detached before destruction. +} + +bool HTMLDocumentParser::usePreHTML5ParserQuirks(Document* document) +{ + ASSERT(document); + return document->settings() && document->settings()->usePreHTML5ParserQuirks(); +} + +unsigned HTMLDocumentParser::maximumDOMTreeDepth(Document* document) +{ + ASSERT(document); + return document->settings() ? document->settings()->maximumHTMLParserDOMTreeDepth() : Settings::defaultMaximumHTMLParserDOMTreeDepth; +} + +void HTMLDocumentParser::suspendScheduledTasks() +{ + if (m_parserScheduler) + m_parserScheduler->suspend(); +} + +void HTMLDocumentParser::resumeScheduledTasks() +{ + if (m_parserScheduler) + m_parserScheduler->resume(); +} + +} diff --git a/Source/WebCore/html/parser/HTMLDocumentParser.h b/Source/WebCore/html/parser/HTMLDocumentParser.h new file mode 100644 index 000000000..eee882c72 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLDocumentParser.h @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLDocumentParser_h +#define HTMLDocumentParser_h + +#include "CachedResourceClient.h" +#include "FragmentScriptingPermission.h" +#include "HTMLInputStream.h" +#include "HTMLScriptRunnerHost.h" +#include "HTMLSourceTracker.h" +#include "HTMLToken.h" +#include "ScriptableDocumentParser.h" +#include "SegmentedString.h" +#include "Timer.h" +#include "XSSAuditor.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class Document; +class DocumentFragment; +class HTMLDocument; +class HTMLParserScheduler; +class HTMLTokenizer; +class HTMLScriptRunner; +class HTMLTreeBuilder; +class HTMLPreloadScanner; +class ScriptController; +class ScriptSourceCode; + +class PumpSession; + +class HTMLDocumentParser : public ScriptableDocumentParser, HTMLScriptRunnerHost, CachedResourceClient { + WTF_MAKE_FAST_ALLOCATED; +public: + static PassRefPtr<HTMLDocumentParser> create(HTMLDocument* document, bool reportErrors) + { + return adoptRef(new HTMLDocumentParser(document, reportErrors)); + } + static PassRefPtr<HTMLDocumentParser> create(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission permission) + { + return adoptRef(new HTMLDocumentParser(fragment, contextElement, permission)); + } + + virtual ~HTMLDocumentParser(); + + // Exposed for HTMLParserScheduler + void resumeParsingAfterYield(); + + static void parseDocumentFragment(const String&, DocumentFragment*, Element* contextElement, FragmentScriptingPermission = FragmentScriptingAllowed); + + static bool usePreHTML5ParserQuirks(Document*); + static unsigned maximumDOMTreeDepth(Document*); + + HTMLTokenizer* tokenizer() const { return m_tokenizer.get(); } + String sourceForToken(const HTMLToken&); + + virtual TextPosition textPosition() const; + virtual OrdinalNumber lineNumber() const; + + virtual void suspendScheduledTasks(); + virtual void resumeScheduledTasks(); + +protected: + virtual void insert(const SegmentedString&); + virtual void append(const SegmentedString&); + virtual void finish(); + + HTMLDocumentParser(HTMLDocument*, bool reportErrors); + HTMLDocumentParser(DocumentFragment*, Element* contextElement, FragmentScriptingPermission); + + HTMLTreeBuilder* treeBuilder() const { return m_treeBuilder.get(); } + +private: + // DocumentParser + virtual void detach(); + virtual bool hasInsertionPoint(); + virtual bool finishWasCalled(); + virtual bool processingData() const; + virtual void prepareToStopParsing(); + virtual void stopParsing(); + virtual bool isWaitingForScripts() const; + virtual bool isExecutingScript() const; + virtual void executeScriptsWaitingForStylesheets(); + + // HTMLScriptRunnerHost + virtual void watchForLoad(CachedResource*); + virtual void stopWatchingForLoad(CachedResource*); + virtual HTMLInputStream& inputStream() { return m_input; } + virtual bool hasPreloadScanner() const { return m_preloadScanner.get(); } + virtual void appendCurrentInputStreamToPreloadScannerAndScan(); + + // CachedResourceClient + virtual void notifyFinished(CachedResource*); + + enum SynchronousMode { + AllowYield, + ForceSynchronous, + }; + bool canTakeNextToken(SynchronousMode, PumpSession&); + void pumpTokenizer(SynchronousMode); + void pumpTokenizerIfPossible(SynchronousMode); + + bool runScriptsForPausedTreeBuilder(); + void resumeParsingAfterScriptExecution(); + + void begin(); + void attemptToEnd(); + void endIfDelayed(); + void attemptToRunDeferredScriptsAndEnd(); + void end(); + + bool isParsingFragment() const; + bool isScheduledForResume() const; + bool inPumpSession() const { return m_pumpSessionNestingLevel > 0; } + bool shouldDelayEnd() const { return inPumpSession() || isWaitingForScripts() || isScheduledForResume() || isExecutingScript(); } + + ScriptController* script() const; + + HTMLInputStream m_input; + + // We hold m_token here because it might be partially complete. + HTMLToken m_token; + + OwnPtr<HTMLTokenizer> m_tokenizer; + OwnPtr<HTMLScriptRunner> m_scriptRunner; + OwnPtr<HTMLTreeBuilder> m_treeBuilder; + OwnPtr<HTMLPreloadScanner> m_preloadScanner; + OwnPtr<HTMLPreloadScanner> m_insertionPreloadScanner; + OwnPtr<HTMLParserScheduler> m_parserScheduler; + HTMLSourceTracker m_sourceTracker; + XSSAuditor m_xssAuditor; + + bool m_endWasDelayed; + unsigned m_pumpSessionNestingLevel; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLElementStack.cpp b/Source/WebCore/html/parser/HTMLElementStack.cpp new file mode 100644 index 000000000..98885743e --- /dev/null +++ b/Source/WebCore/html/parser/HTMLElementStack.cpp @@ -0,0 +1,629 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011 Apple 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 GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLElementStack.h" + +#include "DocumentFragment.h" +#include "Element.h" +#include "HTMLNames.h" +#include "MathMLNames.h" +#include "SVGNames.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +using namespace HTMLNames; + +namespace { + +inline bool isNumberedHeaderElement(ContainerNode* node) +{ + return node->hasTagName(h1Tag) + || node->hasTagName(h2Tag) + || node->hasTagName(h3Tag) + || node->hasTagName(h4Tag) + || node->hasTagName(h5Tag) + || node->hasTagName(h6Tag); +} + +inline bool isRootNode(ContainerNode* node) +{ + return node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE + || node->nodeType() == Node::SHADOW_ROOT_NODE + || node->hasTagName(htmlTag); +} + +inline bool isScopeMarker(ContainerNode* node) +{ + return node->hasTagName(appletTag) + || node->hasTagName(captionTag) + || node->hasTagName(marqueeTag) + || node->hasTagName(objectTag) + || node->hasTagName(tableTag) + || node->hasTagName(tdTag) + || node->hasTagName(thTag) + || node->hasTagName(MathMLNames::miTag) + || node->hasTagName(MathMLNames::moTag) + || node->hasTagName(MathMLNames::mnTag) + || node->hasTagName(MathMLNames::msTag) + || node->hasTagName(MathMLNames::mtextTag) + || node->hasTagName(MathMLNames::annotation_xmlTag) + || node->hasTagName(SVGNames::foreignObjectTag) + || node->hasTagName(SVGNames::descTag) + || node->hasTagName(SVGNames::titleTag) + || isRootNode(node); +} + +inline bool isListItemScopeMarker(ContainerNode* node) +{ + return isScopeMarker(node) + || node->hasTagName(olTag) + || node->hasTagName(ulTag); +} + +inline bool isTableScopeMarker(ContainerNode* node) +{ + return node->hasTagName(tableTag) + || isRootNode(node); +} + +inline bool isTableBodyScopeMarker(ContainerNode* node) +{ + return node->hasTagName(tbodyTag) + || node->hasTagName(tfootTag) + || node->hasTagName(theadTag) + || isRootNode(node); +} + +inline bool isTableRowScopeMarker(ContainerNode* node) +{ + return node->hasTagName(trTag) + || isRootNode(node); +} + +inline bool isForeignContentScopeMarker(ContainerNode* node) +{ + return HTMLElementStack::isMathMLTextIntegrationPoint(node) + || HTMLElementStack::isHTMLIntegrationPoint(node) + || isInHTMLNamespace(node); +} + +inline bool isButtonScopeMarker(ContainerNode* node) +{ + return isScopeMarker(node) + || node->hasTagName(buttonTag); +} + +inline bool isSelectScopeMarker(ContainerNode* node) +{ + return !node->hasTagName(optgroupTag) + && !node->hasTagName(optionTag); +} + +} + +HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<ContainerNode> node, PassOwnPtr<ElementRecord> next) + : m_node(node) + , m_next(next) +{ + ASSERT(m_node); +} + +HTMLElementStack::ElementRecord::~ElementRecord() +{ +} + +void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element) +{ + ASSERT(element); + ASSERT(!m_node || m_node->isElementNode()); + // FIXME: Should this call finishParsingChildren? + m_node = element; +} + +bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const +{ + for (ElementRecord* below = next(); below; below = below->next()) { + if (below == other) + return true; + } + return false; +} + +HTMLElementStack::HTMLElementStack() + : m_rootNode(0) + , m_headElement(0) + , m_bodyElement(0) + , m_stackDepth(0) +{ +} + +HTMLElementStack::~HTMLElementStack() +{ +} + +bool HTMLElementStack::hasOnlyOneElement() const +{ + return !topRecord()->next(); +} + +bool HTMLElementStack::secondElementIsHTMLBodyElement() const +{ + // This is used the fragment case of <body> and <frameset> in the "in body" + // insertion mode. + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody + ASSERT(m_rootNode); + // If we have a body element, it must always be the second element on the + // stack, as we always start with an html element, and any other element + // would cause the implicit creation of a body element. + return !!m_bodyElement; +} + +void HTMLElementStack::popHTMLHeadElement() +{ + ASSERT(top() == m_headElement); + m_headElement = 0; + popCommon(); +} + +void HTMLElementStack::popHTMLBodyElement() +{ + ASSERT(top() == m_bodyElement); + m_bodyElement = 0; + popCommon(); +} + +void HTMLElementStack::popAll() +{ + m_rootNode = 0; + m_headElement = 0; + m_bodyElement = 0; + m_stackDepth = 0; + while (m_top) { + topNode()->finishParsingChildren(); + m_top = m_top->releaseNext(); + } +} + +void HTMLElementStack::pop() +{ + ASSERT(!top()->hasTagName(HTMLNames::headTag)); + popCommon(); +} + +void HTMLElementStack::popUntil(const AtomicString& tagName) +{ + while (!top()->hasLocalName(tagName)) { + // pop() will ASSERT at <body> if callers fail to check that there is an + // element with localName |tagName| on the stack of open elements. + pop(); + } +} + +void HTMLElementStack::popUntilPopped(const AtomicString& tagName) +{ + popUntil(tagName); + pop(); +} + +void HTMLElementStack::popUntilNumberedHeaderElementPopped() +{ + while (!isNumberedHeaderElement(topNode())) + pop(); + pop(); +} + +void HTMLElementStack::popUntil(Element* element) +{ + while (top() != element) + pop(); +} + +void HTMLElementStack::popUntilPopped(Element* element) +{ + popUntil(element); + pop(); +} + +void HTMLElementStack::popUntilTableScopeMarker() +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-context + while (!isTableScopeMarker(topNode())) + pop(); +} + +void HTMLElementStack::popUntilTableBodyScopeMarker() +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-body-context + while (!isTableBodyScopeMarker(topNode())) + pop(); +} + +void HTMLElementStack::popUntilTableRowScopeMarker() +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#clear-the-stack-back-to-a-table-row-context + while (!isTableRowScopeMarker(topNode())) + pop(); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#mathml-text-integration-point +bool HTMLElementStack::isMathMLTextIntegrationPoint(ContainerNode* node) +{ + if (!node->isElementNode()) + return false; + Element* element = static_cast<Element*>(node); + return element->hasTagName(MathMLNames::miTag) + || element->hasTagName(MathMLNames::moTag) + || element->hasTagName(MathMLNames::mnTag) + || element->hasTagName(MathMLNames::msTag) + || element->hasTagName(MathMLNames::mtextTag); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#html-integration-point +bool HTMLElementStack::isHTMLIntegrationPoint(ContainerNode* node) +{ + if (!node->isElementNode()) + return false; + Element* element = static_cast<Element*>(node); + if (element->hasTagName(MathMLNames::annotation_xmlTag)) { + // FIXME: Technically we shouldn't read back from the DOM here. + // Instead, we're supposed to track this information in the element + // stack, which lets the parser run on its own thread. + String encoding = element->fastGetAttribute(MathMLNames::encodingAttr); + return equalIgnoringCase(encoding, "text/html") + || equalIgnoringCase(encoding, "application/xhtml+xml"); + } + return element->hasTagName(SVGNames::foreignObjectTag) + || element->hasTagName(SVGNames::descTag) + || element->hasTagName(SVGNames::titleTag); +} + +void HTMLElementStack::popUntilForeignContentScopeMarker() +{ + while (!isForeignContentScopeMarker(topNode())) + pop(); +} + +void HTMLElementStack::pushRootNode(PassRefPtr<ContainerNode> rootNode) +{ + ASSERT(rootNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE || rootNode->nodeType() == Node::SHADOW_ROOT_NODE); + pushRootNodeCommon(rootNode); +} + +void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element) +{ + ASSERT(element->hasTagName(HTMLNames::htmlTag)); + pushRootNodeCommon(element); +} + +void HTMLElementStack::pushRootNodeCommon(PassRefPtr<ContainerNode> rootNode) +{ + ASSERT(!m_top); + ASSERT(!m_rootNode); + m_rootNode = rootNode.get(); + pushCommon(rootNode); +} + +void HTMLElementStack::pushHTMLHeadElement(PassRefPtr<Element> element) +{ + ASSERT(element->hasTagName(HTMLNames::headTag)); + ASSERT(!m_headElement); + m_headElement = element.get(); + pushCommon(element); +} + +void HTMLElementStack::pushHTMLBodyElement(PassRefPtr<Element> element) +{ + ASSERT(element->hasTagName(HTMLNames::bodyTag)); + ASSERT(!m_bodyElement); + m_bodyElement = element.get(); + pushCommon(element); +} + +void HTMLElementStack::push(PassRefPtr<Element> element) +{ + ASSERT(!element->hasTagName(HTMLNames::htmlTag)); + ASSERT(!element->hasTagName(HTMLNames::headTag)); + ASSERT(!element->hasTagName(HTMLNames::bodyTag)); + ASSERT(m_rootNode); + pushCommon(element); +} + +void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* recordBelow) +{ + ASSERT(element); + ASSERT(recordBelow); + ASSERT(m_top); + ASSERT(!element->hasTagName(HTMLNames::htmlTag)); + ASSERT(!element->hasTagName(HTMLNames::headTag)); + ASSERT(!element->hasTagName(HTMLNames::bodyTag)); + ASSERT(m_rootNode); + if (recordBelow == m_top) { + push(element); + return; + } + + for (ElementRecord* recordAbove = m_top.get(); recordAbove; recordAbove = recordAbove->next()) { + if (recordAbove->next() != recordBelow) + continue; + + m_stackDepth++; + recordAbove->setNext(adoptPtr(new ElementRecord(element, recordAbove->releaseNext()))); + recordAbove->next()->element()->beginParsingChildren(); + return; + } + ASSERT_NOT_REACHED(); +} + +HTMLElementStack::ElementRecord* HTMLElementStack::topRecord() const +{ + ASSERT(m_top); + return m_top.get(); +} + +Element* HTMLElementStack::oneBelowTop() const +{ + // We should never be calling this if it could be 0. + ASSERT(m_top); + ASSERT(m_top->next()); + return m_top->next()->element(); +} + +Element* HTMLElementStack::bottom() const +{ + return htmlElement(); +} + +void HTMLElementStack::removeHTMLHeadElement(Element* element) +{ + ASSERT(m_headElement == element); + if (m_top->element() == element) { + popHTMLHeadElement(); + return; + } + m_headElement = 0; + removeNonTopCommon(element); +} + +void HTMLElementStack::remove(Element* element) +{ + ASSERT(!element->hasTagName(HTMLNames::headTag)); + if (m_top->element() == element) { + pop(); + return; + } + removeNonTopCommon(element); +} + +HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const +{ + for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) { + if (pos->node() == element) + return pos; + } + return 0; +} + +HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const +{ + for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) { + if (pos->node()->hasLocalName(tagName)) + return pos; + } + return 0; +} + +bool HTMLElementStack::contains(Element* element) const +{ + return !!find(element); +} + +bool HTMLElementStack::contains(const AtomicString& tagName) const +{ + return !!topmost(tagName); +} + +template <bool isMarker(ContainerNode*)> +bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag) +{ + for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) { + ContainerNode* node = pos->node(); + if (node->hasLocalName(targetTag)) + return true; + if (isMarker(node)) + return false; + } + ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker. + return false; +} + +bool HTMLElementStack::hasOnlyHTMLElementsInScope() const +{ + for (ElementRecord* record = m_top.get(); record; record = record->next()) { + ContainerNode* node = record->node(); + if (!isInHTMLNamespace(node)) + return false; + if (isScopeMarker(node)) + return true; + } + ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker. + return true; +} + +bool HTMLElementStack::hasNumberedHeaderElementInScope() const +{ + for (ElementRecord* record = m_top.get(); record; record = record->next()) { + ContainerNode* node = record->node(); + if (isNumberedHeaderElement(node)) + return true; + if (isScopeMarker(node)) + return false; + } + ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker. + return false; +} + +bool HTMLElementStack::inScope(Element* targetElement) const +{ + for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) { + ContainerNode* node = pos->node(); + if (node == targetElement) + return true; + if (isScopeMarker(node)) + return false; + } + ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker. + return false; +} + +bool HTMLElementStack::inScope(const AtomicString& targetTag) const +{ + return inScopeCommon<isScopeMarker>(m_top.get(), targetTag); +} + +bool HTMLElementStack::inScope(const QualifiedName& tagName) const +{ + // FIXME: Is localName() right for non-html elements? + return inScope(tagName.localName()); +} + +bool HTMLElementStack::inListItemScope(const AtomicString& targetTag) const +{ + return inScopeCommon<isListItemScopeMarker>(m_top.get(), targetTag); +} + +bool HTMLElementStack::inListItemScope(const QualifiedName& tagName) const +{ + // FIXME: Is localName() right for non-html elements? + return inListItemScope(tagName.localName()); +} + +bool HTMLElementStack::inTableScope(const AtomicString& targetTag) const +{ + return inScopeCommon<isTableScopeMarker>(m_top.get(), targetTag); +} + +bool HTMLElementStack::inTableScope(const QualifiedName& tagName) const +{ + // FIXME: Is localName() right for non-html elements? + return inTableScope(tagName.localName()); +} + +bool HTMLElementStack::inButtonScope(const AtomicString& targetTag) const +{ + return inScopeCommon<isButtonScopeMarker>(m_top.get(), targetTag); +} + +bool HTMLElementStack::inButtonScope(const QualifiedName& tagName) const +{ + // FIXME: Is localName() right for non-html elements? + return inButtonScope(tagName.localName()); +} + +bool HTMLElementStack::inSelectScope(const AtomicString& targetTag) const +{ + return inScopeCommon<isSelectScopeMarker>(m_top.get(), targetTag); +} + +bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const +{ + // FIXME: Is localName() right for non-html elements? + return inSelectScope(tagName.localName()); +} + +Element* HTMLElementStack::htmlElement() const +{ + ASSERT(m_rootNode); + return toElement(m_rootNode); +} + +Element* HTMLElementStack::headElement() const +{ + ASSERT(m_headElement); + return m_headElement; +} + +Element* HTMLElementStack::bodyElement() const +{ + ASSERT(m_bodyElement); + return m_bodyElement; +} + +ContainerNode* HTMLElementStack::rootNode() const +{ + ASSERT(m_rootNode); + return m_rootNode; +} + +void HTMLElementStack::pushCommon(PassRefPtr<ContainerNode> node) +{ + ASSERT(m_rootNode); + + m_stackDepth++; + m_top = adoptPtr(new ElementRecord(node, m_top.release())); +} + +void HTMLElementStack::popCommon() +{ + ASSERT(!top()->hasTagName(HTMLNames::htmlTag)); + ASSERT(!top()->hasTagName(HTMLNames::headTag) || !m_headElement); + ASSERT(!top()->hasTagName(HTMLNames::bodyTag) || !m_bodyElement); + top()->finishParsingChildren(); + m_top = m_top->releaseNext(); + + m_stackDepth--; +} + +void HTMLElementStack::removeNonTopCommon(Element* element) +{ + ASSERT(!element->hasTagName(HTMLNames::htmlTag)); + ASSERT(!element->hasTagName(HTMLNames::bodyTag)); + ASSERT(top() != element); + for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) { + if (pos->next()->element() == element) { + // FIXME: Is it OK to call finishParsingChildren() + // when the children aren't actually finished? + element->finishParsingChildren(); + pos->setNext(pos->next()->releaseNext()); + m_stackDepth--; + return; + } + } + ASSERT_NOT_REACHED(); +} + +#ifndef NDEBUG + +void HTMLElementStack::show() +{ + for (ElementRecord* record = m_top.get(); record; record = record->next()) + record->element()->showNode(); +} + +#endif + +} diff --git a/Source/WebCore/html/parser/HTMLElementStack.h b/Source/WebCore/html/parser/HTMLElementStack.h new file mode 100644 index 000000000..f604f82c7 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLElementStack.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011 Apple 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 GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLElementStack_h +#define HTMLElementStack_h + +#include "Element.h" +#include "HTMLNames.h" +#include <wtf/Forward.h> +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class ContainerNode; +class DocumentFragment; +class Element; +class QualifiedName; + +// NOTE: The HTML5 spec uses a backwards (grows downward) stack. We're using +// more standard (grows upwards) stack terminology here. +class HTMLElementStack { + WTF_MAKE_NONCOPYABLE(HTMLElementStack); WTF_MAKE_FAST_ALLOCATED; +public: + HTMLElementStack(); + ~HTMLElementStack(); + + class ElementRecord { + WTF_MAKE_NONCOPYABLE(ElementRecord); + public: + ~ElementRecord(); // Public for ~PassOwnPtr() + + Element* element() const { return toElement(m_node.get()); } + ContainerNode* node() const { return m_node.get(); } + void replaceElement(PassRefPtr<Element>); + + bool isAbove(ElementRecord*) const; + + ElementRecord* next() const { return m_next.get(); } + + private: + friend class HTMLElementStack; + + ElementRecord(PassRefPtr<ContainerNode>, PassOwnPtr<ElementRecord>); + + PassOwnPtr<ElementRecord> releaseNext() { return m_next.release(); } + void setNext(PassOwnPtr<ElementRecord> next) { m_next = next; } + + RefPtr<ContainerNode> m_node; + OwnPtr<ElementRecord> m_next; + }; + + unsigned stackDepth() const { return m_stackDepth; } + + // Inlining this function is a (small) performance win on the parsing + // benchmark. + Element* top() const + { + ASSERT(m_top->element()); + return m_top->element(); + } + + ContainerNode* topNode() const + { + ASSERT(m_top->node()); + return m_top->node(); + } + + Element* oneBelowTop() const; + ElementRecord* topRecord() const; + Element* bottom() const; + ElementRecord* find(Element*) const; + ElementRecord* topmost(const AtomicString& tagName) const; + + void insertAbove(PassRefPtr<Element>, ElementRecord*); + + void push(PassRefPtr<Element>); + void pushRootNode(PassRefPtr<ContainerNode>); + void pushHTMLHtmlElement(PassRefPtr<Element>); + void pushHTMLHeadElement(PassRefPtr<Element>); + void pushHTMLBodyElement(PassRefPtr<Element>); + + void pop(); + void popUntil(const AtomicString& tagName); + void popUntil(Element*); + void popUntilPopped(const AtomicString& tagName); + void popUntilPopped(Element*); + void popUntilNumberedHeaderElementPopped(); + void popUntilTableScopeMarker(); // "clear the stack back to a table context" in the spec. + void popUntilTableBodyScopeMarker(); // "clear the stack back to a table body context" in the spec. + void popUntilTableRowScopeMarker(); // "clear the stack back to a table row context" in the spec. + void popUntilForeignContentScopeMarker(); + void popHTMLHeadElement(); + void popHTMLBodyElement(); + void popAll(); + + static bool isMathMLTextIntegrationPoint(ContainerNode*); + static bool isHTMLIntegrationPoint(ContainerNode*); + + void remove(Element*); + void removeHTMLHeadElement(Element*); + + bool contains(Element*) const; + bool contains(const AtomicString& tagName) const; + + bool inScope(Element*) const; + bool inScope(const AtomicString& tagName) const; + bool inScope(const QualifiedName&) const; + bool inListItemScope(const AtomicString& tagName) const; + bool inListItemScope(const QualifiedName&) const; + bool inTableScope(const AtomicString& tagName) const; + bool inTableScope(const QualifiedName&) const; + bool inButtonScope(const AtomicString& tagName) const; + bool inButtonScope(const QualifiedName&) const; + bool inSelectScope(const AtomicString& tagName) const; + bool inSelectScope(const QualifiedName&) const; + + bool hasOnlyHTMLElementsInScope() const; + bool hasNumberedHeaderElementInScope() const; + + bool hasOnlyOneElement() const; + bool secondElementIsHTMLBodyElement() const; + + Element* htmlElement() const; + Element* headElement() const; + Element* bodyElement() const; + + ContainerNode* rootNode() const; + +#ifndef NDEBUG + void show(); +#endif + +private: + void pushCommon(PassRefPtr<ContainerNode>); + void pushRootNodeCommon(PassRefPtr<ContainerNode>); + void popCommon(); + void removeNonTopCommon(Element*); + + OwnPtr<ElementRecord> m_top; + + // We remember the root node, <head> and <body> as they are pushed. Their + // ElementRecords keep them alive. The root node is never popped. + // FIXME: We don't currently require type-specific information about + // these elements so we haven't yet bothered to plumb the types all the + // way down through createElement, etc. + ContainerNode* m_rootNode; + Element* m_headElement; + Element* m_bodyElement; + unsigned m_stackDepth; +}; + +inline bool isInHTMLNamespace(Node* node) +{ + // A DocumentFragment takes the place of the document element when parsing + // fragments and should be considered in the HTML namespace. + return node->namespaceURI() == HTMLNames::xhtmlNamespaceURI + || node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE + || node->nodeType() == Node::SHADOW_ROOT_NODE; // FIXME: Does this also apply to ShadowRoot? +} + + +} // namespace WebCore + +#endif // HTMLElementStack_h diff --git a/Source/WebCore/html/parser/HTMLEntityNames.in b/Source/WebCore/html/parser/HTMLEntityNames.in new file mode 100644 index 000000000..9940a629b --- /dev/null +++ b/Source/WebCore/html/parser/HTMLEntityNames.in @@ -0,0 +1,2231 @@ +"AElig;","U+000C6" +"AElig","U+000C6" +"AMP;","U+00026" +"AMP","U+00026" +"Aacute;","U+000C1" +"Aacute","U+000C1" +"Abreve;","U+00102" +"Acirc;","U+000C2" +"Acirc","U+000C2" +"Acy;","U+00410" +"Afr;","U+1D504" +"Agrave;","U+000C0" +"Agrave","U+000C0" +"Alpha;","U+00391" +"Amacr;","U+00100" +"And;","U+02A53" +"Aogon;","U+00104" +"Aopf;","U+1D538" +"ApplyFunction;","U+02061" +"Aring;","U+000C5" +"Aring","U+000C5" +"Ascr;","U+1D49C" +"Assign;","U+02254" +"Atilde;","U+000C3" +"Atilde","U+000C3" +"Auml;","U+000C4" +"Auml","U+000C4" +"Backslash;","U+02216" +"Barv;","U+02AE7" +"Barwed;","U+02306" +"Bcy;","U+00411" +"Because;","U+02235" +"Bernoullis;","U+0212C" +"Beta;","U+00392" +"Bfr;","U+1D505" +"Bopf;","U+1D539" +"Breve;","U+002D8" +"Bscr;","U+0212C" +"Bumpeq;","U+0224E" +"CHcy;","U+00427" +"COPY;","U+000A9" +"COPY","U+000A9" +"Cacute;","U+00106" +"Cap;","U+022D2" +"CapitalDifferentialD;","U+02145" +"Cayleys;","U+0212D" +"Ccaron;","U+0010C" +"Ccedil;","U+000C7" +"Ccedil","U+000C7" +"Ccirc;","U+00108" +"Cconint;","U+02230" +"Cdot;","U+0010A" +"Cedilla;","U+000B8" +"CenterDot;","U+000B7" +"Cfr;","U+0212D" +"Chi;","U+003A7" +"CircleDot;","U+02299" +"CircleMinus;","U+02296" +"CirclePlus;","U+02295" +"CircleTimes;","U+02297" +"ClockwiseContourIntegral;","U+02232" +"CloseCurlyDoubleQuote;","U+0201D" +"CloseCurlyQuote;","U+02019" +"Colon;","U+02237" +"Colone;","U+02A74" +"Congruent;","U+02261" +"Conint;","U+0222F" +"ContourIntegral;","U+0222E" +"Copf;","U+02102" +"Coproduct;","U+02210" +"CounterClockwiseContourIntegral;","U+02233" +"Cross;","U+02A2F" +"Cscr;","U+1D49E" +"Cup;","U+022D3" +"CupCap;","U+0224D" +"DD;","U+02145" +"DDotrahd;","U+02911" +"DJcy;","U+00402" +"DScy;","U+00405" +"DZcy;","U+0040F" +"Dagger;","U+02021" +"Darr;","U+021A1" +"Dashv;","U+02AE4" +"Dcaron;","U+0010E" +"Dcy;","U+00414" +"Del;","U+02207" +"Delta;","U+00394" +"Dfr;","U+1D507" +"DiacriticalAcute;","U+000B4" +"DiacriticalDot;","U+002D9" +"DiacriticalDoubleAcute;","U+002DD" +"DiacriticalGrave;","U+00060" +"DiacriticalTilde;","U+002DC" +"Diamond;","U+022C4" +"DifferentialD;","U+02146" +"Dopf;","U+1D53B" +"Dot;","U+000A8" +"DotDot;","U+020DC" +"DotEqual;","U+02250" +"DoubleContourIntegral;","U+0222F" +"DoubleDot;","U+000A8" +"DoubleDownArrow;","U+021D3" +"DoubleLeftArrow;","U+021D0" +"DoubleLeftRightArrow;","U+021D4" +"DoubleLeftTee;","U+02AE4" +"DoubleLongLeftArrow;","U+027F8" +"DoubleLongLeftRightArrow;","U+027FA" +"DoubleLongRightArrow;","U+027F9" +"DoubleRightArrow;","U+021D2" +"DoubleRightTee;","U+022A8" +"DoubleUpArrow;","U+021D1" +"DoubleUpDownArrow;","U+021D5" +"DoubleVerticalBar;","U+02225" +"DownArrow;","U+02193" +"DownArrowBar;","U+02913" +"DownArrowUpArrow;","U+021F5" +"DownBreve;","U+00311" +"DownLeftRightVector;","U+02950" +"DownLeftTeeVector;","U+0295E" +"DownLeftVector;","U+021BD" +"DownLeftVectorBar;","U+02956" +"DownRightTeeVector;","U+0295F" +"DownRightVector;","U+021C1" +"DownRightVectorBar;","U+02957" +"DownTee;","U+022A4" +"DownTeeArrow;","U+021A7" +"Downarrow;","U+021D3" +"Dscr;","U+1D49F" +"Dstrok;","U+00110" +"ENG;","U+0014A" +"ETH;","U+000D0" +"ETH","U+000D0" +"Eacute;","U+000C9" +"Eacute","U+000C9" +"Ecaron;","U+0011A" +"Ecirc;","U+000CA" +"Ecirc","U+000CA" +"Ecy;","U+0042D" +"Edot;","U+00116" +"Efr;","U+1D508" +"Egrave;","U+000C8" +"Egrave","U+000C8" +"Element;","U+02208" +"Emacr;","U+00112" +"EmptySmallSquare;","U+025FB" +"EmptyVerySmallSquare;","U+025AB" +"Eogon;","U+00118" +"Eopf;","U+1D53C" +"Epsilon;","U+00395" +"Equal;","U+02A75" +"EqualTilde;","U+02242" +"Equilibrium;","U+021CC" +"Escr;","U+02130" +"Esim;","U+02A73" +"Eta;","U+00397" +"Euml;","U+000CB" +"Euml","U+000CB" +"Exists;","U+02203" +"ExponentialE;","U+02147" +"Fcy;","U+00424" +"Ffr;","U+1D509" +"FilledSmallSquare;","U+025FC" +"FilledVerySmallSquare;","U+025AA" +"Fopf;","U+1D53D" +"ForAll;","U+02200" +"Fouriertrf;","U+02131" +"Fscr;","U+02131" +"GJcy;","U+00403" +"GT;","U+0003E" +"GT","U+0003E" +"Gamma;","U+00393" +"Gammad;","U+003DC" +"Gbreve;","U+0011E" +"Gcedil;","U+00122" +"Gcirc;","U+0011C" +"Gcy;","U+00413" +"Gdot;","U+00120" +"Gfr;","U+1D50A" +"Gg;","U+022D9" +"Gopf;","U+1D53E" +"GreaterEqual;","U+02265" +"GreaterEqualLess;","U+022DB" +"GreaterFullEqual;","U+02267" +"GreaterGreater;","U+02AA2" +"GreaterLess;","U+02277" +"GreaterSlantEqual;","U+02A7E" +"GreaterTilde;","U+02273" +"Gscr;","U+1D4A2" +"Gt;","U+0226B" +"HARDcy;","U+0042A" +"Hacek;","U+002C7" +"Hat;","U+0005E" +"Hcirc;","U+00124" +"Hfr;","U+0210C" +"HilbertSpace;","U+0210B" +"Hopf;","U+0210D" +"HorizontalLine;","U+02500" +"Hscr;","U+0210B" +"Hstrok;","U+00126" +"HumpDownHump;","U+0224E" +"HumpEqual;","U+0224F" +"IEcy;","U+00415" +"IJlig;","U+00132" +"IOcy;","U+00401" +"Iacute;","U+000CD" +"Iacute","U+000CD" +"Icirc;","U+000CE" +"Icirc","U+000CE" +"Icy;","U+00418" +"Idot;","U+00130" +"Ifr;","U+02111" +"Igrave;","U+000CC" +"Igrave","U+000CC" +"Im;","U+02111" +"Imacr;","U+0012A" +"ImaginaryI;","U+02148" +"Implies;","U+021D2" +"Int;","U+0222C" +"Integral;","U+0222B" +"Intersection;","U+022C2" +"InvisibleComma;","U+02063" +"InvisibleTimes;","U+02062" +"Iogon;","U+0012E" +"Iopf;","U+1D540" +"Iota;","U+00399" +"Iscr;","U+02110" +"Itilde;","U+00128" +"Iukcy;","U+00406" +"Iuml;","U+000CF" +"Iuml","U+000CF" +"Jcirc;","U+00134" +"Jcy;","U+00419" +"Jfr;","U+1D50D" +"Jopf;","U+1D541" +"Jscr;","U+1D4A5" +"Jsercy;","U+00408" +"Jukcy;","U+00404" +"KHcy;","U+00425" +"KJcy;","U+0040C" +"Kappa;","U+0039A" +"Kcedil;","U+00136" +"Kcy;","U+0041A" +"Kfr;","U+1D50E" +"Kopf;","U+1D542" +"Kscr;","U+1D4A6" +"LJcy;","U+00409" +"LT;","U+0003C" +"LT","U+0003C" +"Lacute;","U+00139" +"Lambda;","U+0039B" +"Lang;","U+027EA" +"Laplacetrf;","U+02112" +"Larr;","U+0219E" +"Lcaron;","U+0013D" +"Lcedil;","U+0013B" +"Lcy;","U+0041B" +"LeftAngleBracket;","U+027E8" +"LeftArrow;","U+02190" +"LeftArrowBar;","U+021E4" +"LeftArrowRightArrow;","U+021C6" +"LeftCeiling;","U+02308" +"LeftDoubleBracket;","U+027E6" +"LeftDownTeeVector;","U+02961" +"LeftDownVector;","U+021C3" +"LeftDownVectorBar;","U+02959" +"LeftFloor;","U+0230A" +"LeftRightArrow;","U+02194" +"LeftRightVector;","U+0294E" +"LeftTee;","U+022A3" +"LeftTeeArrow;","U+021A4" +"LeftTeeVector;","U+0295A" +"LeftTriangle;","U+022B2" +"LeftTriangleBar;","U+029CF" +"LeftTriangleEqual;","U+022B4" +"LeftUpDownVector;","U+02951" +"LeftUpTeeVector;","U+02960" +"LeftUpVector;","U+021BF" +"LeftUpVectorBar;","U+02958" +"LeftVector;","U+021BC" +"LeftVectorBar;","U+02952" +"Leftarrow;","U+021D0" +"Leftrightarrow;","U+021D4" +"LessEqualGreater;","U+022DA" +"LessFullEqual;","U+02266" +"LessGreater;","U+02276" +"LessLess;","U+02AA1" +"LessSlantEqual;","U+02A7D" +"LessTilde;","U+02272" +"Lfr;","U+1D50F" +"Ll;","U+022D8" +"Lleftarrow;","U+021DA" +"Lmidot;","U+0013F" +"LongLeftArrow;","U+027F5" +"LongLeftRightArrow;","U+027F7" +"LongRightArrow;","U+027F6" +"Longleftarrow;","U+027F8" +"Longleftrightarrow;","U+027FA" +"Longrightarrow;","U+027F9" +"Lopf;","U+1D543" +"LowerLeftArrow;","U+02199" +"LowerRightArrow;","U+02198" +"Lscr;","U+02112" +"Lsh;","U+021B0" +"Lstrok;","U+00141" +"Lt;","U+0226A" +"Map;","U+02905" +"Mcy;","U+0041C" +"MediumSpace;","U+0205F" +"Mellintrf;","U+02133" +"Mfr;","U+1D510" +"MinusPlus;","U+02213" +"Mopf;","U+1D544" +"Mscr;","U+02133" +"Mu;","U+0039C" +"NJcy;","U+0040A" +"Nacute;","U+00143" +"Ncaron;","U+00147" +"Ncedil;","U+00145" +"Ncy;","U+0041D" +"NegativeMediumSpace;","U+0200B" +"NegativeThickSpace;","U+0200B" +"NegativeThinSpace;","U+0200B" +"NegativeVeryThinSpace;","U+0200B" +"NestedGreaterGreater;","U+0226B" +"NestedLessLess;","U+0226A" +"NewLine;","U+0000A" +"Nfr;","U+1D511" +"NoBreak;","U+02060" +"NonBreakingSpace;","U+000A0" +"Nopf;","U+02115" +"Not;","U+02AEC" +"NotCongruent;","U+02262" +"NotCupCap;","U+0226D" +"NotDoubleVerticalBar;","U+02226" +"NotElement;","U+02209" +"NotEqual;","U+02260" +"NotExists;","U+02204" +"NotGreater;","U+0226F" +"NotGreaterEqual;","U+02271" +"NotGreaterLess;","U+02279" +"NotGreaterTilde;","U+02275" +"NotLeftTriangle;","U+022EA" +"NotLeftTriangleEqual;","U+022EC" +"NotLess;","U+0226E" +"NotLessEqual;","U+02270" +"NotLessGreater;","U+02278" +"NotLessTilde;","U+02274" +"NotPrecedes;","U+02280" +"NotPrecedesSlantEqual;","U+022E0" +"NotReverseElement;","U+0220C" +"NotRightTriangle;","U+022EB" +"NotRightTriangleEqual;","U+022ED" +"NotSquareSubsetEqual;","U+022E2" +"NotSquareSupersetEqual;","U+022E3" +"NotSubsetEqual;","U+02288" +"NotSucceeds;","U+02281" +"NotSucceedsSlantEqual;","U+022E1" +"NotSupersetEqual;","U+02289" +"NotTilde;","U+02241" +"NotTildeEqual;","U+02244" +"NotTildeFullEqual;","U+02247" +"NotTildeTilde;","U+02249" +"NotVerticalBar;","U+02224" +"Nscr;","U+1D4A9" +"Ntilde;","U+000D1" +"Ntilde","U+000D1" +"Nu;","U+0039D" +"OElig;","U+00152" +"Oacute;","U+000D3" +"Oacute","U+000D3" +"Ocirc;","U+000D4" +"Ocirc","U+000D4" +"Ocy;","U+0041E" +"Odblac;","U+00150" +"Ofr;","U+1D512" +"Ograve;","U+000D2" +"Ograve","U+000D2" +"Omacr;","U+0014C" +"Omega;","U+003A9" +"Omicron;","U+0039F" +"Oopf;","U+1D546" +"OpenCurlyDoubleQuote;","U+0201C" +"OpenCurlyQuote;","U+02018" +"Or;","U+02A54" +"Oscr;","U+1D4AA" +"Oslash;","U+000D8" +"Oslash","U+000D8" +"Otilde;","U+000D5" +"Otilde","U+000D5" +"Otimes;","U+02A37" +"Ouml;","U+000D6" +"Ouml","U+000D6" +"OverBar;","U+0203E" +"OverBrace;","U+023DE" +"OverBracket;","U+023B4" +"OverParenthesis;","U+023DC" +"PartialD;","U+02202" +"Pcy;","U+0041F" +"Pfr;","U+1D513" +"Phi;","U+003A6" +"Pi;","U+003A0" +"PlusMinus;","U+000B1" +"Poincareplane;","U+0210C" +"Popf;","U+02119" +"Pr;","U+02ABB" +"Precedes;","U+0227A" +"PrecedesEqual;","U+02AAF" +"PrecedesSlantEqual;","U+0227C" +"PrecedesTilde;","U+0227E" +"Prime;","U+02033" +"Product;","U+0220F" +"Proportion;","U+02237" +"Proportional;","U+0221D" +"Pscr;","U+1D4AB" +"Psi;","U+003A8" +"QUOT;","U+00022" +"QUOT","U+00022" +"Qfr;","U+1D514" +"Qopf;","U+0211A" +"Qscr;","U+1D4AC" +"RBarr;","U+02910" +"REG;","U+000AE" +"REG","U+000AE" +"Racute;","U+00154" +"Rang;","U+027EB" +"Rarr;","U+021A0" +"Rarrtl;","U+02916" +"Rcaron;","U+00158" +"Rcedil;","U+00156" +"Rcy;","U+00420" +"Re;","U+0211C" +"ReverseElement;","U+0220B" +"ReverseEquilibrium;","U+021CB" +"ReverseUpEquilibrium;","U+0296F" +"Rfr;","U+0211C" +"Rho;","U+003A1" +"RightAngleBracket;","U+027E9" +"RightArrow;","U+02192" +"RightArrowBar;","U+021E5" +"RightArrowLeftArrow;","U+021C4" +"RightCeiling;","U+02309" +"RightDoubleBracket;","U+027E7" +"RightDownTeeVector;","U+0295D" +"RightDownVector;","U+021C2" +"RightDownVectorBar;","U+02955" +"RightFloor;","U+0230B" +"RightTee;","U+022A2" +"RightTeeArrow;","U+021A6" +"RightTeeVector;","U+0295B" +"RightTriangle;","U+022B3" +"RightTriangleBar;","U+029D0" +"RightTriangleEqual;","U+022B5" +"RightUpDownVector;","U+0294F" +"RightUpTeeVector;","U+0295C" +"RightUpVector;","U+021BE" +"RightUpVectorBar;","U+02954" +"RightVector;","U+021C0" +"RightVectorBar;","U+02953" +"Rightarrow;","U+021D2" +"Ropf;","U+0211D" +"RoundImplies;","U+02970" +"Rrightarrow;","U+021DB" +"Rscr;","U+0211B" +"Rsh;","U+021B1" +"RuleDelayed;","U+029F4" +"SHCHcy;","U+00429" +"SHcy;","U+00428" +"SOFTcy;","U+0042C" +"Sacute;","U+0015A" +"Sc;","U+02ABC" +"Scaron;","U+00160" +"Scedil;","U+0015E" +"Scirc;","U+0015C" +"Scy;","U+00421" +"Sfr;","U+1D516" +"ShortDownArrow;","U+02193" +"ShortLeftArrow;","U+02190" +"ShortRightArrow;","U+02192" +"ShortUpArrow;","U+02191" +"Sigma;","U+003A3" +"SmallCircle;","U+02218" +"Sopf;","U+1D54A" +"Sqrt;","U+0221A" +"Square;","U+025A1" +"SquareIntersection;","U+02293" +"SquareSubset;","U+0228F" +"SquareSubsetEqual;","U+02291" +"SquareSuperset;","U+02290" +"SquareSupersetEqual;","U+02292" +"SquareUnion;","U+02294" +"Sscr;","U+1D4AE" +"Star;","U+022C6" +"Sub;","U+022D0" +"Subset;","U+022D0" +"SubsetEqual;","U+02286" +"Succeeds;","U+0227B" +"SucceedsEqual;","U+02AB0" +"SucceedsSlantEqual;","U+0227D" +"SucceedsTilde;","U+0227F" +"SuchThat;","U+0220B" +"Sum;","U+02211" +"Sup;","U+022D1" +"Superset;","U+02283" +"SupersetEqual;","U+02287" +"Supset;","U+022D1" +"THORN;","U+000DE" +"THORN","U+000DE" +"TRADE;","U+02122" +"TSHcy;","U+0040B" +"TScy;","U+00426" +"Tab;","U+00009" +"Tau;","U+003A4" +"Tcaron;","U+00164" +"Tcedil;","U+00162" +"Tcy;","U+00422" +"Tfr;","U+1D517" +"Therefore;","U+02234" +"Theta;","U+00398" +"ThinSpace;","U+02009" +"Tilde;","U+0223C" +"TildeEqual;","U+02243" +"TildeFullEqual;","U+02245" +"TildeTilde;","U+02248" +"Topf;","U+1D54B" +"TripleDot;","U+020DB" +"Tscr;","U+1D4AF" +"Tstrok;","U+00166" +"Uacute;","U+000DA" +"Uacute","U+000DA" +"Uarr;","U+0219F" +"Uarrocir;","U+02949" +"Ubrcy;","U+0040E" +"Ubreve;","U+0016C" +"Ucirc;","U+000DB" +"Ucirc","U+000DB" +"Ucy;","U+00423" +"Udblac;","U+00170" +"Ufr;","U+1D518" +"Ugrave;","U+000D9" +"Ugrave","U+000D9" +"Umacr;","U+0016A" +"UnderBar;","U+0005F" +"UnderBrace;","U+023DF" +"UnderBracket;","U+023B5" +"UnderParenthesis;","U+023DD" +"Union;","U+022C3" +"UnionPlus;","U+0228E" +"Uogon;","U+00172" +"Uopf;","U+1D54C" +"UpArrow;","U+02191" +"UpArrowBar;","U+02912" +"UpArrowDownArrow;","U+021C5" +"UpDownArrow;","U+02195" +"UpEquilibrium;","U+0296E" +"UpTee;","U+022A5" +"UpTeeArrow;","U+021A5" +"Uparrow;","U+021D1" +"Updownarrow;","U+021D5" +"UpperLeftArrow;","U+02196" +"UpperRightArrow;","U+02197" +"Upsi;","U+003D2" +"Upsilon;","U+003A5" +"Uring;","U+0016E" +"Uscr;","U+1D4B0" +"Utilde;","U+00168" +"Uuml;","U+000DC" +"Uuml","U+000DC" +"VDash;","U+022AB" +"Vbar;","U+02AEB" +"Vcy;","U+00412" +"Vdash;","U+022A9" +"Vdashl;","U+02AE6" +"Vee;","U+022C1" +"Verbar;","U+02016" +"Vert;","U+02016" +"VerticalBar;","U+02223" +"VerticalLine;","U+0007C" +"VerticalSeparator;","U+02758" +"VerticalTilde;","U+02240" +"VeryThinSpace;","U+0200A" +"Vfr;","U+1D519" +"Vopf;","U+1D54D" +"Vscr;","U+1D4B1" +"Vvdash;","U+022AA" +"Wcirc;","U+00174" +"Wedge;","U+022C0" +"Wfr;","U+1D51A" +"Wopf;","U+1D54E" +"Wscr;","U+1D4B2" +"Xfr;","U+1D51B" +"Xi;","U+0039E" +"Xopf;","U+1D54F" +"Xscr;","U+1D4B3" +"YAcy;","U+0042F" +"YIcy;","U+00407" +"YUcy;","U+0042E" +"Yacute;","U+000DD" +"Yacute","U+000DD" +"Ycirc;","U+00176" +"Ycy;","U+0042B" +"Yfr;","U+1D51C" +"Yopf;","U+1D550" +"Yscr;","U+1D4B4" +"Yuml;","U+00178" +"ZHcy;","U+00416" +"Zacute;","U+00179" +"Zcaron;","U+0017D" +"Zcy;","U+00417" +"Zdot;","U+0017B" +"ZeroWidthSpace;","U+0200B" +"Zeta;","U+00396" +"Zfr;","U+02128" +"Zopf;","U+02124" +"Zscr;","U+1D4B5" +"aacute;","U+000E1" +"aacute","U+000E1" +"abreve;","U+00103" +"ac;","U+0223E" +"acd;","U+0223F" +"acirc;","U+000E2" +"acirc","U+000E2" +"acute;","U+000B4" +"acute","U+000B4" +"acy;","U+00430" +"aelig;","U+000E6" +"aelig","U+000E6" +"af;","U+02061" +"afr;","U+1D51E" +"agrave;","U+000E0" +"agrave","U+000E0" +"alefsym;","U+02135" +"aleph;","U+02135" +"alpha;","U+003B1" +"amacr;","U+00101" +"amalg;","U+02A3F" +"amp;","U+00026" +"amp","U+00026" +"and;","U+02227" +"andand;","U+02A55" +"andd;","U+02A5C" +"andslope;","U+02A58" +"andv;","U+02A5A" +"ang;","U+02220" +"ange;","U+029A4" +"angle;","U+02220" +"angmsd;","U+02221" +"angmsdaa;","U+029A8" +"angmsdab;","U+029A9" +"angmsdac;","U+029AA" +"angmsdad;","U+029AB" +"angmsdae;","U+029AC" +"angmsdaf;","U+029AD" +"angmsdag;","U+029AE" +"angmsdah;","U+029AF" +"angrt;","U+0221F" +"angrtvb;","U+022BE" +"angrtvbd;","U+0299D" +"angsph;","U+02222" +"angst;","U+000C5" +"angzarr;","U+0237C" +"aogon;","U+00105" +"aopf;","U+1D552" +"ap;","U+02248" +"apE;","U+02A70" +"apacir;","U+02A6F" +"ape;","U+0224A" +"apid;","U+0224B" +"apos;","U+00027" +"approx;","U+02248" +"approxeq;","U+0224A" +"aring;","U+000E5" +"aring","U+000E5" +"ascr;","U+1D4B6" +"ast;","U+0002A" +"asymp;","U+02248" +"asympeq;","U+0224D" +"atilde;","U+000E3" +"atilde","U+000E3" +"auml;","U+000E4" +"auml","U+000E4" +"awconint;","U+02233" +"awint;","U+02A11" +"bNot;","U+02AED" +"backcong;","U+0224C" +"backepsilon;","U+003F6" +"backprime;","U+02035" +"backsim;","U+0223D" +"backsimeq;","U+022CD" +"barvee;","U+022BD" +"barwed;","U+02305" +"barwedge;","U+02305" +"bbrk;","U+023B5" +"bbrktbrk;","U+023B6" +"bcong;","U+0224C" +"bcy;","U+00431" +"bdquo;","U+0201E" +"becaus;","U+02235" +"because;","U+02235" +"bemptyv;","U+029B0" +"bepsi;","U+003F6" +"bernou;","U+0212C" +"beta;","U+003B2" +"beth;","U+02136" +"between;","U+0226C" +"bfr;","U+1D51F" +"bigcap;","U+022C2" +"bigcirc;","U+025EF" +"bigcup;","U+022C3" +"bigodot;","U+02A00" +"bigoplus;","U+02A01" +"bigotimes;","U+02A02" +"bigsqcup;","U+02A06" +"bigstar;","U+02605" +"bigtriangledown;","U+025BD" +"bigtriangleup;","U+025B3" +"biguplus;","U+02A04" +"bigvee;","U+022C1" +"bigwedge;","U+022C0" +"bkarow;","U+0290D" +"blacklozenge;","U+029EB" +"blacksquare;","U+025AA" +"blacktriangle;","U+025B4" +"blacktriangledown;","U+025BE" +"blacktriangleleft;","U+025C2" +"blacktriangleright;","U+025B8" +"blank;","U+02423" +"blk12;","U+02592" +"blk14;","U+02591" +"blk34;","U+02593" +"block;","U+02588" +"bnot;","U+02310" +"bopf;","U+1D553" +"bot;","U+022A5" +"bottom;","U+022A5" +"bowtie;","U+022C8" +"boxDL;","U+02557" +"boxDR;","U+02554" +"boxDl;","U+02556" +"boxDr;","U+02553" +"boxH;","U+02550" +"boxHD;","U+02566" +"boxHU;","U+02569" +"boxHd;","U+02564" +"boxHu;","U+02567" +"boxUL;","U+0255D" +"boxUR;","U+0255A" +"boxUl;","U+0255C" +"boxUr;","U+02559" +"boxV;","U+02551" +"boxVH;","U+0256C" +"boxVL;","U+02563" +"boxVR;","U+02560" +"boxVh;","U+0256B" +"boxVl;","U+02562" +"boxVr;","U+0255F" +"boxbox;","U+029C9" +"boxdL;","U+02555" +"boxdR;","U+02552" +"boxdl;","U+02510" +"boxdr;","U+0250C" +"boxh;","U+02500" +"boxhD;","U+02565" +"boxhU;","U+02568" +"boxhd;","U+0252C" +"boxhu;","U+02534" +"boxminus;","U+0229F" +"boxplus;","U+0229E" +"boxtimes;","U+022A0" +"boxuL;","U+0255B" +"boxuR;","U+02558" +"boxul;","U+02518" +"boxur;","U+02514" +"boxv;","U+02502" +"boxvH;","U+0256A" +"boxvL;","U+02561" +"boxvR;","U+0255E" +"boxvh;","U+0253C" +"boxvl;","U+02524" +"boxvr;","U+0251C" +"bprime;","U+02035" +"breve;","U+002D8" +"brvbar;","U+000A6" +"brvbar","U+000A6" +"bscr;","U+1D4B7" +"bsemi;","U+0204F" +"bsim;","U+0223D" +"bsime;","U+022CD" +"bsol;","U+0005C" +"bsolb;","U+029C5" +"bsolhsub;","U+027C8" +"bull;","U+02022" +"bullet;","U+02022" +"bump;","U+0224E" +"bumpE;","U+02AAE" +"bumpe;","U+0224F" +"bumpeq;","U+0224F" +"cacute;","U+00107" +"cap;","U+02229" +"capand;","U+02A44" +"capbrcup;","U+02A49" +"capcap;","U+02A4B" +"capcup;","U+02A47" +"capdot;","U+02A40" +"caret;","U+02041" +"caron;","U+002C7" +"ccaps;","U+02A4D" +"ccaron;","U+0010D" +"ccedil;","U+000E7" +"ccedil","U+000E7" +"ccirc;","U+00109" +"ccups;","U+02A4C" +"ccupssm;","U+02A50" +"cdot;","U+0010B" +"cedil;","U+000B8" +"cedil","U+000B8" +"cemptyv;","U+029B2" +"cent;","U+000A2" +"cent","U+000A2" +"centerdot;","U+000B7" +"cfr;","U+1D520" +"chcy;","U+00447" +"check;","U+02713" +"checkmark;","U+02713" +"chi;","U+003C7" +"cir;","U+025CB" +"cirE;","U+029C3" +"circ;","U+002C6" +"circeq;","U+02257" +"circlearrowleft;","U+021BA" +"circlearrowright;","U+021BB" +"circledR;","U+000AE" +"circledS;","U+024C8" +"circledast;","U+0229B" +"circledcirc;","U+0229A" +"circleddash;","U+0229D" +"cire;","U+02257" +"cirfnint;","U+02A10" +"cirmid;","U+02AEF" +"cirscir;","U+029C2" +"clubs;","U+02663" +"clubsuit;","U+02663" +"colon;","U+0003A" +"colone;","U+02254" +"coloneq;","U+02254" +"comma;","U+0002C" +"commat;","U+00040" +"comp;","U+02201" +"compfn;","U+02218" +"complement;","U+02201" +"complexes;","U+02102" +"cong;","U+02245" +"congdot;","U+02A6D" +"conint;","U+0222E" +"copf;","U+1D554" +"coprod;","U+02210" +"copy;","U+000A9" +"copy","U+000A9" +"copysr;","U+02117" +"crarr;","U+021B5" +"cross;","U+02717" +"cscr;","U+1D4B8" +"csub;","U+02ACF" +"csube;","U+02AD1" +"csup;","U+02AD0" +"csupe;","U+02AD2" +"ctdot;","U+022EF" +"cudarrl;","U+02938" +"cudarrr;","U+02935" +"cuepr;","U+022DE" +"cuesc;","U+022DF" +"cularr;","U+021B6" +"cularrp;","U+0293D" +"cup;","U+0222A" +"cupbrcap;","U+02A48" +"cupcap;","U+02A46" +"cupcup;","U+02A4A" +"cupdot;","U+0228D" +"cupor;","U+02A45" +"curarr;","U+021B7" +"curarrm;","U+0293C" +"curlyeqprec;","U+022DE" +"curlyeqsucc;","U+022DF" +"curlyvee;","U+022CE" +"curlywedge;","U+022CF" +"curren;","U+000A4" +"curren","U+000A4" +"curvearrowleft;","U+021B6" +"curvearrowright;","U+021B7" +"cuvee;","U+022CE" +"cuwed;","U+022CF" +"cwconint;","U+02232" +"cwint;","U+02231" +"cylcty;","U+0232D" +"dArr;","U+021D3" +"dHar;","U+02965" +"dagger;","U+02020" +"daleth;","U+02138" +"darr;","U+02193" +"dash;","U+02010" +"dashv;","U+022A3" +"dbkarow;","U+0290F" +"dblac;","U+002DD" +"dcaron;","U+0010F" +"dcy;","U+00434" +"dd;","U+02146" +"ddagger;","U+02021" +"ddarr;","U+021CA" +"ddotseq;","U+02A77" +"deg;","U+000B0" +"deg","U+000B0" +"delta;","U+003B4" +"demptyv;","U+029B1" +"dfisht;","U+0297F" +"dfr;","U+1D521" +"dharl;","U+021C3" +"dharr;","U+021C2" +"diam;","U+022C4" +"diamond;","U+022C4" +"diamondsuit;","U+02666" +"diams;","U+02666" +"die;","U+000A8" +"digamma;","U+003DD" +"disin;","U+022F2" +"div;","U+000F7" +"divide;","U+000F7" +"divide","U+000F7" +"divideontimes;","U+022C7" +"divonx;","U+022C7" +"djcy;","U+00452" +"dlcorn;","U+0231E" +"dlcrop;","U+0230D" +"dollar;","U+00024" +"dopf;","U+1D555" +"dot;","U+002D9" +"doteq;","U+02250" +"doteqdot;","U+02251" +"dotminus;","U+02238" +"dotplus;","U+02214" +"dotsquare;","U+022A1" +"doublebarwedge;","U+02306" +"downarrow;","U+02193" +"downdownarrows;","U+021CA" +"downharpoonleft;","U+021C3" +"downharpoonright;","U+021C2" +"drbkarow;","U+02910" +"drcorn;","U+0231F" +"drcrop;","U+0230C" +"dscr;","U+1D4B9" +"dscy;","U+00455" +"dsol;","U+029F6" +"dstrok;","U+00111" +"dtdot;","U+022F1" +"dtri;","U+025BF" +"dtrif;","U+025BE" +"duarr;","U+021F5" +"duhar;","U+0296F" +"dwangle;","U+029A6" +"dzcy;","U+0045F" +"dzigrarr;","U+027FF" +"eDDot;","U+02A77" +"eDot;","U+02251" +"eacute;","U+000E9" +"eacute","U+000E9" +"easter;","U+02A6E" +"ecaron;","U+0011B" +"ecir;","U+02256" +"ecirc;","U+000EA" +"ecirc","U+000EA" +"ecolon;","U+02255" +"ecy;","U+0044D" +"edot;","U+00117" +"ee;","U+02147" +"efDot;","U+02252" +"efr;","U+1D522" +"eg;","U+02A9A" +"egrave;","U+000E8" +"egrave","U+000E8" +"egs;","U+02A96" +"egsdot;","U+02A98" +"el;","U+02A99" +"elinters;","U+023E7" +"ell;","U+02113" +"els;","U+02A95" +"elsdot;","U+02A97" +"emacr;","U+00113" +"empty;","U+02205" +"emptyset;","U+02205" +"emptyv;","U+02205" +"emsp13;","U+02004" +"emsp14;","U+02005" +"emsp;","U+02003" +"eng;","U+0014B" +"ensp;","U+02002" +"eogon;","U+00119" +"eopf;","U+1D556" +"epar;","U+022D5" +"eparsl;","U+029E3" +"eplus;","U+02A71" +"epsi;","U+003B5" +"epsilon;","U+003B5" +"epsiv;","U+003F5" +"eqcirc;","U+02256" +"eqcolon;","U+02255" +"eqsim;","U+02242" +"eqslantgtr;","U+02A96" +"eqslantless;","U+02A95" +"equals;","U+0003D" +"equest;","U+0225F" +"equiv;","U+02261" +"equivDD;","U+02A78" +"eqvparsl;","U+029E5" +"erDot;","U+02253" +"erarr;","U+02971" +"escr;","U+0212F" +"esdot;","U+02250" +"esim;","U+02242" +"eta;","U+003B7" +"eth;","U+000F0" +"eth","U+000F0" +"euml;","U+000EB" +"euml","U+000EB" +"euro;","U+020AC" +"excl;","U+00021" +"exist;","U+02203" +"expectation;","U+02130" +"exponentiale;","U+02147" +"fallingdotseq;","U+02252" +"fcy;","U+00444" +"female;","U+02640" +"ffilig;","U+0FB03" +"fflig;","U+0FB00" +"ffllig;","U+0FB04" +"ffr;","U+1D523" +"filig;","U+0FB01" +"flat;","U+0266D" +"fllig;","U+0FB02" +"fltns;","U+025B1" +"fnof;","U+00192" +"fopf;","U+1D557" +"forall;","U+02200" +"fork;","U+022D4" +"forkv;","U+02AD9" +"fpartint;","U+02A0D" +"frac12;","U+000BD" +"frac12","U+000BD" +"frac13;","U+02153" +"frac14;","U+000BC" +"frac14","U+000BC" +"frac15;","U+02155" +"frac16;","U+02159" +"frac18;","U+0215B" +"frac23;","U+02154" +"frac25;","U+02156" +"frac34;","U+000BE" +"frac34","U+000BE" +"frac35;","U+02157" +"frac38;","U+0215C" +"frac45;","U+02158" +"frac56;","U+0215A" +"frac58;","U+0215D" +"frac78;","U+0215E" +"frasl;","U+02044" +"frown;","U+02322" +"fscr;","U+1D4BB" +"gE;","U+02267" +"gEl;","U+02A8C" +"gacute;","U+001F5" +"gamma;","U+003B3" +"gammad;","U+003DD" +"gap;","U+02A86" +"gbreve;","U+0011F" +"gcirc;","U+0011D" +"gcy;","U+00433" +"gdot;","U+00121" +"ge;","U+02265" +"gel;","U+022DB" +"geq;","U+02265" +"geqq;","U+02267" +"geqslant;","U+02A7E" +"ges;","U+02A7E" +"gescc;","U+02AA9" +"gesdot;","U+02A80" +"gesdoto;","U+02A82" +"gesdotol;","U+02A84" +"gesles;","U+02A94" +"gfr;","U+1D524" +"gg;","U+0226B" +"ggg;","U+022D9" +"gimel;","U+02137" +"gjcy;","U+00453" +"gl;","U+02277" +"glE;","U+02A92" +"gla;","U+02AA5" +"glj;","U+02AA4" +"gnE;","U+02269" +"gnap;","U+02A8A" +"gnapprox;","U+02A8A" +"gne;","U+02A88" +"gneq;","U+02A88" +"gneqq;","U+02269" +"gnsim;","U+022E7" +"gopf;","U+1D558" +"grave;","U+00060" +"gscr;","U+0210A" +"gsim;","U+02273" +"gsime;","U+02A8E" +"gsiml;","U+02A90" +"gt;","U+0003E" +"gt","U+0003E" +"gtcc;","U+02AA7" +"gtcir;","U+02A7A" +"gtdot;","U+022D7" +"gtlPar;","U+02995" +"gtquest;","U+02A7C" +"gtrapprox;","U+02A86" +"gtrarr;","U+02978" +"gtrdot;","U+022D7" +"gtreqless;","U+022DB" +"gtreqqless;","U+02A8C" +"gtrless;","U+02277" +"gtrsim;","U+02273" +"hArr;","U+021D4" +"hairsp;","U+0200A" +"half;","U+000BD" +"hamilt;","U+0210B" +"hardcy;","U+0044A" +"harr;","U+02194" +"harrcir;","U+02948" +"harrw;","U+021AD" +"hbar;","U+0210F" +"hcirc;","U+00125" +"hearts;","U+02665" +"heartsuit;","U+02665" +"hellip;","U+02026" +"hercon;","U+022B9" +"hfr;","U+1D525" +"hksearow;","U+02925" +"hkswarow;","U+02926" +"hoarr;","U+021FF" +"homtht;","U+0223B" +"hookleftarrow;","U+021A9" +"hookrightarrow;","U+021AA" +"hopf;","U+1D559" +"horbar;","U+02015" +"hscr;","U+1D4BD" +"hslash;","U+0210F" +"hstrok;","U+00127" +"hybull;","U+02043" +"hyphen;","U+02010" +"iacute;","U+000ED" +"iacute","U+000ED" +"ic;","U+02063" +"icirc;","U+000EE" +"icirc","U+000EE" +"icy;","U+00438" +"iecy;","U+00435" +"iexcl;","U+000A1" +"iexcl","U+000A1" +"iff;","U+021D4" +"ifr;","U+1D526" +"igrave;","U+000EC" +"igrave","U+000EC" +"ii;","U+02148" +"iiiint;","U+02A0C" +"iiint;","U+0222D" +"iinfin;","U+029DC" +"iiota;","U+02129" +"ijlig;","U+00133" +"imacr;","U+0012B" +"image;","U+02111" +"imagline;","U+02110" +"imagpart;","U+02111" +"imath;","U+00131" +"imof;","U+022B7" +"imped;","U+001B5" +"in;","U+02208" +"incare;","U+02105" +"infin;","U+0221E" +"infintie;","U+029DD" +"inodot;","U+00131" +"int;","U+0222B" +"intcal;","U+022BA" +"integers;","U+02124" +"intercal;","U+022BA" +"intlarhk;","U+02A17" +"intprod;","U+02A3C" +"iocy;","U+00451" +"iogon;","U+0012F" +"iopf;","U+1D55A" +"iota;","U+003B9" +"iprod;","U+02A3C" +"iquest;","U+000BF" +"iquest","U+000BF" +"iscr;","U+1D4BE" +"isin;","U+02208" +"isinE;","U+022F9" +"isindot;","U+022F5" +"isins;","U+022F4" +"isinsv;","U+022F3" +"isinv;","U+02208" +"it;","U+02062" +"itilde;","U+00129" +"iukcy;","U+00456" +"iuml;","U+000EF" +"iuml","U+000EF" +"jcirc;","U+00135" +"jcy;","U+00439" +"jfr;","U+1D527" +"jmath;","U+00237" +"jopf;","U+1D55B" +"jscr;","U+1D4BF" +"jsercy;","U+00458" +"jukcy;","U+00454" +"kappa;","U+003BA" +"kappav;","U+003F0" +"kcedil;","U+00137" +"kcy;","U+0043A" +"kfr;","U+1D528" +"kgreen;","U+00138" +"khcy;","U+00445" +"kjcy;","U+0045C" +"kopf;","U+1D55C" +"kscr;","U+1D4C0" +"lAarr;","U+021DA" +"lArr;","U+021D0" +"lAtail;","U+0291B" +"lBarr;","U+0290E" +"lE;","U+02266" +"lEg;","U+02A8B" +"lHar;","U+02962" +"lacute;","U+0013A" +"laemptyv;","U+029B4" +"lagran;","U+02112" +"lambda;","U+003BB" +"lang;","U+027E8" +"langd;","U+02991" +"langle;","U+027E8" +"lap;","U+02A85" +"laquo;","U+000AB" +"laquo","U+000AB" +"larr;","U+02190" +"larrb;","U+021E4" +"larrbfs;","U+0291F" +"larrfs;","U+0291D" +"larrhk;","U+021A9" +"larrlp;","U+021AB" +"larrpl;","U+02939" +"larrsim;","U+02973" +"larrtl;","U+021A2" +"lat;","U+02AAB" +"latail;","U+02919" +"late;","U+02AAD" +"lbarr;","U+0290C" +"lbbrk;","U+02772" +"lbrace;","U+0007B" +"lbrack;","U+0005B" +"lbrke;","U+0298B" +"lbrksld;","U+0298F" +"lbrkslu;","U+0298D" +"lcaron;","U+0013E" +"lcedil;","U+0013C" +"lceil;","U+02308" +"lcub;","U+0007B" +"lcy;","U+0043B" +"ldca;","U+02936" +"ldquo;","U+0201C" +"ldquor;","U+0201E" +"ldrdhar;","U+02967" +"ldrushar;","U+0294B" +"ldsh;","U+021B2" +"le;","U+02264" +"leftarrow;","U+02190" +"leftarrowtail;","U+021A2" +"leftharpoondown;","U+021BD" +"leftharpoonup;","U+021BC" +"leftleftarrows;","U+021C7" +"leftrightarrow;","U+02194" +"leftrightarrows;","U+021C6" +"leftrightharpoons;","U+021CB" +"leftrightsquigarrow;","U+021AD" +"leftthreetimes;","U+022CB" +"leg;","U+022DA" +"leq;","U+02264" +"leqq;","U+02266" +"leqslant;","U+02A7D" +"les;","U+02A7D" +"lescc;","U+02AA8" +"lesdot;","U+02A7F" +"lesdoto;","U+02A81" +"lesdotor;","U+02A83" +"lesges;","U+02A93" +"lessapprox;","U+02A85" +"lessdot;","U+022D6" +"lesseqgtr;","U+022DA" +"lesseqqgtr;","U+02A8B" +"lessgtr;","U+02276" +"lesssim;","U+02272" +"lfisht;","U+0297C" +"lfloor;","U+0230A" +"lfr;","U+1D529" +"lg;","U+02276" +"lgE;","U+02A91" +"lhard;","U+021BD" +"lharu;","U+021BC" +"lharul;","U+0296A" +"lhblk;","U+02584" +"ljcy;","U+00459" +"ll;","U+0226A" +"llarr;","U+021C7" +"llcorner;","U+0231E" +"llhard;","U+0296B" +"lltri;","U+025FA" +"lmidot;","U+00140" +"lmoust;","U+023B0" +"lmoustache;","U+023B0" +"lnE;","U+02268" +"lnap;","U+02A89" +"lnapprox;","U+02A89" +"lne;","U+02A87" +"lneq;","U+02A87" +"lneqq;","U+02268" +"lnsim;","U+022E6" +"loang;","U+027EC" +"loarr;","U+021FD" +"lobrk;","U+027E6" +"longleftarrow;","U+027F5" +"longleftrightarrow;","U+027F7" +"longmapsto;","U+027FC" +"longrightarrow;","U+027F6" +"looparrowleft;","U+021AB" +"looparrowright;","U+021AC" +"lopar;","U+02985" +"lopf;","U+1D55D" +"loplus;","U+02A2D" +"lotimes;","U+02A34" +"lowast;","U+02217" +"lowbar;","U+0005F" +"loz;","U+025CA" +"lozenge;","U+025CA" +"lozf;","U+029EB" +"lpar;","U+00028" +"lparlt;","U+02993" +"lrarr;","U+021C6" +"lrcorner;","U+0231F" +"lrhar;","U+021CB" +"lrhard;","U+0296D" +"lrm;","U+0200E" +"lrtri;","U+022BF" +"lsaquo;","U+02039" +"lscr;","U+1D4C1" +"lsh;","U+021B0" +"lsim;","U+02272" +"lsime;","U+02A8D" +"lsimg;","U+02A8F" +"lsqb;","U+0005B" +"lsquo;","U+02018" +"lsquor;","U+0201A" +"lstrok;","U+00142" +"lt;","U+0003C" +"lt","U+0003C" +"ltcc;","U+02AA6" +"ltcir;","U+02A79" +"ltdot;","U+022D6" +"lthree;","U+022CB" +"ltimes;","U+022C9" +"ltlarr;","U+02976" +"ltquest;","U+02A7B" +"ltrPar;","U+02996" +"ltri;","U+025C3" +"ltrie;","U+022B4" +"ltrif;","U+025C2" +"lurdshar;","U+0294A" +"luruhar;","U+02966" +"mDDot;","U+0223A" +"macr;","U+000AF" +"macr","U+000AF" +"male;","U+02642" +"malt;","U+02720" +"maltese;","U+02720" +"map;","U+021A6" +"mapsto;","U+021A6" +"mapstodown;","U+021A7" +"mapstoleft;","U+021A4" +"mapstoup;","U+021A5" +"marker;","U+025AE" +"mcomma;","U+02A29" +"mcy;","U+0043C" +"mdash;","U+02014" +"measuredangle;","U+02221" +"mfr;","U+1D52A" +"mho;","U+02127" +"micro;","U+000B5" +"micro","U+000B5" +"mid;","U+02223" +"midast;","U+0002A" +"midcir;","U+02AF0" +"middot;","U+000B7" +"middot","U+000B7" +"minus;","U+02212" +"minusb;","U+0229F" +"minusd;","U+02238" +"minusdu;","U+02A2A" +"mlcp;","U+02ADB" +"mldr;","U+02026" +"mnplus;","U+02213" +"models;","U+022A7" +"mopf;","U+1D55E" +"mp;","U+02213" +"mscr;","U+1D4C2" +"mstpos;","U+0223E" +"mu;","U+003BC" +"multimap;","U+022B8" +"mumap;","U+022B8" +"nLeftarrow;","U+021CD" +"nLeftrightarrow;","U+021CE" +"nRightarrow;","U+021CF" +"nVDash;","U+022AF" +"nVdash;","U+022AE" +"nabla;","U+02207" +"nacute;","U+00144" +"nap;","U+02249" +"napos;","U+00149" +"napprox;","U+02249" +"natur;","U+0266E" +"natural;","U+0266E" +"naturals;","U+02115" +"nbsp;","U+000A0" +"nbsp","U+000A0" +"ncap;","U+02A43" +"ncaron;","U+00148" +"ncedil;","U+00146" +"ncong;","U+02247" +"ncup;","U+02A42" +"ncy;","U+0043D" +"ndash;","U+02013" +"ne;","U+02260" +"neArr;","U+021D7" +"nearhk;","U+02924" +"nearr;","U+02197" +"nearrow;","U+02197" +"nequiv;","U+02262" +"nesear;","U+02928" +"nexist;","U+02204" +"nexists;","U+02204" +"nfr;","U+1D52B" +"nge;","U+02271" +"ngeq;","U+02271" +"ngsim;","U+02275" +"ngt;","U+0226F" +"ngtr;","U+0226F" +"nhArr;","U+021CE" +"nharr;","U+021AE" +"nhpar;","U+02AF2" +"ni;","U+0220B" +"nis;","U+022FC" +"nisd;","U+022FA" +"niv;","U+0220B" +"njcy;","U+0045A" +"nlArr;","U+021CD" +"nlarr;","U+0219A" +"nldr;","U+02025" +"nle;","U+02270" +"nleftarrow;","U+0219A" +"nleftrightarrow;","U+021AE" +"nleq;","U+02270" +"nless;","U+0226E" +"nlsim;","U+02274" +"nlt;","U+0226E" +"nltri;","U+022EA" +"nltrie;","U+022EC" +"nmid;","U+02224" +"nopf;","U+1D55F" +"not;","U+000AC" +"not","U+000AC" +"notin;","U+02209" +"notinva;","U+02209" +"notinvb;","U+022F7" +"notinvc;","U+022F6" +"notni;","U+0220C" +"notniva;","U+0220C" +"notnivb;","U+022FE" +"notnivc;","U+022FD" +"npar;","U+02226" +"nparallel;","U+02226" +"npolint;","U+02A14" +"npr;","U+02280" +"nprcue;","U+022E0" +"nprec;","U+02280" +"nrArr;","U+021CF" +"nrarr;","U+0219B" +"nrightarrow;","U+0219B" +"nrtri;","U+022EB" +"nrtrie;","U+022ED" +"nsc;","U+02281" +"nsccue;","U+022E1" +"nscr;","U+1D4C3" +"nshortmid;","U+02224" +"nshortparallel;","U+02226" +"nsim;","U+02241" +"nsime;","U+02244" +"nsimeq;","U+02244" +"nsmid;","U+02224" +"nspar;","U+02226" +"nsqsube;","U+022E2" +"nsqsupe;","U+022E3" +"nsub;","U+02284" +"nsube;","U+02288" +"nsubseteq;","U+02288" +"nsucc;","U+02281" +"nsup;","U+02285" +"nsupe;","U+02289" +"nsupseteq;","U+02289" +"ntgl;","U+02279" +"ntilde;","U+000F1" +"ntilde","U+000F1" +"ntlg;","U+02278" +"ntriangleleft;","U+022EA" +"ntrianglelefteq;","U+022EC" +"ntriangleright;","U+022EB" +"ntrianglerighteq;","U+022ED" +"nu;","U+003BD" +"num;","U+00023" +"numero;","U+02116" +"numsp;","U+02007" +"nvDash;","U+022AD" +"nvHarr;","U+02904" +"nvdash;","U+022AC" +"nvinfin;","U+029DE" +"nvlArr;","U+02902" +"nvrArr;","U+02903" +"nwArr;","U+021D6" +"nwarhk;","U+02923" +"nwarr;","U+02196" +"nwarrow;","U+02196" +"nwnear;","U+02927" +"oS;","U+024C8" +"oacute;","U+000F3" +"oacute","U+000F3" +"oast;","U+0229B" +"ocir;","U+0229A" +"ocirc;","U+000F4" +"ocirc","U+000F4" +"ocy;","U+0043E" +"odash;","U+0229D" +"odblac;","U+00151" +"odiv;","U+02A38" +"odot;","U+02299" +"odsold;","U+029BC" +"oelig;","U+00153" +"ofcir;","U+029BF" +"ofr;","U+1D52C" +"ogon;","U+002DB" +"ograve;","U+000F2" +"ograve","U+000F2" +"ogt;","U+029C1" +"ohbar;","U+029B5" +"ohm;","U+003A9" +"oint;","U+0222E" +"olarr;","U+021BA" +"olcir;","U+029BE" +"olcross;","U+029BB" +"oline;","U+0203E" +"olt;","U+029C0" +"omacr;","U+0014D" +"omega;","U+003C9" +"omicron;","U+003BF" +"omid;","U+029B6" +"ominus;","U+02296" +"oopf;","U+1D560" +"opar;","U+029B7" +"operp;","U+029B9" +"oplus;","U+02295" +"or;","U+02228" +"orarr;","U+021BB" +"ord;","U+02A5D" +"order;","U+02134" +"orderof;","U+02134" +"ordf;","U+000AA" +"ordf","U+000AA" +"ordm;","U+000BA" +"ordm","U+000BA" +"origof;","U+022B6" +"oror;","U+02A56" +"orslope;","U+02A57" +"orv;","U+02A5B" +"oscr;","U+02134" +"oslash;","U+000F8" +"oslash","U+000F8" +"osol;","U+02298" +"otilde;","U+000F5" +"otilde","U+000F5" +"otimes;","U+02297" +"otimesas;","U+02A36" +"ouml;","U+000F6" +"ouml","U+000F6" +"ovbar;","U+0233D" +"par;","U+02225" +"para;","U+000B6" +"para","U+000B6" +"parallel;","U+02225" +"parsim;","U+02AF3" +"parsl;","U+02AFD" +"part;","U+02202" +"pcy;","U+0043F" +"percnt;","U+00025" +"period;","U+0002E" +"permil;","U+02030" +"perp;","U+022A5" +"pertenk;","U+02031" +"pfr;","U+1D52D" +"phi;","U+003C6" +"phiv;","U+003D5" +"phmmat;","U+02133" +"phone;","U+0260E" +"pi;","U+003C0" +"pitchfork;","U+022D4" +"piv;","U+003D6" +"planck;","U+0210F" +"planckh;","U+0210E" +"plankv;","U+0210F" +"plus;","U+0002B" +"plusacir;","U+02A23" +"plusb;","U+0229E" +"pluscir;","U+02A22" +"plusdo;","U+02214" +"plusdu;","U+02A25" +"pluse;","U+02A72" +"plusmn;","U+000B1" +"plusmn","U+000B1" +"plussim;","U+02A26" +"plustwo;","U+02A27" +"pm;","U+000B1" +"pointint;","U+02A15" +"popf;","U+1D561" +"pound;","U+000A3" +"pound","U+000A3" +"pr;","U+0227A" +"prE;","U+02AB3" +"prap;","U+02AB7" +"prcue;","U+0227C" +"pre;","U+02AAF" +"prec;","U+0227A" +"precapprox;","U+02AB7" +"preccurlyeq;","U+0227C" +"preceq;","U+02AAF" +"precnapprox;","U+02AB9" +"precneqq;","U+02AB5" +"precnsim;","U+022E8" +"precsim;","U+0227E" +"prime;","U+02032" +"primes;","U+02119" +"prnE;","U+02AB5" +"prnap;","U+02AB9" +"prnsim;","U+022E8" +"prod;","U+0220F" +"profalar;","U+0232E" +"profline;","U+02312" +"profsurf;","U+02313" +"prop;","U+0221D" +"propto;","U+0221D" +"prsim;","U+0227E" +"prurel;","U+022B0" +"pscr;","U+1D4C5" +"psi;","U+003C8" +"puncsp;","U+02008" +"qfr;","U+1D52E" +"qint;","U+02A0C" +"qopf;","U+1D562" +"qprime;","U+02057" +"qscr;","U+1D4C6" +"quaternions;","U+0210D" +"quatint;","U+02A16" +"quest;","U+0003F" +"questeq;","U+0225F" +"quot;","U+00022" +"quot","U+00022" +"rAarr;","U+021DB" +"rArr;","U+021D2" +"rAtail;","U+0291C" +"rBarr;","U+0290F" +"rHar;","U+02964" +"racute;","U+00155" +"radic;","U+0221A" +"raemptyv;","U+029B3" +"rang;","U+027E9" +"rangd;","U+02992" +"range;","U+029A5" +"rangle;","U+027E9" +"raquo;","U+000BB" +"raquo","U+000BB" +"rarr;","U+02192" +"rarrap;","U+02975" +"rarrb;","U+021E5" +"rarrbfs;","U+02920" +"rarrc;","U+02933" +"rarrfs;","U+0291E" +"rarrhk;","U+021AA" +"rarrlp;","U+021AC" +"rarrpl;","U+02945" +"rarrsim;","U+02974" +"rarrtl;","U+021A3" +"rarrw;","U+0219D" +"ratail;","U+0291A" +"ratio;","U+02236" +"rationals;","U+0211A" +"rbarr;","U+0290D" +"rbbrk;","U+02773" +"rbrace;","U+0007D" +"rbrack;","U+0005D" +"rbrke;","U+0298C" +"rbrksld;","U+0298E" +"rbrkslu;","U+02990" +"rcaron;","U+00159" +"rcedil;","U+00157" +"rceil;","U+02309" +"rcub;","U+0007D" +"rcy;","U+00440" +"rdca;","U+02937" +"rdldhar;","U+02969" +"rdquo;","U+0201D" +"rdquor;","U+0201D" +"rdsh;","U+021B3" +"real;","U+0211C" +"realine;","U+0211B" +"realpart;","U+0211C" +"reals;","U+0211D" +"rect;","U+025AD" +"reg;","U+000AE" +"reg","U+000AE" +"rfisht;","U+0297D" +"rfloor;","U+0230B" +"rfr;","U+1D52F" +"rhard;","U+021C1" +"rharu;","U+021C0" +"rharul;","U+0296C" +"rho;","U+003C1" +"rhov;","U+003F1" +"rightarrow;","U+02192" +"rightarrowtail;","U+021A3" +"rightharpoondown;","U+021C1" +"rightharpoonup;","U+021C0" +"rightleftarrows;","U+021C4" +"rightleftharpoons;","U+021CC" +"rightrightarrows;","U+021C9" +"rightsquigarrow;","U+0219D" +"rightthreetimes;","U+022CC" +"ring;","U+002DA" +"risingdotseq;","U+02253" +"rlarr;","U+021C4" +"rlhar;","U+021CC" +"rlm;","U+0200F" +"rmoust;","U+023B1" +"rmoustache;","U+023B1" +"rnmid;","U+02AEE" +"roang;","U+027ED" +"roarr;","U+021FE" +"robrk;","U+027E7" +"ropar;","U+02986" +"ropf;","U+1D563" +"roplus;","U+02A2E" +"rotimes;","U+02A35" +"rpar;","U+00029" +"rpargt;","U+02994" +"rppolint;","U+02A12" +"rrarr;","U+021C9" +"rsaquo;","U+0203A" +"rscr;","U+1D4C7" +"rsh;","U+021B1" +"rsqb;","U+0005D" +"rsquo;","U+02019" +"rsquor;","U+02019" +"rthree;","U+022CC" +"rtimes;","U+022CA" +"rtri;","U+025B9" +"rtrie;","U+022B5" +"rtrif;","U+025B8" +"rtriltri;","U+029CE" +"ruluhar;","U+02968" +"rx;","U+0211E" +"sacute;","U+0015B" +"sbquo;","U+0201A" +"sc;","U+0227B" +"scE;","U+02AB4" +"scap;","U+02AB8" +"scaron;","U+00161" +"sccue;","U+0227D" +"sce;","U+02AB0" +"scedil;","U+0015F" +"scirc;","U+0015D" +"scnE;","U+02AB6" +"scnap;","U+02ABA" +"scnsim;","U+022E9" +"scpolint;","U+02A13" +"scsim;","U+0227F" +"scy;","U+00441" +"sdot;","U+022C5" +"sdotb;","U+022A1" +"sdote;","U+02A66" +"seArr;","U+021D8" +"searhk;","U+02925" +"searr;","U+02198" +"searrow;","U+02198" +"sect;","U+000A7" +"sect","U+000A7" +"semi;","U+0003B" +"seswar;","U+02929" +"setminus;","U+02216" +"setmn;","U+02216" +"sext;","U+02736" +"sfr;","U+1D530" +"sfrown;","U+02322" +"sharp;","U+0266F" +"shchcy;","U+00449" +"shcy;","U+00448" +"shortmid;","U+02223" +"shortparallel;","U+02225" +"shy;","U+000AD" +"shy","U+000AD" +"sigma;","U+003C3" +"sigmaf;","U+003C2" +"sigmav;","U+003C2" +"sim;","U+0223C" +"simdot;","U+02A6A" +"sime;","U+02243" +"simeq;","U+02243" +"simg;","U+02A9E" +"simgE;","U+02AA0" +"siml;","U+02A9D" +"simlE;","U+02A9F" +"simne;","U+02246" +"simplus;","U+02A24" +"simrarr;","U+02972" +"slarr;","U+02190" +"smallsetminus;","U+02216" +"smashp;","U+02A33" +"smeparsl;","U+029E4" +"smid;","U+02223" +"smile;","U+02323" +"smt;","U+02AAA" +"smte;","U+02AAC" +"softcy;","U+0044C" +"sol;","U+0002F" +"solb;","U+029C4" +"solbar;","U+0233F" +"sopf;","U+1D564" +"spades;","U+02660" +"spadesuit;","U+02660" +"spar;","U+02225" +"sqcap;","U+02293" +"sqcup;","U+02294" +"sqsub;","U+0228F" +"sqsube;","U+02291" +"sqsubset;","U+0228F" +"sqsubseteq;","U+02291" +"sqsup;","U+02290" +"sqsupe;","U+02292" +"sqsupset;","U+02290" +"sqsupseteq;","U+02292" +"squ;","U+025A1" +"square;","U+025A1" +"squarf;","U+025AA" +"squf;","U+025AA" +"srarr;","U+02192" +"sscr;","U+1D4C8" +"ssetmn;","U+02216" +"ssmile;","U+02323" +"sstarf;","U+022C6" +"star;","U+02606" +"starf;","U+02605" +"straightepsilon;","U+003F5" +"straightphi;","U+003D5" +"strns;","U+000AF" +"sub;","U+02282" +"subE;","U+02AC5" +"subdot;","U+02ABD" +"sube;","U+02286" +"subedot;","U+02AC3" +"submult;","U+02AC1" +"subnE;","U+02ACB" +"subne;","U+0228A" +"subplus;","U+02ABF" +"subrarr;","U+02979" +"subset;","U+02282" +"subseteq;","U+02286" +"subseteqq;","U+02AC5" +"subsetneq;","U+0228A" +"subsetneqq;","U+02ACB" +"subsim;","U+02AC7" +"subsub;","U+02AD5" +"subsup;","U+02AD3" +"succ;","U+0227B" +"succapprox;","U+02AB8" +"succcurlyeq;","U+0227D" +"succeq;","U+02AB0" +"succnapprox;","U+02ABA" +"succneqq;","U+02AB6" +"succnsim;","U+022E9" +"succsim;","U+0227F" +"sum;","U+02211" +"sung;","U+0266A" +"sup1;","U+000B9" +"sup1","U+000B9" +"sup2;","U+000B2" +"sup2","U+000B2" +"sup3;","U+000B3" +"sup3","U+000B3" +"sup;","U+02283" +"supE;","U+02AC6" +"supdot;","U+02ABE" +"supdsub;","U+02AD8" +"supe;","U+02287" +"supedot;","U+02AC4" +"suphsol;","U+027C9" +"suphsub;","U+02AD7" +"suplarr;","U+0297B" +"supmult;","U+02AC2" +"supnE;","U+02ACC" +"supne;","U+0228B" +"supplus;","U+02AC0" +"supset;","U+02283" +"supseteq;","U+02287" +"supseteqq;","U+02AC6" +"supsetneq;","U+0228B" +"supsetneqq;","U+02ACC" +"supsim;","U+02AC8" +"supsub;","U+02AD4" +"supsup;","U+02AD6" +"swArr;","U+021D9" +"swarhk;","U+02926" +"swarr;","U+02199" +"swarrow;","U+02199" +"swnwar;","U+0292A" +"szlig;","U+000DF" +"szlig","U+000DF" +"target;","U+02316" +"tau;","U+003C4" +"tbrk;","U+023B4" +"tcaron;","U+00165" +"tcedil;","U+00163" +"tcy;","U+00442" +"tdot;","U+020DB" +"telrec;","U+02315" +"tfr;","U+1D531" +"there4;","U+02234" +"therefore;","U+02234" +"theta;","U+003B8" +"thetasym;","U+003D1" +"thetav;","U+003D1" +"thickapprox;","U+02248" +"thicksim;","U+0223C" +"thinsp;","U+02009" +"thkap;","U+02248" +"thksim;","U+0223C" +"thorn;","U+000FE" +"thorn","U+000FE" +"tilde;","U+002DC" +"times;","U+000D7" +"times","U+000D7" +"timesb;","U+022A0" +"timesbar;","U+02A31" +"timesd;","U+02A30" +"tint;","U+0222D" +"toea;","U+02928" +"top;","U+022A4" +"topbot;","U+02336" +"topcir;","U+02AF1" +"topf;","U+1D565" +"topfork;","U+02ADA" +"tosa;","U+02929" +"tprime;","U+02034" +"trade;","U+02122" +"triangle;","U+025B5" +"triangledown;","U+025BF" +"triangleleft;","U+025C3" +"trianglelefteq;","U+022B4" +"triangleq;","U+0225C" +"triangleright;","U+025B9" +"trianglerighteq;","U+022B5" +"tridot;","U+025EC" +"trie;","U+0225C" +"triminus;","U+02A3A" +"triplus;","U+02A39" +"trisb;","U+029CD" +"tritime;","U+02A3B" +"trpezium;","U+023E2" +"tscr;","U+1D4C9" +"tscy;","U+00446" +"tshcy;","U+0045B" +"tstrok;","U+00167" +"twixt;","U+0226C" +"twoheadleftarrow;","U+0219E" +"twoheadrightarrow;","U+021A0" +"uArr;","U+021D1" +"uHar;","U+02963" +"uacute;","U+000FA" +"uacute","U+000FA" +"uarr;","U+02191" +"ubrcy;","U+0045E" +"ubreve;","U+0016D" +"ucirc;","U+000FB" +"ucirc","U+000FB" +"ucy;","U+00443" +"udarr;","U+021C5" +"udblac;","U+00171" +"udhar;","U+0296E" +"ufisht;","U+0297E" +"ufr;","U+1D532" +"ugrave;","U+000F9" +"ugrave","U+000F9" +"uharl;","U+021BF" +"uharr;","U+021BE" +"uhblk;","U+02580" +"ulcorn;","U+0231C" +"ulcorner;","U+0231C" +"ulcrop;","U+0230F" +"ultri;","U+025F8" +"umacr;","U+0016B" +"uml;","U+000A8" +"uml","U+000A8" +"uogon;","U+00173" +"uopf;","U+1D566" +"uparrow;","U+02191" +"updownarrow;","U+02195" +"upharpoonleft;","U+021BF" +"upharpoonright;","U+021BE" +"uplus;","U+0228E" +"upsi;","U+003C5" +"upsih;","U+003D2" +"upsilon;","U+003C5" +"upuparrows;","U+021C8" +"urcorn;","U+0231D" +"urcorner;","U+0231D" +"urcrop;","U+0230E" +"uring;","U+0016F" +"urtri;","U+025F9" +"uscr;","U+1D4CA" +"utdot;","U+022F0" +"utilde;","U+00169" +"utri;","U+025B5" +"utrif;","U+025B4" +"uuarr;","U+021C8" +"uuml;","U+000FC" +"uuml","U+000FC" +"uwangle;","U+029A7" +"vArr;","U+021D5" +"vBar;","U+02AE8" +"vBarv;","U+02AE9" +"vDash;","U+022A8" +"vangrt;","U+0299C" +"varepsilon;","U+003F5" +"varkappa;","U+003F0" +"varnothing;","U+02205" +"varphi;","U+003D5" +"varpi;","U+003D6" +"varpropto;","U+0221D" +"varr;","U+02195" +"varrho;","U+003F1" +"varsigma;","U+003C2" +"vartheta;","U+003D1" +"vartriangleleft;","U+022B2" +"vartriangleright;","U+022B3" +"vcy;","U+00432" +"vdash;","U+022A2" +"vee;","U+02228" +"veebar;","U+022BB" +"veeeq;","U+0225A" +"vellip;","U+022EE" +"verbar;","U+0007C" +"vert;","U+0007C" +"vfr;","U+1D533" +"vltri;","U+022B2" +"vopf;","U+1D567" +"vprop;","U+0221D" +"vrtri;","U+022B3" +"vscr;","U+1D4CB" +"vzigzag;","U+0299A" +"wcirc;","U+00175" +"wedbar;","U+02A5F" +"wedge;","U+02227" +"wedgeq;","U+02259" +"weierp;","U+02118" +"wfr;","U+1D534" +"wopf;","U+1D568" +"wp;","U+02118" +"wr;","U+02240" +"wreath;","U+02240" +"wscr;","U+1D4CC" +"xcap;","U+022C2" +"xcirc;","U+025EF" +"xcup;","U+022C3" +"xdtri;","U+025BD" +"xfr;","U+1D535" +"xhArr;","U+027FA" +"xharr;","U+027F7" +"xi;","U+003BE" +"xlArr;","U+027F8" +"xlarr;","U+027F5" +"xmap;","U+027FC" +"xnis;","U+022FB" +"xodot;","U+02A00" +"xopf;","U+1D569" +"xoplus;","U+02A01" +"xotime;","U+02A02" +"xrArr;","U+027F9" +"xrarr;","U+027F6" +"xscr;","U+1D4CD" +"xsqcup;","U+02A06" +"xuplus;","U+02A04" +"xutri;","U+025B3" +"xvee;","U+022C1" +"xwedge;","U+022C0" +"yacute;","U+000FD" +"yacute","U+000FD" +"yacy;","U+0044F" +"ycirc;","U+00177" +"ycy;","U+0044B" +"yen;","U+000A5" +"yen","U+000A5" +"yfr;","U+1D536" +"yicy;","U+00457" +"yopf;","U+1D56A" +"yscr;","U+1D4CE" +"yucy;","U+0044E" +"yuml;","U+000FF" +"yuml","U+000FF" +"zacute;","U+0017A" +"zcaron;","U+0017E" +"zcy;","U+00437" +"zdot;","U+0017C" +"zeetrf;","U+02128" +"zeta;","U+003B6" +"zfr;","U+1D537" +"zhcy;","U+00436" +"zigrarr;","U+021DD" +"zopf;","U+1D56B" +"zscr;","U+1D4CF" +"zwj;","U+0200D" +"zwnj;","U+0200C" +"acE;","U+0223E U+00333" +"bne;","U+0003D U+020E5" +"bnequiv;","U+02261 U+020E5" +"caps;","U+02229 U+0FE00" +"cups;","U+0222A U+0FE00" +"fjlig;","U+00066 U+0006A" +"gesl;","U+022DB U+0FE00" +"gvertneqq;","U+02269 U+0FE00" +"gvnE;","U+02269 U+0FE00" +"lates;","U+02AAD U+0FE00" +"lesg;","U+022DA U+0FE00" +"lvertneqq;","U+02268 U+0FE00" +"lvnE;","U+02268 U+0FE00" +"nang;","U+02220 U+020D2" +"napE;","U+02A70 U+00338" +"napid;","U+0224B U+00338" +"nbump;","U+0224E U+00338" +"nbumpe;","U+0224F U+00338" +"ncongdot;","U+02A6D U+00338" +"nedot;","U+02250 U+00338" +"nesim;","U+02242 U+00338" +"ngE;","U+02267 U+00338" +"ngeqq;","U+02267 U+00338" +"ngeqslant;","U+02A7E U+00338" +"nges;","U+02A7E U+00338" +"nGg;","U+022D9 U+00338" +"nGt;","U+0226B U+020D2" +"nGtv;","U+0226B U+00338" +"nlE;","U+02266 U+00338" +"nleqq;","U+02266 U+00338" +"nleqslant;","U+02A7D U+00338" +"nles;","U+02A7D U+00338" +"nLl;","U+022D8 U+00338" +"nLt;","U+0226A U+020D2" +"nLtv;","U+0226A U+00338" +"NotEqualTilde;","U+02242 U+00338" +"NotGreaterFullEqual;","U+02267 U+00338" +"NotGreaterGreater;","U+0226B U+00338" +"NotGreaterSlantEqual;","U+02A7E U+00338" +"NotHumpDownHump;","U+0224E U+00338" +"NotHumpEqual;","U+0224F U+00338" +"notindot;","U+022F5 U+00338" +"notinE;","U+022F9 U+00338" +"NotLeftTriangleBar;","U+029CF U+00338" +"NotLessLess;","U+0226A U+00338" +"NotLessSlantEqual;","U+02A7D U+00338" +"NotNestedGreaterGreater;","U+02AA2 U+00338" +"NotNestedLessLess;","U+02AA1 U+00338" +"NotPrecedesEqual;","U+02AAF U+00338" +"NotRightTriangleBar;","U+029D0 U+00338" +"NotSquareSubset;","U+0228F U+00338" +"NotSquareSuperset;","U+02290 U+00338" +"NotSubset;","U+02282 U+020D2" +"NotSucceedsEqual;","U+02AB0 U+00338" +"NotSucceedsTilde;","U+0227F U+00338" +"NotSuperset;","U+02283 U+020D2" +"nparsl;","U+02AFD U+020E5" +"npart;","U+02202 U+00338" +"npre;","U+02AAF U+00338" +"npreceq;","U+02AAF U+00338" +"nrarrc;","U+02933 U+00338" +"nrarrw;","U+0219D U+00338" +"nsce;","U+02AB0 U+00338" +"nsubE;","U+02AC5 U+00338" +"nsubset;","U+02282 U+020D2" +"nsubseteqq;","U+02AC5 U+00338" +"nsucceq;","U+02AB0 U+00338" +"nsupE;","U+02AC6 U+00338" +"nsupset;","U+02283 U+020D2" +"nsupseteqq;","U+02AC6 U+00338" +"nvap;","U+0224D U+020D2" +"nvge;","U+02265 U+020D2" +"nvgt;","U+0003E U+020D2" +"nvle;","U+02264 U+020D2" +"nvlt;","U+0003C U+020D2" +"nvltrie;","U+022B4 U+020D2" +"nvrtrie;","U+022B5 U+020D2" +"nvsim;","U+0223C U+020D2" +"race;","U+0223D U+00331" +"smtes;","U+02AAC U+0FE00" +"sqcaps;","U+02293 U+0FE00" +"sqcups;","U+02294 U+0FE00" +"ThickSpace;","U+0205F U+0200A" +"varsubsetneq;","U+0228A U+0FE00" +"varsubsetneqq;","U+02ACB U+0FE00" +"varsupsetneq;","U+0228B U+0FE00" +"varsupsetneqq;","U+02ACC U+0FE00" +"vnsub;","U+02282 U+020D2" +"vnsup;","U+02283 U+020D2" +"vsubnE;","U+02ACB U+0FE00" +"vsubne;","U+0228A U+0FE00" +"vsupnE;","U+02ACC U+0FE00" +"vsupne;","U+0228B U+0FE00" diff --git a/Source/WebCore/html/parser/HTMLEntityParser.cpp b/Source/WebCore/html/parser/HTMLEntityParser.cpp new file mode 100644 index 000000000..bb32f7f34 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLEntityParser.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLEntityParser.h" + +#include "CharacterReferenceParserInlineMethods.h" +#include "HTMLEntitySearch.h" +#include "HTMLEntityTable.h" +#include <wtf/text/StringBuilder.h> + +using namespace WTF; + +namespace WebCore { + +namespace { + +static const UChar windowsLatin1ExtensionArray[32] = { + 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87 + 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F + 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97 + 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178, // 98-9F +}; + +inline bool isAlphaNumeric(UChar cc) +{ + return (cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'z') || (cc >= 'A' && cc <= 'Z'); +} + +class HTMLEntityParser { +public: + inline static UChar adjustEntity(UChar32 value) + { + if ((value & ~0x1F) != 0x0080) + return value; + return windowsLatin1ExtensionArray[value - 0x80]; + } + + inline static UChar32 legalEntityFor(UChar32 value) + { + // FIXME: A number of specific entity values generate parse errors. + if (!value || value > 0x10FFFF || (value >= 0xD800 && value <= 0xDFFF)) + return 0xFFFD; + if (U_IS_BMP(value)) + return adjustEntity(value); + return value; + } + + inline static void convertToUTF16(UChar32 value, StringBuilder& decodedEntity) + { + if (U_IS_BMP(value)) { + UChar character = static_cast<UChar>(value); + ASSERT(character == value); + decodedEntity.append(character); + return; + } + decodedEntity.append(U16_LEAD(value)); + decodedEntity.append(U16_TRAIL(value)); + } + + inline static bool acceptMalformed() { return true; } + + inline static bool consumeNamedEntity(SegmentedString& source, StringBuilder& decodedEntity, bool& notEnoughCharacters, UChar additionalAllowedCharacter, UChar& cc) + { + StringBuilder consumedCharacters; + HTMLEntitySearch entitySearch; + while (!source.isEmpty()) { + cc = *source; + entitySearch.advance(cc); + if (!entitySearch.isEntityPrefix()) + break; + consumedCharacters.append(cc); + source.advanceAndASSERT(cc); + } + notEnoughCharacters = source.isEmpty(); + if (notEnoughCharacters) { + // We can't an entity because there might be a longer entity + // that we could match if we had more data. + unconsumeCharacters(source, consumedCharacters); + return false; + } + if (!entitySearch.mostRecentMatch()) { + unconsumeCharacters(source, consumedCharacters); + return false; + } + if (entitySearch.mostRecentMatch()->length != entitySearch.currentLength()) { + // We've consumed too many characters. We need to walk the + // source back to the point at which we had consumed an + // actual entity. + unconsumeCharacters(source, consumedCharacters); + consumedCharacters.clear(); + const int length = entitySearch.mostRecentMatch()->length; + const UChar* reference = entitySearch.mostRecentMatch()->entity; + for (int i = 0; i < length; ++i) { + cc = *source; + ASSERT_UNUSED(reference, cc == *reference++); + consumedCharacters.append(cc); + source.advanceAndASSERT(cc); + ASSERT(!source.isEmpty()); + } + cc = *source; + } + if (entitySearch.mostRecentMatch()->lastCharacter() == ';' + || !additionalAllowedCharacter + || !(isAlphaNumeric(cc) || cc == '=')) { + convertToUTF16(entitySearch.mostRecentMatch()->firstValue, decodedEntity); + if (entitySearch.mostRecentMatch()->secondValue) + convertToUTF16(entitySearch.mostRecentMatch()->secondValue, decodedEntity); + return true; + } + unconsumeCharacters(source, consumedCharacters); + return false; + } +}; + +} + +bool consumeHTMLEntity(SegmentedString& source, StringBuilder& decodedEntity, bool& notEnoughCharacters, UChar additionalAllowedCharacter) +{ + return consumeCharacterReference<HTMLEntityParser>(source, decodedEntity, notEnoughCharacters, additionalAllowedCharacter); +} + +UChar decodeNamedEntity(const char* name) +{ + HTMLEntitySearch search; + while (*name) { + search.advance(*name++); + if (!search.isEntityPrefix()) + return 0; + } + search.advance(';'); + if (!search.isEntityPrefix()) + return 0; + + UChar32 firstValue = search.mostRecentMatch()->firstValue; + if (U16_LENGTH(firstValue) != 1 || search.mostRecentMatch()->secondValue) { + // FIXME: Callers need to move off this API. Not all entities can be + // represented in a single UChar! + return 0; + } + return static_cast<UChar>(firstValue); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/parser/HTMLEntityParser.h b/Source/WebCore/html/parser/HTMLEntityParser.h new file mode 100644 index 000000000..268b2dde5 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLEntityParser.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLEntityParser_h +#define HTMLEntityParser_h + +#include "SegmentedString.h" + +namespace WebCore { + +bool consumeHTMLEntity(SegmentedString&, StringBuilder& decodedEntity, bool& notEnoughCharacters, UChar additionalAllowedCharacter = '\0'); + +// Used by the XML parser. Not suitable for use in HTML parsing. Use consumeHTMLEntity instead. +// FIXME: Move the XML parser to an entity decoding function works for non-BMP characters! +UChar decodeNamedEntity(const char*); + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLEntitySearch.cpp b/Source/WebCore/html/parser/HTMLEntitySearch.cpp new file mode 100644 index 000000000..57bc4e205 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLEntitySearch.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLEntitySearch.h" + +#include "HTMLEntityTable.h" + +namespace WebCore { + +namespace { + +const HTMLEntityTableEntry* halfway(const HTMLEntityTableEntry* left, const HTMLEntityTableEntry* right) +{ + return &left[(right - left) / 2]; +} + +} + +HTMLEntitySearch::HTMLEntitySearch() + : m_currentLength(0) + , m_mostRecentMatch(0) + , m_first(HTMLEntityTable::firstEntry()) + , m_last(HTMLEntityTable::lastEntry()) +{ +} + +HTMLEntitySearch::CompareResult HTMLEntitySearch::compare(const HTMLEntityTableEntry* entry, UChar nextCharacter) const +{ + if (entry->length < m_currentLength + 1) + return Before; + UChar entryNextCharacter = entry->entity[m_currentLength]; + if (entryNextCharacter == nextCharacter) + return Prefix; + return entryNextCharacter < nextCharacter ? Before : After; +} + +const HTMLEntityTableEntry* HTMLEntitySearch::findFirst(UChar nextCharacter) const +{ + const HTMLEntityTableEntry* left = m_first; + const HTMLEntityTableEntry* right = m_last; + if (left == right) + return left; + CompareResult result = compare(left, nextCharacter); + if (result == Prefix) + return left; + if (result == After) + return right; + while (left + 1 < right) { + const HTMLEntityTableEntry* probe = halfway(left, right); + result = compare(probe, nextCharacter); + if (result == Before) + left = probe; + else { + ASSERT(result == After || result == Prefix); + right = probe; + } + } + ASSERT(left + 1 == right); + return right; +} + +const HTMLEntityTableEntry* HTMLEntitySearch::findLast(UChar nextCharacter) const +{ + const HTMLEntityTableEntry* left = m_first; + const HTMLEntityTableEntry* right = m_last; + if (left == right) + return right; + CompareResult result = compare(right, nextCharacter); + if (result == Prefix) + return right; + if (result == Before) + return left; + while (left + 1 < right) { + const HTMLEntityTableEntry* probe = halfway(left, right); + result = compare(probe, nextCharacter); + if (result == After) + right = probe; + else { + ASSERT(result == Before || result == Prefix); + left = probe; + } + } + ASSERT(left + 1 == right); + return left; +} + +void HTMLEntitySearch::advance(UChar nextCharacter) +{ + ASSERT(isEntityPrefix()); + if (!m_currentLength) { + m_first = HTMLEntityTable::firstEntryStartingWith(nextCharacter); + m_last = HTMLEntityTable::lastEntryStartingWith(nextCharacter); + if (!m_first || !m_last) + return fail(); + } else { + m_first = findFirst(nextCharacter); + m_last = findLast(nextCharacter); + if (m_first == m_last && compare(m_first, nextCharacter) != Prefix) + return fail(); + } + ++m_currentLength; + if (m_first->length != m_currentLength) { + return; + } + m_mostRecentMatch = m_first; +} + +} diff --git a/Source/WebCore/html/parser/HTMLEntitySearch.h b/Source/WebCore/html/parser/HTMLEntitySearch.h new file mode 100644 index 000000000..f90d401b3 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLEntitySearch.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLEntitySearch_h +#define HTMLEntitySearch_h + +#include "PlatformString.h" + +namespace WebCore { + +struct HTMLEntityTableEntry; + +class HTMLEntitySearch { +public: + HTMLEntitySearch(); + + void advance(UChar); + + bool isEntityPrefix() const { return !!m_first; } + int currentLength() const { return m_currentLength; } + + const HTMLEntityTableEntry* mostRecentMatch() const { return m_mostRecentMatch; } + +private: + enum CompareResult { + Before, + Prefix, + After, + }; + + CompareResult compare(const HTMLEntityTableEntry*, UChar) const; + const HTMLEntityTableEntry* findFirst(UChar) const; + const HTMLEntityTableEntry* findLast(UChar) const; + + void fail() + { + m_first = 0; + m_last = 0; + } + + int m_currentLength; + + const HTMLEntityTableEntry* m_mostRecentMatch; + const HTMLEntityTableEntry* m_first; + const HTMLEntityTableEntry* m_last; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLEntityTable.h b/Source/WebCore/html/parser/HTMLEntityTable.h new file mode 100644 index 000000000..4403c364b --- /dev/null +++ b/Source/WebCore/html/parser/HTMLEntityTable.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLEntityTable_h +#define HTMLEntityTable_h + +#include "PlatformString.h" + +namespace WebCore { + +struct HTMLEntityTableEntry { + UChar lastCharacter() const { return entity[length - 1]; } + + const UChar* entity; + int length; + UChar32 firstValue; + UChar32 secondValue; +}; + +class HTMLEntityTable { +public: + static const HTMLEntityTableEntry* firstEntry(); + static const HTMLEntityTableEntry* lastEntry(); + + static const HTMLEntityTableEntry* firstEntryStartingWith(UChar); + static const HTMLEntityTableEntry* lastEntryStartingWith(UChar); +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLFormattingElementList.cpp b/Source/WebCore/html/parser/HTMLFormattingElementList.cpp new file mode 100644 index 000000000..f331af7e7 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLFormattingElementList.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLFormattingElementList.h" + +#include "Element.h" +#include "NotImplemented.h" + +#ifndef NDEBUG +#include <stdio.h> +#endif + +namespace WebCore { + +// Biblically, Noah's Ark only had room for two of each animal, but in the +// Book of Hixie (aka http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#list-of-active-formatting-elements), +// Noah's Ark of Formatting Elements can fit three of each element. +static const size_t kNoahsArkCapacity = 3; + +static inline size_t attributeCount(Element* element) +{ + return element->attributeMap() ? element->attributeMap()->length() : 0; +} + +HTMLFormattingElementList::HTMLFormattingElementList() +{ +} + +HTMLFormattingElementList::~HTMLFormattingElementList() +{ +} + +Element* HTMLFormattingElementList::closestElementInScopeWithName(const AtomicString& targetName) +{ + for (unsigned i = 1; i <= m_entries.size(); ++i) { + const Entry& entry = m_entries[m_entries.size() - i]; + if (entry.isMarker()) + return 0; + if (entry.element()->hasLocalName(targetName)) + return entry.element(); + } + return 0; +} + +bool HTMLFormattingElementList::contains(Element* element) +{ + return !!find(element); +} + +HTMLFormattingElementList::Entry* HTMLFormattingElementList::find(Element* element) +{ + size_t index = m_entries.reverseFind(element); + if (index != notFound) { + // This is somewhat of a hack, and is why this method can't be const. + return &m_entries[index]; + } + return 0; +} + +HTMLFormattingElementList::Bookmark HTMLFormattingElementList::bookmarkFor(Element* element) +{ + size_t index = m_entries.reverseFind(element); + ASSERT(index != notFound); + return Bookmark(&at(index)); +} + +void HTMLFormattingElementList::swapTo(Element* oldElement, Element* newElement, const Bookmark& bookmark) +{ + ASSERT(contains(oldElement)); + ASSERT(!contains(newElement)); + if (!bookmark.hasBeenMoved()) { + ASSERT(bookmark.mark()->element() == oldElement); + bookmark.mark()->replaceElement(newElement); + return; + } + size_t index = bookmark.mark() - first(); + ASSERT(index < size()); + m_entries.insert(index + 1, newElement); + remove(oldElement); +} + +void HTMLFormattingElementList::append(Element* element) +{ + ensureNoahsArkCondition(element); + m_entries.append(element); +} + +void HTMLFormattingElementList::remove(Element* element) +{ + size_t index = m_entries.reverseFind(element); + if (index != notFound) + m_entries.remove(index); +} + +void HTMLFormattingElementList::appendMarker() +{ + m_entries.append(Entry::MarkerEntry); +} + +void HTMLFormattingElementList::clearToLastMarker() +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#clear-the-list-of-active-formatting-elements-up-to-the-last-marker + while (m_entries.size()) { + bool shouldStop = m_entries.last().isMarker(); + m_entries.removeLast(); + if (shouldStop) + break; + } +} + +void HTMLFormattingElementList::tryToEnsureNoahsArkConditionQuickly(Element* newElement, Vector<Element*>& remainingCandidates) +{ + ASSERT(remainingCandidates.isEmpty()); + + if (m_entries.size() < kNoahsArkCapacity) + return; + + // Use a vector with inline capacity to avoid a malloc in the common case + // of a quickly ensuring the condition. + Vector<Element*, 10> candidates; + + size_t newElementAttributeCount = attributeCount(newElement); + + for (size_t i = m_entries.size(); i; ) { + --i; + Entry& entry = m_entries[i]; + if (entry.isMarker()) + break; + + // Quickly reject obviously non-matching candidates. + Element* candidate = entry.element(); + if (newElement->tagQName() != candidate->tagQName()) + continue; + if (attributeCount(candidate) != newElementAttributeCount) + continue; + + candidates.append(candidate); + } + + if (candidates.size() < kNoahsArkCapacity) + return; // There's room for the new element in the ark. There's no need to copy out the remainingCandidates. + + remainingCandidates.append(candidates); +} + +void HTMLFormattingElementList::ensureNoahsArkCondition(Element* newElement) +{ + Vector<Element*> candidates; + tryToEnsureNoahsArkConditionQuickly(newElement, candidates); + if (candidates.isEmpty()) + return; + + // We pre-allocate and re-use this second vector to save one malloc per + // attribute that we verify. + Vector<Element*> remainingCandidates; + remainingCandidates.reserveInitialCapacity(candidates.size()); + + NamedNodeMap* attributeMap = newElement->attributeMap(); + size_t newElementAttributeCount = attributeCount(newElement); + + for (size_t i = 0; i < newElementAttributeCount; ++i) { + QualifiedName attributeName = attributeMap->attributeItem(i)->name(); + AtomicString attributeValue = newElement->fastGetAttribute(attributeName); + + for (size_t j = 0; j < candidates.size(); ++j) { + Element* candidate = candidates[j]; + + // These properties should already have been checked by tryToEnsureNoahsArkConditionQuickly. + ASSERT(newElement->attributeMap()->length() == candidate->attributeMap()->length()); + ASSERT(newElement->tagQName() == candidate->tagQName()); + + // FIXME: Technically we shouldn't read this information back from + // the DOM. Instead, the parser should keep a copy of the information. + // This isn't really much of a problem for our implementation because + // we run the parser on the main thread, but the spec is written so + // that implementations can run off the main thread. If JavaScript + // changes the attributes values, we could get a slightly wrong + // output here. + if (candidate->fastGetAttribute(attributeName) == attributeValue) + remainingCandidates.append(candidate); + } + + if (remainingCandidates.size() < kNoahsArkCapacity) + return; + + candidates.swap(remainingCandidates); + remainingCandidates.shrink(0); + } + + // Inductively, we shouldn't spin this loop very many times. It's possible, + // however, that we wil spin the loop more than once because of how the + // formatting element list gets permuted. + for (size_t i = kNoahsArkCapacity - 1; i < candidates.size(); ++i) + remove(candidates[i]); +} + +#ifndef NDEBUG + +void HTMLFormattingElementList::show() +{ + for (unsigned i = 1; i <= m_entries.size(); ++i) { + const Entry& entry = m_entries[m_entries.size() - i]; + if (entry.isMarker()) + fprintf(stderr, "marker\n"); + else + entry.element()->showNode(); + } +} + +#endif + +} diff --git a/Source/WebCore/html/parser/HTMLFormattingElementList.h b/Source/WebCore/html/parser/HTMLFormattingElementList.h new file mode 100644 index 000000000..79fe2ff37 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLFormattingElementList.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLFormattingElementList_h +#define HTMLFormattingElementList_h + +#include <wtf/Forward.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class Element; + +// This may end up merged into HTMLElementStack. +class HTMLFormattingElementList { + WTF_MAKE_NONCOPYABLE(HTMLFormattingElementList); +public: + HTMLFormattingElementList(); + ~HTMLFormattingElementList(); + + // Ideally Entry would be private, but HTMLTreeBuilder has to coordinate + // between the HTMLFormattingElementList and HTMLElementStack and needs + // access to Entry::isMarker() and Entry::replaceElement() to do so. + class Entry { + public: + // Inline because they're hot and Vector<T> uses them. + explicit Entry(Element* element) + : m_element(element) + { + ASSERT(element); + } + enum MarkerEntryType { MarkerEntry }; + Entry(MarkerEntryType) + : m_element(0) + { + } + ~Entry() {} + + bool isMarker() const { return !m_element; } + + Element* element() const + { + // The fact that !m_element == isMarker() is an implementation detail + // callers should check isMarker() before calling element(). + ASSERT(m_element); + return m_element.get(); + } + void replaceElement(PassRefPtr<Element> element) { m_element = element; } + + // Needed for use with Vector. These are super-hot and must be inline. + bool operator==(Element* element) const { return m_element == element; } + bool operator!=(Element* element) const { return m_element != element; } + + private: + RefPtr<Element> m_element; + }; + + class Bookmark { + public: + Bookmark(Entry* entry) + : m_hasBeenMoved(false) + , m_mark(entry) + { + } + + void moveToAfter(Entry* before) + { + m_hasBeenMoved = true; + m_mark = before; + } + + bool hasBeenMoved() const { return m_hasBeenMoved; } + Entry* mark() const { return m_mark; } + + private: + bool m_hasBeenMoved; + Entry* m_mark; + }; + + bool isEmpty() const { return !size(); } + size_t size() const { return m_entries.size(); } + + Element* closestElementInScopeWithName(const AtomicString&); + + Entry* find(Element*); + bool contains(Element*); + void append(Element*); + void remove(Element*); + + Bookmark bookmarkFor(Element*); + void swapTo(Element* oldElement, Element* newElement, const Bookmark&); + + void appendMarker(); + // clearToLastMarker also clears the marker (per the HTML5 spec). + void clearToLastMarker(); + + const Entry& at(size_t i) const { return m_entries[i]; } + Entry& at(size_t i) { return m_entries[i]; } + +#ifndef NDEBUG + void show(); +#endif + +private: + Entry* first() { return &at(0); } + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#list-of-active-formatting-elements + // These functions enforce the "Noah's Ark" condition, which removes redundant mis-nested elements. + void tryToEnsureNoahsArkConditionQuickly(Element*, Vector<Element*>& remainingCandiates); + void ensureNoahsArkCondition(Element*); + + Vector<Entry> m_entries; +}; + +} + +#endif // HTMLFormattingElementList_h diff --git a/Source/WebCore/html/parser/HTMLInputStream.h b/Source/WebCore/html/parser/HTMLInputStream.h new file mode 100644 index 000000000..da6932fd4 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLInputStream.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLInputStream_h +#define HTMLInputStream_h + +#include "SegmentedString.h" + +namespace WebCore { + +// The InputStream is made up of a sequence of SegmentedStrings: +// +// [--current--][--next--][--next--] ... [--next--] +// /\ (also called m_last) +// L_ current insertion point +// +// The current segmented string is stored in InputStream. Each of the +// afterInsertionPoint buffers are stored in InsertionPointRecords on the +// stack. +// +// We remove characters from the "current" string in the InputStream. +// document.write() will add characters at the current insertion point, +// which appends them to the "current" string. +// +// m_last is a pointer to the last of the afterInsertionPoint strings. +// The network adds data at the end of the InputStream, which appends +// them to the "last" string. +class HTMLInputStream { + WTF_MAKE_NONCOPYABLE(HTMLInputStream); +public: + HTMLInputStream() + : m_last(&m_first) + { + } + + void appendToEnd(const SegmentedString& string) + { + m_last->append(string); + } + + void insertAtCurrentInsertionPoint(const SegmentedString& string) + { + m_first.append(string); + } + + bool hasInsertionPoint() const + { + return &m_first != m_last; + } + + void markEndOfFile() + { + // FIXME: This should use InputStreamPreprocessor::endOfFileMarker + // once InputStreamPreprocessor is split off into its own header. + static const UChar endOfFileMarker = 0; + m_last->append(SegmentedString(String(&endOfFileMarker, 1))); + m_last->close(); + } + + bool haveSeenEndOfFile() const + { + return m_last->isClosed(); + } + + SegmentedString& current() { return m_first; } + const SegmentedString& current() const { return m_first; } + + void splitInto(SegmentedString& next) + { + next = m_first; + m_first = SegmentedString(); + if (m_last == &m_first) { + // We used to only have one SegmentedString in the InputStream + // but now we have two. That means m_first is no longer also + // the m_last string, |next| is now the last one. + m_last = &next; + } + } + + void mergeFrom(SegmentedString& next) + { + m_first.append(next); + if (m_last == &next) { + // The string |next| used to be the last SegmentedString in + // the InputStream. Now that it's been merged into m_first, + // that makes m_first the last one. + m_last = &m_first; + } + if (next.isClosed()) { + // We also need to merge the "closed" state from next to + // m_first. Arguably, this work could be done in append(). + m_first.close(); + } + } + +private: + SegmentedString m_first; + SegmentedString* m_last; +}; + +class InsertionPointRecord { + WTF_MAKE_NONCOPYABLE(InsertionPointRecord); +public: + explicit InsertionPointRecord(HTMLInputStream& inputStream) + : m_inputStream(&inputStream) + { + m_line = m_inputStream->current().currentLine(); + m_column = m_inputStream->current().currentColumn(); + m_inputStream->splitInto(m_next); + // We 'fork' current position and use it for the generated script part. + // This is a bit weird, because generated part does not have positions within an HTML document. + m_inputStream->current().setCurrentPosition(m_line, m_column, 0); + } + + ~InsertionPointRecord() + { + // Some inserted text may have remained in input stream. E.g. if script has written "&" or "<table", + // it stays in buffer because it cannot be properly tokenized before we see next part. + int unparsedRemainderLength = m_inputStream->current().length(); + m_inputStream->mergeFrom(m_next); + // We restore position for the character that goes right after unparsed remainder. + m_inputStream->current().setCurrentPosition(m_line, m_column, unparsedRemainderLength); + } + +private: + HTMLInputStream* m_inputStream; + SegmentedString m_next; + OrdinalNumber m_line; + OrdinalNumber m_column; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLMetaCharsetParser.cpp b/Source/WebCore/html/parser/HTMLMetaCharsetParser.cpp new file mode 100644 index 000000000..9ceaae9af --- /dev/null +++ b/Source/WebCore/html/parser/HTMLMetaCharsetParser.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2010 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLMetaCharsetParser.h" + +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLTokenizer.h" +#include "PlatformString.h" +#include "TextCodec.h" +#include "TextEncodingRegistry.h" + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +HTMLMetaCharsetParser::HTMLMetaCharsetParser() + : m_tokenizer(HTMLTokenizer::create(false)) // No pre-HTML5 parser quirks. + , m_assumedCodec(newTextCodec(Latin1Encoding())) + , m_inHeadSection(true) + , m_doneChecking(false) +{ +} + +HTMLMetaCharsetParser::~HTMLMetaCharsetParser() +{ +} + +static const char charsetString[] = "charset"; +static const size_t charsetLength = sizeof("charset") - 1; + +String HTMLMetaCharsetParser::extractCharset(const String& value) +{ + size_t pos = 0; + unsigned length = value.length(); + + while (pos < length) { + pos = value.find(charsetString, pos, false); + if (pos == notFound) + break; + + pos += charsetLength; + + // Skip whitespace. + while (pos < length && value[pos] <= ' ') + ++pos; + + if (value[pos] != '=') + continue; + + ++pos; + + while (pos < length && value[pos] <= ' ') + ++pos; + + char quoteMark = 0; + if (pos < length && (value[pos] == '"' || value[pos] == '\'')) { + quoteMark = static_cast<char>(value[pos++]); + ASSERT(!(quoteMark & 0x80)); + } + + if (pos == length) + break; + + unsigned end = pos; + while (end < length && ((quoteMark && value[end] != quoteMark) || (!quoteMark && value[end] > ' ' && value[end] != '"' && value[end] != '\'' && value[end] != ';'))) + ++end; + + if (quoteMark && (end == length)) + break; // Close quote not found. + + return value.substring(pos, end - pos); + } + + return ""; +} + +bool HTMLMetaCharsetParser::processMeta() +{ + const HTMLToken::AttributeList& tokenAttributes = m_token.attributes(); + AttributeList attributes; + for (HTMLToken::AttributeList::const_iterator iter = tokenAttributes.begin(); iter != tokenAttributes.end(); ++iter) { + String attributeName(iter->m_name.data(), iter->m_name.size()); + String attributeValue(iter->m_value.data(), iter->m_value.size()); + attributes.append(make_pair(attributeName, attributeValue)); + } + + m_encoding = encodingFromMetaAttributes(attributes); + return m_encoding.isValid(); +} + +TextEncoding HTMLMetaCharsetParser::encodingFromMetaAttributes(const AttributeList& attributes) +{ + bool gotPragma = false; + Mode mode = None; + String charset; + + for (AttributeList::const_iterator iter = attributes.begin(); iter != attributes.end(); ++iter) { + const AtomicString& attributeName = iter->first; + const String& attributeValue = iter->second; + + if (attributeName == http_equivAttr) { + if (equalIgnoringCase(attributeValue, "content-type")) + gotPragma = true; + } else if (charset.isEmpty()) { + if (attributeName == charsetAttr) { + charset = attributeValue; + mode = Charset; + } else if (attributeName == contentAttr) { + charset = extractCharset(attributeValue); + if (charset.length()) + mode = Pragma; + } + } + } + + if (mode == Charset || (mode == Pragma && gotPragma)) + return TextEncoding(stripLeadingAndTrailingHTMLSpaces(charset)); + + return TextEncoding(); +} + +static const int bytesToCheckUnconditionally = 1024; // That many input bytes will be checked for meta charset even if <head> section is over. + +bool HTMLMetaCharsetParser::checkForMetaCharset(const char* data, size_t length) +{ + if (m_doneChecking) + return true; + + ASSERT(!m_encoding.isValid()); + + // We still don't have an encoding, and are in the head. + // The following tags are allowed in <head>: + // SCRIPT|STYLE|META|LINK|OBJECT|TITLE|BASE + + // We stop scanning when a tag that is not permitted in <head> + // is seen, rather when </head> is seen, because that more closely + // matches behavior in other browsers; more details in + // <http://bugs.webkit.org/show_bug.cgi?id=3590>. + + // Additionally, we ignore things that looks like tags in <title>, <script> + // and <noscript>; see <http://bugs.webkit.org/show_bug.cgi?id=4560>, + // <http://bugs.webkit.org/show_bug.cgi?id=12165> and + // <http://bugs.webkit.org/show_bug.cgi?id=12389>. + + // Since many sites have charset declarations after <body> or other tags + // that are disallowed in <head>, we don't bail out until we've checked at + // least bytesToCheckUnconditionally bytes of input. + + m_input.append(SegmentedString(m_assumedCodec->decode(data, length))); + + while (m_tokenizer->nextToken(m_input, m_token)) { + bool end = m_token.type() == HTMLTokenTypes::EndTag; + if (end || m_token.type() == HTMLTokenTypes::StartTag) { + AtomicString tagName(m_token.name().data(), m_token.name().size()); + if (!end) { + m_tokenizer->updateStateFor(tagName, 0); + if (tagName == metaTag && processMeta()) { + m_doneChecking = true; + return true; + } + } + + if (tagName != scriptTag && tagName != noscriptTag + && tagName != styleTag && tagName != linkTag + && tagName != metaTag && tagName != objectTag + && tagName != titleTag && tagName != baseTag + && (end || tagName != htmlTag) && (end || tagName != headTag)) { + m_inHeadSection = false; + } + } + + if (!m_inHeadSection && m_input.numberOfCharactersConsumed() >= bytesToCheckUnconditionally) { + m_doneChecking = true; + return true; + } + + m_token.clear(); + } + + return false; +} + +} diff --git a/Source/WebCore/html/parser/HTMLMetaCharsetParser.h b/Source/WebCore/html/parser/HTMLMetaCharsetParser.h new file mode 100644 index 000000000..1ee87df9a --- /dev/null +++ b/Source/WebCore/html/parser/HTMLMetaCharsetParser.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLMetaCharsetParser_h +#define HTMLMetaCharsetParser_h + +#include "HTMLToken.h" +#include "SegmentedString.h" +#include "TextEncoding.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class HTMLTokenizer; +class TextCodec; + +class HTMLMetaCharsetParser { + WTF_MAKE_NONCOPYABLE(HTMLMetaCharsetParser); +public: + static PassOwnPtr<HTMLMetaCharsetParser> create() { return adoptPtr(new HTMLMetaCharsetParser()); } + + ~HTMLMetaCharsetParser(); + + // Returns true if done checking, regardless whether an encoding is found. + bool checkForMetaCharset(const char*, size_t); + + const TextEncoding& encoding() { return m_encoding; } + + typedef Vector<pair<String, String> > AttributeList; + // The returned encoding might not be valid. + static TextEncoding encodingFromMetaAttributes(const AttributeList& +); + +private: + HTMLMetaCharsetParser(); + + bool processMeta(); + static String extractCharset(const String&); + + enum Mode { + None, + Charset, + Pragma, + }; + + OwnPtr<HTMLTokenizer> m_tokenizer; + OwnPtr<TextCodec> m_assumedCodec; + SegmentedString m_input; + HTMLToken m_token; + bool m_inHeadSection; + + bool m_doneChecking; + TextEncoding m_encoding; +}; + +} +#endif diff --git a/Source/WebCore/html/parser/HTMLParserIdioms.cpp b/Source/WebCore/html/parser/HTMLParserIdioms.cpp new file mode 100644 index 000000000..b8602025e --- /dev/null +++ b/Source/WebCore/html/parser/HTMLParserIdioms.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLParserIdioms.h" + +#include <limits> +#include <wtf/MathExtras.h> +#include <wtf/dtoa.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +String stripLeadingAndTrailingHTMLSpaces(const String& string) +{ + const UChar* characters = string.characters(); + unsigned length = string.length(); + + unsigned numLeadingSpaces; + for (numLeadingSpaces = 0; numLeadingSpaces < length; ++numLeadingSpaces) { + if (isNotHTMLSpace(characters[numLeadingSpaces])) + break; + } + + if (numLeadingSpaces == length) + return string.isNull() ? string : emptyAtom.string(); + + unsigned numTrailingSpaces; + for (numTrailingSpaces = 0; numTrailingSpaces < length; ++numTrailingSpaces) { + if (isNotHTMLSpace(characters[length - numTrailingSpaces - 1])) + break; + } + + ASSERT(numLeadingSpaces + numTrailingSpaces < length); + + return string.substring(numLeadingSpaces, length - (numLeadingSpaces + numTrailingSpaces)); +} + +String serializeForNumberType(double number) +{ + // According to HTML5, "the best representation of the number n as a floating + // point number" is a string produced by applying ToString() to n. + NumberToStringBuffer buffer; + return String(numberToString(number, buffer)); +} + +bool parseToDoubleForNumberType(const String& string, double* result) +{ + // See HTML5 2.5.4.3 `Real numbers.' + + // String::toDouble() accepts leading + and whitespace characters, which are not valid here. + UChar firstCharacter = string[0]; + if (firstCharacter != '-' && firstCharacter != '.' && !isASCIIDigit(firstCharacter)) + return false; + + bool valid = false; + double value = string.toDouble(&valid); + if (!valid) + return false; + + // NaN and infinity are considered valid by String::toDouble, but not valid here. + if (!isfinite(value)) + return false; + + // Numbers are considered finite IEEE 754 single-precision floating point values. + // See HTML5 2.5.4.3 `Real numbers.' + if (-std::numeric_limits<float>::max() > value || value > std::numeric_limits<float>::max()) + return false; + + if (result) { + // The following expression converts -0 to +0. + *result = value ? value : 0; + } + + return true; +} + +bool parseToDoubleForNumberTypeWithDecimalPlaces(const String& string, double *result, unsigned *decimalPlaces) +{ + if (decimalPlaces) + *decimalPlaces = 0; + + if (!parseToDoubleForNumberType(string, result)) + return false; + + if (!decimalPlaces) + return true; + + size_t dotIndex = string.find('.'); + size_t eIndex = string.find('e'); + if (eIndex == notFound) + eIndex = string.find('E'); + + unsigned baseDecimalPlaces = 0; + if (dotIndex != notFound) { + if (eIndex == notFound) + baseDecimalPlaces = string.length() - dotIndex - 1; + else + baseDecimalPlaces = eIndex - dotIndex - 1; + } + + int exponent = 0; + if (eIndex != notFound) { + unsigned cursor = eIndex + 1, cursorSaved; + int digit, exponentSign; + int32_t exponent32; + size_t length = string.length(); + + // Not using String.toInt() in order to perform the same computation as dtoa() does. + exponentSign = 0; + switch (digit = string[cursor]) { + case '-': + exponentSign = 1; + case '+': + digit = string[++cursor]; + } + if (digit >= '0' && digit <= '9') { + while (cursor < length && digit == '0') + digit = string[++cursor]; + if (digit > '0' && digit <= '9') { + exponent32 = digit - '0'; + cursorSaved = cursor; + while (cursor < length && (digit = string[++cursor]) >= '0' && digit <= '9') + exponent32 = (10 * exponent32) + digit - '0'; + if (cursor - cursorSaved > 8 || exponent32 > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + exponent = 19999; /* safe for 16 bit ints */ + else + exponent = static_cast<int>(exponent32); + if (exponentSign) + exponent = -exponent; + } else + exponent = 0; + } + } + + int intDecimalPlaces = baseDecimalPlaces - exponent; + if (intDecimalPlaces < 0) + *decimalPlaces = 0; + else if (intDecimalPlaces > 19999) + *decimalPlaces = 19999; + else + *decimalPlaces = static_cast<unsigned>(intDecimalPlaces); + + return true; +} + +// http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-integers +bool parseHTMLInteger(const String& input, int& value) +{ + // Step 1 + // Step 2 + const UChar* position = input.characters(); + const UChar* end = position + input.length(); + + // Step 3 + int sign = 1; + + // Step 4 + while (position < end) { + if (!isHTMLSpace(*position)) + break; + ++position; + } + + // Step 5 + if (position == end) + return false; + ASSERT(position < end); + + // Step 6 + if (*position == '-') { + sign = -1; + ++position; + } else if (*position == '+') + ++position; + if (position == end) + return false; + ASSERT(position < end); + + // Step 7 + if (!isASCIIDigit(*position)) + return false; + + // Step 8 + StringBuilder digits; + while (position < end) { + if (!isASCIIDigit(*position)) + break; + digits.append(*position++); + } + + // Step 9 + bool ok; + value = sign * charactersToIntStrict(digits.characters(), digits.length(), &ok); + return ok; +} + +// http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-non-negative-integers +bool parseHTMLNonNegativeInteger(const String& input, unsigned int& value) +{ + // Step 1 + // Step 2 + const UChar* position = input.characters(); + const UChar* end = position + input.length(); + + // Step 3 + while (position < end) { + if (!isHTMLSpace(*position)) + break; + ++position; + } + + // Step 4 + if (position == end) + return false; + ASSERT(position < end); + + // Step 5 + if (*position == '+') + ++position; + + // Step 6 + if (position == end) + return false; + ASSERT(position < end); + + // Step 7 + if (!isASCIIDigit(*position)) + return false; + + // Step 8 + StringBuilder digits; + while (position < end) { + if (!isASCIIDigit(*position)) + break; + digits.append(*position++); + } + + // Step 9 + bool ok; + value = charactersToUIntStrict(digits.characters(), digits.length(), &ok); + return ok; +} + +} diff --git a/Source/WebCore/html/parser/HTMLParserIdioms.h b/Source/WebCore/html/parser/HTMLParserIdioms.h new file mode 100644 index 000000000..19cd27693 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLParserIdioms.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLParserIdioms_h +#define HTMLParserIdioms_h + +#include <wtf/Forward.h> +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + +// Space characters as defined by the HTML specification. +bool isHTMLSpace(UChar); +bool isHTMLLineBreak(UChar); +bool isNotHTMLSpace(UChar); + +// Strip leading and trailing whitespace as defined by the HTML specification. +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(double); + +// Convert the specified string to a double. If the conversion fails, the return value is false. +// 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. +bool parseToDoubleForNumberType(const String&, double*); +bool parseToDoubleForNumberTypeWithDecimalPlaces(const String&, double*, unsigned*); + +// http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-integers +bool parseHTMLInteger(const String&, int&); + +// http://www.whatwg.org/specs/web-apps/current-work/#rules-for-parsing-non-negative-integers +bool parseHTMLNonNegativeInteger(const String&, unsigned int&); + +// Inline implementations of some of the functions declared above. + +inline bool isHTMLSpace(UChar character) +{ + // Histogram from Apple's page load test combined with some ad hoc browsing some other test suites. + // + // 82%: 216330 non-space characters, all > U+0020 + // 11%: 30017 plain space characters, U+0020 + // 5%: 12099 newline characters, U+000A + // 2%: 5346 tab characters, U+0009 + // + // No other characters seen. No U+000C or U+000D, and no other control characters. + // Accordingly, we check for non-spaces first, then space, then newline, then tab, then the other characters. + + return character <= ' ' && (character == ' ' || character == '\n' || character == '\t' || character == '\r' || character == '\f'); +} + +inline bool isHTMLLineBreak(UChar character) +{ + return character <= '\r' && (character == '\n' || character == '\r'); +} + +inline bool isNotHTMLSpace(UChar character) +{ + return !isHTMLSpace(character); +} + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLParserScheduler.cpp b/Source/WebCore/html/parser/HTMLParserScheduler.cpp new file mode 100644 index 000000000..2692036f3 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLParserScheduler.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLParserScheduler.h" + +#include "Document.h" +#include "FrameView.h" +#include "HTMLDocumentParser.h" +#include "Page.h" + +// defaultParserChunkSize is used to define how many tokens the parser will +// process before checking against parserTimeLimit and possibly yielding. +// This is a performance optimization to prevent checking after every token. +static const int defaultParserChunkSize = 4096; + +// defaultParserTimeLimit is the seconds the parser will run in one write() call +// before yielding. Inline <script> execution can cause it to excede the limit. +// FIXME: We would like this value to be 0.2. +static const double defaultParserTimeLimit = 0.500; + +namespace WebCore { + +static double parserTimeLimit(Page* page) +{ + // We're using the poorly named customHTMLTokenizerTimeDelay setting. + if (page && page->hasCustomHTMLTokenizerTimeDelay()) + return page->customHTMLTokenizerTimeDelay(); + return defaultParserTimeLimit; +} + +static int parserChunkSize(Page* page) +{ + // FIXME: We may need to divide the value from customHTMLTokenizerChunkSize + // by some constant to translate from the "character" based behavior of the + // old LegacyHTMLDocumentParser to the token-based behavior of this parser. + if (page && page->hasCustomHTMLTokenizerChunkSize()) + return page->customHTMLTokenizerChunkSize(); + return defaultParserChunkSize; +} + +HTMLParserScheduler::HTMLParserScheduler(HTMLDocumentParser* parser) + : m_parser(parser) + , m_parserTimeLimit(parserTimeLimit(m_parser->document()->page())) + , m_parserChunkSize(parserChunkSize(m_parser->document()->page())) + , m_continueNextChunkTimer(this, &HTMLParserScheduler::continueNextChunkTimerFired) + , m_isSuspendedWithActiveTimer(false) +{ +} + +HTMLParserScheduler::~HTMLParserScheduler() +{ + m_continueNextChunkTimer.stop(); +} + +void HTMLParserScheduler::continueNextChunkTimerFired(Timer<HTMLParserScheduler>* timer) +{ + ASSERT_UNUSED(timer, timer == &m_continueNextChunkTimer); + // FIXME: The timer class should handle timer priorities instead of this code. + // If a layout is scheduled, wait again to let the layout timer run first. + if (m_parser->document()->isLayoutTimerActive()) { + m_continueNextChunkTimer.startOneShot(0); + return; + } + m_parser->resumeParsingAfterYield(); +} + +void HTMLParserScheduler::checkForYieldBeforeScript(PumpSession& session) +{ + // If we've never painted before and a layout is pending, yield prior to running + // scripts to give the page a chance to paint earlier. + Document* document = m_parser->document(); + bool needsFirstPaint = document->view() && !document->view()->hasEverPainted(); + if (needsFirstPaint && document->isLayoutTimerActive()) + session.needsYield = true; +} + +void HTMLParserScheduler::scheduleForResume() +{ + m_continueNextChunkTimer.startOneShot(0); +} + + +void HTMLParserScheduler::suspend() +{ + ASSERT(!m_isSuspendedWithActiveTimer); + if (!m_continueNextChunkTimer.isActive()) + return; + m_isSuspendedWithActiveTimer = true; + m_continueNextChunkTimer.stop(); +} + +void HTMLParserScheduler::resume() +{ + ASSERT(!m_continueNextChunkTimer.isActive()); + if (!m_isSuspendedWithActiveTimer) + return; + m_isSuspendedWithActiveTimer = false; + m_continueNextChunkTimer.startOneShot(0); +} + +} diff --git a/Source/WebCore/html/parser/HTMLParserScheduler.h b/Source/WebCore/html/parser/HTMLParserScheduler.h new file mode 100644 index 000000000..b0e2e85f0 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLParserScheduler.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLParserScheduler_h +#define HTMLParserScheduler_h + +#include <limits.h> + +#include "NestingLevelIncrementer.h" +#include "Timer.h" +#include <wtf/CurrentTime.h> +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class HTMLDocumentParser; + +class PumpSession : public NestingLevelIncrementer { +public: + PumpSession(unsigned& nestingLevel) + : NestingLevelIncrementer(nestingLevel) + // Setting processedTokens to INT_MAX causes us to check for yields + // after any token during any parse where yielding is allowed. + // At that time we'll initialize startTime. + , processedTokens(INT_MAX) + , startTime(0) + , needsYield(false) + { + } + + int processedTokens; + double startTime; + bool needsYield; +}; + +class HTMLParserScheduler { + WTF_MAKE_NONCOPYABLE(HTMLParserScheduler); WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<HTMLParserScheduler> create(HTMLDocumentParser* parser) + { + return adoptPtr(new HTMLParserScheduler(parser)); + } + ~HTMLParserScheduler(); + + // Inline as this is called after every token in the parser. + void checkForYieldBeforeToken(PumpSession& session) + { + if (session.processedTokens > m_parserChunkSize) { + // currentTime() can be expensive. By delaying, we avoided calling + // currentTime() when constructing non-yielding PumpSessions. + if (!session.startTime) + session.startTime = currentTime(); + + session.processedTokens = 0; + double elapsedTime = currentTime() - session.startTime; + if (elapsedTime > m_parserTimeLimit) + session.needsYield = true; + } + ++session.processedTokens; + } + void checkForYieldBeforeScript(PumpSession&); + + void scheduleForResume(); + bool isScheduledForResume() const { return m_isSuspendedWithActiveTimer || m_continueNextChunkTimer.isActive(); } + + void suspend(); + void resume(); + +private: + HTMLParserScheduler(HTMLDocumentParser*); + + void continueNextChunkTimerFired(Timer<HTMLParserScheduler>*); + + HTMLDocumentParser* m_parser; + + double m_parserTimeLimit; + int m_parserChunkSize; + Timer<HTMLParserScheduler> m_continueNextChunkTimer; + bool m_isSuspendedWithActiveTimer; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLPreloadScanner.cpp b/Source/WebCore/html/parser/HTMLPreloadScanner.cpp new file mode 100644 index 000000000..b9421d884 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLPreloadScanner.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ + * Copyright (C) 2010 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLPreloadScanner.h" + +#include "CachedResourceLoader.h" +#include "Document.h" +#include "InputType.h" +#include "HTMLDocumentParser.h" +#include "HTMLTokenizer.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "LinkRelAttribute.h" +#include "MediaList.h" +#include "MediaQueryEvaluator.h" + +namespace WebCore { + +using namespace HTMLNames; + +namespace { + +class PreloadTask { +public: + PreloadTask(const HTMLToken& token) + : m_tagName(token.name().data(), token.name().size()) + , m_linkIsStyleSheet(false) + , m_linkMediaAttributeIsScreen(true) + , m_inputIsImage(false) + { + processAttributes(token.attributes()); + } + + void processAttributes(const HTMLToken::AttributeList& attributes) + { + if (m_tagName != imgTag + && m_tagName != inputTag + && m_tagName != linkTag + && m_tagName != scriptTag) + return; + + for (HTMLToken::AttributeList::const_iterator iter = attributes.begin(); + iter != attributes.end(); ++iter) { + AtomicString attributeName(iter->m_name.data(), iter->m_name.size()); + String attributeValue(iter->m_value.data(), iter->m_value.size()); + + if (attributeName == charsetAttr) + m_charset = attributeValue; + + if (m_tagName == scriptTag || m_tagName == imgTag) { + if (attributeName == srcAttr) + setUrlToLoad(attributeValue); + } else if (m_tagName == linkTag) { + if (attributeName == hrefAttr) + setUrlToLoad(attributeValue); + else if (attributeName == relAttr) + m_linkIsStyleSheet = relAttributeIsStyleSheet(attributeValue); + else if (attributeName == mediaAttr) + m_linkMediaAttributeIsScreen = linkMediaAttributeIsScreen(attributeValue); + } else if (m_tagName == inputTag) { + if (attributeName == srcAttr) + setUrlToLoad(attributeValue); + else if (attributeName == typeAttr) + m_inputIsImage = equalIgnoringCase(attributeValue, InputTypeNames::image()); + } + } + } + + static bool relAttributeIsStyleSheet(const String& attributeValue) + { + LinkRelAttribute rel(attributeValue); + return rel.m_isStyleSheet && !rel.m_isAlternate && rel.m_iconType == InvalidIcon && !rel.m_isDNSPrefetch; + } + + static bool linkMediaAttributeIsScreen(const String& attributeValue) + { + if (attributeValue.isEmpty()) + return true; + RefPtr<MediaList> mediaList = MediaList::createAllowingDescriptionSyntax(attributeValue); + + // Only preload screen media stylesheets. Used this way, the evaluator evaluates to true for any + // rules containing complex queries (full evaluation is possible but it requires a frame and a style selector which + // may be problematic here). + MediaQueryEvaluator mediaQueryEvaluator("screen"); + return mediaQueryEvaluator.eval(mediaList.get()); + } + + void setUrlToLoad(const String& attributeValue) + { + // We only respect the first src/href, per HTML5: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#attribute-name-state + if (!m_urlToLoad.isEmpty()) + return; + m_urlToLoad = stripLeadingAndTrailingHTMLSpaces(attributeValue); + } + + void preload(Document* document, bool scanningBody) + { + if (m_urlToLoad.isEmpty()) + return; + + CachedResourceLoader* cachedResourceLoader = document->cachedResourceLoader(); + ResourceRequest request = document->completeURL(m_urlToLoad); + if (m_tagName == scriptTag) + cachedResourceLoader->preload(CachedResource::Script, request, m_charset, scanningBody); + else if (m_tagName == imgTag || (m_tagName == inputTag && m_inputIsImage)) + cachedResourceLoader->preload(CachedResource::ImageResource, request, String(), scanningBody); + else if (m_tagName == linkTag && m_linkIsStyleSheet && m_linkMediaAttributeIsScreen) + cachedResourceLoader->preload(CachedResource::CSSStyleSheet, request, m_charset, scanningBody); + } + + const AtomicString& tagName() const { return m_tagName; } + +private: + AtomicString m_tagName; + String m_urlToLoad; + String m_charset; + bool m_linkIsStyleSheet; + bool m_linkMediaAttributeIsScreen; + bool m_inputIsImage; +}; + +} // namespace + +HTMLPreloadScanner::HTMLPreloadScanner(Document* document) + : m_document(document) + , m_cssScanner(document) + , m_tokenizer(HTMLTokenizer::create(HTMLDocumentParser::usePreHTML5ParserQuirks(document))) + , m_bodySeen(false) + , m_inStyle(false) +{ +} + +void HTMLPreloadScanner::appendToEnd(const SegmentedString& source) +{ + m_source.append(source); +} + +void HTMLPreloadScanner::scan() +{ + // FIXME: We should save and re-use these tokens in HTMLDocumentParser if + // the pending script doesn't end up calling document.write. + while (m_tokenizer->nextToken(m_source, m_token)) { + processToken(); + m_token.clear(); + } +} + +void HTMLPreloadScanner::processToken() +{ + if (m_inStyle) { + if (m_token.type() == HTMLTokenTypes::Character) + m_cssScanner.scan(m_token, scanningBody()); + else if (m_token.type() == HTMLTokenTypes::EndTag) { + m_inStyle = false; + m_cssScanner.reset(); + } + } + + if (m_token.type() != HTMLTokenTypes::StartTag) + return; + + PreloadTask task(m_token); + m_tokenizer->updateStateFor(task.tagName(), m_document->frame()); + + if (task.tagName() == bodyTag) + m_bodySeen = true; + + if (task.tagName() == styleTag) + m_inStyle = true; + + task.preload(m_document, scanningBody()); +} + +bool HTMLPreloadScanner::scanningBody() const +{ + return m_document->body() || m_bodySeen; +} + +} diff --git a/Source/WebCore/html/parser/HTMLPreloadScanner.h b/Source/WebCore/html/parser/HTMLPreloadScanner.h new file mode 100644 index 000000000..bed77fe5b --- /dev/null +++ b/Source/WebCore/html/parser/HTMLPreloadScanner.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2010 Google Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLPreloadScanner_h +#define HTMLPreloadScanner_h + +#include "CSSPreloadScanner.h" +#include "HTMLToken.h" +#include "SegmentedString.h" + +namespace WebCore { + +class Document; +class HTMLToken; +class HTMLTokenizer; +class SegmentedString; + +class HTMLPreloadScanner { + WTF_MAKE_NONCOPYABLE(HTMLPreloadScanner); WTF_MAKE_FAST_ALLOCATED; +public: + HTMLPreloadScanner(Document*); + + void appendToEnd(const SegmentedString&); + void scan(); + +private: + void processToken(); + bool scanningBody() const; + + Document* m_document; + SegmentedString m_source; + CSSPreloadScanner m_cssScanner; + OwnPtr<HTMLTokenizer> m_tokenizer; + HTMLToken m_token; + bool m_bodySeen; + bool m_inStyle; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLScriptRunner.cpp b/Source/WebCore/html/parser/HTMLScriptRunner.cpp new file mode 100644 index 000000000..d14d69d1d --- /dev/null +++ b/Source/WebCore/html/parser/HTMLScriptRunner.cpp @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLScriptRunner.h" + +#include "Attribute.h" +#include "CachedScript.h" +#include "CachedResourceLoader.h" +#include "Element.h" +#include "Event.h" +#include "Frame.h" +#include "HTMLInputStream.h" +#include "HTMLNames.h" +#include "HTMLScriptRunnerHost.h" +#include "IgnoreDestructiveWriteCountIncrementer.h" +#include "NestingLevelIncrementer.h" +#include "NotImplemented.h" +#include "ScriptElement.h" +#include "ScriptSourceCode.h" + +namespace WebCore { + +using namespace HTMLNames; + +HTMLScriptRunner::HTMLScriptRunner(Document* document, HTMLScriptRunnerHost* host) + : m_document(document) + , m_host(host) + , m_scriptNestingLevel(0) + , m_hasScriptsWaitingForStylesheets(false) +{ + ASSERT(m_host); +} + +HTMLScriptRunner::~HTMLScriptRunner() +{ + // FIXME: Should we be passed a "done loading/parsing" callback sooner than destruction? + if (m_parsingBlockingScript.cachedScript() && m_parsingBlockingScript.watchingForLoad()) + stopWatchingForLoad(m_parsingBlockingScript); + + while (!m_scriptsToExecuteAfterParsing.isEmpty()) { + PendingScript pendingScript = m_scriptsToExecuteAfterParsing.takeFirst(); + if (pendingScript.cachedScript() && pendingScript.watchingForLoad()) + stopWatchingForLoad(pendingScript); + } +} + +void HTMLScriptRunner::detach() +{ + m_document = 0; +} + +static KURL documentURLForScriptExecution(Document* document) +{ + if (!document || !document->frame()) + return KURL(); + + // Use the URL of the currently active document for this frame. + return document->frame()->document()->url(); +} + +inline PassRefPtr<Event> createScriptLoadEvent() +{ + return Event::create(eventNames().loadEvent, false, false); +} + +ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) const +{ + if (script.cachedScript()) { + errorOccurred = script.cachedScript()->errorOccurred(); + ASSERT(script.cachedScript()->isLoaded()); + return ScriptSourceCode(script.cachedScript()); + } + errorOccurred = false; + return ScriptSourceCode(script.element()->textContent(), documentURLForScriptExecution(m_document), script.startingPosition()); +} + +bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script) +{ + m_hasScriptsWaitingForStylesheets = !m_document->haveStylesheetsLoaded(); + if (m_hasScriptsWaitingForStylesheets) + return false; + if (script.cachedScript() && !script.cachedScript()->isLoaded()) + return false; + return true; +} + +void HTMLScriptRunner::executeParsingBlockingScript() +{ + ASSERT(m_document); + ASSERT(!m_scriptNestingLevel); + ASSERT(m_document->haveStylesheetsLoaded()); + ASSERT(isPendingScriptReady(m_parsingBlockingScript)); + + InsertionPointRecord insertionPointRecord(m_host->inputStream()); + executePendingScriptAndDispatchEvent(m_parsingBlockingScript); +} + +void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendingScript) +{ + bool errorOccurred = false; + ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred); + + // Stop watching loads before executeScript to prevent recursion if the script reloads itself. + if (pendingScript.cachedScript() && pendingScript.watchingForLoad()) + stopWatchingForLoad(pendingScript); + + // Clear the pending script before possible rentrancy from executeScript() + RefPtr<Element> element = pendingScript.releaseElementAndClear(); + if (ScriptElement* scriptElement = toScriptElement(element.get())) { + NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel); + IgnoreDestructiveWriteCountIncrementer ignoreDestructiveWriteCountIncrementer(m_document); + if (errorOccurred) + scriptElement->dispatchErrorEvent(); + else { + ASSERT(isExecutingScript()); + scriptElement->executeScript(sourceCode); + element->dispatchEvent(createScriptLoadEvent()); + } + } + ASSERT(!m_scriptNestingLevel); +} + +void HTMLScriptRunner::watchForLoad(PendingScript& pendingScript) +{ + ASSERT(!pendingScript.watchingForLoad()); + m_host->watchForLoad(pendingScript.cachedScript()); + pendingScript.setWatchingForLoad(true); +} + +void HTMLScriptRunner::stopWatchingForLoad(PendingScript& pendingScript) +{ + ASSERT(pendingScript.watchingForLoad()); + m_host->stopWatchingForLoad(pendingScript.cachedScript()); + pendingScript.setWatchingForLoad(false); +} + +// This function should match 10.2.5.11 "An end tag whose tag name is 'script'" +// Script handling lives outside the tree builder to keep the each class simple. +bool HTMLScriptRunner::execute(PassRefPtr<Element> scriptElement, const TextPosition& scriptStartPosition) +{ + ASSERT(scriptElement); + // FIXME: If scripting is disabled, always just return true; + + bool hadPreloadScanner = m_host->hasPreloadScanner(); + + // Try to execute the script given to us. + runScript(scriptElement.get(), scriptStartPosition); + + if (haveParsingBlockingScript()) { + if (m_scriptNestingLevel) + return false; // Block the parser. Unwind to the outermost HTMLScriptRunner::execute before continuing parsing. + // If preload scanner got created, it is missing the source after the current insertion point. Append it and scan. + if (!hadPreloadScanner && m_host->hasPreloadScanner()) + m_host->appendCurrentInputStreamToPreloadScannerAndScan(); + if (!executeParsingBlockingScripts()) + return false; // We still have a parsing blocking script, block the parser. + } + return true; // Scripts executed as expected, continue parsing. +} + +bool HTMLScriptRunner::haveParsingBlockingScript() const +{ + return !!m_parsingBlockingScript.element(); +} + +bool HTMLScriptRunner::executeParsingBlockingScripts() +{ + while (haveParsingBlockingScript()) { + // We only really need to check once. + if (!isPendingScriptReady(m_parsingBlockingScript)) + return false; + executeParsingBlockingScript(); + } + return true; +} + +bool HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript) +{ + ASSERT(!m_scriptNestingLevel); + ASSERT(haveParsingBlockingScript()); + ASSERT_UNUSED(cachedScript, m_parsingBlockingScript.cachedScript() == cachedScript); + ASSERT(m_parsingBlockingScript.cachedScript()->isLoaded()); + return executeParsingBlockingScripts(); +} + +bool HTMLScriptRunner::executeScriptsWaitingForStylesheets() +{ + ASSERT(m_document); + // Callers should check hasScriptsWaitingForStylesheets() before calling + // to prevent parser or script re-entry during </style> parsing. + ASSERT(hasScriptsWaitingForStylesheets()); + ASSERT(!m_scriptNestingLevel); + ASSERT(m_document->haveStylesheetsLoaded()); + return executeParsingBlockingScripts(); +} + +bool HTMLScriptRunner::executeScriptsWaitingForParsing() +{ + while (!m_scriptsToExecuteAfterParsing.isEmpty()) { + ASSERT(!m_scriptNestingLevel); + ASSERT(!haveParsingBlockingScript()); + ASSERT(m_scriptsToExecuteAfterParsing.first().cachedScript()); + if (!m_scriptsToExecuteAfterParsing.first().cachedScript()->isLoaded()) { + watchForLoad(m_scriptsToExecuteAfterParsing.first()); + return false; + } + PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst(); + executePendingScriptAndDispatchEvent(first); + if (!m_document) + return false; + } + return true; +} + +void HTMLScriptRunner::requestParsingBlockingScript(Element* element) +{ + if (!requestPendingScript(m_parsingBlockingScript, element)) + return; + + ASSERT(m_parsingBlockingScript.cachedScript()); + + // We only care about a load callback if cachedScript is not already + // in the cache. Callers will attempt to run the m_parsingBlockingScript + // if possible before returning control to the parser. + if (!m_parsingBlockingScript.cachedScript()->isLoaded()) + watchForLoad(m_parsingBlockingScript); +} + +void HTMLScriptRunner::requestDeferredScript(Element* element) +{ + PendingScript pendingScript; + if (!requestPendingScript(pendingScript, element)) + return; + + ASSERT(pendingScript.cachedScript()); + m_scriptsToExecuteAfterParsing.append(pendingScript); +} + +bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const +{ + ASSERT(!pendingScript.element()); + pendingScript.setElement(script); + // This should correctly return 0 for empty or invalid srcValues. + CachedScript* cachedScript = toScriptElement(script)->cachedScript().get(); + if (!cachedScript) { + notImplemented(); // Dispatch error event. + return false; + } + pendingScript.setCachedScript(cachedScript); + return true; +} + +// This method is meant to match the HTML5 definition of "running a script" +// http://www.whatwg.org/specs/web-apps/current-work/multipage/scripting-1.html#running-a-script +void HTMLScriptRunner::runScript(Element* script, const TextPosition& scriptStartPosition) +{ + ASSERT(m_document); + ASSERT(!haveParsingBlockingScript()); + { + InsertionPointRecord insertionPointRecord(m_host->inputStream()); + NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel); + + ScriptElement* scriptElement = toScriptElement(script); + + // This contains both and ASSERTION and a null check since we should not + // be getting into the case of a null script element, but seem to be from + // time to time. The assertion is left in to help find those cases and + // is being tracked by <https://bugs.webkit.org/show_bug.cgi?id=60559>. + ASSERT(scriptElement); + if (!scriptElement) + return; + + scriptElement->prepareScript(scriptStartPosition); + + if (!scriptElement->willBeParserExecuted()) + return; + + if (scriptElement->willExecuteWhenDocumentFinishedParsing()) + requestDeferredScript(script); + else if (scriptElement->readyToBeParserExecuted()) { + if (m_scriptNestingLevel == 1) { + m_parsingBlockingScript.setElement(script); + m_parsingBlockingScript.setStartingPosition(scriptStartPosition); + } else { + ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition); + scriptElement->executeScript(sourceCode); + } + } else + requestParsingBlockingScript(script); + } +} + +} diff --git a/Source/WebCore/html/parser/HTMLScriptRunner.h b/Source/WebCore/html/parser/HTMLScriptRunner.h new file mode 100644 index 000000000..77aad8c9c --- /dev/null +++ b/Source/WebCore/html/parser/HTMLScriptRunner.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLScriptRunner_h +#define HTMLScriptRunner_h + +#include "PendingScript.h" +#include <wtf/Deque.h> +#include <wtf/text/TextPosition.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class CachedResource; +class CachedScript; +class Document; +class Element; +class Frame; +class HTMLScriptRunnerHost; +class ScriptSourceCode; + +class HTMLScriptRunner { + WTF_MAKE_NONCOPYABLE(HTMLScriptRunner); WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<HTMLScriptRunner> create(Document* document, HTMLScriptRunnerHost* host) + { + return adoptPtr(new HTMLScriptRunner(document, host)); + } + ~HTMLScriptRunner(); + + void detach(); + + // Processes the passed in script and any pending scripts if possible. + bool execute(PassRefPtr<Element> scriptToProcess, const TextPosition& scriptStartPosition); + + bool executeScriptsWaitingForLoad(CachedResource*); + bool hasScriptsWaitingForStylesheets() const { return m_hasScriptsWaitingForStylesheets; } + bool executeScriptsWaitingForStylesheets(); + bool executeScriptsWaitingForParsing(); + + bool isExecutingScript() const { return !!m_scriptNestingLevel; } + +private: + HTMLScriptRunner(Document*, HTMLScriptRunnerHost*); + + Frame* frame() const; + + void executeParsingBlockingScript(); + void executePendingScriptAndDispatchEvent(PendingScript&); + bool haveParsingBlockingScript() const; + bool executeParsingBlockingScripts(); + + void requestParsingBlockingScript(Element*); + void requestDeferredScript(Element*); + bool requestPendingScript(PendingScript&, Element*) const; + + void runScript(Element*, const TextPosition& scriptStartPosition); + + // Helpers for dealing with HTMLScriptRunnerHost + void watchForLoad(PendingScript&); + void stopWatchingForLoad(PendingScript&); + bool isPendingScriptReady(const PendingScript&); + ScriptSourceCode sourceFromPendingScript(const PendingScript&, bool& errorOccurred) const; + + Document* m_document; + HTMLScriptRunnerHost* m_host; + PendingScript m_parsingBlockingScript; + Deque<PendingScript> m_scriptsToExecuteAfterParsing; // http://www.whatwg.org/specs/web-apps/current-work/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing + unsigned m_scriptNestingLevel; + + // We only want stylesheet loads to trigger script execution if script + // execution is currently stopped due to stylesheet loads, otherwise we'd + // cause nested script execution when parsing <style> tags since </style> + // tags can cause Document to call executeScriptsWaitingForStylesheets. + bool m_hasScriptsWaitingForStylesheets; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLScriptRunnerHost.h b/Source/WebCore/html/parser/HTMLScriptRunnerHost.h new file mode 100644 index 000000000..1f2289657 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLScriptRunnerHost.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLScriptRunnerHost_h +#define HTMLScriptRunnerHost_h + +#include <wtf/Forward.h> + +namespace WebCore { + +class CachedResource; +class Element; +class HTMLInputStream; +class ScriptSourceCode; + +class HTMLScriptRunnerHost { +public: + virtual ~HTMLScriptRunnerHost() { } + + // Implementors should call cachedResource->addClient() here or soon after. + virtual void watchForLoad(CachedResource*) = 0; + // Implementors must call cachedResource->removeClient() immediately. + virtual void stopWatchingForLoad(CachedResource*) = 0; + + virtual HTMLInputStream& inputStream() = 0; + + virtual bool hasPreloadScanner() const = 0; + virtual void appendCurrentInputStreamToPreloadScannerAndScan() = 0; + +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLSourceTracker.cpp b/Source/WebCore/html/parser/HTMLSourceTracker.cpp new file mode 100644 index 000000000..e7c68764b --- /dev/null +++ b/Source/WebCore/html/parser/HTMLSourceTracker.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2010 Adam Barth. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLSourceTracker.h" +#include "HTMLTokenizer.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +HTMLSourceTracker::HTMLSourceTracker() +{ +} + +void HTMLSourceTracker::start(const HTMLInputStream& input, HTMLTokenizer* tokenizer, HTMLToken& token) +{ + if (token.type() == HTMLTokenTypes::Uninitialized) { + m_previousSource.clear(); + if (tokenizer->numberOfBufferedCharacters()) + m_previousSource = tokenizer->bufferedCharacters(); + } else + m_previousSource.append(m_currentSource); + + m_currentSource = input.current(); + token.setBaseOffset(m_currentSource.numberOfCharactersConsumed() - m_previousSource.length()); +} + +void HTMLSourceTracker::end(const HTMLInputStream& input, HTMLTokenizer* tokenizer, HTMLToken& token) +{ + m_cachedSourceForToken = String(); + + // FIXME: This work should really be done by the HTMLTokenizer. + token.end(input.current().numberOfCharactersConsumed() - tokenizer->numberOfBufferedCharacters()); +} + +String HTMLSourceTracker::sourceForToken(const HTMLToken& token) +{ + if (token.type() == HTMLTokenTypes::EndOfFile) + return String(); // Hides the null character we use to mark the end of file. + + if (!m_cachedSourceForToken.isEmpty()) + return m_cachedSourceForToken; + + ASSERT(!token.startIndex()); + size_t length = static_cast<size_t>(token.endIndex() - token.startIndex()); + + StringBuilder source; + source.reserveCapacity(length); + + size_t i = 0; + for ( ; i < length && !m_previousSource.isEmpty(); ++i) { + source.append(*m_previousSource); + m_previousSource.advance(); + } + for ( ; i < length; ++i) { + ASSERT(!m_currentSource.isEmpty()); + source.append(*m_currentSource); + m_currentSource.advance(); + } + + m_cachedSourceForToken = source.toString(); + return m_cachedSourceForToken; +} + +} diff --git a/Source/WebCore/html/parser/HTMLSourceTracker.h b/Source/WebCore/html/parser/HTMLSourceTracker.h new file mode 100644 index 000000000..95d6d33b5 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLSourceTracker.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2010 Adam Barth. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 HTMLSourceTracker_h +#define HTMLSourceTracker_h + +#include "HTMLInputStream.h" +#include "HTMLToken.h" +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +class HTMLTokenizer; + +class HTMLSourceTracker { + WTF_MAKE_NONCOPYABLE(HTMLSourceTracker); +public: + HTMLSourceTracker(); + + // FIXME: Once we move "end" into HTMLTokenizer, rename "start" to + // something that makes it obvious that this method can be called multiple + // times. + void start(const HTMLInputStream&, HTMLTokenizer*, HTMLToken&); + void end(const HTMLInputStream&, HTMLTokenizer*, HTMLToken&); + + String sourceForToken(const HTMLToken&); + +private: + SegmentedString m_previousSource; + SegmentedString m_currentSource; + + String m_cachedSourceForToken; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLToken.h b/Source/WebCore/html/parser/HTMLToken.h new file mode 100644 index 000000000..c17828434 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLToken.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLToken_h +#define HTMLToken_h + +#include "MarkupTokenBase.h" + +namespace WebCore { + +class HTMLTokenTypes { +public: + enum Type { + Uninitialized, + DOCTYPE, + StartTag, + EndTag, + Comment, + Character, + EndOfFile, + }; + + class DoctypeData : public DoctypeDataBase { + WTF_MAKE_NONCOPYABLE(DoctypeData); + public: + DoctypeData() + : m_forceQuirks(false) + { + } + + bool m_forceQuirks; + }; +}; + +class HTMLToken : public MarkupTokenBase<HTMLTokenTypes, HTMLTokenTypes::DoctypeData> { +public: + void appendToName(UChar character) + { + ASSERT(m_type == HTMLTokenTypes::StartTag || m_type == HTMLTokenTypes::EndTag || m_type == HTMLTokenTypes::DOCTYPE); + MarkupTokenBase<HTMLTokenTypes, HTMLTokenTypes::DoctypeData>::appendToName(character); + } + + const DataVector& name() const + { + ASSERT(m_type == HTMLTokenTypes::StartTag || m_type == HTMLTokenTypes::EndTag || m_type == HTMLTokenTypes::DOCTYPE); + return MarkupTokenBase<HTMLTokenTypes, HTMLTokenTypes::DoctypeData>::name(); + } + + bool forceQuirks() const + { + ASSERT(m_type == HTMLTokenTypes::DOCTYPE); + return m_doctypeData->m_forceQuirks; + } + + void setForceQuirks() + { + ASSERT(m_type == HTMLTokenTypes::DOCTYPE); + m_doctypeData->m_forceQuirks = true; + } +}; + +class AtomicHTMLToken : public AtomicMarkupTokenBase<HTMLToken> { + WTF_MAKE_NONCOPYABLE(AtomicHTMLToken); +public: + AtomicHTMLToken(HTMLToken& token) : AtomicMarkupTokenBase<HTMLToken>(&token) { } + + AtomicHTMLToken(HTMLTokenTypes::Type type, AtomicString name, PassOwnPtr<NamedNodeMap> attributes = nullptr) + : AtomicMarkupTokenBase<HTMLToken>(type, name, attributes) + { + } + + bool forceQuirks() const + { + ASSERT(m_type == HTMLTokenTypes::DOCTYPE); + return m_doctypeData->m_forceQuirks; + } +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLTokenizer.cpp b/Source/WebCore/html/parser/HTMLTokenizer.cpp new file mode 100644 index 000000000..efe0d545f --- /dev/null +++ b/Source/WebCore/html/parser/HTMLTokenizer.cpp @@ -0,0 +1,1634 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLTokenizer.h" + +#include "HTMLEntityParser.h" +#include "HTMLToken.h" +#include "HTMLTreeBuilder.h" +#include "HTMLNames.h" +#include "MarkupTokenizerInlineMethods.h" +#include "NotImplemented.h" +#include <wtf/ASCIICType.h> +#include <wtf/CurrentTime.h> +#include <wtf/UnusedParam.h> +#include <wtf/text/AtomicString.h> +#include <wtf/text/CString.h> +#include <wtf/unicode/Unicode.h> + +using namespace WTF; + +namespace WebCore { + +using namespace HTMLNames; + +// This has to go in a .cpp file, as the linker doesn't like it being included more than once. +// We don't have an HTMLToken.cpp though, so this is the next best place. +template<> +QualifiedName AtomicMarkupTokenBase<HTMLToken>::nameForAttribute(const AttributeBase& attribute) const +{ + return QualifiedName(nullAtom, AtomicString(attribute.m_name.data(), attribute.m_name.size()), nullAtom); +} + +template<> +bool AtomicMarkupTokenBase<HTMLToken>::usesName() const +{ + return m_type == HTMLTokenTypes::StartTag || m_type == HTMLTokenTypes::EndTag || m_type == HTMLTokenTypes::DOCTYPE; +} + +template<> +bool AtomicMarkupTokenBase<HTMLToken>::usesAttributes() const +{ + return m_type == HTMLTokenTypes::StartTag || m_type == HTMLTokenTypes::EndTag; +} + +namespace { + +inline UChar toLowerCase(UChar cc) +{ + ASSERT(isASCIIUpper(cc)); + const int lowerCaseOffset = 0x20; + return cc + lowerCaseOffset; +} + +inline bool vectorEqualsString(const Vector<UChar, 32>& vector, const String& string) +{ + if (vector.size() != string.length()) + return false; + const UChar* stringData = string.characters(); + const UChar* vectorData = vector.data(); + // FIXME: Is there a higher-level function we should be calling here? + return !memcmp(stringData, vectorData, vector.size() * sizeof(UChar)); +} + +inline bool isEndTagBufferingState(HTMLTokenizerState::State state) +{ + switch (state) { + case HTMLTokenizerState::RCDATAEndTagOpenState: + case HTMLTokenizerState::RCDATAEndTagNameState: + case HTMLTokenizerState::RAWTEXTEndTagOpenState: + case HTMLTokenizerState::RAWTEXTEndTagNameState: + case HTMLTokenizerState::ScriptDataEndTagOpenState: + case HTMLTokenizerState::ScriptDataEndTagNameState: + case HTMLTokenizerState::ScriptDataEscapedEndTagOpenState: + case HTMLTokenizerState::ScriptDataEscapedEndTagNameState: + return true; + default: + return false; + } +} + +} + +#define HTML_BEGIN_STATE(stateName) BEGIN_STATE(HTMLTokenizerState, stateName) +#define HTML_RECONSUME_IN(stateName) RECONSUME_IN(HTMLTokenizerState, stateName) +#define HTML_ADVANCE_TO(stateName) ADVANCE_TO(HTMLTokenizerState, stateName) +#define HTML_SWITCH_TO(stateName) SWITCH_TO(HTMLTokenizerState, stateName) + +HTMLTokenizer::HTMLTokenizer(bool usePreHTML5ParserQuirks) + : m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks) +{ + reset(); +} + +HTMLTokenizer::~HTMLTokenizer() +{ +} + +template<> +inline bool MarkupTokenizerBase<HTMLToken, HTMLTokenizerState>::shouldSkipNullCharacters() const +{ + return !m_forceNullCharacterReplacement + && (m_state == HTMLTokenizerState::DataState + || m_state == HTMLTokenizerState::RCDATAState + || m_state == HTMLTokenizerState::RAWTEXTState); +} + + +void HTMLTokenizer::reset() +{ + m_state = HTMLTokenizerState::DataState; + m_token = 0; + m_lineNumber = 0; + m_forceNullCharacterReplacement = false; + m_shouldAllowCDATA = false; + m_additionalAllowedCharacter = '\0'; +} + +inline bool HTMLTokenizer::processEntity(SegmentedString& source) +{ + bool notEnoughCharacters = false; + StringBuilder decodedEntity; + bool success = consumeHTMLEntity(source, decodedEntity, notEnoughCharacters); + if (notEnoughCharacters) + return false; + if (!success) { + ASSERT(decodedEntity.isEmpty()); + bufferCharacter('&'); + } else { + for (unsigned i = 0; i < decodedEntity.length(); ++i) + bufferCharacter(decodedEntity[i]); + } + return true; +} + +bool HTMLTokenizer::flushBufferedEndTag(SegmentedString& source) +{ + ASSERT(m_token->type() == HTMLTokenTypes::Character || m_token->type() == HTMLTokenTypes::Uninitialized); + source.advance(m_lineNumber); + if (m_token->type() == HTMLTokenTypes::Character) + return true; + m_token->beginEndTag(m_bufferedEndTagName); + m_bufferedEndTagName.clear(); + m_temporaryBuffer.clear(); + return false; +} + +#define FLUSH_AND_ADVANCE_TO(stateName) \ + do { \ + m_state = HTMLTokenizerState::stateName; \ + if (flushBufferedEndTag(source)) \ + return true; \ + if (source.isEmpty() \ + || !m_inputStreamPreprocessor.peek(source, m_lineNumber)) \ + return haveBufferedCharacterToken(); \ + cc = m_inputStreamPreprocessor.nextInputCharacter(); \ + goto stateName; \ + } while (false) + +bool HTMLTokenizer::flushEmitAndResumeIn(SegmentedString& source, HTMLTokenizerState::State state) +{ + m_state = state; + flushBufferedEndTag(source); + return true; +} + +bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) +{ + // If we have a token in progress, then we're supposed to be called back + // with the same token so we can finish it. + ASSERT(!m_token || m_token == &token || token.type() == HTMLTokenTypes::Uninitialized); + m_token = &token; + + if (!m_bufferedEndTagName.isEmpty() && !isEndTagBufferingState(m_state)) { + // FIXME: This should call flushBufferedEndTag(). + // We started an end tag during our last iteration. + m_token->beginEndTag(m_bufferedEndTagName); + m_bufferedEndTagName.clear(); + m_temporaryBuffer.clear(); + if (m_state == HTMLTokenizerState::DataState) { + // We're back in the data state, so we must be done with the tag. + return true; + } + } + + if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source, m_lineNumber)) + return haveBufferedCharacterToken(); + UChar cc = m_inputStreamPreprocessor.nextInputCharacter(); + + // Source: http://www.whatwg.org/specs/web-apps/current-work/#tokenisation0 + switch (m_state) { + HTML_BEGIN_STATE(DataState) { + if (cc == '&') + HTML_ADVANCE_TO(CharacterReferenceInDataState); + else if (cc == '<') { + if (m_token->type() == HTMLTokenTypes::Character) { + // We have a bunch of character tokens queued up that we + // are emitting lazily here. + return true; + } + HTML_ADVANCE_TO(TagOpenState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitEndOfFile(source); + else { + bufferCharacter(cc); + HTML_ADVANCE_TO(DataState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CharacterReferenceInDataState) { + if (!processEntity(source)) + return haveBufferedCharacterToken(); + HTML_SWITCH_TO(DataState); + } + END_STATE() + + HTML_BEGIN_STATE(RCDATAState) { + if (cc == '&') + HTML_ADVANCE_TO(CharacterReferenceInRCDATAState); + else if (cc == '<') + HTML_ADVANCE_TO(RCDATALessThanSignState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitEndOfFile(source); + else { + bufferCharacter(cc); + HTML_ADVANCE_TO(RCDATAState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CharacterReferenceInRCDATAState) { + if (!processEntity(source)) + return haveBufferedCharacterToken(); + HTML_SWITCH_TO(RCDATAState); + } + END_STATE() + + HTML_BEGIN_STATE(RAWTEXTState) { + if (cc == '<') + HTML_ADVANCE_TO(RAWTEXTLessThanSignState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitEndOfFile(source); + else { + bufferCharacter(cc); + HTML_ADVANCE_TO(RAWTEXTState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataState) { + if (cc == '<') + HTML_ADVANCE_TO(ScriptDataLessThanSignState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitEndOfFile(source); + else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataState); + } + } + END_STATE() + + HTML_BEGIN_STATE(PLAINTEXTState) { + if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitEndOfFile(source); + else + bufferCharacter(cc); + HTML_ADVANCE_TO(PLAINTEXTState); + } + END_STATE() + + HTML_BEGIN_STATE(TagOpenState) { + if (cc == '!') + HTML_ADVANCE_TO(MarkupDeclarationOpenState); + else if (cc == '/') + HTML_ADVANCE_TO(EndTagOpenState); + else if (isASCIIUpper(cc)) { + m_token->beginStartTag(toLowerCase(cc)); + HTML_ADVANCE_TO(TagNameState); + } else if (isASCIILower(cc)) { + m_token->beginStartTag(cc); + HTML_ADVANCE_TO(TagNameState); + } else if (cc == '?') { + parseError(); + // The spec consumes the current character before switching + // to the bogus comment state, but it's easier to implement + // if we reconsume the current character. + HTML_RECONSUME_IN(BogusCommentState); + } else { + parseError(); + bufferCharacter('<'); + HTML_RECONSUME_IN(DataState); + } + } + END_STATE() + + HTML_BEGIN_STATE(EndTagOpenState) { + if (isASCIIUpper(cc)) { + m_token->beginEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(TagNameState); + } else if (isASCIILower(cc)) { + m_token->beginEndTag(cc); + HTML_ADVANCE_TO(TagNameState); + } else if (cc == '>') { + parseError(); + HTML_ADVANCE_TO(DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + bufferCharacter('<'); + bufferCharacter('/'); + HTML_RECONSUME_IN(DataState); + } else { + parseError(); + HTML_RECONSUME_IN(BogusCommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(TagNameState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeAttributeNameState); + else if (cc == '/') + HTML_ADVANCE_TO(SelfClosingStartTagState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (m_usePreHTML5ParserQuirks && cc == '<') + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + else if (isASCIIUpper(cc)) { + m_token->appendToName(toLowerCase(cc)); + HTML_ADVANCE_TO(TagNameState); + } if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + m_token->appendToName(cc); + HTML_ADVANCE_TO(TagNameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(RCDATALessThanSignState) { + if (cc == '/') { + m_temporaryBuffer.clear(); + ASSERT(m_bufferedEndTagName.isEmpty()); + HTML_ADVANCE_TO(RCDATAEndTagOpenState); + } else { + bufferCharacter('<'); + HTML_RECONSUME_IN(RCDATAState); + } + } + END_STATE() + + HTML_BEGIN_STATE(RCDATAEndTagOpenState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(RCDATAEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(RCDATAEndTagNameState); + } else { + bufferCharacter('<'); + bufferCharacter('/'); + HTML_RECONSUME_IN(RCDATAState); + } + } + END_STATE() + + HTML_BEGIN_STATE(RCDATAEndTagNameState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(RCDATAEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(RCDATAEndTagNameState); + } else { + if (isTokenizerWhitespace(cc)) { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState); + } + } else if (cc == '/') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState); + } + } else if (cc == '>') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + return flushEmitAndResumeIn(source, HTMLTokenizerState::DataState); + } + } + bufferCharacter('<'); + bufferCharacter('/'); + m_token->appendToCharacter(m_temporaryBuffer); + m_bufferedEndTagName.clear(); + m_temporaryBuffer.clear(); + HTML_RECONSUME_IN(RCDATAState); + } + } + END_STATE() + + HTML_BEGIN_STATE(RAWTEXTLessThanSignState) { + if (cc == '/') { + m_temporaryBuffer.clear(); + ASSERT(m_bufferedEndTagName.isEmpty()); + HTML_ADVANCE_TO(RAWTEXTEndTagOpenState); + } else { + bufferCharacter('<'); + HTML_RECONSUME_IN(RAWTEXTState); + } + } + END_STATE() + + HTML_BEGIN_STATE(RAWTEXTEndTagOpenState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(RAWTEXTEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(RAWTEXTEndTagNameState); + } else { + bufferCharacter('<'); + bufferCharacter('/'); + HTML_RECONSUME_IN(RAWTEXTState); + } + } + END_STATE() + + HTML_BEGIN_STATE(RAWTEXTEndTagNameState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(RAWTEXTEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(RAWTEXTEndTagNameState); + } else { + if (isTokenizerWhitespace(cc)) { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState); + } + } else if (cc == '/') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState); + } + } else if (cc == '>') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + return flushEmitAndResumeIn(source, HTMLTokenizerState::DataState); + } + } + bufferCharacter('<'); + bufferCharacter('/'); + m_token->appendToCharacter(m_temporaryBuffer); + m_bufferedEndTagName.clear(); + m_temporaryBuffer.clear(); + HTML_RECONSUME_IN(RAWTEXTState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataLessThanSignState) { + if (cc == '/') { + m_temporaryBuffer.clear(); + ASSERT(m_bufferedEndTagName.isEmpty()); + HTML_ADVANCE_TO(ScriptDataEndTagOpenState); + } else if (cc == '!') { + bufferCharacter('<'); + bufferCharacter('!'); + HTML_ADVANCE_TO(ScriptDataEscapeStartState); + } else { + bufferCharacter('<'); + HTML_RECONSUME_IN(ScriptDataState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEndTagOpenState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(ScriptDataEndTagNameState); + } else { + bufferCharacter('<'); + bufferCharacter('/'); + HTML_RECONSUME_IN(ScriptDataState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEndTagNameState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(ScriptDataEndTagNameState); + } else { + if (isTokenizerWhitespace(cc)) { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState); + } + } else if (cc == '/') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState); + } + } else if (cc == '>') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + return flushEmitAndResumeIn(source, HTMLTokenizerState::DataState); + } + } + bufferCharacter('<'); + bufferCharacter('/'); + m_token->appendToCharacter(m_temporaryBuffer); + m_bufferedEndTagName.clear(); + m_temporaryBuffer.clear(); + HTML_RECONSUME_IN(ScriptDataState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapeStartState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapeStartDashState); + } else + HTML_RECONSUME_IN(ScriptDataState); + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapeStartDashState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedDashDashState); + } else + HTML_RECONSUME_IN(ScriptDataState); + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapedState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedDashState); + } else if (cc == '<') + HTML_ADVANCE_TO(ScriptDataEscapedLessThanSignState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapedDashState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedDashDashState); + } else if (cc == '<') + HTML_ADVANCE_TO(ScriptDataEscapedLessThanSignState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapedDashDashState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedDashDashState); + } else if (cc == '<') + HTML_ADVANCE_TO(ScriptDataEscapedLessThanSignState); + else if (cc == '>') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataState); + } if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapedLessThanSignState) { + if (cc == '/') { + m_temporaryBuffer.clear(); + ASSERT(m_bufferedEndTagName.isEmpty()); + HTML_ADVANCE_TO(ScriptDataEscapedEndTagOpenState); + } else if (isASCIIUpper(cc)) { + bufferCharacter('<'); + bufferCharacter(cc); + m_temporaryBuffer.clear(); + m_temporaryBuffer.append(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState); + } else if (isASCIILower(cc)) { + bufferCharacter('<'); + bufferCharacter(cc); + m_temporaryBuffer.clear(); + m_temporaryBuffer.append(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState); + } else { + bufferCharacter('<'); + HTML_RECONSUME_IN(ScriptDataEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapedEndTagOpenState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState); + } else { + bufferCharacter('<'); + bufferCharacter('/'); + HTML_RECONSUME_IN(ScriptDataEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataEscapedEndTagNameState) { + if (isASCIIUpper(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState); + } else if (isASCIILower(cc)) { + m_temporaryBuffer.append(cc); + addToPossibleEndTag(cc); + HTML_ADVANCE_TO(ScriptDataEscapedEndTagNameState); + } else { + if (isTokenizerWhitespace(cc)) { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(BeforeAttributeNameState); + } + } else if (cc == '/') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + FLUSH_AND_ADVANCE_TO(SelfClosingStartTagState); + } + } else if (cc == '>') { + if (isAppropriateEndTag()) { + m_temporaryBuffer.append(cc); + return flushEmitAndResumeIn(source, HTMLTokenizerState::DataState); + } + } + bufferCharacter('<'); + bufferCharacter('/'); + m_token->appendToCharacter(m_temporaryBuffer); + m_bufferedEndTagName.clear(); + m_temporaryBuffer.clear(); + HTML_RECONSUME_IN(ScriptDataEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataDoubleEscapeStartState) { + if (isTokenizerWhitespace(cc) || cc == '/' || cc == '>') { + bufferCharacter(cc); + if (temporaryBufferIs(scriptTag.localName())) + HTML_ADVANCE_TO(ScriptDataDoubleEscapedState); + else + HTML_ADVANCE_TO(ScriptDataEscapedState); + } else if (isASCIIUpper(cc)) { + bufferCharacter(cc); + m_temporaryBuffer.append(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState); + } else if (isASCIILower(cc)) { + bufferCharacter(cc); + m_temporaryBuffer.append(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeStartState); + } else + HTML_RECONSUME_IN(ScriptDataEscapedState); + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataDoubleEscapedState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedDashState); + } else if (cc == '<') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedLessThanSignState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataDoubleEscapedDashState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedDashDashState); + } else if (cc == '<') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedLessThanSignState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataDoubleEscapedDashDashState) { + if (cc == '-') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedDashDashState); + } else if (cc == '<') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedLessThanSignState); + } else if (cc == '>') { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + bufferCharacter(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataDoubleEscapedLessThanSignState) { + if (cc == '/') { + bufferCharacter(cc); + m_temporaryBuffer.clear(); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeEndState); + } else + HTML_RECONSUME_IN(ScriptDataDoubleEscapedState); + } + END_STATE() + + HTML_BEGIN_STATE(ScriptDataDoubleEscapeEndState) { + if (isTokenizerWhitespace(cc) || cc == '/' || cc == '>') { + bufferCharacter(cc); + if (temporaryBufferIs(scriptTag.localName())) + HTML_ADVANCE_TO(ScriptDataEscapedState); + else + HTML_ADVANCE_TO(ScriptDataDoubleEscapedState); + } else if (isASCIIUpper(cc)) { + bufferCharacter(cc); + m_temporaryBuffer.append(toLowerCase(cc)); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeEndState); + } else if (isASCIILower(cc)) { + bufferCharacter(cc); + m_temporaryBuffer.append(cc); + HTML_ADVANCE_TO(ScriptDataDoubleEscapeEndState); + } else + HTML_RECONSUME_IN(ScriptDataDoubleEscapedState); + } + END_STATE() + + HTML_BEGIN_STATE(BeforeAttributeNameState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeAttributeNameState); + else if (cc == '/') + HTML_ADVANCE_TO(SelfClosingStartTagState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (m_usePreHTML5ParserQuirks && cc == '<') + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + else if (isASCIIUpper(cc)) { + m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); + m_token->appendToAttributeName(toLowerCase(cc)); + HTML_ADVANCE_TO(AttributeNameState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + if (cc == '"' || cc == '\'' || cc == '<' || cc == '=') + parseError(); + m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); + m_token->appendToAttributeName(cc); + HTML_ADVANCE_TO(AttributeNameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AttributeNameState) { + if (isTokenizerWhitespace(cc)) { + m_token->endAttributeName(source.numberOfCharactersConsumed()); + HTML_ADVANCE_TO(AfterAttributeNameState); + } else if (cc == '/') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); + HTML_ADVANCE_TO(SelfClosingStartTagState); + } else if (cc == '=') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); + HTML_ADVANCE_TO(BeforeAttributeValueState); + } else if (cc == '>') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (m_usePreHTML5ParserQuirks && cc == '<') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else if (isASCIIUpper(cc)) { + m_token->appendToAttributeName(toLowerCase(cc)); + HTML_ADVANCE_TO(AttributeNameState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->endAttributeName(source.numberOfCharactersConsumed()); + HTML_RECONSUME_IN(DataState); + } else { + if (cc == '"' || cc == '\'' || cc == '<' || cc == '=') + parseError(); + m_token->appendToAttributeName(cc); + HTML_ADVANCE_TO(AttributeNameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AfterAttributeNameState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(AfterAttributeNameState); + else if (cc == '/') + HTML_ADVANCE_TO(SelfClosingStartTagState); + else if (cc == '=') + HTML_ADVANCE_TO(BeforeAttributeValueState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (m_usePreHTML5ParserQuirks && cc == '<') + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + else if (isASCIIUpper(cc)) { + m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); + m_token->appendToAttributeName(toLowerCase(cc)); + HTML_ADVANCE_TO(AttributeNameState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + if (cc == '"' || cc == '\'' || cc == '<') + parseError(); + m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); + m_token->appendToAttributeName(cc); + HTML_ADVANCE_TO(AttributeNameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BeforeAttributeValueState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeAttributeValueState); + else if (cc == '"') { + m_token->beginAttributeValue(source.numberOfCharactersConsumed() + 1); + HTML_ADVANCE_TO(AttributeValueDoubleQuotedState); + } else if (cc == '&') { + m_token->beginAttributeValue(source.numberOfCharactersConsumed()); + HTML_RECONSUME_IN(AttributeValueUnquotedState); + } else if (cc == '\'') { + m_token->beginAttributeValue(source.numberOfCharactersConsumed() + 1); + HTML_ADVANCE_TO(AttributeValueSingleQuotedState); + } else if (cc == '>') { + parseError(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + if (cc == '<' || cc == '=' || cc == '`') + parseError(); + m_token->beginAttributeValue(source.numberOfCharactersConsumed()); + m_token->appendToAttributeValue(cc); + HTML_ADVANCE_TO(AttributeValueUnquotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AttributeValueDoubleQuotedState) { + if (cc == '"') { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + HTML_ADVANCE_TO(AfterAttributeValueQuotedState); + } else if (cc == '&') { + m_additionalAllowedCharacter = '"'; + HTML_ADVANCE_TO(CharacterReferenceInAttributeValueState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + HTML_RECONSUME_IN(DataState); + } else { + m_token->appendToAttributeValue(cc); + HTML_ADVANCE_TO(AttributeValueDoubleQuotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AttributeValueSingleQuotedState) { + if (cc == '\'') { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + HTML_ADVANCE_TO(AfterAttributeValueQuotedState); + } else if (cc == '&') { + m_additionalAllowedCharacter = '\''; + HTML_ADVANCE_TO(CharacterReferenceInAttributeValueState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + HTML_RECONSUME_IN(DataState); + } else { + m_token->appendToAttributeValue(cc); + HTML_ADVANCE_TO(AttributeValueSingleQuotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AttributeValueUnquotedState) { + if (isTokenizerWhitespace(cc)) { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + HTML_ADVANCE_TO(BeforeAttributeNameState); + } else if (cc == '&') { + m_additionalAllowedCharacter = '>'; + HTML_ADVANCE_TO(CharacterReferenceInAttributeValueState); + } else if (cc == '>') { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->endAttributeValue(source.numberOfCharactersConsumed()); + HTML_RECONSUME_IN(DataState); + } else { + if (cc == '"' || cc == '\'' || cc == '<' || cc == '=' || cc == '`') + parseError(); + m_token->appendToAttributeValue(cc); + HTML_ADVANCE_TO(AttributeValueUnquotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CharacterReferenceInAttributeValueState) { + bool notEnoughCharacters = false; + StringBuilder decodedEntity; + bool success = consumeHTMLEntity(source, decodedEntity, notEnoughCharacters, m_additionalAllowedCharacter); + if (notEnoughCharacters) + return haveBufferedCharacterToken(); + if (!success) { + ASSERT(decodedEntity.isEmpty()); + m_token->appendToAttributeValue('&'); + } else { + for (unsigned i = 0; i < decodedEntity.length(); ++i) + m_token->appendToAttributeValue(decodedEntity[i]); + } + // We're supposed to switch back to the attribute value state that + // we were in when we were switched into this state. Rather than + // keeping track of this explictly, we observe that the previous + // state can be determined by m_additionalAllowedCharacter. + if (m_additionalAllowedCharacter == '"') + HTML_SWITCH_TO(AttributeValueDoubleQuotedState); + else if (m_additionalAllowedCharacter == '\'') + HTML_SWITCH_TO(AttributeValueSingleQuotedState); + else if (m_additionalAllowedCharacter == '>') + HTML_SWITCH_TO(AttributeValueUnquotedState); + else + ASSERT_NOT_REACHED(); + } + END_STATE() + + HTML_BEGIN_STATE(AfterAttributeValueQuotedState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeAttributeNameState); + else if (cc == '/') + HTML_ADVANCE_TO(SelfClosingStartTagState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (m_usePreHTML5ParserQuirks && cc == '<') + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + parseError(); + HTML_RECONSUME_IN(BeforeAttributeNameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(SelfClosingStartTagState) { + if (cc == '>') { + m_token->setSelfClosing(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + HTML_RECONSUME_IN(DataState); + } else { + parseError(); + HTML_RECONSUME_IN(BeforeAttributeNameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BogusCommentState) { + m_token->beginComment(); + HTML_RECONSUME_IN(ContinueBogusCommentState); + } + END_STATE() + + HTML_BEGIN_STATE(ContinueBogusCommentState) { + if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + else { + m_token->appendToComment(cc); + HTML_ADVANCE_TO(ContinueBogusCommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(MarkupDeclarationOpenState) { + DEFINE_STATIC_LOCAL(String, dashDashString, ("--")); + DEFINE_STATIC_LOCAL(String, doctypeString, ("doctype")); + DEFINE_STATIC_LOCAL(String, cdataString, ("[CDATA[")); + if (cc == '-') { + SegmentedString::LookAheadResult result = source.lookAhead(dashDashString); + if (result == SegmentedString::DidMatch) { + source.advanceAndASSERT('-'); + source.advanceAndASSERT('-'); + m_token->beginComment(); + HTML_SWITCH_TO(CommentStartState); + } else if (result == SegmentedString::NotEnoughCharacters) + return haveBufferedCharacterToken(); + } else if (cc == 'D' || cc == 'd') { + SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(doctypeString); + if (result == SegmentedString::DidMatch) { + advanceStringAndASSERTIgnoringCase(source, "doctype"); + HTML_SWITCH_TO(DOCTYPEState); + } else if (result == SegmentedString::NotEnoughCharacters) + return haveBufferedCharacterToken(); + } else if (cc == '[' && shouldAllowCDATA()) { + SegmentedString::LookAheadResult result = source.lookAhead(cdataString); + if (result == SegmentedString::DidMatch) { + advanceStringAndASSERT(source, "[CDATA["); + HTML_SWITCH_TO(CDATASectionState); + } else if (result == SegmentedString::NotEnoughCharacters) + return haveBufferedCharacterToken(); + } + parseError(); + HTML_RECONSUME_IN(BogusCommentState); + } + END_STATE() + + HTML_BEGIN_STATE(CommentStartState) { + if (cc == '-') + HTML_ADVANCE_TO(CommentStartDashState); + else if (cc == '>') { + parseError(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToComment(cc); + HTML_ADVANCE_TO(CommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CommentStartDashState) { + if (cc == '-') + HTML_ADVANCE_TO(CommentEndState); + else if (cc == '>') { + parseError(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToComment('-'); + m_token->appendToComment(cc); + HTML_ADVANCE_TO(CommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CommentState) { + if (cc == '-') + HTML_ADVANCE_TO(CommentEndDashState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToComment(cc); + HTML_ADVANCE_TO(CommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CommentEndDashState) { + if (cc == '-') + HTML_ADVANCE_TO(CommentEndState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToComment('-'); + m_token->appendToComment(cc); + HTML_ADVANCE_TO(CommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CommentEndState) { + if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == '!') { + parseError(); + HTML_ADVANCE_TO(CommentEndBangState); + } else if (cc == '-') { + parseError(); + m_token->appendToComment('-'); + HTML_ADVANCE_TO(CommentEndState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->appendToComment('-'); + m_token->appendToComment('-'); + m_token->appendToComment(cc); + HTML_ADVANCE_TO(CommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CommentEndBangState) { + if (cc == '-') { + m_token->appendToComment('-'); + m_token->appendToComment('-'); + m_token->appendToComment('!'); + HTML_ADVANCE_TO(CommentEndDashState); + } else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToComment('-'); + m_token->appendToComment('-'); + m_token->appendToComment('!'); + m_token->appendToComment(cc); + HTML_ADVANCE_TO(CommentState); + } + } + END_STATE() + + HTML_BEGIN_STATE(DOCTYPEState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeDOCTYPENameState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->beginDOCTYPE(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + HTML_RECONSUME_IN(BeforeDOCTYPENameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BeforeDOCTYPENameState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeDOCTYPENameState); + else if (isASCIIUpper(cc)) { + m_token->beginDOCTYPE(toLowerCase(cc)); + HTML_ADVANCE_TO(DOCTYPENameState); + } else if (cc == '>') { + parseError(); + m_token->beginDOCTYPE(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->beginDOCTYPE(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->beginDOCTYPE(cc); + HTML_ADVANCE_TO(DOCTYPENameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(DOCTYPENameState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(AfterDOCTYPENameState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (isASCIIUpper(cc)) { + m_token->appendToName(toLowerCase(cc)); + HTML_ADVANCE_TO(DOCTYPENameState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToName(cc); + HTML_ADVANCE_TO(DOCTYPENameState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AfterDOCTYPENameState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(AfterDOCTYPENameState); + if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + DEFINE_STATIC_LOCAL(String, publicString, ("public")); + DEFINE_STATIC_LOCAL(String, systemString, ("system")); + if (cc == 'P' || cc == 'p') { + SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(publicString); + if (result == SegmentedString::DidMatch) { + advanceStringAndASSERTIgnoringCase(source, "public"); + HTML_SWITCH_TO(AfterDOCTYPEPublicKeywordState); + } else if (result == SegmentedString::NotEnoughCharacters) + return haveBufferedCharacterToken(); + } else if (cc == 'S' || cc == 's') { + SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(systemString); + if (result == SegmentedString::DidMatch) { + advanceStringAndASSERTIgnoringCase(source, "system"); + HTML_SWITCH_TO(AfterDOCTYPESystemKeywordState); + } else if (result == SegmentedString::NotEnoughCharacters) + return haveBufferedCharacterToken(); + } + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AfterDOCTYPEPublicKeywordState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeDOCTYPEPublicIdentifierState); + else if (cc == '"') { + parseError(); + m_token->setPublicIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState); + } else if (cc == '\'') { + parseError(); + m_token->setPublicIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState); + } else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BeforeDOCTYPEPublicIdentifierState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeDOCTYPEPublicIdentifierState); + else if (cc == '"') { + m_token->setPublicIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState); + } else if (cc == '\'') { + m_token->setPublicIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState); + } else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(DOCTYPEPublicIdentifierDoubleQuotedState) { + if (cc == '"') + HTML_ADVANCE_TO(AfterDOCTYPEPublicIdentifierState); + else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToPublicIdentifier(cc); + HTML_ADVANCE_TO(DOCTYPEPublicIdentifierDoubleQuotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(DOCTYPEPublicIdentifierSingleQuotedState) { + if (cc == '\'') + HTML_ADVANCE_TO(AfterDOCTYPEPublicIdentifierState); + else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToPublicIdentifier(cc); + HTML_ADVANCE_TO(DOCTYPEPublicIdentifierSingleQuotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AfterDOCTYPEPublicIdentifierState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BetweenDOCTYPEPublicAndSystemIdentifiersState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == '"') { + parseError(); + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState); + } else if (cc == '\'') { + parseError(); + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BetweenDOCTYPEPublicAndSystemIdentifiersState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BetweenDOCTYPEPublicAndSystemIdentifiersState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == '"') { + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState); + } else if (cc == '\'') { + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AfterDOCTYPESystemKeywordState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeDOCTYPESystemIdentifierState); + else if (cc == '"') { + parseError(); + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState); + } else if (cc == '\'') { + parseError(); + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState); + } else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BeforeDOCTYPESystemIdentifierState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(BeforeDOCTYPESystemIdentifierState); + if (cc == '"') { + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState); + } else if (cc == '\'') { + m_token->setSystemIdentifierToEmptyString(); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState); + } else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + m_token->setForceQuirks(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(DOCTYPESystemIdentifierDoubleQuotedState) { + if (cc == '"') + HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState); + else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToSystemIdentifier(cc); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierDoubleQuotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(DOCTYPESystemIdentifierSingleQuotedState) { + if (cc == '\'') + HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState); + else if (cc == '>') { + parseError(); + m_token->setForceQuirks(); + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + m_token->appendToSystemIdentifier(cc); + HTML_ADVANCE_TO(DOCTYPESystemIdentifierSingleQuotedState); + } + } + END_STATE() + + HTML_BEGIN_STATE(AfterDOCTYPESystemIdentifierState) { + if (isTokenizerWhitespace(cc)) + HTML_ADVANCE_TO(AfterDOCTYPESystemIdentifierState); + else if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) { + parseError(); + m_token->setForceQuirks(); + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + } else { + parseError(); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + } + END_STATE() + + HTML_BEGIN_STATE(BogusDOCTYPEState) { + if (cc == '>') + return emitAndResumeIn(source, HTMLTokenizerState::DataState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitAndReconsumeIn(source, HTMLTokenizerState::DataState); + HTML_ADVANCE_TO(BogusDOCTYPEState); + } + END_STATE() + + HTML_BEGIN_STATE(CDATASectionState) { + if (cc == ']') + HTML_ADVANCE_TO(CDATASectionRightSquareBracketState); + else if (cc == InputStreamPreprocessor::endOfFileMarker) + HTML_RECONSUME_IN(DataState); + else { + bufferCharacter(cc); + HTML_ADVANCE_TO(CDATASectionState); + } + } + END_STATE() + + HTML_BEGIN_STATE(CDATASectionRightSquareBracketState) { + if (cc == ']') + HTML_ADVANCE_TO(CDATASectionDoubleRightSquareBracketState); + else { + bufferCharacter(']'); + HTML_RECONSUME_IN(CDATASectionState); + } + } + + HTML_BEGIN_STATE(CDATASectionDoubleRightSquareBracketState) { + if (cc == '>') + HTML_ADVANCE_TO(DataState); + else { + bufferCharacter(']'); + bufferCharacter(']'); + HTML_RECONSUME_IN(CDATASectionState); + } + } + END_STATE() + + } + + ASSERT_NOT_REACHED(); + return false; +} + +String HTMLTokenizer::bufferedCharacters() const +{ + // FIXME: Add an assert about m_state. + StringBuilder characters; + characters.reserveCapacity(numberOfBufferedCharacters()); + characters.append('<'); + characters.append('/'); + characters.append(m_temporaryBuffer.data(), m_temporaryBuffer.size()); + return characters.toString(); +} + +void HTMLTokenizer::updateStateFor(const AtomicString& tagName, Frame* frame) +{ + if (tagName == textareaTag || tagName == titleTag) + setState(HTMLTokenizerState::RCDATAState); + else if (tagName == plaintextTag) + setState(HTMLTokenizerState::PLAINTEXTState); + else if (tagName == scriptTag) + setState(HTMLTokenizerState::ScriptDataState); + else if (tagName == styleTag + || tagName == iframeTag + || tagName == xmpTag + || (tagName == noembedTag && HTMLTreeBuilder::pluginsEnabled(frame)) + || tagName == noframesTag + || (tagName == noscriptTag && HTMLTreeBuilder::scriptEnabled(frame))) + setState(HTMLTokenizerState::RAWTEXTState); +} + +inline bool HTMLTokenizer::temporaryBufferIs(const String& expectedString) +{ + return vectorEqualsString(m_temporaryBuffer, expectedString); +} + +inline void HTMLTokenizer::addToPossibleEndTag(UChar cc) +{ + ASSERT(isEndTagBufferingState(m_state)); + m_bufferedEndTagName.append(cc); +} + +inline bool HTMLTokenizer::isAppropriateEndTag() +{ + return m_bufferedEndTagName == m_appropriateEndTagName; +} + +inline void HTMLTokenizer::parseError() +{ + notImplemented(); +} + +} diff --git a/Source/WebCore/html/parser/HTMLTokenizer.h b/Source/WebCore/html/parser/HTMLTokenizer.h new file mode 100644 index 000000000..ff2d9ac95 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLTokenizer.h @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLTokenizer_h +#define HTMLTokenizer_h + +#include "HTMLToken.h" +#include "MarkupTokenizerBase.h" +#include "SegmentedString.h" + +namespace WebCore { + +class Frame; + +class HTMLTokenizerState { +public: + enum State { + DataState, + CharacterReferenceInDataState, + RCDATAState, + CharacterReferenceInRCDATAState, + RAWTEXTState, + ScriptDataState, + PLAINTEXTState, + TagOpenState, + EndTagOpenState, + TagNameState, + RCDATALessThanSignState, + RCDATAEndTagOpenState, + RCDATAEndTagNameState, + RAWTEXTLessThanSignState, + RAWTEXTEndTagOpenState, + RAWTEXTEndTagNameState, + ScriptDataLessThanSignState, + ScriptDataEndTagOpenState, + ScriptDataEndTagNameState, + ScriptDataEscapeStartState, + ScriptDataEscapeStartDashState, + ScriptDataEscapedState, + ScriptDataEscapedDashState, + ScriptDataEscapedDashDashState, + ScriptDataEscapedLessThanSignState, + ScriptDataEscapedEndTagOpenState, + ScriptDataEscapedEndTagNameState, + ScriptDataDoubleEscapeStartState, + ScriptDataDoubleEscapedState, + ScriptDataDoubleEscapedDashState, + ScriptDataDoubleEscapedDashDashState, + ScriptDataDoubleEscapedLessThanSignState, + ScriptDataDoubleEscapeEndState, + BeforeAttributeNameState, + AttributeNameState, + AfterAttributeNameState, + BeforeAttributeValueState, + AttributeValueDoubleQuotedState, + AttributeValueSingleQuotedState, + AttributeValueUnquotedState, + CharacterReferenceInAttributeValueState, + AfterAttributeValueQuotedState, + SelfClosingStartTagState, + BogusCommentState, + // The ContinueBogusCommentState is not in the HTML5 spec, but we use + // it internally to keep track of whether we've started the bogus + // comment token yet. + ContinueBogusCommentState, + MarkupDeclarationOpenState, + CommentStartState, + CommentStartDashState, + CommentState, + CommentEndDashState, + CommentEndState, + CommentEndBangState, + DOCTYPEState, + BeforeDOCTYPENameState, + DOCTYPENameState, + AfterDOCTYPENameState, + AfterDOCTYPEPublicKeywordState, + BeforeDOCTYPEPublicIdentifierState, + DOCTYPEPublicIdentifierDoubleQuotedState, + DOCTYPEPublicIdentifierSingleQuotedState, + AfterDOCTYPEPublicIdentifierState, + BetweenDOCTYPEPublicAndSystemIdentifiersState, + AfterDOCTYPESystemKeywordState, + BeforeDOCTYPESystemIdentifierState, + DOCTYPESystemIdentifierDoubleQuotedState, + DOCTYPESystemIdentifierSingleQuotedState, + AfterDOCTYPESystemIdentifierState, + BogusDOCTYPEState, + CDATASectionState, + // These CDATA states are not in the HTML5 spec, but we use them internally. + CDATASectionRightSquareBracketState, + CDATASectionDoubleRightSquareBracketState, + }; +}; + +class HTMLTokenizer : public MarkupTokenizerBase<HTMLToken, HTMLTokenizerState> { + WTF_MAKE_NONCOPYABLE(HTMLTokenizer); + WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<HTMLTokenizer> create(bool usePreHTML5ParserQuirks) { return adoptPtr(new HTMLTokenizer(usePreHTML5ParserQuirks)); } + ~HTMLTokenizer(); + + void reset(); + + // This function returns true if it emits a token. Otherwise, callers + // must provide the same (in progress) token on the next call (unless + // they call reset() first). + bool nextToken(SegmentedString&, HTMLToken&); + + // Returns a copy of any characters buffered internally by the tokenizer. + // The tokenizer buffers characters when searching for the </script> token + // that terminates a script element. + String bufferedCharacters() const; + + size_t numberOfBufferedCharacters() const + { + // Notice that we add 2 to the length of the m_temporaryBuffer to + // account for the "</" characters, which are effecitvely buffered in + // the tokenizer's state machine. + return m_temporaryBuffer.size() ? m_temporaryBuffer.size() + 2 : 0; + } + + // Updates the tokenizer's state according to the given tag name. This is + // an approximation of how the tree builder would update the tokenizer's + // state. This method is useful for approximating HTML tokenization. To + // get exactly the correct tokenization, you need the real tree builder. + // + // The main failures in the approximation are as follows: + // + // * The first set of character tokens emitted for a <pre> element might + // contain an extra leading newline. + // * The replacement of U+0000 with U+FFFD will not be sensitive to the + // tree builder's insertion mode. + // * CDATA sections in foreign content will be tokenized as bogus comments + // instead of as character tokens. + // + void updateStateFor(const AtomicString& tagName, Frame*); + + bool forceNullCharacterReplacement() const { return m_forceNullCharacterReplacement; } + void setForceNullCharacterReplacement(bool value) { m_forceNullCharacterReplacement = value; } + + bool shouldAllowCDATA() const { return m_shouldAllowCDATA; } + void setShouldAllowCDATA(bool value) { m_shouldAllowCDATA = value; } + +private: + explicit HTMLTokenizer(bool usePreHTML5ParserQuirks); + + inline bool processEntity(SegmentedString&); + + inline void parseError(); + + inline bool emitAndResumeIn(SegmentedString& source, HTMLTokenizerState::State state) + { + saveEndTagNameIfNeeded(); + return MarkupTokenizerBase<HTMLToken, HTMLTokenizerState>::emitAndResumeIn(source, state); + } + + inline bool emitAndReconsumeIn(SegmentedString& source, HTMLTokenizerState::State state) + { + saveEndTagNameIfNeeded(); + return MarkupTokenizerBase<HTMLToken, HTMLTokenizerState>::emitAndReconsumeIn(source, state); + } + + inline bool flushEmitAndResumeIn(SegmentedString&, HTMLTokenizerState::State); + + // Return whether we need to emit a character token before dealing with + // the buffered end tag. + inline bool flushBufferedEndTag(SegmentedString&); + inline bool temporaryBufferIs(const String&); + + // Sometimes we speculatively consume input characters and we don't + // know whether they represent end tags or RCDATA, etc. These + // functions help manage these state. + inline void addToPossibleEndTag(UChar cc); + + inline void saveEndTagNameIfNeeded() + { + ASSERT(m_token->type() != HTMLTokenTypes::Uninitialized); + if (m_token->type() == HTMLTokenTypes::StartTag) + m_appropriateEndTagName = m_token->name(); + } + inline bool isAppropriateEndTag(); + + Vector<UChar, 32> m_appropriateEndTagName; + + bool m_shouldAllowCDATA; + + // http://www.whatwg.org/specs/web-apps/current-work/#temporary-buffer + Vector<UChar, 32> m_temporaryBuffer; + + // We occationally want to emit both a character token and an end tag + // token (e.g., when lexing script). We buffer the name of the end tag + // token here so we remember it next time we re-enter the tokenizer. + Vector<UChar, 32> m_bufferedEndTagName; + + bool m_usePreHTML5ParserQuirks; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLTreeBuilder.cpp b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp new file mode 100644 index 000000000..09e0a8e10 --- /dev/null +++ b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp @@ -0,0 +1,2841 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011 Apple 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 GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLTreeBuilder.h" + +#include "Comment.h" +#include "DOMWindow.h" +#include "DocumentFragment.h" +#include "DocumentType.h" +#include "Frame.h" +#include "HTMLDocument.h" +#include "HTMLDocumentParser.h" +#include "HTMLElementFactory.h" +#include "HTMLFormElement.h" +#include "HTMLHtmlElement.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include "HTMLScriptElement.h" +#include "HTMLToken.h" +#include "HTMLTokenizer.h" +#include "LocalizedStrings.h" +#include "MathMLNames.h" +#include "NotImplemented.h" +#include "SVGNames.h" +#include "Text.h" +#include "XLinkNames.h" +#include "XMLNSNames.h" +#include "XMLNames.h" +#include <wtf/unicode/CharacterNames.h> + +namespace WebCore { + +using namespace HTMLNames; + +static const int uninitializedLineNumberValue = -1; + +static TextPosition uninitializedPositionValue1() +{ + return TextPosition(OrdinalNumber::fromOneBasedInt(-1), OrdinalNumber::first()); +} + +namespace { + +inline bool isHTMLSpaceOrReplacementCharacter(UChar character) +{ + return isHTMLSpace(character) || character == replacementCharacter; +} + +inline bool isAllWhitespace(const String& string) +{ + return string.isAllSpecialCharacters<isHTMLSpace>(); +} + +inline bool isAllWhitespaceOrReplacementCharacters(const String& string) +{ + return string.isAllSpecialCharacters<isHTMLSpaceOrReplacementCharacter>(); +} + +bool isNumberedHeaderTag(const AtomicString& tagName) +{ + return tagName == h1Tag + || tagName == h2Tag + || tagName == h3Tag + || tagName == h4Tag + || tagName == h5Tag + || tagName == h6Tag; +} + +bool isCaptionColOrColgroupTag(const AtomicString& tagName) +{ + return tagName == captionTag + || tagName == colTag + || tagName == colgroupTag; +} + +bool isTableCellContextTag(const AtomicString& tagName) +{ + return tagName == thTag || tagName == tdTag; +} + +bool isTableBodyContextTag(const AtomicString& tagName) +{ + return tagName == tbodyTag + || tagName == tfootTag + || tagName == theadTag; +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#special +bool isSpecialNode(Node* node) +{ + if (node->hasTagName(MathMLNames::miTag) + || node->hasTagName(MathMLNames::moTag) + || node->hasTagName(MathMLNames::mnTag) + || node->hasTagName(MathMLNames::msTag) + || node->hasTagName(MathMLNames::mtextTag) + || node->hasTagName(MathMLNames::annotation_xmlTag) + || node->hasTagName(SVGNames::foreignObjectTag) + || node->hasTagName(SVGNames::descTag) + || node->hasTagName(SVGNames::titleTag)) + return true; + if (node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) + return true; + if (!isInHTMLNamespace(node)) + return false; + const AtomicString& tagName = node->localName(); + return tagName == addressTag + || tagName == appletTag + || tagName == areaTag + || tagName == articleTag + || tagName == asideTag + || tagName == baseTag + || tagName == basefontTag + || tagName == bgsoundTag + || tagName == blockquoteTag + || tagName == bodyTag + || tagName == brTag + || tagName == buttonTag + || tagName == captionTag + || tagName == centerTag + || tagName == colTag + || tagName == colgroupTag + || tagName == commandTag + || tagName == ddTag + || tagName == detailsTag + || tagName == dirTag + || tagName == divTag + || tagName == dlTag + || tagName == dtTag + || tagName == embedTag + || tagName == fieldsetTag + || tagName == figcaptionTag + || tagName == figureTag + || tagName == footerTag + || tagName == formTag + || tagName == frameTag + || tagName == framesetTag + || isNumberedHeaderTag(tagName) + || tagName == headTag + || tagName == headerTag + || tagName == hgroupTag + || tagName == hrTag + || tagName == htmlTag + || tagName == iframeTag + || tagName == imgTag + || tagName == inputTag + || tagName == isindexTag + || tagName == liTag + || tagName == linkTag + || tagName == listingTag + || tagName == marqueeTag + || tagName == menuTag + || tagName == metaTag + || tagName == navTag + || tagName == noembedTag + || tagName == noframesTag + || tagName == noscriptTag + || tagName == objectTag + || tagName == olTag + || tagName == pTag + || tagName == paramTag + || tagName == plaintextTag + || tagName == preTag + || tagName == scriptTag + || tagName == sectionTag + || tagName == selectTag + || tagName == styleTag + || tagName == summaryTag + || tagName == tableTag + || isTableBodyContextTag(tagName) + || tagName == tdTag + || tagName == textareaTag + || tagName == thTag + || tagName == titleTag + || tagName == trTag + || tagName == ulTag + || tagName == wbrTag + || tagName == xmpTag; +} + +bool isNonAnchorNonNobrFormattingTag(const AtomicString& tagName) +{ + return tagName == bTag + || tagName == bigTag + || tagName == codeTag + || tagName == emTag + || tagName == fontTag + || tagName == iTag + || tagName == sTag + || tagName == smallTag + || tagName == strikeTag + || tagName == strongTag + || tagName == ttTag + || tagName == uTag; +} + +bool isNonAnchorFormattingTag(const AtomicString& tagName) +{ + return tagName == nobrTag + || isNonAnchorNonNobrFormattingTag(tagName); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting +bool isFormattingTag(const AtomicString& tagName) +{ + return tagName == aTag || isNonAnchorFormattingTag(tagName); +} + +HTMLFormElement* closestFormAncestor(Element* element) +{ + while (element) { + if (element->hasTagName(formTag)) + return static_cast<HTMLFormElement*>(element); + ContainerNode* parent = element->parentNode(); + if (!parent || !parent->isElementNode()) + return 0; + element = static_cast<Element*>(parent); + } + return 0; +} + +} // namespace + +class HTMLTreeBuilder::ExternalCharacterTokenBuffer { + WTF_MAKE_NONCOPYABLE(ExternalCharacterTokenBuffer); +public: + explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token) + : m_current(token.characters().data()) + , m_end(m_current + token.characters().size()) + { + ASSERT(!isEmpty()); + } + + explicit ExternalCharacterTokenBuffer(const String& string) + : m_current(string.characters()) + , m_end(m_current + string.length()) + { + ASSERT(!isEmpty()); + } + + ~ExternalCharacterTokenBuffer() + { + ASSERT(isEmpty()); + } + + bool isEmpty() const { return m_current == m_end; } + + void skipAtMostOneLeadingNewline() + { + ASSERT(!isEmpty()); + if (*m_current == '\n') + ++m_current; + } + + void skipLeadingWhitespace() + { + skipLeading<isHTMLSpace>(); + } + + String takeLeadingWhitespace() + { + return takeLeading<isHTMLSpace>(); + } + + void skipLeadingNonWhitespace() + { + skipLeading<isNotHTMLSpace>(); + } + + String takeRemaining() + { + ASSERT(!isEmpty()); + const UChar* start = m_current; + m_current = m_end; + return String(start, m_current - start); + } + + void giveRemainingTo(StringBuilder& recipient) + { + recipient.append(m_current, m_end - m_current); + m_current = m_end; + } + + String takeRemainingWhitespace() + { + ASSERT(!isEmpty()); + Vector<UChar> whitespace; + do { + UChar cc = *m_current++; + if (isHTMLSpace(cc)) + whitespace.append(cc); + } while (m_current < m_end); + // Returning the null string when there aren't any whitespace + // characters is slightly cleaner semantically because we don't want + // to insert a text node (as opposed to inserting an empty text node). + if (whitespace.isEmpty()) + return String(); + return String::adopt(whitespace); + } + +private: + template<bool characterPredicate(UChar)> + void skipLeading() + { + ASSERT(!isEmpty()); + while (characterPredicate(*m_current)) { + if (++m_current == m_end) + return; + } + } + + template<bool characterPredicate(UChar)> + String takeLeading() + { + ASSERT(!isEmpty()); + const UChar* start = m_current; + skipLeading<characterPredicate>(); + if (start == m_current) + return String(); + return String(start, m_current - start); + } + + const UChar* m_current; + const UChar* m_end; +}; + + +HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks, unsigned maximumDOMTreeDepth) + : m_framesetOk(true) + , m_document(document) + , m_tree(document, maximumDOMTreeDepth) + , m_reportErrors(reportErrors) + , m_isPaused(false) + , m_insertionMode(InitialMode) + , m_originalInsertionMode(InitialMode) + , m_shouldSkipLeadingNewline(false) + , m_parser(parser) + , m_scriptToProcessStartPosition(uninitializedPositionValue1()) + , m_lastScriptElementStartPosition(TextPosition::belowRangePosition()) + , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks) +{ +} + +// FIXME: Member variables should be grouped into self-initializing structs to +// minimize code duplication between these constructors. +HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks, unsigned maximumDOMTreeDepth) + : m_framesetOk(true) + , m_fragmentContext(fragment, contextElement, scriptingPermission) + , m_document(fragment->document()) + , m_tree(fragment, scriptingPermission, maximumDOMTreeDepth) + , m_reportErrors(false) // FIXME: Why not report errors in fragments? + , m_isPaused(false) + , m_insertionMode(InitialMode) + , m_originalInsertionMode(InitialMode) + , m_shouldSkipLeadingNewline(false) + , m_parser(parser) + , m_scriptToProcessStartPosition(uninitializedPositionValue1()) + , m_lastScriptElementStartPosition(TextPosition::belowRangePosition()) + , m_usePreHTML5ParserQuirks(usePreHTML5ParserQuirks) +{ + // FIXME: This assertion will become invalid if <http://webkit.org/b/60316> is fixed. + ASSERT(contextElement); + if (contextElement) { + // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case + // For efficiency, we skip step 4.2 ("Let root be a new html element with no attributes") + // and instead use the DocumentFragment as a root node. + m_tree.openElements()->pushRootNode(fragment); + resetInsertionModeAppropriately(); + m_tree.setForm(closestFormAncestor(contextElement)); + } +} + +HTMLTreeBuilder::~HTMLTreeBuilder() +{ +} + +void HTMLTreeBuilder::detach() +{ + // This call makes little sense in fragment mode, but for consistency + // DocumentParser expects detach() to always be called before it's destroyed. + m_document = 0; + // HTMLConstructionSite might be on the callstack when detach() is called + // otherwise we'd just call m_tree.clear() here instead. + m_tree.detach(); +} + +HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext() + : m_fragment(0) + , m_contextElement(0) + , m_scriptingPermission(FragmentScriptingAllowed) +{ +} + +HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission) + : m_fragment(fragment) + , m_contextElement(contextElement) + , m_scriptingPermission(scriptingPermission) +{ + ASSERT(!fragment->hasChildNodes()); +} + +HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext() +{ +} + +PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition& scriptStartPosition) +{ + // Unpause ourselves, callers may pause us again when processing the script. + // The HTML5 spec is written as though scripts are executed inside the tree + // builder. We pause the parser to exit the tree builder, and then resume + // before running scripts. + m_isPaused = false; + scriptStartPosition = m_scriptToProcessStartPosition; + m_scriptToProcessStartPosition = uninitializedPositionValue1(); + return m_scriptToProcess.release(); +} + +void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken) +{ + AtomicHTMLToken token(rawToken); + + // We clear the rawToken in case constructTreeFromAtomicToken + // synchronously re-enters the parser. We don't clear the token immedately + // for Character tokens because the AtomicHTMLToken avoids copying the + // characters by keeping a pointer to the underlying buffer in the + // HTMLToken. Fortuantely, Character tokens can't cause use to re-enter + // the parser. + // + // FIXME: Stop clearing the rawToken once we start running the parser off + // the main thread or once we stop allowing synchronous JavaScript + // execution from parseMappedAttribute. + if (rawToken.type() != HTMLTokenTypes::Character) + rawToken.clear(); + + constructTreeFromAtomicToken(token); + + if (!rawToken.isUninitialized()) { + ASSERT(rawToken.type() == HTMLTokenTypes::Character); + rawToken.clear(); + } +} + +void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token) +{ + if (shouldProcessTokenInForeignContent(token)) + processTokenInForeignContent(token); + else + processToken(token); + + bool inForeignContent = !m_tree.isEmpty() + && !isInHTMLNamespace(m_tree.currentNode()) + && !HTMLElementStack::isHTMLIntegrationPoint(m_tree.currentNode()) + && !HTMLElementStack::isMathMLTextIntegrationPoint(m_tree.currentNode()); + + m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || inForeignContent); + m_parser->tokenizer()->setShouldAllowCDATA(inForeignContent); + + m_tree.executeQueuedTasks(); + // We might be detached now. +} + +void HTMLTreeBuilder::processToken(AtomicHTMLToken& token) +{ + switch (token.type()) { + case HTMLTokenTypes::Uninitialized: + ASSERT_NOT_REACHED(); + break; + case HTMLTokenTypes::DOCTYPE: + m_shouldSkipLeadingNewline = false; + processDoctypeToken(token); + break; + case HTMLTokenTypes::StartTag: + m_shouldSkipLeadingNewline = false; + processStartTag(token); + break; + case HTMLTokenTypes::EndTag: + m_shouldSkipLeadingNewline = false; + processEndTag(token); + break; + case HTMLTokenTypes::Comment: + m_shouldSkipLeadingNewline = false; + processComment(token); + return; + case HTMLTokenTypes::Character: + processCharacter(token); + break; + case HTMLTokenTypes::EndOfFile: + m_shouldSkipLeadingNewline = false; + processEndOfFile(token); + break; + } +} + +void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::DOCTYPE); + if (m_insertionMode == InitialMode) { + m_tree.insertDoctype(token); + setInsertionMode(BeforeHTMLMode); + return; + } + if (m_insertionMode == InTableTextMode) { + defaultForInTableText(); + processDoctypeToken(token); + return; + } + parseError(token); +} + +void HTMLTreeBuilder::processFakeStartTag(const QualifiedName& tagName, PassOwnPtr<NamedNodeMap> attributes) +{ + // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags. + AtomicHTMLToken fakeToken(HTMLTokenTypes::StartTag, tagName.localName(), attributes); + processStartTag(fakeToken); +} + +void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName) +{ + // FIXME: We'll need a fancier conversion than just "localName" for SVG/MathML tags. + AtomicHTMLToken fakeToken(HTMLTokenTypes::EndTag, tagName.localName()); + processEndTag(fakeToken); +} + +void HTMLTreeBuilder::processFakeCharacters(const String& characters) +{ + ASSERT(!characters.isEmpty()); + ExternalCharacterTokenBuffer buffer(characters); + processCharacterBuffer(buffer); +} + +void HTMLTreeBuilder::processFakePEndTagIfPInButtonScope() +{ + if (!m_tree.openElements()->inButtonScope(pTag.localName())) + return; + AtomicHTMLToken endP(HTMLTokenTypes::EndTag, pTag.localName()); + processEndTag(endP); +} + +PassOwnPtr<NamedNodeMap> HTMLTreeBuilder::attributesForIsindexInput(AtomicHTMLToken& token) +{ + OwnPtr<NamedNodeMap> attributes = token.takeAttributes(); + if (!attributes) + attributes = NamedNodeMap::create(); + else { + attributes->removeAttribute(nameAttr); + attributes->removeAttribute(actionAttr); + attributes->removeAttribute(promptAttr); + } + + RefPtr<Attribute> mappedAttribute = Attribute::createMapped(nameAttr, isindexTag.localName()); + attributes->insertAttribute(mappedAttribute.release(), false); + return attributes.release(); +} + +void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(token.name() == isindexTag); + parseError(token); + if (m_tree.form()) + return; + notImplemented(); // Acknowledge self-closing flag + processFakeStartTag(formTag); + RefPtr<Attribute> actionAttribute = token.getAttributeItem(actionAttr); + if (actionAttribute) { + ASSERT(m_tree.currentElement()->hasTagName(formTag)); + m_tree.currentElement()->setAttribute(actionAttr, actionAttribute->value()); + } + processFakeStartTag(hrTag); + processFakeStartTag(labelTag); + RefPtr<Attribute> promptAttribute = token.getAttributeItem(promptAttr); + if (promptAttribute) + processFakeCharacters(promptAttribute->value()); + else + processFakeCharacters(searchableIndexIntroduction()); + processFakeStartTag(inputTag, attributesForIsindexInput(token)); + notImplemented(); // This second set of characters may be needed by non-english locales. + processFakeEndTag(labelTag); + processFakeStartTag(hrTag); + processFakeEndTag(formTag); +} + +namespace { + +bool isLi(const ContainerNode* element) +{ + return element->hasTagName(liTag); +} + +bool isDdOrDt(const ContainerNode* element) +{ + return element->hasTagName(ddTag) + || element->hasTagName(dtTag); +} + +} + +template <bool shouldClose(const ContainerNode*)> +void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token) +{ + m_framesetOk = false; + HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); + while (1) { + RefPtr<ContainerNode> node = nodeRecord->node(); + if (shouldClose(node.get())) { + ASSERT(node->isElementNode()); + processFakeEndTag(toElement(node.get())->tagQName()); + break; + } + if (isSpecialNode(node.get()) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag)) + break; + nodeRecord = nodeRecord->next(); + } + processFakePEndTagIfPInButtonScope(); + m_tree.insertHTMLElement(token); +} + +namespace { + +typedef HashMap<AtomicString, QualifiedName> PrefixedNameToQualifiedNameMap; + +void mapLoweredLocalNameToName(PrefixedNameToQualifiedNameMap* map, QualifiedName** names, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + const QualifiedName& name = *names[i]; + const AtomicString& localName = name.localName(); + AtomicString loweredLocalName = localName.lower(); + if (loweredLocalName != localName) + map->add(loweredLocalName, name); + } +} + +void adjustSVGTagNameCase(AtomicHTMLToken& token) +{ + static PrefixedNameToQualifiedNameMap* caseMap = 0; + if (!caseMap) { + caseMap = new PrefixedNameToQualifiedNameMap; + size_t length = 0; + QualifiedName** svgTags = SVGNames::getSVGTags(&length); + mapLoweredLocalNameToName(caseMap, svgTags, length); + } + + const QualifiedName& casedName = caseMap->get(token.name()); + if (casedName.localName().isNull()) + return; + token.setName(casedName.localName()); +} + +template<QualifiedName** getAttrs(size_t* length)> +void adjustAttributes(AtomicHTMLToken& token) +{ + static PrefixedNameToQualifiedNameMap* caseMap = 0; + if (!caseMap) { + caseMap = new PrefixedNameToQualifiedNameMap; + size_t length = 0; + QualifiedName** attrs = getAttrs(&length); + mapLoweredLocalNameToName(caseMap, attrs, length); + } + + NamedNodeMap* attributes = token.attributes(); + if (!attributes) + return; + + for (unsigned x = 0; x < attributes->length(); ++x) { + Attribute* attribute = attributes->attributeItem(x); + const QualifiedName& casedName = caseMap->get(attribute->localName()); + if (!casedName.localName().isNull()) + attribute->parserSetName(casedName); + } +} + +void adjustSVGAttributes(AtomicHTMLToken& token) +{ + adjustAttributes<SVGNames::getSVGAttrs>(token); +} + +void adjustMathMLAttributes(AtomicHTMLToken& token) +{ + adjustAttributes<MathMLNames::getMathMLAttrs>(token); +} + +void addNamesWithPrefix(PrefixedNameToQualifiedNameMap* map, const AtomicString& prefix, QualifiedName** names, size_t length) +{ + for (size_t i = 0; i < length; ++i) { + QualifiedName* name = names[i]; + const AtomicString& localName = name->localName(); + AtomicString prefixColonLocalName = prefix + ':' + localName; + QualifiedName nameWithPrefix(prefix, localName, name->namespaceURI()); + map->add(prefixColonLocalName, nameWithPrefix); + } +} + +void adjustForeignAttributes(AtomicHTMLToken& token) +{ + static PrefixedNameToQualifiedNameMap* map = 0; + if (!map) { + map = new PrefixedNameToQualifiedNameMap; + size_t length = 0; + QualifiedName** attrs = XLinkNames::getXLinkAttrs(&length); + addNamesWithPrefix(map, "xlink", attrs, length); + + attrs = XMLNames::getXMLAttrs(&length); + addNamesWithPrefix(map, "xml", attrs, length); + + map->add("xmlns", XMLNSNames::xmlnsAttr); + map->add("xmlns:xlink", QualifiedName("xmlns", "xlink", XMLNSNames::xmlnsNamespaceURI)); + } + + NamedNodeMap* attributes = token.attributes(); + if (!attributes) + return; + + for (unsigned x = 0; x < attributes->length(); ++x) { + Attribute* attribute = attributes->attributeItem(x); + const QualifiedName& name = map->get(attribute->localName()); + if (!name.localName().isNull()) + attribute->parserSetName(name); + } +} + +} + +void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == baseTag + || token.name() == basefontTag + || token.name() == bgsoundTag + || token.name() == commandTag + || token.name() == linkTag + || token.name() == metaTag + || token.name() == noframesTag + || token.name() == scriptTag + || token.name() == styleTag + || token.name() == titleTag) { + bool didProcess = processStartTagForInHead(token); + ASSERT_UNUSED(didProcess, didProcess); + return; + } + if (token.name() == bodyTag) { + if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) { + ASSERT(isParsingFragment()); + return; + } + m_framesetOk = false; + m_tree.insertHTMLBodyStartTagInBody(token); + return; + } + if (token.name() == framesetTag) { + parseError(token); + if (!m_tree.openElements()->secondElementIsHTMLBodyElement() || m_tree.openElements()->hasOnlyOneElement()) { + ASSERT(isParsingFragment()); + return; + } + if (!m_framesetOk) + return; + ExceptionCode ec = 0; + m_tree.openElements()->bodyElement()->remove(ec); + ASSERT(!ec); + m_tree.openElements()->popUntil(m_tree.openElements()->bodyElement()); + m_tree.openElements()->popHTMLBodyElement(); + ASSERT(m_tree.openElements()->top() == m_tree.openElements()->htmlElement()); + m_tree.insertHTMLElement(token); + setInsertionMode(InFramesetMode); + return; + } + if (token.name() == addressTag + || token.name() == articleTag + || token.name() == asideTag + || token.name() == blockquoteTag + || token.name() == centerTag + || token.name() == detailsTag + || token.name() == dirTag + || token.name() == divTag + || token.name() == dlTag + || token.name() == fieldsetTag + || token.name() == figcaptionTag + || token.name() == figureTag + || token.name() == footerTag + || token.name() == headerTag + || token.name() == hgroupTag + || token.name() == menuTag + || token.name() == navTag + || token.name() == olTag + || token.name() == pTag + || token.name() == sectionTag + || token.name() == summaryTag + || token.name() == ulTag) { + processFakePEndTagIfPInButtonScope(); + m_tree.insertHTMLElement(token); + return; + } + if (isNumberedHeaderTag(token.name())) { + processFakePEndTagIfPInButtonScope(); + if (isNumberedHeaderTag(m_tree.currentNode()->localName())) { + parseError(token); + m_tree.openElements()->pop(); + } + m_tree.insertHTMLElement(token); + return; + } + if (token.name() == preTag || token.name() == listingTag) { + processFakePEndTagIfPInButtonScope(); + m_tree.insertHTMLElement(token); + m_shouldSkipLeadingNewline = true; + m_framesetOk = false; + return; + } + if (token.name() == formTag) { + if (m_tree.form()) { + parseError(token); + return; + } + processFakePEndTagIfPInButtonScope(); + m_tree.insertHTMLFormElement(token); + return; + } + if (token.name() == liTag) { + processCloseWhenNestedTag<isLi>(token); + return; + } + if (token.name() == ddTag || token.name() == dtTag) { + processCloseWhenNestedTag<isDdOrDt>(token); + return; + } + if (token.name() == plaintextTag) { + processFakePEndTagIfPInButtonScope(); + m_tree.insertHTMLElement(token); + m_parser->tokenizer()->setState(HTMLTokenizerState::PLAINTEXTState); + return; + } + if (token.name() == buttonTag) { + if (m_tree.openElements()->inScope(buttonTag)) { + parseError(token); + processFakeEndTag(buttonTag); + processStartTag(token); // FIXME: Could we just fall through here? + return; + } + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertHTMLElement(token); + m_framesetOk = false; + return; + } + if (token.name() == aTag) { + Element* activeATag = m_tree.activeFormattingElements()->closestElementInScopeWithName(aTag.localName()); + if (activeATag) { + parseError(token); + processFakeEndTag(aTag); + m_tree.activeFormattingElements()->remove(activeATag); + if (m_tree.openElements()->contains(activeATag)) + m_tree.openElements()->remove(activeATag); + } + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertFormattingElement(token); + return; + } + if (isNonAnchorNonNobrFormattingTag(token.name())) { + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertFormattingElement(token); + return; + } + if (token.name() == nobrTag) { + m_tree.reconstructTheActiveFormattingElements(); + if (m_tree.openElements()->inScope(nobrTag)) { + parseError(token); + processFakeEndTag(nobrTag); + m_tree.reconstructTheActiveFormattingElements(); + } + m_tree.insertFormattingElement(token); + return; + } + if (token.name() == appletTag + || token.name() == marqueeTag + || token.name() == objectTag) { + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertHTMLElement(token); + m_tree.activeFormattingElements()->appendMarker(); + m_framesetOk = false; + return; + } + if (token.name() == tableTag) { + if (!m_document->inQuirksMode() && m_tree.openElements()->inButtonScope(pTag)) + processFakeEndTag(pTag); + m_tree.insertHTMLElement(token); + m_framesetOk = false; + setInsertionMode(InTableMode); + return; + } + if (token.name() == imageTag) { + parseError(token); + // Apparently we're not supposed to ask. + token.setName(imgTag.localName()); + // Note the fall through to the imgTag handling below! + } + if (token.name() == areaTag + || token.name() == brTag + || token.name() == embedTag + || token.name() == imgTag + || token.name() == keygenTag + || token.name() == wbrTag) { + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertSelfClosingHTMLElement(token); + m_framesetOk = false; + return; + } + if (token.name() == inputTag) { + RefPtr<Attribute> typeAttribute = token.getAttributeItem(typeAttr); + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertSelfClosingHTMLElement(token); + if (!typeAttribute || !equalIgnoringCase(typeAttribute->value(), "hidden")) + m_framesetOk = false; + return; + } + if (token.name() == paramTag + || token.name() == sourceTag + || token.name() == trackTag) { + m_tree.insertSelfClosingHTMLElement(token); + return; + } + if (token.name() == hrTag) { + processFakePEndTagIfPInButtonScope(); + m_tree.insertSelfClosingHTMLElement(token); + m_framesetOk = false; + return; + } + if (token.name() == isindexTag) { + processIsindexStartTagForInBody(token); + return; + } + if (token.name() == textareaTag) { + m_tree.insertHTMLElement(token); + m_shouldSkipLeadingNewline = true; + m_parser->tokenizer()->setState(HTMLTokenizerState::RCDATAState); + m_originalInsertionMode = m_insertionMode; + m_framesetOk = false; + setInsertionMode(TextMode); + return; + } + if (token.name() == xmpTag) { + processFakePEndTagIfPInButtonScope(); + m_tree.reconstructTheActiveFormattingElements(); + m_framesetOk = false; + processGenericRawTextStartTag(token); + return; + } + if (token.name() == iframeTag) { + m_framesetOk = false; + processGenericRawTextStartTag(token); + return; + } + if (token.name() == noembedTag && pluginsEnabled(m_document->frame())) { + processGenericRawTextStartTag(token); + return; + } + if (token.name() == noscriptTag && scriptEnabled(m_document->frame())) { + processGenericRawTextStartTag(token); + return; + } + if (token.name() == selectTag) { + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertHTMLElement(token); + m_framesetOk = false; + if (m_insertionMode == InTableMode + || m_insertionMode == InCaptionMode + || m_insertionMode == InColumnGroupMode + || m_insertionMode == InTableBodyMode + || m_insertionMode == InRowMode + || m_insertionMode == InCellMode) + setInsertionMode(InSelectInTableMode); + else + setInsertionMode(InSelectMode); + return; + } + if (token.name() == optgroupTag || token.name() == optionTag) { + if (m_tree.currentNode()->hasTagName(optionTag)) { + AtomicHTMLToken endOption(HTMLTokenTypes::EndTag, optionTag.localName()); + processEndTag(endOption); + } + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertHTMLElement(token); + return; + } + if (token.name() == rpTag || token.name() == rtTag) { + if (m_tree.openElements()->inScope(rubyTag.localName())) { + m_tree.generateImpliedEndTags(); + if (!m_tree.currentNode()->hasTagName(rubyTag)) + parseError(token); + } + m_tree.insertHTMLElement(token); + return; + } + if (token.name() == MathMLNames::mathTag.localName()) { + m_tree.reconstructTheActiveFormattingElements(); + adjustMathMLAttributes(token); + adjustForeignAttributes(token); + m_tree.insertForeignElement(token, MathMLNames::mathmlNamespaceURI); + return; + } + if (token.name() == SVGNames::svgTag.localName()) { + m_tree.reconstructTheActiveFormattingElements(); + adjustSVGAttributes(token); + adjustForeignAttributes(token); + m_tree.insertForeignElement(token, SVGNames::svgNamespaceURI); + return; + } + if (isCaptionColOrColgroupTag(token.name()) + || token.name() == frameTag + || token.name() == headTag + || isTableBodyContextTag(token.name()) + || isTableCellContextTag(token.name()) + || token.name() == trTag) { + parseError(token); + return; + } + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertHTMLElement(token); +} + +bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup() +{ + if (m_tree.currentNode() == m_tree.openElements()->rootNode()) { + ASSERT(isParsingFragment()); + // FIXME: parse error + return false; + } + m_tree.openElements()->pop(); + setInsertionMode(InTableMode); + return true; +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#close-the-cell +void HTMLTreeBuilder::closeTheCell() +{ + ASSERT(insertionMode() == InCellMode); + if (m_tree.openElements()->inTableScope(tdTag)) { + ASSERT(!m_tree.openElements()->inTableScope(thTag)); + processFakeEndTag(tdTag); + return; + } + ASSERT(m_tree.openElements()->inTableScope(thTag)); + processFakeEndTag(thTag); + ASSERT(insertionMode() == InRowMode); +} + +void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + if (token.name() == captionTag) { + m_tree.openElements()->popUntilTableScopeMarker(); + m_tree.activeFormattingElements()->appendMarker(); + m_tree.insertHTMLElement(token); + setInsertionMode(InCaptionMode); + return; + } + if (token.name() == colgroupTag) { + m_tree.openElements()->popUntilTableScopeMarker(); + m_tree.insertHTMLElement(token); + setInsertionMode(InColumnGroupMode); + return; + } + if (token.name() == colTag) { + processFakeStartTag(colgroupTag); + ASSERT(InColumnGroupMode); + processStartTag(token); + return; + } + if (isTableBodyContextTag(token.name())) { + m_tree.openElements()->popUntilTableScopeMarker(); + m_tree.insertHTMLElement(token); + setInsertionMode(InTableBodyMode); + return; + } + if (isTableCellContextTag(token.name()) + || token.name() == trTag) { + processFakeStartTag(tbodyTag); + ASSERT(insertionMode() == InTableBodyMode); + processStartTag(token); + return; + } + if (token.name() == tableTag) { + parseError(token); + if (!processTableEndTagForInTable()) { + ASSERT(isParsingFragment()); + return; + } + processStartTag(token); + return; + } + if (token.name() == styleTag || token.name() == scriptTag) { + processStartTagForInHead(token); + return; + } + if (token.name() == inputTag) { + Attribute* typeAttribute = token.getAttributeItem(typeAttr); + if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) { + parseError(token); + m_tree.insertSelfClosingHTMLElement(token); + return; + } + // Fall through to "anything else" case. + } + if (token.name() == formTag) { + parseError(token); + if (m_tree.form()) + return; + m_tree.insertHTMLFormElement(token, true); + m_tree.openElements()->pop(); + return; + } + parseError(token); + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); + processStartTagForInBody(token); +} + +void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + switch (insertionMode()) { + case InitialMode: + ASSERT(insertionMode() == InitialMode); + defaultForInitial(); + // Fall through. + case BeforeHTMLMode: + ASSERT(insertionMode() == BeforeHTMLMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagBeforeHTML(token); + setInsertionMode(BeforeHeadMode); + return; + } + defaultForBeforeHTML(); + // Fall through. + case BeforeHeadMode: + ASSERT(insertionMode() == BeforeHeadMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == headTag) { + m_tree.insertHTMLHeadElement(token); + setInsertionMode(InHeadMode); + return; + } + defaultForBeforeHead(); + // Fall through. + case InHeadMode: + ASSERT(insertionMode() == InHeadMode); + if (processStartTagForInHead(token)) + return; + defaultForInHead(); + // Fall through. + case AfterHeadMode: + ASSERT(insertionMode() == AfterHeadMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == bodyTag) { + m_framesetOk = false; + m_tree.insertHTMLBodyElement(token); + setInsertionMode(InBodyMode); + return; + } + if (token.name() == framesetTag) { + m_tree.insertHTMLElement(token); + setInsertionMode(InFramesetMode); + return; + } + if (token.name() == baseTag + || token.name() == basefontTag + || token.name() == bgsoundTag + || token.name() == linkTag + || token.name() == metaTag + || token.name() == noframesTag + || token.name() == scriptTag + || token.name() == styleTag + || token.name() == titleTag) { + parseError(token); + ASSERT(m_tree.head()); + m_tree.openElements()->pushHTMLHeadElement(m_tree.head()); + processStartTagForInHead(token); + m_tree.openElements()->removeHTMLHeadElement(m_tree.head()); + return; + } + if (token.name() == headTag) { + parseError(token); + return; + } + defaultForAfterHead(); + // Fall through + case InBodyMode: + ASSERT(insertionMode() == InBodyMode); + processStartTagForInBody(token); + break; + case InTableMode: + ASSERT(insertionMode() == InTableMode); + processStartTagForInTable(token); + break; + case InCaptionMode: + ASSERT(insertionMode() == InCaptionMode); + if (isCaptionColOrColgroupTag(token.name()) + || isTableBodyContextTag(token.name()) + || isTableCellContextTag(token.name()) + || token.name() == trTag) { + parseError(token); + if (!processCaptionEndTagForInCaption()) { + ASSERT(isParsingFragment()); + return; + } + processStartTag(token); + return; + } + processStartTagForInBody(token); + break; + case InColumnGroupMode: + ASSERT(insertionMode() == InColumnGroupMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == colTag) { + m_tree.insertSelfClosingHTMLElement(token); + return; + } + if (!processColgroupEndTagForInColumnGroup()) { + ASSERT(isParsingFragment()); + return; + } + processStartTag(token); + break; + case InTableBodyMode: + ASSERT(insertionMode() == InTableBodyMode); + if (token.name() == trTag) { + m_tree.openElements()->popUntilTableBodyScopeMarker(); // How is there ever anything to pop? + m_tree.insertHTMLElement(token); + setInsertionMode(InRowMode); + return; + } + if (isTableCellContextTag(token.name())) { + parseError(token); + processFakeStartTag(trTag); + ASSERT(insertionMode() == InRowMode); + processStartTag(token); + return; + } + if (isCaptionColOrColgroupTag(token.name()) || isTableBodyContextTag(token.name())) { + // FIXME: This is slow. + if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) { + ASSERT(isParsingFragment()); + parseError(token); + return; + } + m_tree.openElements()->popUntilTableBodyScopeMarker(); + ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName())); + processFakeEndTag(m_tree.currentElement()->tagQName()); + processStartTag(token); + return; + } + processStartTagForInTable(token); + break; + case InRowMode: + ASSERT(insertionMode() == InRowMode); + if (isTableCellContextTag(token.name())) { + m_tree.openElements()->popUntilTableRowScopeMarker(); + m_tree.insertHTMLElement(token); + setInsertionMode(InCellMode); + m_tree.activeFormattingElements()->appendMarker(); + return; + } + if (token.name() == trTag + || isCaptionColOrColgroupTag(token.name()) + || isTableBodyContextTag(token.name())) { + if (!processTrEndTagForInRow()) { + ASSERT(isParsingFragment()); + return; + } + ASSERT(insertionMode() == InTableBodyMode); + processStartTag(token); + return; + } + processStartTagForInTable(token); + break; + case InCellMode: + ASSERT(insertionMode() == InCellMode); + if (isCaptionColOrColgroupTag(token.name()) + || isTableCellContextTag(token.name()) + || token.name() == trTag + || isTableBodyContextTag(token.name())) { + // FIXME: This could be more efficient. + if (!m_tree.openElements()->inTableScope(tdTag) && !m_tree.openElements()->inTableScope(thTag)) { + ASSERT(isParsingFragment()); + parseError(token); + return; + } + closeTheCell(); + processStartTag(token); + return; + } + processStartTagForInBody(token); + break; + case AfterBodyMode: + case AfterAfterBodyMode: + ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + setInsertionMode(InBodyMode); + processStartTag(token); + break; + case InHeadNoscriptMode: + ASSERT(insertionMode() == InHeadNoscriptMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == basefontTag + || token.name() == bgsoundTag + || token.name() == linkTag + || token.name() == metaTag + || token.name() == noframesTag + || token.name() == styleTag) { + bool didProcess = processStartTagForInHead(token); + ASSERT_UNUSED(didProcess, didProcess); + return; + } + if (token.name() == htmlTag || token.name() == noscriptTag) { + parseError(token); + return; + } + defaultForInHeadNoscript(); + processToken(token); + break; + case InFramesetMode: + ASSERT(insertionMode() == InFramesetMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == framesetTag) { + m_tree.insertHTMLElement(token); + return; + } + if (token.name() == frameTag) { + m_tree.insertSelfClosingHTMLElement(token); + return; + } + if (token.name() == noframesTag) { + processStartTagForInHead(token); + return; + } + parseError(token); + break; + case AfterFramesetMode: + case AfterAfterFramesetMode: + ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == noframesTag) { + processStartTagForInHead(token); + return; + } + parseError(token); + break; + case InSelectInTableMode: + ASSERT(insertionMode() == InSelectInTableMode); + if (token.name() == captionTag + || token.name() == tableTag + || isTableBodyContextTag(token.name()) + || token.name() == trTag + || isTableCellContextTag(token.name())) { + parseError(token); + AtomicHTMLToken endSelect(HTMLTokenTypes::EndTag, selectTag.localName()); + processEndTag(endSelect); + processStartTag(token); + return; + } + // Fall through + case InSelectMode: + ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return; + } + if (token.name() == optionTag) { + if (m_tree.currentNode()->hasTagName(optionTag)) { + AtomicHTMLToken endOption(HTMLTokenTypes::EndTag, optionTag.localName()); + processEndTag(endOption); + } + m_tree.insertHTMLElement(token); + return; + } + if (token.name() == optgroupTag) { + if (m_tree.currentNode()->hasTagName(optionTag)) { + AtomicHTMLToken endOption(HTMLTokenTypes::EndTag, optionTag.localName()); + processEndTag(endOption); + } + if (m_tree.currentNode()->hasTagName(optgroupTag)) { + AtomicHTMLToken endOptgroup(HTMLTokenTypes::EndTag, optgroupTag.localName()); + processEndTag(endOptgroup); + } + m_tree.insertHTMLElement(token); + return; + } + if (token.name() == selectTag) { + parseError(token); + AtomicHTMLToken endSelect(HTMLTokenTypes::EndTag, selectTag.localName()); + processEndTag(endSelect); + return; + } + if (token.name() == inputTag + || token.name() == keygenTag + || token.name() == textareaTag) { + parseError(token); + if (!m_tree.openElements()->inSelectScope(selectTag)) { + ASSERT(isParsingFragment()); + return; + } + AtomicHTMLToken endSelect(HTMLTokenTypes::EndTag, selectTag.localName()); + processEndTag(endSelect); + processStartTag(token); + return; + } + if (token.name() == scriptTag) { + bool didProcess = processStartTagForInHead(token); + ASSERT_UNUSED(didProcess, didProcess); + return; + } + break; + case InTableTextMode: + defaultForInTableText(); + processStartTag(token); + break; + case TextMode: + ASSERT_NOT_REACHED(); + break; + } +} + +bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + ASSERT(token.name() == bodyTag); + if (!m_tree.openElements()->inScope(bodyTag.localName())) { + parseError(token); + return false; + } + notImplemented(); // Emit a more specific parse error based on stack contents. + setInsertionMode(AfterBodyMode); + return true; +} + +void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); + while (1) { + RefPtr<ContainerNode> node = record->node(); + if (node->hasLocalName(token.name())) { + m_tree.generateImpliedEndTags(); + // FIXME: The ElementRecord pointed to by record might be deleted by + // the preceding call. Perhaps we should hold a RefPtr so that it + // stays alive for the duration of record's scope. + record = 0; + if (!m_tree.currentNode()->hasLocalName(token.name())) { + parseError(token); + // FIXME: This is either a bug in the spec, or a bug in our + // implementation. Filed a bug with HTML5: + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10080 + // We might have already popped the node for the token in + // generateImpliedEndTags, just abort. + if (!m_tree.openElements()->contains(toElement(node.get()))) + return; + } + m_tree.openElements()->popUntilPopped(toElement(node.get())); + return; + } + if (isSpecialNode(node.get())) { + parseError(token); + return; + } + record = record->next(); + } +} + +// FIXME: This probably belongs on HTMLElementStack. +HTMLElementStack::ElementRecord* HTMLTreeBuilder::furthestBlockForFormattingElement(Element* formattingElement) +{ + HTMLElementStack::ElementRecord* furthestBlock = 0; + HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); + for (; record; record = record->next()) { + if (record->element() == formattingElement) + return furthestBlock; + if (isSpecialNode(record->element())) + furthestBlock = record; + } + ASSERT_NOT_REACHED(); + return 0; +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody +void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) +{ + // The adoption agency algorithm is N^2. We limit the number of iterations + // to stop from hanging the whole browser. This limit is specified in the + // adoption agency algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#parsing-main-inbody + static const int outerIterationLimit = 8; + static const int innerIterationLimit = 3; + + for (int i = 0; i < outerIterationLimit; ++i) { + // 1. + Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name()); + if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) { + parseError(token); + notImplemented(); // Check the stack of open elements for a more specific parse error. + return; + } + HTMLElementStack::ElementRecord* formattingElementRecord = m_tree.openElements()->find(formattingElement); + if (!formattingElementRecord) { + parseError(token); + m_tree.activeFormattingElements()->remove(formattingElement); + return; + } + if (formattingElement != m_tree.currentElement()) + parseError(token); + // 2. + HTMLElementStack::ElementRecord* furthestBlock = furthestBlockForFormattingElement(formattingElement); + // 3. + if (!furthestBlock) { + m_tree.openElements()->popUntilPopped(formattingElement); + m_tree.activeFormattingElements()->remove(formattingElement); + return; + } + // 4. + ASSERT(furthestBlock->isAbove(formattingElementRecord)); + RefPtr<ContainerNode> commonAncestor = formattingElementRecord->next()->node(); + // 5. + HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement); + // 6. + HTMLElementStack::ElementRecord* node = furthestBlock; + HTMLElementStack::ElementRecord* nextNode = node->next(); + HTMLElementStack::ElementRecord* lastNode = furthestBlock; + for (int i = 0; i < innerIterationLimit; ++i) { + // 6.1 + node = nextNode; + ASSERT(node); + nextNode = node->next(); // Save node->next() for the next iteration in case node is deleted in 6.2. + // 6.2 + if (!m_tree.activeFormattingElements()->contains(node->element())) { + m_tree.openElements()->remove(node->element()); + node = 0; + continue; + } + // 6.3 + if (node == formattingElementRecord) + break; + // 6.5 + RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node); + HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element()); + nodeEntry->replaceElement(newElement.get()); + node->replaceElement(newElement.release()); + // 6.4 -- Intentionally out of order to handle the case where node + // was replaced in 6.5. + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096 + if (lastNode == furthestBlock) + bookmark.moveToAfter(nodeEntry); + // 6.6 + if (ContainerNode* parent = lastNode->element()->parentNode()) + parent->parserRemoveChild(lastNode->element()); + node->element()->parserAddChild(lastNode->element()); + if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached()) + lastNode->element()->lazyAttach(); + // 6.7 + lastNode = node; + } + // 7 + const AtomicString& commonAncestorTag = commonAncestor->localName(); + if (ContainerNode* parent = lastNode->element()->parentNode()) + parent->parserRemoveChild(lastNode->element()); + // FIXME: If this moves to HTMLConstructionSite, this check should use + // causesFosterParenting(tagName) instead. + if (commonAncestorTag == tableTag + || commonAncestorTag == trTag + || isTableBodyContextTag(commonAncestorTag)) + m_tree.fosterParent(lastNode->element()); + else { + commonAncestor->parserAddChild(lastNode->element()); + ASSERT(lastNode->node()->isElementNode()); + ASSERT(lastNode->element()->parentNode()); + if (lastNode->element()->parentNode()->attached() && !lastNode->element()->attached()) + lastNode->element()->lazyAttach(); + } + // 8 + RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord); + // 9 + newElement->takeAllChildrenFrom(furthestBlock->element()); + // 10 + Element* furthestBlockElement = furthestBlock->element(); + // FIXME: All this creation / parserAddChild / attach business should + // be in HTMLConstructionSite. My guess is that steps 8--12 + // should all be in some HTMLConstructionSite function. + furthestBlockElement->parserAddChild(newElement); + if (furthestBlockElement->attached() && !newElement->attached()) { + // Notice that newElement might already be attached if, for example, one of the reparented + // children is a style element, which attaches itself automatically. + newElement->attach(); + } + // 11 + m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark); + // 12 + m_tree.openElements()->remove(formattingElement); + m_tree.openElements()->insertAbove(newElement, furthestBlock); + } +} + +void HTMLTreeBuilder::resetInsertionModeAppropriately() +{ + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#reset-the-insertion-mode-appropriately + bool last = false; + HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); + while (1) { + ContainerNode* node = nodeRecord->node(); + if (node == m_tree.openElements()->rootNode()) { + ASSERT(isParsingFragment()); + last = true; + node = m_fragmentContext.contextElement(); + } + if (node->hasTagName(selectTag)) { + ASSERT(isParsingFragment()); + return setInsertionMode(InSelectMode); + } + if (node->hasTagName(tdTag) || node->hasTagName(thTag)) + return setInsertionMode(InCellMode); + if (node->hasTagName(trTag)) + return setInsertionMode(InRowMode); + if (node->hasTagName(tbodyTag) || node->hasTagName(theadTag) || node->hasTagName(tfootTag)) + return setInsertionMode(InTableBodyMode); + if (node->hasTagName(captionTag)) + return setInsertionMode(InCaptionMode); + if (node->hasTagName(colgroupTag)) { + ASSERT(isParsingFragment()); + return setInsertionMode(InColumnGroupMode); + } + if (node->hasTagName(tableTag)) + return setInsertionMode(InTableMode); + if (node->hasTagName(headTag)) { + ASSERT(isParsingFragment()); + return setInsertionMode(InBodyMode); + } + if (node->hasTagName(bodyTag)) + return setInsertionMode(InBodyMode); + if (node->hasTagName(framesetTag)) { + ASSERT(isParsingFragment()); + return setInsertionMode(InFramesetMode); + } + if (node->hasTagName(htmlTag)) { + ASSERT(isParsingFragment()); + return setInsertionMode(BeforeHeadMode); + } + if (last) { + ASSERT(isParsingFragment()); + return setInsertionMode(InBodyMode); + } + nodeRecord = nodeRecord->next(); + } +} + +void HTMLTreeBuilder::processEndTagForInTableBody(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + if (isTableBodyContextTag(token.name())) { + if (!m_tree.openElements()->inTableScope(token.name())) { + parseError(token); + return; + } + m_tree.openElements()->popUntilTableBodyScopeMarker(); + m_tree.openElements()->pop(); + setInsertionMode(InTableMode); + return; + } + if (token.name() == tableTag) { + // FIXME: This is slow. + if (!m_tree.openElements()->inTableScope(tbodyTag.localName()) && !m_tree.openElements()->inTableScope(theadTag.localName()) && !m_tree.openElements()->inTableScope(tfootTag.localName())) { + ASSERT(isParsingFragment()); + parseError(token); + return; + } + m_tree.openElements()->popUntilTableBodyScopeMarker(); + ASSERT(isTableBodyContextTag(m_tree.currentElement()->localName())); + processFakeEndTag(m_tree.currentElement()->tagQName()); + processEndTag(token); + return; + } + if (token.name() == bodyTag + || isCaptionColOrColgroupTag(token.name()) + || token.name() == htmlTag + || isTableCellContextTag(token.name()) + || token.name() == trTag) { + parseError(token); + return; + } + processEndTagForInTable(token); +} + +void HTMLTreeBuilder::processEndTagForInRow(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + if (token.name() == trTag) { + processTrEndTagForInRow(); + return; + } + if (token.name() == tableTag) { + if (!processTrEndTagForInRow()) { + ASSERT(isParsingFragment()); + return; + } + ASSERT(insertionMode() == InTableBodyMode); + processEndTag(token); + return; + } + if (isTableBodyContextTag(token.name())) { + if (!m_tree.openElements()->inTableScope(token.name())) { + parseError(token); + return; + } + processFakeEndTag(trTag); + ASSERT(insertionMode() == InTableBodyMode); + processEndTag(token); + return; + } + if (token.name() == bodyTag + || isCaptionColOrColgroupTag(token.name()) + || token.name() == htmlTag + || isTableCellContextTag(token.name())) { + parseError(token); + return; + } + processEndTagForInTable(token); +} + +void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + if (isTableCellContextTag(token.name())) { + if (!m_tree.openElements()->inTableScope(token.name())) { + parseError(token); + return; + } + m_tree.generateImpliedEndTags(); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilPopped(token.name()); + m_tree.activeFormattingElements()->clearToLastMarker(); + setInsertionMode(InRowMode); + return; + } + if (token.name() == bodyTag + || isCaptionColOrColgroupTag(token.name()) + || token.name() == htmlTag) { + parseError(token); + return; + } + if (token.name() == tableTag + || token.name() == trTag + || isTableBodyContextTag(token.name())) { + if (!m_tree.openElements()->inTableScope(token.name())) { + ASSERT(isTableBodyContextTag(token.name()) || isParsingFragment()); + parseError(token); + return; + } + closeTheCell(); + processEndTag(token); + return; + } + processEndTagForInBody(token); +} + +void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + if (token.name() == bodyTag) { + processBodyEndTagForInBody(token); + return; + } + if (token.name() == htmlTag) { + AtomicHTMLToken endBody(HTMLTokenTypes::EndTag, bodyTag.localName()); + if (processBodyEndTagForInBody(endBody)) + processEndTag(token); + return; + } + if (token.name() == addressTag + || token.name() == articleTag + || token.name() == asideTag + || token.name() == blockquoteTag + || token.name() == buttonTag + || token.name() == centerTag + || token.name() == detailsTag + || token.name() == dirTag + || token.name() == divTag + || token.name() == dlTag + || token.name() == fieldsetTag + || token.name() == figcaptionTag + || token.name() == figureTag + || token.name() == footerTag + || token.name() == headerTag + || token.name() == hgroupTag + || token.name() == listingTag + || token.name() == menuTag + || token.name() == navTag + || token.name() == olTag + || token.name() == preTag + || token.name() == sectionTag + || token.name() == summaryTag + || token.name() == ulTag) { + if (!m_tree.openElements()->inScope(token.name())) { + parseError(token); + return; + } + m_tree.generateImpliedEndTags(); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilPopped(token.name()); + return; + } + if (token.name() == formTag) { + RefPtr<Element> node = m_tree.takeForm(); + if (!node || !m_tree.openElements()->inScope(node.get())) { + parseError(token); + return; + } + m_tree.generateImpliedEndTags(); + if (m_tree.currentElement() != node.get()) + parseError(token); + m_tree.openElements()->remove(node.get()); + } + if (token.name() == pTag) { + if (!m_tree.openElements()->inButtonScope(token.name())) { + parseError(token); + processFakeStartTag(pTag); + ASSERT(m_tree.openElements()->inScope(token.name())); + processEndTag(token); + return; + } + m_tree.generateImpliedEndTagsWithExclusion(token.name()); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilPopped(token.name()); + return; + } + if (token.name() == liTag) { + if (!m_tree.openElements()->inListItemScope(token.name())) { + parseError(token); + return; + } + m_tree.generateImpliedEndTagsWithExclusion(token.name()); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilPopped(token.name()); + return; + } + if (token.name() == ddTag + || token.name() == dtTag) { + if (!m_tree.openElements()->inScope(token.name())) { + parseError(token); + return; + } + m_tree.generateImpliedEndTagsWithExclusion(token.name()); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilPopped(token.name()); + return; + } + if (isNumberedHeaderTag(token.name())) { + if (!m_tree.openElements()->hasNumberedHeaderElementInScope()) { + parseError(token); + return; + } + m_tree.generateImpliedEndTags(); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilNumberedHeaderElementPopped(); + return; + } + if (isFormattingTag(token.name())) { + callTheAdoptionAgency(token); + return; + } + if (token.name() == appletTag + || token.name() == marqueeTag + || token.name() == objectTag) { + if (!m_tree.openElements()->inScope(token.name())) { + parseError(token); + return; + } + m_tree.generateImpliedEndTags(); + if (!m_tree.currentNode()->hasLocalName(token.name())) + parseError(token); + m_tree.openElements()->popUntilPopped(token.name()); + m_tree.activeFormattingElements()->clearToLastMarker(); + return; + } + if (token.name() == brTag) { + parseError(token); + processFakeStartTag(brTag); + return; + } + processAnyOtherEndTagForInBody(token); +} + +bool HTMLTreeBuilder::processCaptionEndTagForInCaption() +{ + if (!m_tree.openElements()->inTableScope(captionTag.localName())) { + ASSERT(isParsingFragment()); + // FIXME: parse error + return false; + } + m_tree.generateImpliedEndTags(); + // FIXME: parse error if (!m_tree.currentElement()->hasTagName(captionTag)) + m_tree.openElements()->popUntilPopped(captionTag.localName()); + m_tree.activeFormattingElements()->clearToLastMarker(); + setInsertionMode(InTableMode); + return true; +} + +bool HTMLTreeBuilder::processTrEndTagForInRow() +{ + if (!m_tree.openElements()->inTableScope(trTag.localName())) { + ASSERT(isParsingFragment()); + // FIXME: parse error + return false; + } + m_tree.openElements()->popUntilTableRowScopeMarker(); + ASSERT(m_tree.currentElement()->hasTagName(trTag)); + m_tree.openElements()->pop(); + setInsertionMode(InTableBodyMode); + return true; +} + +bool HTMLTreeBuilder::processTableEndTagForInTable() +{ + if (!m_tree.openElements()->inTableScope(tableTag)) { + ASSERT(isParsingFragment()); + // FIXME: parse error. + return false; + } + m_tree.openElements()->popUntilPopped(tableTag.localName()); + resetInsertionModeAppropriately(); + return true; +} + +void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + if (token.name() == tableTag) { + processTableEndTagForInTable(); + return; + } + if (token.name() == bodyTag + || isCaptionColOrColgroupTag(token.name()) + || token.name() == htmlTag + || isTableBodyContextTag(token.name()) + || isTableCellContextTag(token.name()) + || token.name() == trTag) { + parseError(token); + return; + } + // Is this redirection necessary here? + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); + processEndTagForInBody(token); +} + +void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndTag); + switch (insertionMode()) { + case InitialMode: + ASSERT(insertionMode() == InitialMode); + defaultForInitial(); + // Fall through. + case BeforeHTMLMode: + ASSERT(insertionMode() == BeforeHTMLMode); + if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { + parseError(token); + return; + } + defaultForBeforeHTML(); + // Fall through. + case BeforeHeadMode: + ASSERT(insertionMode() == BeforeHeadMode); + if (token.name() != headTag && token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { + parseError(token); + return; + } + defaultForBeforeHead(); + // Fall through. + case InHeadMode: + ASSERT(insertionMode() == InHeadMode); + if (token.name() == headTag) { + m_tree.openElements()->popHTMLHeadElement(); + setInsertionMode(AfterHeadMode); + return; + } + if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { + parseError(token); + return; + } + defaultForInHead(); + // Fall through. + case AfterHeadMode: + ASSERT(insertionMode() == AfterHeadMode); + if (token.name() != bodyTag && token.name() != htmlTag && token.name() != brTag) { + parseError(token); + return; + } + defaultForAfterHead(); + // Fall through + case InBodyMode: + ASSERT(insertionMode() == InBodyMode); + processEndTagForInBody(token); + break; + case InTableMode: + ASSERT(insertionMode() == InTableMode); + processEndTagForInTable(token); + break; + case InCaptionMode: + ASSERT(insertionMode() == InCaptionMode); + if (token.name() == captionTag) { + processCaptionEndTagForInCaption(); + return; + } + if (token.name() == tableTag) { + parseError(token); + if (!processCaptionEndTagForInCaption()) { + ASSERT(isParsingFragment()); + return; + } + processEndTag(token); + return; + } + if (token.name() == bodyTag + || token.name() == colTag + || token.name() == colgroupTag + || token.name() == htmlTag + || isTableBodyContextTag(token.name()) + || isTableCellContextTag(token.name()) + || token.name() == trTag) { + parseError(token); + return; + } + processEndTagForInBody(token); + break; + case InColumnGroupMode: + ASSERT(insertionMode() == InColumnGroupMode); + if (token.name() == colgroupTag) { + processColgroupEndTagForInColumnGroup(); + return; + } + if (token.name() == colTag) { + parseError(token); + return; + } + if (!processColgroupEndTagForInColumnGroup()) { + ASSERT(isParsingFragment()); + return; + } + processEndTag(token); + break; + case InRowMode: + ASSERT(insertionMode() == InRowMode); + processEndTagForInRow(token); + break; + case InCellMode: + ASSERT(insertionMode() == InCellMode); + processEndTagForInCell(token); + break; + case InTableBodyMode: + ASSERT(insertionMode() == InTableBodyMode); + processEndTagForInTableBody(token); + break; + case AfterBodyMode: + ASSERT(insertionMode() == AfterBodyMode); + if (token.name() == htmlTag) { + if (isParsingFragment()) { + parseError(token); + return; + } + setInsertionMode(AfterAfterBodyMode); + return; + } + // Fall through. + case AfterAfterBodyMode: + ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); + parseError(token); + setInsertionMode(InBodyMode); + processEndTag(token); + break; + case InHeadNoscriptMode: + ASSERT(insertionMode() == InHeadNoscriptMode); + if (token.name() == noscriptTag) { + ASSERT(m_tree.currentElement()->hasTagName(noscriptTag)); + m_tree.openElements()->pop(); + ASSERT(m_tree.currentElement()->hasTagName(headTag)); + setInsertionMode(InHeadMode); + return; + } + if (token.name() != brTag) { + parseError(token); + return; + } + defaultForInHeadNoscript(); + processToken(token); + break; + case TextMode: + if (token.name() == scriptTag) { + // Pause ourselves so that parsing stops until the script can be processed by the caller. + m_isPaused = true; + ASSERT(m_tree.currentElement()->hasTagName(scriptTag)); + m_scriptToProcess = m_tree.currentElement(); + m_scriptToProcessStartPosition = m_lastScriptElementStartPosition; + m_tree.openElements()->pop(); + if (isParsingFragment() && m_fragmentContext.scriptingPermission() == FragmentScriptingNotAllowed) + m_scriptToProcess->removeAllChildren(); + setInsertionMode(m_originalInsertionMode); + + // This token will not have been created by the tokenizer if a + // self-closing script tag was encountered and pre-HTML5 parser + // quirks are enabled. We must set the tokenizer's state to + // DataState explicitly if the tokenizer didn't have a chance to. + ASSERT(m_parser->tokenizer()->state() == HTMLTokenizerState::DataState || m_usePreHTML5ParserQuirks); + m_parser->tokenizer()->setState(HTMLTokenizerState::DataState); + return; + } + m_tree.openElements()->pop(); + setInsertionMode(m_originalInsertionMode); + break; + case InFramesetMode: + ASSERT(insertionMode() == InFramesetMode); + if (token.name() == framesetTag) { + if (m_tree.currentNode() == m_tree.openElements()->rootNode()) { + parseError(token); + return; + } + m_tree.openElements()->pop(); + if (!isParsingFragment() && !m_tree.currentElement()->hasTagName(framesetTag)) + setInsertionMode(AfterFramesetMode); + return; + } + break; + case AfterFramesetMode: + ASSERT(insertionMode() == AfterFramesetMode); + if (token.name() == htmlTag) { + setInsertionMode(AfterAfterFramesetMode); + return; + } + // Fall through. + case AfterAfterFramesetMode: + ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); + parseError(token); + break; + case InSelectInTableMode: + ASSERT(insertionMode() == InSelectInTableMode); + if (token.name() == captionTag + || token.name() == tableTag + || isTableBodyContextTag(token.name()) + || token.name() == trTag + || isTableCellContextTag(token.name())) { + parseError(token); + if (m_tree.openElements()->inTableScope(token.name())) { + AtomicHTMLToken endSelect(HTMLTokenTypes::EndTag, selectTag.localName()); + processEndTag(endSelect); + processEndTag(token); + } + return; + } + // Fall through. + case InSelectMode: + ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); + if (token.name() == optgroupTag) { + if (m_tree.currentNode()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag)) + processFakeEndTag(optionTag); + if (m_tree.currentNode()->hasTagName(optgroupTag)) { + m_tree.openElements()->pop(); + return; + } + parseError(token); + return; + } + if (token.name() == optionTag) { + if (m_tree.currentNode()->hasTagName(optionTag)) { + m_tree.openElements()->pop(); + return; + } + parseError(token); + return; + } + if (token.name() == selectTag) { + if (!m_tree.openElements()->inSelectScope(token.name())) { + ASSERT(isParsingFragment()); + parseError(token); + return; + } + m_tree.openElements()->popUntilPopped(selectTag.localName()); + resetInsertionModeAppropriately(); + return; + } + break; + case InTableTextMode: + defaultForInTableText(); + processEndTag(token); + break; + } +} + +void HTMLTreeBuilder::processComment(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::Comment); + if (m_insertionMode == InitialMode + || m_insertionMode == BeforeHTMLMode + || m_insertionMode == AfterAfterBodyMode + || m_insertionMode == AfterAfterFramesetMode) { + m_tree.insertCommentOnDocument(token); + return; + } + if (m_insertionMode == AfterBodyMode) { + m_tree.insertCommentOnHTMLHtmlElement(token); + return; + } + if (m_insertionMode == InTableTextMode) { + defaultForInTableText(); + processComment(token); + return; + } + m_tree.insertComment(token); +} + +void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::Character); + ExternalCharacterTokenBuffer buffer(token); + processCharacterBuffer(buffer); +} + +void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer) +{ +ReprocessBuffer: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody + // Note that this logic is different than the generic \r\n collapsing + // handled in the input stream preprocessor. This logic is here as an + // "authoring convenience" so folks can write: + // + // <pre> + // lorem ipsum + // lorem ipsum + // </pre> + // + // without getting an extra newline at the start of their <pre> element. + if (m_shouldSkipLeadingNewline) { + m_shouldSkipLeadingNewline = false; + buffer.skipAtMostOneLeadingNewline(); + if (buffer.isEmpty()) + return; + } + + switch (insertionMode()) { + case InitialMode: { + ASSERT(insertionMode() == InitialMode); + buffer.skipLeadingWhitespace(); + if (buffer.isEmpty()) + return; + defaultForInitial(); + // Fall through. + } + case BeforeHTMLMode: { + ASSERT(insertionMode() == BeforeHTMLMode); + buffer.skipLeadingWhitespace(); + if (buffer.isEmpty()) + return; + defaultForBeforeHTML(); + // Fall through. + } + case BeforeHeadMode: { + ASSERT(insertionMode() == BeforeHeadMode); + buffer.skipLeadingWhitespace(); + if (buffer.isEmpty()) + return; + defaultForBeforeHead(); + // Fall through. + } + case InHeadMode: { + ASSERT(insertionMode() == InHeadMode); + String leadingWhitespace = buffer.takeLeadingWhitespace(); + if (!leadingWhitespace.isEmpty()) + m_tree.insertTextNode(leadingWhitespace, AllWhitespace); + if (buffer.isEmpty()) + return; + defaultForInHead(); + // Fall through. + } + case AfterHeadMode: { + ASSERT(insertionMode() == AfterHeadMode); + String leadingWhitespace = buffer.takeLeadingWhitespace(); + if (!leadingWhitespace.isEmpty()) + m_tree.insertTextNode(leadingWhitespace, AllWhitespace); + if (buffer.isEmpty()) + return; + defaultForAfterHead(); + // Fall through. + } + case InBodyMode: + case InCaptionMode: + case InCellMode: { + ASSERT(insertionMode() == InBodyMode || insertionMode() == InCaptionMode || insertionMode() == InCellMode); + processCharacterBufferForInBody(buffer); + break; + } + case InTableMode: + case InTableBodyMode: + case InRowMode: { + ASSERT(insertionMode() == InTableMode || insertionMode() == InTableBodyMode || insertionMode() == InRowMode); + ASSERT(m_pendingTableCharacters.isEmpty()); + if (m_tree.currentNode()->isElementNode() + && (m_tree.currentElement()->hasTagName(HTMLNames::tableTag) + || m_tree.currentElement()->hasTagName(HTMLNames::tbodyTag) + || m_tree.currentElement()->hasTagName(HTMLNames::tfootTag) + || m_tree.currentElement()->hasTagName(HTMLNames::theadTag) + || m_tree.currentElement()->hasTagName(HTMLNames::trTag))) { + m_originalInsertionMode = m_insertionMode; + setInsertionMode(InTableTextMode); + // Note that we fall through to the InTableTextMode case below. + } else { + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); + processCharacterBufferForInBody(buffer); + break; + } + // Fall through. + } + case InTableTextMode: { + buffer.giveRemainingTo(m_pendingTableCharacters); + break; + } + case InColumnGroupMode: { + ASSERT(insertionMode() == InColumnGroupMode); + String leadingWhitespace = buffer.takeLeadingWhitespace(); + if (!leadingWhitespace.isEmpty()) + m_tree.insertTextNode(leadingWhitespace, AllWhitespace); + if (buffer.isEmpty()) + return; + if (!processColgroupEndTagForInColumnGroup()) { + ASSERT(isParsingFragment()); + // The spec tells us to drop these characters on the floor. + buffer.skipLeadingNonWhitespace(); + if (buffer.isEmpty()) + return; + } + goto ReprocessBuffer; + } + case AfterBodyMode: + case AfterAfterBodyMode: { + ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); + // FIXME: parse error + setInsertionMode(InBodyMode); + goto ReprocessBuffer; + break; + } + case TextMode: { + ASSERT(insertionMode() == TextMode); + m_tree.insertTextNode(buffer.takeRemaining()); + break; + } + case InHeadNoscriptMode: { + ASSERT(insertionMode() == InHeadNoscriptMode); + String leadingWhitespace = buffer.takeLeadingWhitespace(); + if (!leadingWhitespace.isEmpty()) + m_tree.insertTextNode(leadingWhitespace, AllWhitespace); + if (buffer.isEmpty()) + return; + defaultForInHeadNoscript(); + goto ReprocessBuffer; + break; + } + case InFramesetMode: + case AfterFramesetMode: { + ASSERT(insertionMode() == InFramesetMode || insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); + String leadingWhitespace = buffer.takeRemainingWhitespace(); + if (!leadingWhitespace.isEmpty()) + m_tree.insertTextNode(leadingWhitespace, AllWhitespace); + // FIXME: We should generate a parse error if we skipped over any + // non-whitespace characters. + break; + } + case InSelectInTableMode: + case InSelectMode: { + ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); + m_tree.insertTextNode(buffer.takeRemaining()); + break; + } + case AfterAfterFramesetMode: { + String leadingWhitespace = buffer.takeRemainingWhitespace(); + if (!leadingWhitespace.isEmpty()) { + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertTextNode(leadingWhitespace, AllWhitespace); + } + // FIXME: We should generate a parse error if we skipped over any + // non-whitespace characters. + break; + } + } +} + +void HTMLTreeBuilder::processCharacterBufferForInBody(ExternalCharacterTokenBuffer& buffer) +{ + m_tree.reconstructTheActiveFormattingElements(); + String characters = buffer.takeRemaining(); + m_tree.insertTextNode(characters); + if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters)) + m_framesetOk = false; +} + +void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::EndOfFile); + switch (insertionMode()) { + case InitialMode: + ASSERT(insertionMode() == InitialMode); + defaultForInitial(); + // Fall through. + case BeforeHTMLMode: + ASSERT(insertionMode() == BeforeHTMLMode); + defaultForBeforeHTML(); + // Fall through. + case BeforeHeadMode: + ASSERT(insertionMode() == BeforeHeadMode); + defaultForBeforeHead(); + // Fall through. + case InHeadMode: + ASSERT(insertionMode() == InHeadMode); + defaultForInHead(); + // Fall through. + case AfterHeadMode: + ASSERT(insertionMode() == AfterHeadMode); + defaultForAfterHead(); + // Fall through + case InBodyMode: + case InCellMode: + case InCaptionMode: + case InRowMode: + ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode || insertionMode() == InCaptionMode || insertionMode() == InRowMode); + notImplemented(); // Emit parse error based on what elements are still open. + break; + case AfterBodyMode: + case AfterAfterBodyMode: + ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); + break; + case InHeadNoscriptMode: + ASSERT(insertionMode() == InHeadNoscriptMode); + defaultForInHeadNoscript(); + processEndOfFile(token); + return; + case AfterFramesetMode: + case AfterAfterFramesetMode: + ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); + break; + case InFramesetMode: + case InTableMode: + case InTableBodyMode: + case InSelectInTableMode: + case InSelectMode: + ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode); + if (m_tree.currentNode() != m_tree.openElements()->rootNode()) + parseError(token); + break; + case InColumnGroupMode: + if (m_tree.currentNode() == m_tree.openElements()->rootNode()) { + ASSERT(isParsingFragment()); + return; // FIXME: Should we break here instead of returning? + } + if (!processColgroupEndTagForInColumnGroup()) { + ASSERT(isParsingFragment()); + return; // FIXME: Should we break here instead of returning? + } + processEndOfFile(token); + return; + case InTableTextMode: + defaultForInTableText(); + processEndOfFile(token); + return; + case TextMode: + parseError(token); + if (m_tree.currentNode()->hasTagName(scriptTag)) + notImplemented(); // mark the script element as "already started". + m_tree.openElements()->pop(); + ASSERT(m_originalInsertionMode != TextMode); + setInsertionMode(m_originalInsertionMode); + processEndOfFile(token); + return; + } + ASSERT(m_tree.currentNode()); + m_tree.openElements()->popAll(); +} + +void HTMLTreeBuilder::defaultForInitial() +{ + notImplemented(); + if (!m_fragmentContext.fragment()) + m_document->setCompatibilityMode(Document::QuirksMode); + // FIXME: parse error + setInsertionMode(BeforeHTMLMode); +} + +void HTMLTreeBuilder::defaultForBeforeHTML() +{ + AtomicHTMLToken startHTML(HTMLTokenTypes::StartTag, htmlTag.localName()); + m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML); + setInsertionMode(BeforeHeadMode); +} + +void HTMLTreeBuilder::defaultForBeforeHead() +{ + AtomicHTMLToken startHead(HTMLTokenTypes::StartTag, headTag.localName()); + processStartTag(startHead); +} + +void HTMLTreeBuilder::defaultForInHead() +{ + AtomicHTMLToken endHead(HTMLTokenTypes::EndTag, headTag.localName()); + processEndTag(endHead); +} + +void HTMLTreeBuilder::defaultForInHeadNoscript() +{ + AtomicHTMLToken endNoscript(HTMLTokenTypes::EndTag, noscriptTag.localName()); + processEndTag(endNoscript); +} + +void HTMLTreeBuilder::defaultForAfterHead() +{ + AtomicHTMLToken startBody(HTMLTokenTypes::StartTag, bodyTag.localName()); + processStartTag(startBody); + m_framesetOk = true; +} + +void HTMLTreeBuilder::defaultForInTableText() +{ + String characters = m_pendingTableCharacters.toString(); + m_pendingTableCharacters.clear(); + if (!isAllWhitespace(characters)) { + // FIXME: parse error + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); + m_tree.reconstructTheActiveFormattingElements(); + m_tree.insertTextNode(characters, NotAllWhitespace); + m_framesetOk = false; + setInsertionMode(m_originalInsertionMode); + return; + } + m_tree.insertTextNode(characters); + setInsertionMode(m_originalInsertionMode); +} + +bool HTMLTreeBuilder::processStartTagForInHead(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + if (token.name() == htmlTag) { + m_tree.insertHTMLHtmlStartTagInBody(token); + return true; + } + if (token.name() == baseTag + || token.name() == basefontTag + || token.name() == bgsoundTag + || token.name() == commandTag + || token.name() == linkTag + || token.name() == metaTag) { + m_tree.insertSelfClosingHTMLElement(token); + // Note: The custom processing for the <meta> tag is done in HTMLMetaElement::process(). + return true; + } + if (token.name() == titleTag) { + processGenericRCDATAStartTag(token); + return true; + } + if (token.name() == noscriptTag) { + if (scriptEnabled(m_document->frame())) { + processGenericRawTextStartTag(token); + return true; + } + m_tree.insertHTMLElement(token); + setInsertionMode(InHeadNoscriptMode); + return true; + } + if (token.name() == noframesTag || token.name() == styleTag) { + processGenericRawTextStartTag(token); + return true; + } + if (token.name() == scriptTag) { + processScriptStartTag(token); + if (m_usePreHTML5ParserQuirks && token.selfClosing()) + processFakeEndTag(scriptTag); + return true; + } + if (token.name() == headTag) { + parseError(token); + return true; + } + return false; +} + +void HTMLTreeBuilder::processGenericRCDATAStartTag(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + m_tree.insertHTMLElement(token); + m_parser->tokenizer()->setState(HTMLTokenizerState::RCDATAState); + m_originalInsertionMode = m_insertionMode; + setInsertionMode(TextMode); +} + +void HTMLTreeBuilder::processGenericRawTextStartTag(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + m_tree.insertHTMLElement(token); + m_parser->tokenizer()->setState(HTMLTokenizerState::RAWTEXTState); + m_originalInsertionMode = m_insertionMode; + setInsertionMode(TextMode); +} + +void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token) +{ + ASSERT(token.type() == HTMLTokenTypes::StartTag); + m_tree.insertScriptElement(token); + m_parser->tokenizer()->setState(HTMLTokenizerState::ScriptDataState); + m_originalInsertionMode = m_insertionMode; + + TextPosition position = m_parser->textPosition(); + + ASSERT(position.m_line == m_parser->tokenizer()->lineNumber()); + + m_lastScriptElementStartPosition = position; + + setInsertionMode(TextMode); +} + +// http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#tree-construction +bool HTMLTreeBuilder::shouldProcessTokenInForeignContent(AtomicHTMLToken& token) +{ + if (m_tree.isEmpty()) + return false; + ContainerNode* node = m_tree.currentNode(); + if (isInHTMLNamespace(node)) + return false; + if (HTMLElementStack::isMathMLTextIntegrationPoint(node)) { + if (token.type() == HTMLTokenTypes::StartTag + && token.name() != MathMLNames::mglyphTag + && token.name() != MathMLNames::malignmarkTag) + return false; + if (token.type() == HTMLTokenTypes::Character) + return false; + } + if (node->hasTagName(MathMLNames::annotation_xmlTag) + && token.type() == HTMLTokenTypes::StartTag + && token.name() == SVGNames::svgTag) + return false; + if (HTMLElementStack::isHTMLIntegrationPoint(node)) { + if (token.type() == HTMLTokenTypes::StartTag) + return false; + if (token.type() == HTMLTokenTypes::Character) + return false; + } + if (token.type() == HTMLTokenTypes::EndOfFile) + return false; + return true; +} + +void HTMLTreeBuilder::processTokenInForeignContent(AtomicHTMLToken& token) +{ + switch (token.type()) { + case HTMLTokenTypes::Uninitialized: + ASSERT_NOT_REACHED(); + break; + case HTMLTokenTypes::DOCTYPE: + parseError(token); + break; + case HTMLTokenTypes::StartTag: { + if (token.name() == bTag + || token.name() == bigTag + || token.name() == blockquoteTag + || token.name() == bodyTag + || token.name() == brTag + || token.name() == centerTag + || token.name() == codeTag + || token.name() == ddTag + || token.name() == divTag + || token.name() == dlTag + || token.name() == dtTag + || token.name() == emTag + || token.name() == embedTag + || isNumberedHeaderTag(token.name()) + || token.name() == headTag + || token.name() == hrTag + || token.name() == iTag + || token.name() == imgTag + || token.name() == liTag + || token.name() == listingTag + || token.name() == menuTag + || token.name() == metaTag + || token.name() == nobrTag + || token.name() == olTag + || token.name() == pTag + || token.name() == preTag + || token.name() == rubyTag + || token.name() == sTag + || token.name() == smallTag + || token.name() == spanTag + || token.name() == strongTag + || token.name() == strikeTag + || token.name() == subTag + || token.name() == supTag + || token.name() == tableTag + || token.name() == ttTag + || token.name() == uTag + || token.name() == ulTag + || token.name() == varTag + || (token.name() == fontTag && (token.getAttributeItem(colorAttr) || token.getAttributeItem(faceAttr) || token.getAttributeItem(sizeAttr)))) { + parseError(token); + m_tree.openElements()->popUntilForeignContentScopeMarker(); + processStartTag(token); + return; + } + const AtomicString& currentNamespace = m_tree.currentElement()->namespaceURI(); + if (currentNamespace == MathMLNames::mathmlNamespaceURI) + adjustMathMLAttributes(token); + if (currentNamespace == SVGNames::svgNamespaceURI) { + adjustSVGTagNameCase(token); + adjustSVGAttributes(token); + } + adjustForeignAttributes(token); + m_tree.insertForeignElement(token, currentNamespace); + break; + } + case HTMLTokenTypes::EndTag: { + if (m_tree.currentNode()->namespaceURI() == SVGNames::svgNamespaceURI) + adjustSVGTagNameCase(token); + + if (token.name() == SVGNames::scriptTag && m_tree.currentNode()->hasTagName(SVGNames::scriptTag)) { + m_isPaused = true; + m_scriptToProcess = m_tree.currentElement(); + m_tree.openElements()->pop(); + return; + } + if (!isInHTMLNamespace(m_tree.currentNode())) { + // FIXME: This code just wants an Element* iterator, instead of an ElementRecord* + HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); + if (!nodeRecord->node()->hasLocalName(token.name())) + parseError(token); + while (1) { + if (nodeRecord->node()->hasLocalName(token.name())) { + m_tree.openElements()->popUntilPopped(nodeRecord->element()); + return; + } + nodeRecord = nodeRecord->next(); + + if (isInHTMLNamespace(nodeRecord->node())) + break; + } + } + // Otherwise, process the token according to the rules given in the section corresponding to the current insertion mode in HTML content. + processEndTag(token); + break; + } + case HTMLTokenTypes::Comment: + m_tree.insertComment(token); + return; + case HTMLTokenTypes::Character: { + String characters = String(token.characters().data(), token.characters().size()); + m_tree.insertTextNode(characters); + if (m_framesetOk && !isAllWhitespaceOrReplacementCharacters(characters)) + m_framesetOk = false; + break; + } + case HTMLTokenTypes::EndOfFile: + ASSERT_NOT_REACHED(); + break; + } +} + +void HTMLTreeBuilder::finished() +{ + if (isParsingFragment()) + return; + + ASSERT(m_document); + // Warning, this may detach the parser. Do not do anything else after this. + m_document->finishedParsing(); +} + +void HTMLTreeBuilder::parseError(AtomicHTMLToken&) +{ +} + +bool HTMLTreeBuilder::scriptEnabled(Frame* frame) +{ + if (!frame) + return false; + return frame->script()->canExecuteScripts(NotAboutToExecuteScript); +} + +bool HTMLTreeBuilder::pluginsEnabled(Frame* frame) +{ + if (!frame) + return false; + return frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin); +} + +} diff --git a/Source/WebCore/html/parser/HTMLTreeBuilder.h b/Source/WebCore/html/parser/HTMLTreeBuilder.h new file mode 100644 index 000000000..0183d20ca --- /dev/null +++ b/Source/WebCore/html/parser/HTMLTreeBuilder.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * Copyright (C) 2011 Apple 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 GOOGLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLTreeBuilder_h +#define HTMLTreeBuilder_h + +#include "Element.h" +#include "FragmentScriptingPermission.h" +#include "HTMLConstructionSite.h" +#include "HTMLElementStack.h" +#include "HTMLFormattingElementList.h" +#include "HTMLTokenizer.h" +#include <wtf/Noncopyable.h> +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/TextPosition.h> +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + +class AtomicHTMLToken; +class Document; +class DocumentFragment; +class Frame; +class HTMLToken; +class HTMLDocument; +class Node; +class HTMLDocumentParser; + +class HTMLTreeBuilder { + WTF_MAKE_NONCOPYABLE(HTMLTreeBuilder); WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<HTMLTreeBuilder> create(HTMLDocumentParser* parser, HTMLDocument* document, bool reportErrors, bool usePreHTML5ParserQuirks, unsigned maximumDOMTreeDepth) + { + return adoptPtr(new HTMLTreeBuilder(parser, document, reportErrors, usePreHTML5ParserQuirks, maximumDOMTreeDepth)); + } + static PassOwnPtr<HTMLTreeBuilder> create(HTMLDocumentParser* parser, DocumentFragment* fragment, Element* contextElement, FragmentScriptingPermission scriptingPermission, bool usePreHTML5ParserQuirks, unsigned maximumDOMTreeDepth) + { + return adoptPtr(new HTMLTreeBuilder(parser, fragment, contextElement, scriptingPermission, usePreHTML5ParserQuirks, maximumDOMTreeDepth)); + } + ~HTMLTreeBuilder(); + + bool isParsingFragment() const { return !!m_fragmentContext.fragment(); } + + void detach(); + + void setPaused(bool paused) { m_isPaused = paused; } + bool isPaused() const { return m_isPaused; } + + // The token really should be passed as a const& since it's never modified. + void constructTreeFromToken(HTMLToken&); + void constructTreeFromAtomicToken(AtomicHTMLToken&); + + // Must be called when parser is paused before calling the parser again. + PassRefPtr<Element> takeScriptToProcess(TextPosition& scriptStartPosition); + + // Done, close any open tags, etc. + void finished(); + + void setShouldSkipLeadingNewline(bool shouldSkip) { m_shouldSkipLeadingNewline = shouldSkip; } + + static bool scriptEnabled(Frame*); + static bool pluginsEnabled(Frame*); + +private: + class ExternalCharacterTokenBuffer; + // Represents HTML5 "insertion mode" + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#insertion-mode + enum InsertionMode { + InitialMode, + BeforeHTMLMode, + BeforeHeadMode, + InHeadMode, + InHeadNoscriptMode, + AfterHeadMode, + InBodyMode, + TextMode, + InTableMode, + InTableTextMode, + InCaptionMode, + InColumnGroupMode, + InTableBodyMode, + InRowMode, + InCellMode, + InSelectMode, + InSelectInTableMode, + AfterBodyMode, + InFramesetMode, + AfterFramesetMode, + AfterAfterBodyMode, + AfterAfterFramesetMode, + }; + + HTMLTreeBuilder(HTMLDocumentParser*, HTMLDocument*, bool reportErrors, bool usePreHTML5ParserQuirks, unsigned maximumDOMTreeDepth); + HTMLTreeBuilder(HTMLDocumentParser*, DocumentFragment*, Element* contextElement, FragmentScriptingPermission, bool usePreHTML5ParserQuirks, unsigned maximumDOMTreeDepth); + + void processToken(AtomicHTMLToken&); + + void processDoctypeToken(AtomicHTMLToken&); + void processStartTag(AtomicHTMLToken&); + void processEndTag(AtomicHTMLToken&); + void processComment(AtomicHTMLToken&); + void processCharacter(AtomicHTMLToken&); + void processEndOfFile(AtomicHTMLToken&); + + bool processStartTagForInHead(AtomicHTMLToken&); + void processStartTagForInBody(AtomicHTMLToken&); + void processStartTagForInTable(AtomicHTMLToken&); + void processEndTagForInBody(AtomicHTMLToken&); + void processEndTagForInTable(AtomicHTMLToken&); + void processEndTagForInTableBody(AtomicHTMLToken&); + void processEndTagForInRow(AtomicHTMLToken&); + void processEndTagForInCell(AtomicHTMLToken&); + + void processIsindexStartTagForInBody(AtomicHTMLToken&); + bool processBodyEndTagForInBody(AtomicHTMLToken&); + bool processTableEndTagForInTable(); + bool processCaptionEndTagForInCaption(); + bool processColgroupEndTagForInColumnGroup(); + bool processTrEndTagForInRow(); + // FIXME: This function should be inlined into its one call site or it + // needs to assert which tokens it can be called with. + void processAnyOtherEndTagForInBody(AtomicHTMLToken&); + + void processCharacterBuffer(ExternalCharacterTokenBuffer&); + inline void processCharacterBufferForInBody(ExternalCharacterTokenBuffer&); + + void processFakeStartTag(const QualifiedName&, PassOwnPtr<NamedNodeMap> attributes = nullptr); + void processFakeEndTag(const QualifiedName&); + void processFakeCharacters(const String&); + void processFakePEndTagIfPInButtonScope(); + + void processGenericRCDATAStartTag(AtomicHTMLToken&); + void processGenericRawTextStartTag(AtomicHTMLToken&); + void processScriptStartTag(AtomicHTMLToken&); + + // Default processing for the different insertion modes. + void defaultForInitial(); + void defaultForBeforeHTML(); + void defaultForBeforeHead(); + void defaultForInHead(); + void defaultForInHeadNoscript(); + void defaultForAfterHead(); + void defaultForInTableText(); + + inline bool shouldProcessTokenInForeignContent(AtomicHTMLToken&); + void processTokenInForeignContent(AtomicHTMLToken&); + + PassOwnPtr<NamedNodeMap> attributesForIsindexInput(AtomicHTMLToken&); + + HTMLElementStack::ElementRecord* furthestBlockForFormattingElement(Element*); + void callTheAdoptionAgency(AtomicHTMLToken&); + + void closeTheCell(); + + template <bool shouldClose(const ContainerNode*)> + void processCloseWhenNestedTag(AtomicHTMLToken&); + + bool m_framesetOk; + + void parseError(AtomicHTMLToken&); + + InsertionMode insertionMode() const { return m_insertionMode; } + void setInsertionMode(InsertionMode mode) { m_insertionMode = mode; } + + void resetInsertionModeAppropriately(); + + class FragmentParsingContext { + WTF_MAKE_NONCOPYABLE(FragmentParsingContext); + public: + FragmentParsingContext(); + FragmentParsingContext(DocumentFragment*, Element* contextElement, FragmentScriptingPermission); + ~FragmentParsingContext(); + + DocumentFragment* fragment() const { return m_fragment; } + Element* contextElement() const { ASSERT(m_fragment); return m_contextElement; } + FragmentScriptingPermission scriptingPermission() const { ASSERT(m_fragment); return m_scriptingPermission; } + + private: + DocumentFragment* m_fragment; + Element* m_contextElement; + + // FragmentScriptingNotAllowed causes the Parser to remove children + // from <script> tags (so javascript doesn't show up in pastes). + FragmentScriptingPermission m_scriptingPermission; + }; + + FragmentParsingContext m_fragmentContext; + + Document* m_document; + HTMLConstructionSite m_tree; + + bool m_reportErrors; + bool m_isPaused; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#insertion-mode + InsertionMode m_insertionMode; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#original-insertion-mode + InsertionMode m_originalInsertionMode; + + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#pending-table-character-tokens + StringBuilder m_pendingTableCharacters; + + bool m_shouldSkipLeadingNewline; + + // We access parser because HTML5 spec requires that we be able to change the state of the tokenizer + // from within parser actions. We also need it to track the current position. + HTMLDocumentParser* m_parser; + + RefPtr<Element> m_scriptToProcess; // <script> tag which needs processing before resuming the parser. + TextPosition m_scriptToProcessStartPosition; // Starting line number of the script tag needing processing. + + // FIXME: We probably want to remove this member. Originally, it was + // created to service the legacy tree builder, but it seems to be used for + // some other things now. + TextPosition m_lastScriptElementStartPosition; + + bool m_usePreHTML5ParserQuirks; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/HTMLViewSourceParser.cpp b/Source/WebCore/html/parser/HTMLViewSourceParser.cpp new file mode 100644 index 000000000..0e31fc9ef --- /dev/null +++ b/Source/WebCore/html/parser/HTMLViewSourceParser.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLViewSourceParser.h" + +#include "HTMLDocumentParser.h" +#include "HTMLNames.h" +#include "HTMLViewSourceDocument.h" + +namespace WebCore { + +HTMLViewSourceParser::HTMLViewSourceParser(HTMLViewSourceDocument* document) + : DecodedDataDocumentParser(document) + , m_tokenizer(HTMLTokenizer::create(HTMLDocumentParser::usePreHTML5ParserQuirks(document))) +{ +} + +HTMLViewSourceParser::~HTMLViewSourceParser() +{ +} + +void HTMLViewSourceParser::insert(const SegmentedString&) +{ + ASSERT_NOT_REACHED(); +} + +void HTMLViewSourceParser::pumpTokenizer() +{ + while (true) { + m_sourceTracker.start(m_input, m_tokenizer.get(), m_token); + if (!m_tokenizer->nextToken(m_input.current(), m_token)) + break; + m_sourceTracker.end(m_input, m_tokenizer.get(), m_token); + + document()->addSource(sourceForToken(), m_token); + updateTokenizerState(); + m_token.clear(); + } +} + +void HTMLViewSourceParser::append(const SegmentedString& input) +{ + m_input.appendToEnd(input); + pumpTokenizer(); +} + +String HTMLViewSourceParser::sourceForToken() +{ + return m_sourceTracker.sourceForToken(m_token); +} + +void HTMLViewSourceParser::updateTokenizerState() +{ + // FIXME: The tokenizer should do this work for us. + if (m_token.type() != HTMLTokenTypes::StartTag) + return; + + AtomicString tagName(m_token.name().data(), m_token.name().size()); + m_tokenizer->updateStateFor(tagName, document()->frame()); +} + +void HTMLViewSourceParser::finish() +{ + if (!m_input.haveSeenEndOfFile()) + m_input.markEndOfFile(); + pumpTokenizer(); + document()->finishedParsing(); +} + +bool HTMLViewSourceParser::finishWasCalled() +{ + return m_input.haveSeenEndOfFile(); +} + +} diff --git a/Source/WebCore/html/parser/HTMLViewSourceParser.h b/Source/WebCore/html/parser/HTMLViewSourceParser.h new file mode 100644 index 000000000..2e6ddfe7a --- /dev/null +++ b/Source/WebCore/html/parser/HTMLViewSourceParser.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLViewSourceParser_h +#define HTMLViewSourceParser_h + +#include "DecodedDataDocumentParser.h" +#include "HTMLInputStream.h" +#include "HTMLSourceTracker.h" +#include "HTMLToken.h" +#include "HTMLTokenizer.h" +#include "HTMLViewSourceDocument.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class HTMLTokenizer; +class HTMLScriptRunner; +class HTMLTreeBuilder; +class HTMLPreloadScanner; +class ScriptController; +class ScriptSourceCode; + +class HTMLViewSourceParser : public DecodedDataDocumentParser { +public: + static PassRefPtr<HTMLViewSourceParser> create(HTMLViewSourceDocument* document) + { + return adoptRef(new HTMLViewSourceParser(document)); + } + virtual ~HTMLViewSourceParser(); + +protected: + explicit HTMLViewSourceParser(HTMLViewSourceDocument*); + + HTMLTokenizer* tokenizer() const { return m_tokenizer.get(); } + +private: + // DocumentParser + virtual void insert(const SegmentedString&); + virtual void append(const SegmentedString&); + virtual void finish(); + virtual bool finishWasCalled(); + + HTMLViewSourceDocument* document() const { return static_cast<HTMLViewSourceDocument*>(DecodedDataDocumentParser::document()); } + + void pumpTokenizer(); + String sourceForToken(); + void updateTokenizerState(); + + HTMLInputStream m_input; + HTMLToken m_token; + HTMLSourceTracker m_sourceTracker; + OwnPtr<HTMLTokenizer> m_tokenizer; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/NestingLevelIncrementer.h b/Source/WebCore/html/parser/NestingLevelIncrementer.h new file mode 100644 index 000000000..bf08425be --- /dev/null +++ b/Source/WebCore/html/parser/NestingLevelIncrementer.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef NestingLevelIncrementer_h +#define NestingLevelIncrementer_h + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class NestingLevelIncrementer { + WTF_MAKE_NONCOPYABLE(NestingLevelIncrementer); +public: + explicit NestingLevelIncrementer(unsigned& nestingLevel) + : m_nestingLevel(&nestingLevel) + { + ++(*m_nestingLevel); + } + + ~NestingLevelIncrementer() + { + --(*m_nestingLevel); + } + +private: + unsigned* m_nestingLevel; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/TextDocumentParser.cpp b/Source/WebCore/html/parser/TextDocumentParser.cpp new file mode 100644 index 000000000..1c55fc254 --- /dev/null +++ b/Source/WebCore/html/parser/TextDocumentParser.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextDocumentParser.h" + +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLTokenizer.h" +#include "HTMLTreeBuilder.h" + +namespace WebCore { + +using namespace HTMLNames; + +TextDocumentParser::TextDocumentParser(HTMLDocument* document) + : HTMLDocumentParser(document, false) + , m_haveInsertedFakePreElement(false) +{ + tokenizer()->setState(HTMLTokenizerState::PLAINTEXTState); +} + +TextDocumentParser::~TextDocumentParser() +{ +} + +void TextDocumentParser::append(const SegmentedString& text) +{ + if (!m_haveInsertedFakePreElement) + insertFakePreElement(); + HTMLDocumentParser::append(text); +} + +void TextDocumentParser::insertFakePreElement() +{ + // In principle, we should create a specialized tree builder for + // TextDocuments, but instead we re-use the existing HTMLTreeBuilder. + // We create a fake token and give it to the tree builder rather than + // sending fake bytes through the front-end of the parser to avoid + // distrubing the line/column number calculations. + + RefPtr<Attribute> styleAttribute = Attribute::createMapped("style", "word-wrap: break-word; white-space: pre-wrap;"); + OwnPtr<NamedNodeMap> attributes = NamedNodeMap::create(); + attributes->insertAttribute(styleAttribute.release(), false); + AtomicHTMLToken fakePre(HTMLTokenTypes::StartTag, preTag.localName(), attributes.release()); + + treeBuilder()->constructTreeFromAtomicToken(fakePre); + // Normally we would skip the first \n after a <pre> element, but we don't + // want to skip the first \n for text documents! + treeBuilder()->setShouldSkipLeadingNewline(false); + + m_haveInsertedFakePreElement = true; +} + +} diff --git a/Source/WebCore/html/parser/TextDocumentParser.h b/Source/WebCore/html/parser/TextDocumentParser.h new file mode 100644 index 000000000..1cccc5b0f --- /dev/null +++ b/Source/WebCore/html/parser/TextDocumentParser.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 TextDocumentParser_h +#define TextDocumentParser_h + +#include "HTMLDocumentParser.h" + +namespace WebCore { + +class TextDocumentParser : public HTMLDocumentParser { +public: + static PassRefPtr<TextDocumentParser> create(HTMLDocument* document) + { + return adoptRef(new TextDocumentParser(document)); + } + virtual ~TextDocumentParser(); + +private: + explicit TextDocumentParser(HTMLDocument*); + + virtual void append(const SegmentedString&); + void insertFakePreElement(); + + bool m_haveInsertedFakePreElement; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/TextViewSourceParser.cpp b/Source/WebCore/html/parser/TextViewSourceParser.cpp new file mode 100644 index 000000000..343b23c29 --- /dev/null +++ b/Source/WebCore/html/parser/TextViewSourceParser.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextViewSourceParser.h" + +#include "HTMLTokenizer.h" + +namespace WebCore { + +TextViewSourceParser::TextViewSourceParser(HTMLViewSourceDocument* document) + : HTMLViewSourceParser(document) +{ + tokenizer()->setState(HTMLTokenizerState::PLAINTEXTState); +} + +TextViewSourceParser::~TextViewSourceParser() +{ +} + +} diff --git a/Source/WebCore/html/parser/TextViewSourceParser.h b/Source/WebCore/html/parser/TextViewSourceParser.h new file mode 100644 index 000000000..e4170ed83 --- /dev/null +++ b/Source/WebCore/html/parser/TextViewSourceParser.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextViewSourceParser_h +#define TextViewSourceParser_h + +#include "HTMLViewSourceParser.h" + +namespace WebCore { + +class TextViewSourceParser : public HTMLViewSourceParser { +public: + static PassRefPtr<TextViewSourceParser> create(HTMLViewSourceDocument* document) + { + return adoptRef(new TextViewSourceParser(document)); + } + virtual ~TextViewSourceParser(); + +private: + explicit TextViewSourceParser(HTMLViewSourceDocument*); +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/XSSAuditor.cpp b/Source/WebCore/html/parser/XSSAuditor.cpp new file mode 100644 index 000000000..c48e73e8c --- /dev/null +++ b/Source/WebCore/html/parser/XSSAuditor.cpp @@ -0,0 +1,642 @@ +/* + * Copyright (C) 2011 Adam Barth. All Rights Reserved. + * Copyright (C) 2011 Daniel Bates (dbates@intudata.com). + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "XSSAuditor.h" + +#include "Console.h" +#include "DOMWindow.h" +#include "DecodeEscapeSequences.h" +#include "Document.h" +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLDocumentParser.h" +#include "HTMLNames.h" +#include "HTMLParamElement.h" +#include "HTMLParserIdioms.h" +#include "SecurityOrigin.h" +#include "Settings.h" +#include "TextEncoding.h" +#include "TextResourceDecoder.h" + +#include <wtf/text/CString.h> + +namespace WebCore { + +using namespace HTMLNames; + +static bool isNonCanonicalCharacter(UChar c) +{ + // We remove all non-ASCII characters, including non-printable ASCII characters. + // + // Note, we don't remove backslashes like PHP stripslashes(), which among other things converts "\\0" to the \0 character. + // Instead, we remove backslashes and zeros (since the string "\\0" =(remove backslashes)=> "0"). However, this has the + // adverse effect that we remove any legitimate zeros from a string. + // + // For instance: new String("http://localhost:8000") => new String("http://localhost:8"). + return (c == '\\' || c == '0' || c == '\0' || c >= 127); +} + +static String canonicalize(const String& string) +{ + return string.removeCharacters(&isNonCanonicalCharacter); +} + +static bool isRequiredForInjection(UChar c) +{ + return (c == '\'' || c == '"' || c == '<' || c == '>'); +} + +static bool isTerminatingCharacter(UChar c) +{ + return (c == '&' || c == '/' || c == '"' || c == '\'' || c == '<'); +} + +static bool isHTMLQuote(UChar c) +{ + return (c == '"' || c == '\''); +} + +static bool isHTMLNewline(UChar c) +{ + return (c == '\n' || c == '\r'); +} + +static bool startsHTMLEndTagAt(const String& string, size_t start) +{ + return (start + 1 < string.length() && string[start] == '<' && string[start+1] == '/'); +} + + +static bool startsHTMLCommentAt(const String& string, size_t start) +{ + return (start + 3 < string.length() && string[start] == '<' && string[start+1] == '!' && string[start+2] == '-' && string[start+3] == '-'); +} + +static bool startsSingleLineCommentAt(const String& string, size_t start) +{ + return (start + 1 < string.length() && string[start] == '/' && string[start+1] == '/'); +} + +static bool startsMultiLineCommentAt(const String& string, size_t start) +{ + return (start + 1 < string.length() && string[start] == '/' && string[start+1] == '*'); +} + +static bool hasName(const HTMLToken& token, const QualifiedName& name) +{ + return equalIgnoringNullity(token.name(), static_cast<const String&>(name.localName())); +} + +static bool findAttributeWithName(const HTMLToken& token, const QualifiedName& name, size_t& indexOfMatchingAttribute) +{ + for (size_t i = 0; i < token.attributes().size(); ++i) { + if (equalIgnoringNullity(token.attributes().at(i).m_name, name.localName())) { + indexOfMatchingAttribute = i; + return true; + } + } + return false; +} + +static bool isNameOfInlineEventHandler(const Vector<UChar, 32>& name) +{ + const size_t lengthOfShortestInlineEventHandlerName = 5; // To wit: oncut. + if (name.size() < lengthOfShortestInlineEventHandlerName) + return false; + return name[0] == 'o' && name[1] == 'n'; +} + +static bool isDangerousHTTPEquiv(const String& value) +{ + String equiv = value.stripWhiteSpace(); + return equalIgnoringCase(equiv, "refresh") || equalIgnoringCase(equiv, "set-cookie"); +} + +static bool containsJavaScriptURL(const Vector<UChar, 32>& value) +{ + static const char javaScriptScheme[] = "javascript:"; + static const size_t lengthOfJavaScriptScheme = sizeof(javaScriptScheme) - 1; + + size_t i; + for (i = 0; i < value.size(); ++i) { + if (!isHTMLSpace(value[i])) + break; + } + + if (value.size() - i < lengthOfJavaScriptScheme) + return false; + + return equalIgnoringCase(value.data() + i, javaScriptScheme, lengthOfJavaScriptScheme); +} + +static inline String decode16BitUnicodeEscapeSequences(const String& string) +{ + // Note, the encoding is ignored since each %u-escape sequence represents a UTF-16 code unit. + return decodeEscapeSequences<Unicode16BitEscapeSequence>(string, UTF8Encoding()); +} + +static inline String decodeStandardURLEscapeSequences(const String& string, const TextEncoding& encoding) +{ + // We use decodeEscapeSequences() instead of decodeURLEscapeSequences() (declared in KURL.h) to + // avoid platform-specific URL decoding differences (e.g. KURLGoogle). + return decodeEscapeSequences<URLEscapeSequence>(string, encoding); +} + +static String fullyDecodeString(const String& string, const TextResourceDecoder* decoder) +{ + const TextEncoding& encoding = decoder ? decoder->encoding() : UTF8Encoding(); + size_t oldWorkingStringLength; + String workingString = string; + do { + oldWorkingStringLength = workingString.length(); + workingString = decode16BitUnicodeEscapeSequences(decodeStandardURLEscapeSequences(workingString, encoding)); + } while (workingString.length() < oldWorkingStringLength); + ASSERT(!workingString.isEmpty()); + workingString.replace('+', ' '); + workingString = canonicalize(workingString); + return workingString; +} + +XSSAuditor::XSSAuditor(HTMLDocumentParser* parser) + : m_parser(parser) + , m_isEnabled(false) + , m_xssProtection(XSSProtectionEnabled) + , m_state(Uninitialized) + , m_notifiedClient(false) +{ + ASSERT(m_parser); + if (Frame* frame = parser->document()->frame()) { + if (Settings* settings = frame->settings()) + m_isEnabled = settings->xssAuditorEnabled(); + } + // Although tempting to call init() at this point, the various objects + // we want to reference might not all have been constructed yet. +} + +void XSSAuditor::init() +{ + const size_t miniumLengthForSuffixTree = 512; // FIXME: Tune this parameter. + const int suffixTreeDepth = 5; + + ASSERT(m_state == Uninitialized); + m_state = Initial; + + if (!m_isEnabled) + return; + + // In theory, the Document could have detached from the Frame after the + // XSSAuditor was constructed. + if (!m_parser->document()->frame()) { + m_isEnabled = false; + return; + } + + const KURL& url = m_parser->document()->url(); + + if (url.isEmpty()) { + // The URL can be empty when opening a new browser window or calling window.open(""). + m_isEnabled = false; + return; + } + + if (url.protocolIsData()) { + m_isEnabled = false; + return; + } + + TextResourceDecoder* decoder = m_parser->document()->decoder(); + m_decodedURL = fullyDecodeString(url.string(), decoder); + if (m_decodedURL.find(isRequiredForInjection, 0) == notFound) + m_decodedURL = String(); + + if (DocumentLoader* documentLoader = m_parser->document()->frame()->loader()->documentLoader()) { + DEFINE_STATIC_LOCAL(String, XSSProtectionHeader, ("X-XSS-Protection")); + m_xssProtection = parseXSSProtectionHeader(documentLoader->response().httpHeaderField(XSSProtectionHeader)); + + FormData* httpBody = documentLoader->originalRequest().httpBody(); + if (httpBody && !httpBody->isEmpty()) { + String httpBodyAsString = httpBody->flattenToString(); + if (!httpBodyAsString.isEmpty()) { + m_decodedHTTPBody = fullyDecodeString(httpBodyAsString, decoder); + if (m_decodedHTTPBody.find(isRequiredForInjection, 0) == notFound) + m_decodedHTTPBody = String(); + if (m_decodedHTTPBody.length() >= miniumLengthForSuffixTree) + m_decodedHTTPBodySuffixTree = adoptPtr(new SuffixTree<ASCIICodebook>(m_decodedHTTPBody, suffixTreeDepth)); + } + } + } + + if (m_decodedURL.isEmpty() && m_decodedHTTPBody.isEmpty()) + m_isEnabled = false; +} + +void XSSAuditor::filterToken(HTMLToken& token) +{ + if (m_state == Uninitialized) { + init(); + ASSERT(m_state == Initial); + } + + if (!m_isEnabled || m_xssProtection == XSSProtectionDisabled) + return; + + bool didBlockScript = false; + + switch (m_state) { + case Uninitialized: + ASSERT_NOT_REACHED(); + break; + case Initial: + didBlockScript = filterTokenInitial(token); + break; + case AfterScriptStartTag: + didBlockScript = filterTokenAfterScriptStartTag(token); + ASSERT(m_state == Initial); + m_cachedSnippet = String(); + break; + } + + if (didBlockScript) { + // FIXME: Consider using a more helpful console message. + DEFINE_STATIC_LOCAL(String, consoleMessage, ("Refused to execute a JavaScript script. Source code of script found within request.\n")); + m_parser->document()->addConsoleMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, consoleMessage); + + bool didBlockEntirePage = (m_xssProtection == XSSProtectionBlockEnabled); + if (didBlockEntirePage) + m_parser->document()->frame()->loader()->stopAllLoaders(); + + if (!m_notifiedClient) { + m_parser->document()->frame()->loader()->client()->didDetectXSS(m_parser->document()->url(), didBlockEntirePage); + m_notifiedClient = true; + } + + if (didBlockEntirePage) + m_parser->document()->frame()->navigationScheduler()->scheduleLocationChange(m_parser->document()->securityOrigin(), blankURL(), String()); + } +} + +bool XSSAuditor::filterTokenInitial(HTMLToken& token) +{ + ASSERT(m_state == Initial); + + if (token.type() != HTMLTokenTypes::StartTag) + return false; + + bool didBlockScript = eraseDangerousAttributesIfInjected(token); + + if (hasName(token, scriptTag)) + didBlockScript |= filterScriptToken(token); + else if (hasName(token, objectTag)) + didBlockScript |= filterObjectToken(token); + else if (hasName(token, paramTag)) + didBlockScript |= filterParamToken(token); + else if (hasName(token, embedTag)) + didBlockScript |= filterEmbedToken(token); + else if (hasName(token, appletTag)) + didBlockScript |= filterAppletToken(token); + else if (hasName(token, iframeTag)) + didBlockScript |= filterIframeToken(token); + else if (hasName(token, metaTag)) + didBlockScript |= filterMetaToken(token); + else if (hasName(token, baseTag)) + didBlockScript |= filterBaseToken(token); + else if (hasName(token, formTag)) + didBlockScript |= filterFormToken(token); + + return didBlockScript; +} + +bool XSSAuditor::filterTokenAfterScriptStartTag(HTMLToken& token) +{ + ASSERT(m_state == AfterScriptStartTag); + m_state = Initial; + + if (token.type() != HTMLTokenTypes::Character) { + ASSERT(token.type() == HTMLTokenTypes::EndTag || token.type() == HTMLTokenTypes::EndOfFile); + return false; + } + + TextResourceDecoder* decoder = m_parser->document()->decoder(); + if (isContainedInRequest(fullyDecodeString(m_cachedSnippet, decoder))) { + int start = 0; + int end = token.endIndex() - token.startIndex(); + String snippet = snippetForJavaScript(snippetForRange(token, start, end)); + if (isContainedInRequest(fullyDecodeString(snippet, decoder))) { + token.eraseCharacters(); + token.appendToCharacter(' '); // Technically, character tokens can't be empty. + return true; + } + } + return false; +} + +bool XSSAuditor::filterScriptToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, scriptTag)); + + if (eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute)) + return true; + + m_state = AfterScriptStartTag; + m_cachedSnippet = m_parser->sourceForToken(token); + return false; +} + +bool XSSAuditor::filterObjectToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, objectTag)); + + bool didBlockScript = false; + + didBlockScript |= eraseAttributeIfInjected(token, dataAttr, blankURL().string(), SrcLikeAttribute); + didBlockScript |= eraseAttributeIfInjected(token, typeAttr); + didBlockScript |= eraseAttributeIfInjected(token, classidAttr); + + return didBlockScript; +} + +bool XSSAuditor::filterParamToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, paramTag)); + + size_t indexOfNameAttribute; + if (!findAttributeWithName(token, nameAttr, indexOfNameAttribute)) + return false; + + const HTMLToken::Attribute& nameAttribute = token.attributes().at(indexOfNameAttribute); + String name = String(nameAttribute.m_value.data(), nameAttribute.m_value.size()); + + if (!HTMLParamElement::isURLParameter(name)) + return false; + + return eraseAttributeIfInjected(token, valueAttr, blankURL().string(), SrcLikeAttribute); +} + +bool XSSAuditor::filterEmbedToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, embedTag)); + + bool didBlockScript = false; + + didBlockScript |= eraseAttributeIfInjected(token, srcAttr, blankURL().string(), SrcLikeAttribute); + didBlockScript |= eraseAttributeIfInjected(token, typeAttr); + + return didBlockScript; +} + +bool XSSAuditor::filterAppletToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, appletTag)); + + bool didBlockScript = false; + + didBlockScript |= eraseAttributeIfInjected(token, codeAttr, String(), SrcLikeAttribute); + didBlockScript |= eraseAttributeIfInjected(token, objectAttr); + + return didBlockScript; +} + +bool XSSAuditor::filterIframeToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, iframeTag)); + + return eraseAttributeIfInjected(token, srcAttr, String(), SrcLikeAttribute); +} + +bool XSSAuditor::filterMetaToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, metaTag)); + + return eraseAttributeIfInjected(token, http_equivAttr); +} + +bool XSSAuditor::filterBaseToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, baseTag)); + + return eraseAttributeIfInjected(token, hrefAttr); +} + +bool XSSAuditor::filterFormToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLTokenTypes::StartTag); + ASSERT(hasName(token, formTag)); + + return eraseAttributeIfInjected(token, actionAttr); +} + +bool XSSAuditor::eraseDangerousAttributesIfInjected(HTMLToken& token) +{ + DEFINE_STATIC_LOCAL(String, safeJavaScriptURL, ("javascript:void(0)")); + + bool didBlockScript = false; + for (size_t i = 0; i < token.attributes().size(); ++i) { + const HTMLToken::Attribute& attribute = token.attributes().at(i); + bool isInlineEventHandler = isNameOfInlineEventHandler(attribute.m_name); + bool valueContainsJavaScriptURL = isInlineEventHandler ? false : containsJavaScriptURL(attribute.m_value); + if (!isInlineEventHandler && !valueContainsJavaScriptURL) + continue; + // Beware of trailing characters which came from the page itself, not the + // injected vector. Excluding the terminating character covers common cases + // where the page immediately ends the attribute, but doesn't cover more + // complex cases where there is other page data following the injection. + // Generally, these won't parse as javascript, so the injected vector + // typically excludes them from consideration via a single-line comment or + // by enclosing them in a string literal terminated later by the page's own + // closing punctuation. Since the snippet has not been parsed, the vector + // may also try to introduce these via entities. As a result, we'd like to + // stop before the first "//", the first <!--, the first entity, or the first + // quote not immediately following the first equals sign (taking whitespace + // into consideration). To keep things simpler, we don't try to distinguish + // between entity-introducing amperands vs. other uses, nor do we bother to + // check for a second slash for a comment, nor do we bother to check for + // !-- following a less-than sign. We stop instead on any ampersand + // slash, or less-than sign. + String decodedSnippet = decodedSnippetForAttribute(token, attribute); + size_t position; + if ((position = decodedSnippet.find("=")) != notFound + && (position = decodedSnippet.find(isNotHTMLSpace, position + 1)) != notFound + && (position = decodedSnippet.find(isTerminatingCharacter, isHTMLQuote(decodedSnippet[position]) ? position + 1 : position)) != notFound) { + decodedSnippet.truncate(position); + } + if (!isContainedInRequest(decodedSnippet)) + continue; + token.eraseValueOfAttribute(i); + if (valueContainsJavaScriptURL) + token.appendToAttributeValue(i, safeJavaScriptURL); + didBlockScript = true; + } + return didBlockScript; +} + +bool XSSAuditor::eraseAttributeIfInjected(HTMLToken& token, const QualifiedName& attributeName, const String& replacementValue, AttributeKind treatment) +{ + size_t indexOfAttribute; + if (findAttributeWithName(token, attributeName, indexOfAttribute)) { + const HTMLToken::Attribute& attribute = token.attributes().at(indexOfAttribute); + if (isContainedInRequest(decodedSnippetForAttribute(token, attribute, treatment))) { + if (attributeName == srcAttr && isSameOriginResource(String(attribute.m_value.data(), attribute.m_value.size()))) + return false; + if (attributeName == http_equivAttr && !isDangerousHTTPEquiv(String(attribute.m_value.data(), attribute.m_value.size()))) + return false; + token.eraseValueOfAttribute(indexOfAttribute); + if (!replacementValue.isEmpty()) + token.appendToAttributeValue(indexOfAttribute, replacementValue); + return true; + } + } + return false; +} + +String XSSAuditor::snippetForRange(const HTMLToken& token, int start, int end) +{ + // FIXME: There's an extra allocation here that we could save by + // passing the range to the parser. + return m_parser->sourceForToken(token).substring(start, end - start); +} + +String XSSAuditor::decodedSnippetForAttribute(const HTMLToken& token, const HTMLToken::Attribute& attribute, AttributeKind treatment) +{ + const size_t kMaximumSnippetLength = 100; + + // The range doesn't inlcude the character which terminates the value. So, + // for an input of |name="value"|, the snippet is |name="value|. For an + // unquoted input of |name=value |, the snippet is |name=value|. + // FIXME: We should grab one character before the name also. + int start = attribute.m_nameRange.m_start - token.startIndex(); + int end = attribute.m_valueRange.m_end - token.startIndex(); + String decodedSnippet = fullyDecodeString(snippetForRange(token, start, end), m_parser->document()->decoder()); + decodedSnippet.truncate(kMaximumSnippetLength); + if (treatment == SrcLikeAttribute) { + int slashCount; + size_t currentLength; + // Characters following the first ?, #, or third slash may come from + // the page itself and can be merely ignored by an attacker's server + // when a remote script or script-like resource is requested. + for (slashCount = 0, currentLength = 0; currentLength < decodedSnippet.length(); ++currentLength) { + if (decodedSnippet[currentLength] == '?' || decodedSnippet[currentLength] == '#' + || ((decodedSnippet[currentLength] == '/' || decodedSnippet[currentLength] == '\\') && ++slashCount > 2)) { + decodedSnippet.truncate(currentLength); + break; + } + } + } + return decodedSnippet; +} + +bool XSSAuditor::isContainedInRequest(const String& decodedSnippet) +{ + if (decodedSnippet.isEmpty()) + return false; + if (m_decodedURL.find(decodedSnippet, 0, false) != notFound) + return true; + if (m_decodedHTTPBodySuffixTree && !m_decodedHTTPBodySuffixTree->mightContain(decodedSnippet)) + return false; + return m_decodedHTTPBody.find(decodedSnippet, 0, false) != notFound; +} + +bool XSSAuditor::isSameOriginResource(const String& url) +{ + // If the resource is loaded from the same URL as the enclosing page, it's + // probably not an XSS attack, so we reduce false positives by allowing the + // request. If the resource has a query string, we're more suspicious, + // however, because that's pretty rare and the attacker might be able to + // trick a server-side script into doing something dangerous with the query + // string. + KURL resourceURL(m_parser->document()->url(), url); + return (m_parser->document()->url().host() == resourceURL.host() && resourceURL.query().isEmpty()); +} + +String XSSAuditor::snippetForJavaScript(const String& string) +{ + const size_t kMaximumFragmentLengthTarget = 100; + + size_t startPosition = 0; + size_t endPosition = string.length(); + size_t foundPosition = notFound; + + // Skip over initial comments to find start of code. + while (startPosition < endPosition) { + while (startPosition < endPosition && isHTMLSpace(string[startPosition])) + startPosition++; + if (startsHTMLCommentAt(string, startPosition) || startsSingleLineCommentAt(string, startPosition)) { + while (startPosition < endPosition && !isHTMLNewline(string[startPosition])) + startPosition++; + } else if (startsMultiLineCommentAt(string, startPosition)) { + if ((foundPosition = string.find("*/", startPosition)) != notFound) + startPosition = foundPosition + 2; + else + startPosition = endPosition; + } else + break; + } + + // Stop at next comment, or at a closing script tag (which may have been included with + // the code fragment because of buffering in the HTMLSourceTracker), or when we exceed + // the maximum length target. After hitting the length target, we can only stop at a + // point where we know we are not in the middle of a %-escape sequence. For the sake of + // simplicity, approximate stopping at a close script tag by stopping at any close tag, + // and approximate not stopping inside a (possibly multiply encoded) %-esacpe sequence + // by breaking on whitespace only. We should have enough text in these cases to avoid + // false positives. + for (foundPosition = startPosition; foundPosition < endPosition; foundPosition++) { + if (startsSingleLineCommentAt(string, foundPosition) || startsMultiLineCommentAt(string, foundPosition) || startsHTMLEndTagAt(string, foundPosition)) { + endPosition = foundPosition + 2; + break; + } + if (startsHTMLCommentAt(string, foundPosition)) { + endPosition = foundPosition + 4; + break; + } + if (foundPosition > startPosition + kMaximumFragmentLengthTarget && isHTMLSpace(string[foundPosition])) { + endPosition = foundPosition; + break; + } + } + + return string.substring(startPosition, endPosition - startPosition); +} + +} // namespace WebCore diff --git a/Source/WebCore/html/parser/XSSAuditor.h b/Source/WebCore/html/parser/XSSAuditor.h new file mode 100644 index 000000000..89b7b8c65 --- /dev/null +++ b/Source/WebCore/html/parser/XSSAuditor.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2011 Adam Barth. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * 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 XSSAuditor_h +#define XSSAuditor_h + +#include "HTMLToken.h" +#include "HTTPParsers.h" +#include "SuffixTree.h" + +namespace WebCore { + +class HTMLDocumentParser; + +class XSSAuditor { + WTF_MAKE_NONCOPYABLE(XSSAuditor); +public: + explicit XSSAuditor(HTMLDocumentParser*); + + void filterToken(HTMLToken&); + +private: + enum State { + Uninitialized, + Initial, + AfterScriptStartTag, + }; + + enum AttributeKind { + NormalAttribute, + SrcLikeAttribute + }; + + void init(); + + bool filterTokenInitial(HTMLToken&); + bool filterTokenAfterScriptStartTag(HTMLToken&); + + bool filterScriptToken(HTMLToken&); + bool filterObjectToken(HTMLToken&); + bool filterParamToken(HTMLToken&); + bool filterEmbedToken(HTMLToken&); + bool filterAppletToken(HTMLToken&); + bool filterIframeToken(HTMLToken&); + bool filterMetaToken(HTMLToken&); + bool filterBaseToken(HTMLToken&); + bool filterFormToken(HTMLToken&); + + bool eraseDangerousAttributesIfInjected(HTMLToken&); + bool eraseAttributeIfInjected(HTMLToken&, const QualifiedName&, const String& replacementValue = String(), AttributeKind treatment = NormalAttribute); + + String snippetForRange(const HTMLToken&, int start, int end); + String snippetForJavaScript(const String&); + String decodedSnippetForAttribute(const HTMLToken&, const HTMLToken::Attribute&, AttributeKind treatment = NormalAttribute); + + bool isContainedInRequest(const String&); + bool isSameOriginResource(const String& url); + + HTMLDocumentParser* m_parser; + bool m_isEnabled; + XSSProtectionDisposition m_xssProtection; + + String m_decodedURL; + String m_decodedHTTPBody; + OwnPtr<SuffixTree<ASCIICodebook> > m_decodedHTTPBodySuffixTree; + + State m_state; + String m_cachedSnippet; + bool m_notifiedClient; +}; + +} + +#endif diff --git a/Source/WebCore/html/parser/create-html-entity-table b/Source/WebCore/html/parser/create-html-entity-table new file mode 100755 index 000000000..a99a35bae --- /dev/null +++ b/Source/WebCore/html/parser/create-html-entity-table @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# Copyright (c) 2010 Google Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import csv +import os.path +import string +import sys + +ENTITY = 0 +VALUE = 1 + +def convert_entity_to_cpp_name(entity): + postfix = "EntityName" + if entity[-1] == ";": + return "%sSemicolon%s" % (entity[:-1], postfix) + return "%s%s" % (entity, postfix) + + +def convert_entity_to_uchar_array(entity): + return "{'%s'}" % "', '".join(entity) + + +def convert_value_to_int(value): + if not value: + return "0"; + assert(value[0] == "U") + assert(value[1] == "+") + return "0x" + value[2:] + + +def offset_table_entry(offset): + return " &staticEntityTable[%s]," % offset + + +program_name = os.path.basename(__file__) +if len(sys.argv) < 4 or sys.argv[1] != "-o": + # Python 3, change to: print("Usage: %s -o OUTPUT_FILE INPUT_FILE" % program_name, file=sys.stderr) + sys.stderr.write("Usage: %s -o OUTPUT_FILE INPUT_FILE\n" % program_name) + exit(1) + +output_path = sys.argv[2] +input_path = sys.argv[3] + +html_entity_names_file = open(input_path) +entries = list(csv.reader(html_entity_names_file)) +html_entity_names_file.close() + +entries.sort(key = lambda entry: entry[ENTITY]) +entity_count = len(entries) + +output_file = open(output_path, "w") + +output_file.write("""/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// THIS FILE IS GENERATED BY WebCore/html/parser/create-html-entity-table +// DO NOT EDIT (unless you are a ninja)! + +#include "config.h" +#include "HTMLEntityTable.h" + +namespace WebCore { + +namespace { +""") + +for entry in entries: + output_file.write("static const UChar %s[] = %s;\n" % ( + convert_entity_to_cpp_name(entry[ENTITY]), + convert_entity_to_uchar_array(entry[ENTITY]))) + +output_file.write(""" +static const HTMLEntityTableEntry staticEntityTable[%s] = {\n""" % entity_count) + +index = {} +offset = 0 +for entry in entries: + letter = entry[ENTITY][0] + if not index.get(letter): + index[letter] = offset + values = entry[VALUE].split(' ') + assert len(values) <= 2, values + output_file.write(' { %s, %s, %s, %s },\n' % ( + convert_entity_to_cpp_name(entry[ENTITY]), + len(entry[ENTITY]), + convert_value_to_int(values[0]), + convert_value_to_int(values[1] if len(values) >= 2 else ""))) + offset += 1 + +output_file.write("""}; + +""") + +output_file.write("static const HTMLEntityTableEntry* uppercaseOffset[] = {\n") +for letter in string.ascii_uppercase: + output_file.write("%s\n" % offset_table_entry(index[letter])) +output_file.write("%s\n" % offset_table_entry(index['a'])) +output_file.write("""}; + +static const HTMLEntityTableEntry* lowercaseOffset[] = {\n""") +for letter in string.ascii_lowercase: + output_file.write("%s\n" % offset_table_entry(index[letter])) +output_file.write("%s\n" % offset_table_entry(entity_count)) +output_file.write("""}; + +} + +const HTMLEntityTableEntry* HTMLEntityTable::firstEntryStartingWith(UChar c) +{ + if (c >= 'A' && c <= 'Z') + return uppercaseOffset[c - 'A']; + if (c >= 'a' && c <= 'z') + return lowercaseOffset[c - 'a']; + return 0; +} + +const HTMLEntityTableEntry* HTMLEntityTable::lastEntryStartingWith(UChar c) +{ + if (c >= 'A' && c <= 'Z') + return uppercaseOffset[c - 'A' + 1] - 1; + if (c >= 'a' && c <= 'z') + return lowercaseOffset[c - 'a' + 1] - 1; + return 0; +} + +const HTMLEntityTableEntry* HTMLEntityTable::firstEntry() +{ + return &staticEntityTable[0]; +} + +const HTMLEntityTableEntry* HTMLEntityTable::lastEntry() +{ + return &staticEntityTable[%s - 1]; +} + +} +""" % entity_count) diff --git a/Source/WebCore/html/shadow/DetailsMarkerControl.cpp b/Source/WebCore/html/shadow/DetailsMarkerControl.cpp new file mode 100644 index 000000000..d4fddd30f --- /dev/null +++ b/Source/WebCore/html/shadow/DetailsMarkerControl.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DetailsMarkerControl.h" + +#if ENABLE(DETAILS) + +#include "HTMLNames.h" +#include "HTMLSummaryElement.h" +#include "RenderDetailsMarker.h" + +namespace WebCore { + +using namespace HTMLNames; + +DetailsMarkerControl::DetailsMarkerControl(Document* document) + : HTMLDivElement(divTag, document) +{ +} + +RenderObject* DetailsMarkerControl::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderDetailsMarker(this); +} + +bool DetailsMarkerControl::rendererIsNeeded(const NodeRenderingContext& context) +{ + return summaryElement()->isMainSummary() && HTMLDivElement::rendererIsNeeded(context); +} + +const AtomicString& DetailsMarkerControl::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudId, ("-webkit-details-marker")); + return pseudId; +} + +HTMLSummaryElement* DetailsMarkerControl::summaryElement() +{ + Node* node = this->shadowAncestorNode(); + ASSERT(!node || toElement(node)->hasTagName(summaryTag)); + return static_cast<HTMLSummaryElement*>(node); +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/DetailsMarkerControl.h b/Source/WebCore/html/shadow/DetailsMarkerControl.h new file mode 100644 index 000000000..1aa14cab3 --- /dev/null +++ b/Source/WebCore/html/shadow/DetailsMarkerControl.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DetailsMarkerControl_h +#define DetailsMarkerControl_h + +#include "HTMLDivElement.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class HTMLSummaryElement; + +class DetailsMarkerControl : public HTMLDivElement { +public: + DetailsMarkerControl(Document*); + static PassRefPtr<DetailsMarkerControl> create(Document*); + +private: + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual bool rendererIsNeeded(const NodeRenderingContext&); + virtual const AtomicString& shadowPseudoId() const; + + HTMLSummaryElement* summaryElement(); +}; + +inline PassRefPtr<DetailsMarkerControl> DetailsMarkerControl::create(Document* document) +{ + return adoptRef(new DetailsMarkerControl(document)); +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/MediaControlElements.cpp b/Source/WebCore/html/shadow/MediaControlElements.cpp new file mode 100644 index 000000000..e4faae203 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControlElements.cpp @@ -0,0 +1,1225 @@ +/* + * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "MediaControlElements.h" + +#include "CSSStyleSelector.h" +#include "CSSValueKeywords.h" +#include "EventNames.h" +#include "FloatConversion.h" +#include "Frame.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "MediaControls.h" +#include "MouseEvent.h" +#include "Page.h" +#include "RenderDeprecatedFlexibleBox.h" +#include "RenderMedia.h" +#include "RenderSlider.h" +#include "RenderTheme.h" +#include "RenderVideo.h" +#include "RenderView.h" +#include "ScriptController.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; +using namespace std; + +// FIXME: These constants may need to be tweaked to better match the seeking in the QuickTime plug-in. +static const float cSkipRepeatDelay = 0.1f; +static const float cSkipTime = 0.2f; +static const float cScanRepeatDelay = 1.5f; +static const float cScanMaximumRate = 8; + +HTMLMediaElement* toParentMediaElement(Node* node) +{ + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || !mediaNode->isElementNode() || !static_cast<Element*>(mediaNode)->isMediaElement()) + return 0; + + return static_cast<HTMLMediaElement*>(mediaNode); +} + +MediaControlElementType mediaControlElementType(Node* node) +{ + ASSERT(node->isMediaControlElement()); + HTMLElement* element = toHTMLElement(node); + if (element->hasTagName(inputTag)) + return static_cast<MediaControlInputElement*>(element)->displayType(); + return static_cast<MediaControlElement*>(element)->displayType(); +} + +// ---------------------------- + +MediaControlElement::MediaControlElement(Document* document) + : HTMLDivElement(divTag, document) + , m_mediaController(0) +{ +} + +void MediaControlElement::show() +{ + ensureInlineStyleDecl()->removeProperty(CSSPropertyDisplay); +} + +void MediaControlElement::hide() +{ + ensureInlineStyleDecl()->setProperty(CSSPropertyDisplay, CSSValueNone); +} + +// ---------------------------- + +inline MediaControlPanelElement::MediaControlPanelElement(Document* document) + : MediaControlElement(document) + , m_canBeDragged(false) + , m_isBeingDragged(false) + , m_opaque(true) +{ +} + +PassRefPtr<MediaControlPanelElement> MediaControlPanelElement::create(Document* document) +{ + return adoptRef(new MediaControlPanelElement(document)); +} + +MediaControlElementType MediaControlPanelElement::displayType() const +{ + return MediaControlsPanel; +} + +const AtomicString& MediaControlPanelElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-panel")); + return id; +} + +void MediaControlPanelElement::startDrag(const LayoutPoint& eventLocation) +{ + if (!m_canBeDragged) + return; + + if (m_isBeingDragged) + return; + + RenderObject* renderer = this->renderer(); + if (!renderer || !renderer->isBox()) + return; + + Frame* frame = document()->frame(); + if (!frame) + return; + + m_dragStartPosition = toRenderBox(renderer)->location(); + m_dragStartEventLocation = eventLocation; + + frame->eventHandler()->setCapturingMouseEventsNode(this); + + m_isBeingDragged = true; +} + +void MediaControlPanelElement::continueDrag(const LayoutPoint& eventLocation) +{ + if (!m_isBeingDragged) + return; + + LayoutSize distanceDragged = eventLocation - m_dragStartEventLocation; + setPosition(m_dragStartPosition + distanceDragged); +} + +void MediaControlPanelElement::endDrag() +{ + if (!m_isBeingDragged) + return; + + m_isBeingDragged = false; + + Frame* frame = document()->frame(); + if (!frame) + return; + + frame->eventHandler()->setCapturingMouseEventsNode(0); +} + +void MediaControlPanelElement::setPosition(const LayoutPoint& position) +{ + CSSInlineStyleDeclaration* style = ensureInlineStyleDecl(); + + double left = position.x(); + double top = position.y(); + + // Set the left and top to control the panel's position; this depends on it being absolute positioned. + // Set the margin to zero since the position passed in will already include the effect of the margin. + style->setProperty(CSSPropertyLeft, left, CSSPrimitiveValue::CSS_PX); + style->setProperty(CSSPropertyTop, top, CSSPrimitiveValue::CSS_PX); + style->setProperty(CSSPropertyMarginLeft, 0.0, CSSPrimitiveValue::CSS_PX); + style->setProperty(CSSPropertyMarginTop, 0.0, CSSPrimitiveValue::CSS_PX); +} + +void MediaControlPanelElement::resetPosition() +{ + CSSInlineStyleDeclaration* style = ensureInlineStyleDecl(); + + style->removeProperty(CSSPropertyLeft); + style->removeProperty(CSSPropertyTop); + style->removeProperty(CSSPropertyMarginLeft); + style->removeProperty(CSSPropertyMarginTop); +} + +void MediaControlPanelElement::makeOpaque() +{ + if (m_opaque) + return; + + CSSInlineStyleDeclaration* style = ensureInlineStyleDecl(); + style->setProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity); + style->setProperty(CSSPropertyWebkitTransitionDuration, document()->page()->theme()->mediaControlsFadeInDuration(), CSSPrimitiveValue::CSS_S); + style->setProperty(CSSPropertyOpacity, 1.0, CSSPrimitiveValue::CSS_NUMBER); + + m_opaque = true; +} + +void MediaControlPanelElement::makeTransparent() +{ + if (!m_opaque) + return; + + CSSInlineStyleDeclaration* style = ensureInlineStyleDecl(); + style->setProperty(CSSPropertyWebkitTransitionProperty, CSSPropertyOpacity); + style->setProperty(CSSPropertyWebkitTransitionDuration, document()->page()->theme()->mediaControlsFadeOutDuration(), CSSPrimitiveValue::CSS_S); + style->setProperty(CSSPropertyOpacity, 0.0, CSSPrimitiveValue::CSS_NUMBER); + + m_opaque = false; +} + +void MediaControlPanelElement::defaultEventHandler(Event* event) +{ + MediaControlElement::defaultEventHandler(event); + + if (event->isMouseEvent()) { + LayoutPoint location = static_cast<MouseEvent*>(event)->absoluteLocation(); + if (event->type() == eventNames().mousedownEvent) { + startDrag(location); + event->setDefaultHandled(); + } else if (event->type() == eventNames().mousemoveEvent) + continueDrag(location); + else if (event->type() == eventNames().mouseupEvent) { + continueDrag(location); + endDrag(); + event->setDefaultHandled(); + } + } +} + +void MediaControlPanelElement::setCanBeDragged(bool canBeDragged) +{ + if (m_canBeDragged == canBeDragged) + return; + + m_canBeDragged = canBeDragged; + + if (!canBeDragged) + endDrag(); +} + +// ---------------------------- + +inline MediaControlTimelineContainerElement::MediaControlTimelineContainerElement(Document* document) + : MediaControlElement(document) +{ +} + +PassRefPtr<MediaControlTimelineContainerElement> MediaControlTimelineContainerElement::create(Document* document) +{ + RefPtr<MediaControlTimelineContainerElement> element = adoptRef(new MediaControlTimelineContainerElement(document)); + element->hide(); + return element.release(); +} + +MediaControlElementType MediaControlTimelineContainerElement::displayType() const +{ + return MediaTimelineContainer; +} + +const AtomicString& MediaControlTimelineContainerElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline-container")); + return id; +} + +// ---------------------------- + +class RenderMediaVolumeSliderContainer : public RenderBlock { +public: + RenderMediaVolumeSliderContainer(Node*); + +private: + virtual void layout(); +}; + +RenderMediaVolumeSliderContainer::RenderMediaVolumeSliderContainer(Node* node) + : RenderBlock(node) +{ +} + +void RenderMediaVolumeSliderContainer::layout() +{ + RenderBlock::layout(); + if (style()->display() == NONE || !previousSibling() || !previousSibling()->isBox()) + return; + + RenderBox* buttonBox = toRenderBox(previousSibling()); + + LayoutStateDisabler layoutStateDisabler(view()); + + LayoutPoint offset = theme()->volumeSliderOffsetFromMuteButton(buttonBox, size()); + setX(offset.x() + buttonBox->offsetLeft()); + setY(offset.y() + buttonBox->offsetTop()); +} + +inline MediaControlVolumeSliderContainerElement::MediaControlVolumeSliderContainerElement(Document* document) + : MediaControlElement(document) +{ +} + +PassRefPtr<MediaControlVolumeSliderContainerElement> MediaControlVolumeSliderContainerElement::create(Document* document) +{ + RefPtr<MediaControlVolumeSliderContainerElement> element = adoptRef(new MediaControlVolumeSliderContainerElement(document)); + element->hide(); + return element.release(); +} + +RenderObject* MediaControlVolumeSliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderMediaVolumeSliderContainer(this); +} + +void MediaControlVolumeSliderContainerElement::defaultEventHandler(Event* event) +{ + if (!event->isMouseEvent() || event->type() != eventNames().mouseoutEvent) + return; + + // Poor man's mouseleave event detection. + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + if (!mouseEvent->relatedTarget() || !mouseEvent->relatedTarget()->toNode()) + return; + + if (this->containsIncludingShadowDOM(mouseEvent->relatedTarget()->toNode())) + return; + + hide(); +} + + +MediaControlElementType MediaControlVolumeSliderContainerElement::displayType() const +{ + return MediaVolumeSliderContainer; +} + +const AtomicString& MediaControlVolumeSliderContainerElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-container")); + return id; +} + +// ---------------------------- + +inline MediaControlStatusDisplayElement::MediaControlStatusDisplayElement(Document* document) + : MediaControlElement(document) + , m_stateBeingDisplayed(Nothing) +{ +} + +PassRefPtr<MediaControlStatusDisplayElement> MediaControlStatusDisplayElement::create(Document* document) +{ + RefPtr<MediaControlStatusDisplayElement> element = adoptRef(new MediaControlStatusDisplayElement(document)); + element->hide(); + return element.release(); +} + +void MediaControlStatusDisplayElement::update() +{ + // Get the new state that we'll have to display. + StateBeingDisplayed newStateToDisplay = Nothing; + + if (mediaController()->readyState() <= MediaControllerInterface::HAVE_METADATA && mediaController()->hasCurrentSrc()) + newStateToDisplay = Loading; + else if (mediaController()->isLiveStream()) + newStateToDisplay = LiveBroadcast; + + if (newStateToDisplay == m_stateBeingDisplayed) + return; + + ExceptionCode e; + + if (m_stateBeingDisplayed == Nothing) + show(); + else if (newStateToDisplay == Nothing) + hide(); + + m_stateBeingDisplayed = newStateToDisplay; + + switch (m_stateBeingDisplayed) { + case Nothing: + setInnerText("", e); + break; + case Loading: + setInnerText(mediaElementLoadingStateText(), e); + break; + case LiveBroadcast: + setInnerText(mediaElementLiveBroadcastStateText(), e); + break; + } +} + +MediaControlElementType MediaControlStatusDisplayElement::displayType() const +{ + return MediaStatusDisplay; +} + +const AtomicString& MediaControlStatusDisplayElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-status-display")); + return id; +} + +// ---------------------------- + +MediaControlInputElement::MediaControlInputElement(Document* document, MediaControlElementType displayType) + : HTMLInputElement(inputTag, document, 0, false) + , m_mediaController(0) + , m_displayType(displayType) +{ +} + +void MediaControlInputElement::show() +{ + ensureInlineStyleDecl()->removeProperty(CSSPropertyDisplay); +} + +void MediaControlInputElement::hide() +{ + ensureInlineStyleDecl()->setProperty(CSSPropertyDisplay, CSSValueNone); +} + + +void MediaControlInputElement::setDisplayType(MediaControlElementType displayType) +{ + if (displayType == m_displayType) + return; + + m_displayType = displayType; + if (RenderObject* object = renderer()) + object->repaint(); +} + +// ---------------------------- + +inline MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* document, MediaControlElementType displayType) + : MediaControlInputElement(document, displayType) +{ +} + +void MediaControlMuteButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaController()->setMuted(!mediaController()->muted()); + event->setDefaultHandled(); + } + + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlMuteButtonElement::changedMute() +{ + updateDisplayType(); +} + +void MediaControlMuteButtonElement::updateDisplayType() +{ + setDisplayType(mediaController()->muted() ? MediaUnMuteButton : MediaMuteButton); +} + +// ---------------------------- + +inline MediaControlPanelMuteButtonElement::MediaControlPanelMuteButtonElement(Document* document, MediaControls* controls) + : MediaControlMuteButtonElement(document, MediaMuteButton) + , m_controls(controls) +{ +} + +PassRefPtr<MediaControlPanelMuteButtonElement> MediaControlPanelMuteButtonElement::create(Document* document, MediaControls* controls) +{ + ASSERT(controls); + + RefPtr<MediaControlPanelMuteButtonElement> button = adoptRef(new MediaControlPanelMuteButtonElement(document, controls)); + button->setType("button"); + return button.release(); +} + +void MediaControlPanelMuteButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().mouseoverEvent) + m_controls->showVolumeSlider(); + + MediaControlMuteButtonElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlPanelMuteButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-mute-button")); + return id; +} + +// ---------------------------- + +inline MediaControlVolumeSliderMuteButtonElement::MediaControlVolumeSliderMuteButtonElement(Document* document) + : MediaControlMuteButtonElement(document, MediaMuteButton) +{ +} + +PassRefPtr<MediaControlVolumeSliderMuteButtonElement> MediaControlVolumeSliderMuteButtonElement::create(Document* document) +{ + RefPtr<MediaControlVolumeSliderMuteButtonElement> button = adoptRef(new MediaControlVolumeSliderMuteButtonElement(document)); + button->setType("button"); + return button.release(); +} + +const AtomicString& MediaControlVolumeSliderMuteButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider-mute-button")); + return id; +} + +// ---------------------------- + +inline MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* document) + : MediaControlInputElement(document, MediaPlayButton) +{ +} + +PassRefPtr<MediaControlPlayButtonElement> MediaControlPlayButtonElement::create(Document* document) +{ + RefPtr<MediaControlPlayButtonElement> button = adoptRef(new MediaControlPlayButtonElement(document)); + button->setType("button"); + return button.release(); +} + +void MediaControlPlayButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + if (mediaController()->canPlay()) + mediaController()->play(); + else + mediaController()->pause(); + updateDisplayType(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlPlayButtonElement::updateDisplayType() +{ + setDisplayType(mediaController()->canPlay() ? MediaPlayButton : MediaPauseButton); +} + +const AtomicString& MediaControlPlayButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-play-button")); + return id; +} + +// ---------------------------- + +inline MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* document, MediaControlElementType displayType) + : MediaControlInputElement(document, displayType) + , m_actionOnStop(Nothing) + , m_seekType(Skip) + , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired) +{ +} + +void MediaControlSeekButtonElement::defaultEventHandler(Event* event) +{ + // Set the mousedown and mouseup events as defaultHandled so they + // do not trigger drag start or end actions in MediaControlPanelElement. + if (event->type() == eventNames().mousedownEvent || event->type() == eventNames().mouseupEvent) + event->setDefaultHandled(); +} + +void MediaControlSeekButtonElement::setActive(bool flag, bool pause) +{ + if (flag == active()) + return; + + if (flag) + startTimer(); + else + stopTimer(); + + MediaControlInputElement::setActive(flag, pause); +} + +void MediaControlSeekButtonElement::startTimer() +{ + m_seekType = mediaController()->supportsScanning() ? Scan : Skip; + + if (m_seekType == Skip) { + // Seeking by skipping requires the video to be paused during seeking. + m_actionOnStop = mediaController()->paused() ? Nothing : Play; + mediaController()->pause(); + } else { + // Seeking by scanning requires the video to be playing during seeking. + m_actionOnStop = mediaController()->paused() ? Pause : Nothing; + mediaController()->play(); + mediaController()->setPlaybackRate(nextRate()); + } + + m_seekTimer.start(0, m_seekType == Skip ? cSkipRepeatDelay : cScanRepeatDelay); +} + +void MediaControlSeekButtonElement::stopTimer() +{ + if (m_seekType == Scan) + mediaController()->setPlaybackRate(mediaController()->defaultPlaybackRate()); + + if (m_actionOnStop == Play) + mediaController()->play(); + else if (m_actionOnStop == Pause) + mediaController()->pause(); + + if (m_seekTimer.isActive()) + m_seekTimer.stop(); +} + +float MediaControlSeekButtonElement::nextRate() const +{ + float rate = std::min(cScanMaximumRate, fabsf(mediaController()->playbackRate() * 2)); + if (!isForwardButton()) + rate *= -1; + return rate; +} + +void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*) +{ + if (m_seekType == Skip) { + ExceptionCode ec; + float skipTime = isForwardButton() ? cSkipTime : -cSkipTime; + mediaController()->setCurrentTime(mediaController()->currentTime() + skipTime, ec); + } else + mediaController()->setPlaybackRate(nextRate()); +} + +// ---------------------------- + +inline MediaControlSeekForwardButtonElement::MediaControlSeekForwardButtonElement(Document* document) + : MediaControlSeekButtonElement(document, MediaSeekForwardButton) +{ +} + +PassRefPtr<MediaControlSeekForwardButtonElement> MediaControlSeekForwardButtonElement::create(Document* document) +{ + RefPtr<MediaControlSeekForwardButtonElement> button = adoptRef(new MediaControlSeekForwardButtonElement(document)); + button->setType("button"); + return button.release(); +} + +const AtomicString& MediaControlSeekForwardButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-forward-button")); + return id; +} + +// ---------------------------- + +inline MediaControlSeekBackButtonElement::MediaControlSeekBackButtonElement(Document* document) + : MediaControlSeekButtonElement(document, MediaSeekBackButton) +{ +} + +PassRefPtr<MediaControlSeekBackButtonElement> MediaControlSeekBackButtonElement::create(Document* document) +{ + RefPtr<MediaControlSeekBackButtonElement> button = adoptRef(new MediaControlSeekBackButtonElement(document)); + button->setType("button"); + return button.release(); +} + +const AtomicString& MediaControlSeekBackButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-seek-back-button")); + return id; +} + +// ---------------------------- + +inline MediaControlRewindButtonElement::MediaControlRewindButtonElement(Document* document) + : MediaControlInputElement(document, MediaRewindButton) +{ +} + +PassRefPtr<MediaControlRewindButtonElement> MediaControlRewindButtonElement::create(Document* document) +{ + RefPtr<MediaControlRewindButtonElement> button = adoptRef(new MediaControlRewindButtonElement(document)); + button->setType("button"); + return button.release(); +} + +void MediaControlRewindButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + ExceptionCode ignoredCode; + mediaController()->setCurrentTime(max(0.0f, mediaController()->currentTime() - 30), ignoredCode); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlRewindButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-rewind-button")); + return id; +} + +// ---------------------------- + +inline MediaControlReturnToRealtimeButtonElement::MediaControlReturnToRealtimeButtonElement(Document* document) + : MediaControlInputElement(document, MediaReturnToRealtimeButton) +{ +} + +PassRefPtr<MediaControlReturnToRealtimeButtonElement> MediaControlReturnToRealtimeButtonElement::create(Document* document) +{ + RefPtr<MediaControlReturnToRealtimeButtonElement> button = adoptRef(new MediaControlReturnToRealtimeButtonElement(document)); + button->setType("button"); + button->hide(); + return button.release(); +} + +void MediaControlReturnToRealtimeButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaController()->returnToRealtime(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlReturnToRealtimeButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-return-to-realtime-button")); + return id; +} + +// ---------------------------- + +inline MediaControlToggleClosedCaptionsButtonElement::MediaControlToggleClosedCaptionsButtonElement(Document* document) + : MediaControlInputElement(document, MediaShowClosedCaptionsButton) +{ +} + +PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> MediaControlToggleClosedCaptionsButtonElement::create(Document* document) +{ + RefPtr<MediaControlToggleClosedCaptionsButtonElement> button = adoptRef(new MediaControlToggleClosedCaptionsButtonElement(document)); + button->setType("button"); + button->hide(); + return button.release(); +} + +void MediaControlToggleClosedCaptionsButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + mediaController()->setClosedCaptionsVisible(!mediaController()->closedCaptionsVisible()); + setChecked(mediaController()->closedCaptionsVisible()); + updateDisplayType(); + event->setDefaultHandled(); + } + + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlToggleClosedCaptionsButtonElement::updateDisplayType() +{ + setDisplayType(mediaController()->closedCaptionsVisible() ? MediaHideClosedCaptionsButton : MediaShowClosedCaptionsButton); +} + +const AtomicString& MediaControlToggleClosedCaptionsButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-toggle-closed-captions-button")); + return id; +} + +// ---------------------------- + +MediaControlTimelineElement::MediaControlTimelineElement(Document* document, MediaControls* controls) + : MediaControlInputElement(document, MediaSlider) + , m_controls(controls) +{ +} + +PassRefPtr<MediaControlTimelineElement> MediaControlTimelineElement::create(Document* document, MediaControls* controls) +{ + ASSERT(controls); + + RefPtr<MediaControlTimelineElement> timeline = adoptRef(new MediaControlTimelineElement(document, controls)); + timeline->setType("range"); + timeline->setAttribute(precisionAttr, "float"); + return timeline.release(); +} + +void MediaControlTimelineElement::defaultEventHandler(Event* event) +{ + // Left button is 0. Rejects mouse events not from left button. + if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button()) + return; + + if (!attached()) + return; + + if (event->type() == eventNames().mousedownEvent) + mediaController()->beginScrubbing(); + + if (event->type() == eventNames().mouseupEvent) + mediaController()->endScrubbing(); + + MediaControlInputElement::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent) + return; + + float time = narrowPrecisionToFloat(value().toDouble()); + if (time != mediaController()->currentTime()) { + // FIXME: This is fired 3 times on every click. We should not be doing that <http:/webkit.org/b/58160>. + ExceptionCode ec; + mediaController()->setCurrentTime(time, ec); + } + + RenderSlider* slider = toRenderSlider(renderer()); + if (slider && slider->inDragMode()) + m_controls->updateTimeDisplay(); +} + +void MediaControlTimelineElement::setPosition(float currentTime) +{ + setValue(String::number(currentTime)); +} + +void MediaControlTimelineElement::setDuration(float duration) +{ + setAttribute(maxAttr, String::number(isfinite(duration) ? duration : 0)); +} + + +const AtomicString& MediaControlTimelineElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-timeline")); + return id; +} + +// ---------------------------- + +inline MediaControlVolumeSliderElement::MediaControlVolumeSliderElement(Document* document) + : MediaControlInputElement(document, MediaVolumeSlider) +{ +} + +PassRefPtr<MediaControlVolumeSliderElement> MediaControlVolumeSliderElement::create(Document* document) +{ + RefPtr<MediaControlVolumeSliderElement> slider = adoptRef(new MediaControlVolumeSliderElement(document)); + slider->setType("range"); + slider->setAttribute(precisionAttr, "float"); + slider->setAttribute(maxAttr, "1"); + return slider.release(); +} + +void MediaControlVolumeSliderElement::defaultEventHandler(Event* event) +{ + // Left button is 0. Rejects mouse events not from left button. + if (event->isMouseEvent() && static_cast<MouseEvent*>(event)->button()) + return; + + if (!attached()) + return; + + MediaControlInputElement::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent || event->type() == eventNames().mouseoutEvent || event->type() == eventNames().mousemoveEvent) + return; + + float volume = narrowPrecisionToFloat(value().toDouble()); + if (volume != mediaController()->volume()) { + ExceptionCode ec = 0; + mediaController()->setVolume(volume, ec); + ASSERT(!ec); + } +} + +void MediaControlVolumeSliderElement::setVolume(float volume) +{ + if (value().toFloat() != volume) + setValue(String::number(volume)); +} + +const AtomicString& MediaControlVolumeSliderElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-volume-slider")); + return id; +} + +// ---------------------------- + +inline MediaControlFullscreenVolumeSliderElement::MediaControlFullscreenVolumeSliderElement(Document* document) + : MediaControlVolumeSliderElement(document) +{ +} + +PassRefPtr<MediaControlFullscreenVolumeSliderElement> MediaControlFullscreenVolumeSliderElement::create(Document* document) +{ + RefPtr<MediaControlFullscreenVolumeSliderElement> slider = adoptRef(new MediaControlFullscreenVolumeSliderElement(document)); + slider->setType("range"); + slider->setAttribute(precisionAttr, "float"); + slider->setAttribute(maxAttr, "1"); + return slider.release(); +} + +const AtomicString& MediaControlFullscreenVolumeSliderElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-slider")); + return id; +} + +// ---------------------------- + +inline MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* document, MediaControls* controls) + : MediaControlInputElement(document, MediaFullscreenButton) + , m_controls(controls) +{ +} + +PassRefPtr<MediaControlFullscreenButtonElement> MediaControlFullscreenButtonElement::create(Document* document, MediaControls* controls) +{ + ASSERT(controls); + + RefPtr<MediaControlFullscreenButtonElement> button = adoptRef(new MediaControlFullscreenButtonElement(document, controls)); + button->setType("button"); + button->hide(); + return button.release(); +} + +void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { +#if ENABLE(FULLSCREEN_API) + // Only use the new full screen API if the fullScreenEnabled setting has + // been explicitly enabled. Otherwise, use the old fullscreen API. This + // allows apps which embed a WebView to retain the existing full screen + // video implementation without requiring them to implement their own full + // screen behavior. + if (document()->settings() && document()->settings()->fullScreenEnabled()) { + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this)) + document()->webkitCancelFullScreen(); + else + document()->requestFullScreenForElement(toParentMediaElement(this), 0, Document::ExemptIFrameAllowFulScreenRequirement); + } else +#endif + mediaController()->enterFullscreen(); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlFullscreenButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-button")); + return id; +} + +// ---------------------------- + +inline MediaControlFullscreenVolumeMinButtonElement::MediaControlFullscreenVolumeMinButtonElement(Document* document) + : MediaControlInputElement(document, MediaUnMuteButton) +{ +} + +PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> MediaControlFullscreenVolumeMinButtonElement::create(Document* document) +{ + RefPtr<MediaControlFullscreenVolumeMinButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMinButtonElement(document)); + button->setType("button"); + return button.release(); +} + +void MediaControlFullscreenVolumeMinButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + ExceptionCode code = 0; + mediaController()->setVolume(0, code); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlFullscreenVolumeMinButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-min-button")); + return id; +} + +// ---------------------------- + +inline MediaControlFullscreenVolumeMaxButtonElement::MediaControlFullscreenVolumeMaxButtonElement(Document* document) +: MediaControlInputElement(document, MediaMuteButton) +{ +} + +PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> MediaControlFullscreenVolumeMaxButtonElement::create(Document* document) +{ + RefPtr<MediaControlFullscreenVolumeMaxButtonElement> button = adoptRef(new MediaControlFullscreenVolumeMaxButtonElement(document)); + button->setType("button"); + return button.release(); +} + +void MediaControlFullscreenVolumeMaxButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + ExceptionCode code = 0; + mediaController()->setVolume(1, code); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +const AtomicString& MediaControlFullscreenVolumeMaxButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-fullscreen-volume-max-button")); + return id; +} + +// ---------------------------- + +class RenderMediaControlTimeDisplay : public RenderDeprecatedFlexibleBox { +public: + RenderMediaControlTimeDisplay(Node*); + +private: + virtual void layout(); +}; + +RenderMediaControlTimeDisplay::RenderMediaControlTimeDisplay(Node* node) + : RenderDeprecatedFlexibleBox(node) +{ +} + +// We want the timeline slider to be at least 100 pixels wide. +// FIXME: Eliminate hard-coded widths altogether. +static const int minWidthToDisplayTimeDisplays = 45 + 100 + 45; + +void RenderMediaControlTimeDisplay::layout() +{ + RenderDeprecatedFlexibleBox::layout(); + RenderBox* timelineContainerBox = parentBox(); + while (timelineContainerBox && timelineContainerBox->isAnonymous()) + timelineContainerBox = timelineContainerBox->parentBox(); + + if (timelineContainerBox && timelineContainerBox->width() < minWidthToDisplayTimeDisplays) + setWidth(0); +} + +inline MediaControlTimeDisplayElement::MediaControlTimeDisplayElement(Document* document) + : MediaControlElement(document) + , m_currentValue(0) +{ +} + +void MediaControlTimeDisplayElement::setCurrentValue(float time) +{ + m_currentValue = time; +} + +RenderObject* MediaControlTimeDisplayElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderMediaControlTimeDisplay(this); +} + +// ---------------------------- + +PassRefPtr<MediaControlTimeRemainingDisplayElement> MediaControlTimeRemainingDisplayElement::create(Document* document) +{ + return adoptRef(new MediaControlTimeRemainingDisplayElement(document)); +} + +MediaControlTimeRemainingDisplayElement::MediaControlTimeRemainingDisplayElement(Document* document) + : MediaControlTimeDisplayElement(document) +{ +} + +MediaControlElementType MediaControlTimeRemainingDisplayElement::displayType() const +{ + return MediaTimeRemainingDisplay; +} + +const AtomicString& MediaControlTimeRemainingDisplayElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-time-remaining-display")); + return id; +} + +// ---------------------------- + +PassRefPtr<MediaControlCurrentTimeDisplayElement> MediaControlCurrentTimeDisplayElement::create(Document* document) +{ + return adoptRef(new MediaControlCurrentTimeDisplayElement(document)); +} + +MediaControlCurrentTimeDisplayElement::MediaControlCurrentTimeDisplayElement(Document* document) + : MediaControlTimeDisplayElement(document) +{ +} + +MediaControlElementType MediaControlCurrentTimeDisplayElement::displayType() const +{ + return MediaCurrentTimeDisplay; +} + +const AtomicString& MediaControlCurrentTimeDisplayElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls-current-time-display")); + return id; +} + +// ---------------------------- + +#if ENABLE(VIDEO_TRACK) + +class RenderTextTrackContainerElement : public RenderBlock { +public: + RenderTextTrackContainerElement(Node*); + +private: + virtual void layout(); +}; + +RenderTextTrackContainerElement::RenderTextTrackContainerElement(Node* node) + : RenderBlock(node) +{ +} + +void RenderTextTrackContainerElement::layout() +{ + RenderBlock::layout(); + if (style()->display() == NONE) + return; + + ASSERT(mediaControlElementType(node()) == MediaTextTrackDisplayContainer); + + LayoutStateDisabler layoutStateDisabler(view()); + static_cast<MediaControlTextTrackContainerElement*>(node())->updateSizes(); +} + +inline MediaControlTextTrackContainerElement::MediaControlTextTrackContainerElement(Document* document) + : MediaControlElement(document) + , m_fontSize(0) + , m_bottom(0) +{ +} + +PassRefPtr<MediaControlTextTrackContainerElement> MediaControlTextTrackContainerElement::create(Document* document) +{ + RefPtr<MediaControlTextTrackContainerElement> element = adoptRef(new MediaControlTextTrackContainerElement(document)); + element->hide(); + return element.release(); +} + +RenderObject* MediaControlTextTrackContainerElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderTextTrackContainerElement(this); +} + +const AtomicString& MediaControlTextTrackContainerElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-container")); + return id; +} + +static const float mimimumFontSize = 16; +static const float videoHeightFontSizePercentage = .05; +static const float trackBottomMultiplier = .9; + +void MediaControlTextTrackContainerElement::updateSizes() +{ + HTMLMediaElement* mediaElement = toParentMediaElement(this); + if (!mediaElement || !mediaElement->renderer() || !mediaElement->renderer()->isVideo()) + return; + + IntRect videoBox = toRenderVideo(mediaElement->renderer())->videoBox(); + if (m_videoDisplaySize == videoBox) + return; + m_videoDisplaySize = videoBox; + + float fontSize = m_videoDisplaySize.size().height() * videoHeightFontSizePercentage; + if (fontSize != m_fontSize) { + m_fontSize = fontSize; + ensureInlineStyleDecl()->setProperty(CSSPropertyFontSize, String::number(fontSize) + "px"); + } + + LayoutUnit bottom = static_cast<LayoutUnit>(m_videoDisplaySize.y() + m_videoDisplaySize.height() - (m_videoDisplaySize.height() * trackBottomMultiplier)); + if (bottom != m_bottom) { + m_bottom = bottom; + ensureInlineStyleDecl()->setProperty(CSSPropertyBottom, String::number(bottom) + "px"); + } +} + +// ---------------------------- + +MediaControlTextTrackDisplayElement::MediaControlTextTrackDisplayElement(Document* document) + : MediaControlElement(document) +{ +} + +PassRefPtr<MediaControlTextTrackDisplayElement> MediaControlTextTrackDisplayElement::create(Document* document) +{ + return adoptRef(new MediaControlTextTrackDisplayElement(document)); +} + +const AtomicString& MediaControlTextTrackDisplayElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-text-track-display")); + return id; +} + +#endif + +// ---------------------------- + +} // namespace WebCore + +#endif // ENABLE(VIDEO) diff --git a/Source/WebCore/html/shadow/MediaControlElements.h b/Source/WebCore/html/shadow/MediaControlElements.h new file mode 100644 index 000000000..8e67d0039 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControlElements.h @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControlElements_h +#define MediaControlElements_h + +#if ENABLE(VIDEO) + +#include "HTMLDivElement.h" +#include "HTMLInputElement.h" +#include "MediaControllerInterface.h" +#include "RenderBlock.h" + +// These are the shadow elements used in RenderMedia + +namespace WebCore { + +class Event; +class Frame; +class HTMLMediaElement; +class MediaControls; + +// Must match WebKitSystemInterface.h +enum MediaControlElementType { + MediaFullscreenButton = 0, + MediaMuteButton, + MediaPlayButton, + MediaSeekBackButton, + MediaSeekForwardButton, + MediaSlider, + MediaSliderThumb, + MediaRewindButton, + MediaReturnToRealtimeButton, + MediaShowClosedCaptionsButton, + MediaHideClosedCaptionsButton, + MediaUnMuteButton, + MediaPauseButton, + MediaTimelineContainer, + MediaCurrentTimeDisplay, + MediaTimeRemainingDisplay, + MediaStatusDisplay, + MediaControlsPanel, + MediaVolumeSliderContainer, + MediaVolumeSlider, + MediaVolumeSliderThumb, + MediaVolumeSliderMuteButton, + MediaTextTrackDisplayContainer, + MediaTextTrackDisplay, +}; + +HTMLMediaElement* toParentMediaElement(Node*); +inline HTMLMediaElement* toParentMediaElement(RenderObject* renderer) { return toParentMediaElement(renderer->node()); } + +MediaControlElementType mediaControlElementType(Node*); + +// ---------------------------- + +class MediaControlElement : public HTMLDivElement { +public: + void hide(); + void show(); + + virtual MediaControlElementType displayType() const = 0; + + void setMediaController(MediaControllerInterface* controller) { m_mediaController = controller; } + MediaControllerInterface* mediaController() const { return m_mediaController; } + +protected: + MediaControlElement(Document*); + +private: + virtual bool isMediaControlElement() const { return true; } + + MediaControllerInterface* m_mediaController; +}; + +// ---------------------------- + +class MediaControlPanelElement : public MediaControlElement { +public: + static PassRefPtr<MediaControlPanelElement> create(Document*); + + void setCanBeDragged(bool); + void resetPosition(); + void makeOpaque(); + void makeTransparent(); + +private: + MediaControlPanelElement(Document*); + virtual MediaControlElementType displayType() const; + virtual const AtomicString& shadowPseudoId() const; + virtual void defaultEventHandler(Event*); + + void startDrag(const LayoutPoint& eventLocation); + void continueDrag(const LayoutPoint& eventLocation); + void endDrag(); + + void setPosition(const LayoutPoint&); + + bool m_canBeDragged; + bool m_isBeingDragged; + bool m_opaque; + LayoutPoint m_dragStartPosition; + LayoutPoint m_dragStartEventLocation; +}; + +// ---------------------------- + +class MediaControlTimelineContainerElement : public MediaControlElement { +public: + static PassRefPtr<MediaControlTimelineContainerElement> create(Document*); + +private: + MediaControlTimelineContainerElement(Document*); + virtual const AtomicString& shadowPseudoId() const; + + virtual MediaControlElementType displayType() const; +}; + +// ---------------------------- + +class MediaControlVolumeSliderContainerElement : public MediaControlElement { +public: + static PassRefPtr<MediaControlVolumeSliderContainerElement> create(Document*); + +private: + MediaControlVolumeSliderContainerElement(Document*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void defaultEventHandler(Event*); + virtual MediaControlElementType displayType() const; + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlStatusDisplayElement : public MediaControlElement { +public: + static PassRefPtr<MediaControlStatusDisplayElement> create(Document*); + + void update(); + +private: + MediaControlStatusDisplayElement(Document*); + + virtual MediaControlElementType displayType() const; + virtual const AtomicString& shadowPseudoId() const; + + enum StateBeingDisplayed { Nothing, Loading, LiveBroadcast }; + StateBeingDisplayed m_stateBeingDisplayed; +}; + +// ---------------------------- + +class MediaControlInputElement : public HTMLInputElement { +public: + void hide(); + void show(); + + MediaControlElementType displayType() const { return m_displayType; } + + void setMediaController(MediaControllerInterface* controller) { m_mediaController = controller; } + MediaControllerInterface* mediaController() const { return m_mediaController; } + +protected: + MediaControlInputElement(Document*, MediaControlElementType); + + void setDisplayType(MediaControlElementType); + +private: + virtual bool isMediaControlElement() const { return true; } + + virtual void updateDisplayType() { } + + MediaControllerInterface* m_mediaController; + MediaControlElementType m_displayType; +}; + +// ---------------------------- + +class MediaControlMuteButtonElement : public MediaControlInputElement { +public: + void changedMute(); + +protected: + MediaControlMuteButtonElement(Document*, MediaControlElementType); + virtual void defaultEventHandler(Event*); + + +private: + virtual void updateDisplayType(); +}; + +// ---------------------------- + +class MediaControlPanelMuteButtonElement : public MediaControlMuteButtonElement { +public: + static PassRefPtr<MediaControlPanelMuteButtonElement> create(Document*, MediaControls*); + +private: + MediaControlPanelMuteButtonElement(Document*, MediaControls*); + + virtual void defaultEventHandler(Event*); + virtual const AtomicString& shadowPseudoId() const; + + MediaControls* m_controls; +}; + +// ---------------------------- + +class MediaControlVolumeSliderMuteButtonElement : public MediaControlMuteButtonElement { +public: + static PassRefPtr<MediaControlVolumeSliderMuteButtonElement> create(Document*); + +private: + MediaControlVolumeSliderMuteButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + + +// ---------------------------- + +class MediaControlPlayButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlPlayButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + virtual void updateDisplayType(); + +private: + MediaControlPlayButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlSeekButtonElement : public MediaControlInputElement { +public: + virtual void defaultEventHandler(Event*); + +protected: + MediaControlSeekButtonElement(Document*, MediaControlElementType); + +private: + virtual bool isForwardButton() const = 0; + virtual void setActive(bool /*flag*/ = true, bool /*pause*/ = false); + + void startTimer(); + void stopTimer(); + float nextRate() const; + void seekTimerFired(Timer<MediaControlSeekButtonElement>*); + + enum ActionType { Nothing, Play, Pause }; + ActionType m_actionOnStop; + enum SeekType { Skip, Scan }; + SeekType m_seekType; + Timer<MediaControlSeekButtonElement> m_seekTimer; +}; + +// ---------------------------- + +class MediaControlSeekForwardButtonElement : public MediaControlSeekButtonElement { +public: + static PassRefPtr<MediaControlSeekForwardButtonElement> create(Document*); + +private: + MediaControlSeekForwardButtonElement(Document*); + + virtual bool isForwardButton() const { return true; } + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlSeekBackButtonElement : public MediaControlSeekButtonElement { +public: + static PassRefPtr<MediaControlSeekBackButtonElement> create(Document*); + +private: + MediaControlSeekBackButtonElement(Document*); + + virtual bool isForwardButton() const { return false; } + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlRewindButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlRewindButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlRewindButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlReturnToRealtimeButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlReturnToRealtimeButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlReturnToRealtimeButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlToggleClosedCaptionsButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlToggleClosedCaptionsButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + virtual void updateDisplayType(); + +private: + MediaControlToggleClosedCaptionsButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlTimelineElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlTimelineElement> create(Document*, MediaControls*); + + virtual void defaultEventHandler(Event*); + void setPosition(float); + void setDuration(float); + +private: + MediaControlTimelineElement(Document*, MediaControls*); + + virtual const AtomicString& shadowPseudoId() const; + + MediaControls* m_controls; +}; + +// ---------------------------- + +class MediaControlVolumeSliderElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlVolumeSliderElement> create(Document*); + + virtual void defaultEventHandler(Event*); + void setVolume(float); + +protected: + MediaControlVolumeSliderElement(Document*); + +private: + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlFullscreenButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlFullscreenButtonElement> create(Document*, MediaControls*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlFullscreenButtonElement(Document*, MediaControls*); + + virtual const AtomicString& shadowPseudoId() const; + + MediaControls* m_controls; +}; + +// ---------------------------- + +class MediaControlFullscreenVolumeSliderElement : public MediaControlVolumeSliderElement { +public: + static PassRefPtr<MediaControlFullscreenVolumeSliderElement> create(Document*); + +private: + MediaControlFullscreenVolumeSliderElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlFullscreenVolumeMinButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlFullscreenVolumeMinButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlFullscreenVolumeMinButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlFullscreenVolumeMaxButtonElement : public MediaControlInputElement { +public: + static PassRefPtr<MediaControlFullscreenVolumeMaxButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + MediaControlFullscreenVolumeMaxButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlTimeDisplayElement : public MediaControlElement { +public: + void setCurrentValue(float); + float currentValue() const { return m_currentValue; } + +protected: + MediaControlTimeDisplayElement(Document*); + +private: + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + + float m_currentValue; +}; + +// ---------------------------- + +class MediaControlTimeRemainingDisplayElement : public MediaControlTimeDisplayElement { +public: + static PassRefPtr<MediaControlTimeRemainingDisplayElement> create(Document*); + +private: + MediaControlTimeRemainingDisplayElement(Document*); + + virtual MediaControlElementType displayType() const; + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +class MediaControlCurrentTimeDisplayElement : public MediaControlTimeDisplayElement { +public: + static PassRefPtr<MediaControlCurrentTimeDisplayElement> create(Document*); + +private: + MediaControlCurrentTimeDisplayElement(Document*); + + virtual MediaControlElementType displayType() const; + virtual const AtomicString& shadowPseudoId() const; +}; + +// ---------------------------- + +#if ENABLE(VIDEO_TRACK) +class MediaControlTextTrackContainerElement : public MediaControlElement { +public: + + static PassRefPtr<MediaControlTextTrackContainerElement> create(Document*); + + void updateSizes(); + +private: + MediaControlTextTrackContainerElement(Document*); + + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual MediaControlElementType displayType() const { return MediaTextTrackDisplayContainer; } + virtual const AtomicString& shadowPseudoId() const; + + IntRect m_videoDisplaySize; + float m_fontSize; + LayoutUnit m_bottom; +}; + +// ---------------------------- + +class MediaControlTextTrackDisplayElement : public MediaControlElement { +public: + static PassRefPtr<MediaControlTextTrackDisplayElement> create(Document*); + +private: + MediaControlTextTrackDisplayElement(Document*); + + virtual MediaControlElementType displayType() const { return MediaTextTrackDisplay; } + virtual const AtomicString& shadowPseudoId() const; +}; +#endif + +// ---------------------------- + +} // namespace WebCore + +#endif // ENABLE(VIDEO) + +#endif // MediaControlElements_h diff --git a/Source/WebCore/html/shadow/MediaControlRootElement.cpp b/Source/WebCore/html/shadow/MediaControlRootElement.cpp new file mode 100644 index 000000000..47f88bc82 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControlRootElement.cpp @@ -0,0 +1,669 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "MediaControlRootElement.h" + +#include "Chrome.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "MediaControlElements.h" +#include "MouseEvent.h" +#include "Page.h" +#include "RenderTheme.h" +#include "Text.h" + +#if ENABLE(VIDEO_TRACK) +#include "TextTrackCue.h" +#endif + +using namespace std; + +namespace WebCore { + +static const double timeWithoutMouseMovementBeforeHidingControls = 3; + +MediaControlRootElement::MediaControlRootElement(Document* document) + : MediaControls(document) + , m_mediaController(0) + , m_rewindButton(0) + , m_playButton(0) + , m_returnToRealTimeButton(0) + , m_statusDisplay(0) + , m_currentTimeDisplay(0) + , m_timeline(0) + , m_timeRemainingDisplay(0) + , m_timelineContainer(0) + , m_seekBackButton(0) + , m_seekForwardButton(0) + , m_toggleClosedCaptionsButton(0) + , m_panelMuteButton(0) + , m_volumeSlider(0) + , m_volumeSliderMuteButton(0) + , m_volumeSliderContainer(0) + , m_fullScreenButton(0) + , m_fullScreenMinVolumeButton(0) + , m_fullScreenVolumeSlider(0) + , m_fullScreenMaxVolumeButton(0) + , m_panel(0) +#if ENABLE(VIDEO_TRACK) + , m_textDisplayContainer(0) + , m_textTrackDisplay(0) +#endif + , m_hideFullscreenControlsTimer(this, &MediaControlRootElement::hideFullscreenControlsTimerFired) + , m_isMouseOverControls(false) +{ +} + +PassRefPtr<MediaControls> MediaControls::create(Document* document) +{ + return MediaControlRootElement::create(document); +} + +PassRefPtr<MediaControlRootElement> MediaControlRootElement::create(Document* document) +{ + if (!document->page()) + return 0; + + RefPtr<MediaControlRootElement> controls = adoptRef(new MediaControlRootElement(document)); + + RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document); + + ExceptionCode ec; + + RefPtr<MediaControlRewindButtonElement> rewindButton = MediaControlRewindButtonElement::create(document); + controls->m_rewindButton = rewindButton.get(); + panel->appendChild(rewindButton.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(document); + controls->m_playButton = playButton.get(); + panel->appendChild(playButton.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlReturnToRealtimeButtonElement> returnToRealtimeButton = MediaControlReturnToRealtimeButtonElement::create(document); + controls->m_returnToRealTimeButton = returnToRealtimeButton.get(); + panel->appendChild(returnToRealtimeButton.release(), ec, true); + if (ec) + return 0; + + if (document->page()->theme()->usesMediaControlStatusDisplay()) { + RefPtr<MediaControlStatusDisplayElement> statusDisplay = MediaControlStatusDisplayElement::create(document); + controls->m_statusDisplay = statusDisplay.get(); + panel->appendChild(statusDisplay.release(), ec, true); + if (ec) + return 0; + } + + RefPtr<MediaControlTimelineContainerElement> timelineContainer = MediaControlTimelineContainerElement::create(document); + + RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document); + controls->m_currentTimeDisplay = currentTimeDisplay.get(); + timelineContainer->appendChild(currentTimeDisplay.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlTimelineElement> timeline = MediaControlTimelineElement::create(document, controls.get()); + controls->m_timeline = timeline.get(); + timelineContainer->appendChild(timeline.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlTimeRemainingDisplayElement> timeRemainingDisplay = MediaControlTimeRemainingDisplayElement::create(document); + controls->m_timeRemainingDisplay = timeRemainingDisplay.get(); + timelineContainer->appendChild(timeRemainingDisplay.release(), ec, true); + if (ec) + return 0; + + controls->m_timelineContainer = timelineContainer.get(); + panel->appendChild(timelineContainer.release(), ec, true); + if (ec) + return 0; + + // FIXME: Only create when needed <http://webkit.org/b/57163> + RefPtr<MediaControlSeekBackButtonElement> seekBackButton = MediaControlSeekBackButtonElement::create(document); + controls->m_seekBackButton = seekBackButton.get(); + panel->appendChild(seekBackButton.release(), ec, true); + if (ec) + return 0; + + // FIXME: Only create when needed <http://webkit.org/b/57163> + RefPtr<MediaControlSeekForwardButtonElement> seekForwardButton = MediaControlSeekForwardButtonElement::create(document); + controls->m_seekForwardButton = seekForwardButton.get(); + panel->appendChild(seekForwardButton.release(), ec, true); + if (ec) + return 0; + + if (document->page()->theme()->supportsClosedCaptioning()) { + RefPtr<MediaControlToggleClosedCaptionsButtonElement> toggleClosedCaptionsButton = MediaControlToggleClosedCaptionsButtonElement::create(document); + controls->m_toggleClosedCaptionsButton = toggleClosedCaptionsButton.get(); + panel->appendChild(toggleClosedCaptionsButton.release(), ec, true); + if (ec) + return 0; + } + + // FIXME: Only create when needed <http://webkit.org/b/57163> + RefPtr<MediaControlFullscreenButtonElement> fullScreenButton = MediaControlFullscreenButtonElement::create(document, controls.get()); + controls->m_fullScreenButton = fullScreenButton.get(); + panel->appendChild(fullScreenButton.release(), ec, true); + + RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, controls.get()); + controls->m_panelMuteButton = panelMuteButton.get(); + panel->appendChild(panelMuteButton.release(), ec, true); + if (ec) + return 0; + + if (document->page()->theme()->usesMediaControlVolumeSlider()) { + RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(document); + + RefPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(document); + controls->m_volumeSlider = slider.get(); + volumeSliderContainer->appendChild(slider.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlVolumeSliderMuteButtonElement> volumeSliderMuteButton = MediaControlVolumeSliderMuteButtonElement::create(document); + controls->m_volumeSliderMuteButton = volumeSliderMuteButton.get(); + volumeSliderContainer->appendChild(volumeSliderMuteButton.release(), ec, true); + if (ec) + return 0; + + controls->m_volumeSliderContainer = volumeSliderContainer.get(); + panel->appendChild(volumeSliderContainer.release(), ec, true); + if (ec) + return 0; + } + + // FIXME: Only create when needed <http://webkit.org/b/57163> + RefPtr<MediaControlFullscreenVolumeMinButtonElement> fullScreenMinVolumeButton = MediaControlFullscreenVolumeMinButtonElement::create(document); + controls->m_fullScreenMinVolumeButton = fullScreenMinVolumeButton.get(); + panel->appendChild(fullScreenMinVolumeButton.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlFullscreenVolumeSliderElement> fullScreenVolumeSlider = MediaControlFullscreenVolumeSliderElement::create(document); + controls->m_fullScreenVolumeSlider = fullScreenVolumeSlider.get(); + panel->appendChild(fullScreenVolumeSlider.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlFullscreenVolumeMaxButtonElement> fullScreenMaxVolumeButton = MediaControlFullscreenVolumeMaxButtonElement::create(document); + controls->m_fullScreenMaxVolumeButton = fullScreenMaxVolumeButton.get(); + panel->appendChild(fullScreenMaxVolumeButton.release(), ec, true); + if (ec) + return 0; + + controls->m_panel = panel.get(); + controls->appendChild(panel.release(), ec, true); + if (ec) + return 0; + + return controls.release(); +} + +void MediaControlRootElement::setMediaController(MediaControllerInterface* controller) +{ + if (m_mediaController == controller) + return; + m_mediaController = controller; + + if (m_rewindButton) + m_rewindButton->setMediaController(controller); + if (m_playButton) + m_playButton->setMediaController(controller); + if (m_returnToRealTimeButton) + m_returnToRealTimeButton->setMediaController(controller); + if (m_statusDisplay) + m_statusDisplay->setMediaController(controller); + if (m_currentTimeDisplay) + m_currentTimeDisplay->setMediaController(controller); + if (m_timeline) + m_timeline->setMediaController(controller); + if (m_timeRemainingDisplay) + m_timeRemainingDisplay->setMediaController(controller); + if (m_timelineContainer) + m_timelineContainer->setMediaController(controller); + if (m_seekBackButton) + m_seekBackButton->setMediaController(controller); + if (m_seekForwardButton) + m_seekForwardButton->setMediaController(controller); + if (m_toggleClosedCaptionsButton) + m_toggleClosedCaptionsButton->setMediaController(controller); + if (m_panelMuteButton) + m_panelMuteButton->setMediaController(controller); + if (m_volumeSlider) + m_volumeSlider->setMediaController(controller); + if (m_volumeSliderMuteButton) + m_volumeSliderMuteButton->setMediaController(controller); + if (m_volumeSliderContainer) + m_volumeSliderContainer->setMediaController(controller); + if (m_fullScreenButton) + m_fullScreenButton->setMediaController(controller); + if (m_fullScreenMinVolumeButton) + m_fullScreenMinVolumeButton->setMediaController(controller); + if (m_fullScreenVolumeSlider) + m_fullScreenVolumeSlider->setMediaController(controller); + if (m_fullScreenMaxVolumeButton) + m_fullScreenMaxVolumeButton->setMediaController(controller); + if (m_panel) + m_panel->setMediaController(controller); +#if ENABLE(VIDEO_TRACK) + if (m_textDisplayContainer) + m_textDisplayContainer->setMediaController(controller); + if (m_textTrackDisplay) + m_textTrackDisplay->setMediaController(controller); +#endif + reset(); +} + +void MediaControlRootElement::show() +{ + m_panel->show(); +} + +void MediaControlRootElement::hide() +{ + m_panel->hide(); +} + +void MediaControlRootElement::makeOpaque() +{ + m_panel->makeOpaque(); +} + +void MediaControlRootElement::makeTransparent() +{ + m_panel->makeTransparent(); +} + +void MediaControlRootElement::reset() +{ + Page* page = document()->page(); + if (!page) + return; + + updateStatusDisplay(); + + if (m_mediaController->supportsFullscreen()) + m_fullScreenButton->show(); + else + m_fullScreenButton->hide(); + + float duration = m_mediaController->duration(); + if (isfinite(duration) || page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) { + m_timeline->setDuration(duration); + m_timelineContainer->show(); + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + } else + m_timelineContainer->hide(); + + if (m_mediaController->hasAudio() || page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart)) + m_panelMuteButton->show(); + else + m_panelMuteButton->hide(); + + if (m_volumeSlider) + m_volumeSlider->setVolume(m_mediaController->volume()); + + if (m_toggleClosedCaptionsButton) { + if (m_mediaController->hasClosedCaptions()) + m_toggleClosedCaptionsButton->show(); + else + m_toggleClosedCaptionsButton->hide(); + } + + m_playButton->updateDisplayType(); + +#if ENABLE(FULLSCREEN_API) + if (document()->webkitIsFullScreen() && document()->webkitCurrentFullScreenElement() == toParentMediaElement(this)) { + if (m_mediaController->isLiveStream()) { + m_seekBackButton->hide(); + m_seekForwardButton->hide(); + m_rewindButton->show(); + m_returnToRealTimeButton->show(); + } else { + m_seekBackButton->show(); + m_seekForwardButton->show(); + m_rewindButton->hide(); + m_returnToRealTimeButton->hide(); + } + } else +#endif + if (!m_mediaController->isLiveStream()) { + m_returnToRealTimeButton->hide(); + m_rewindButton->show(); + } else { + m_returnToRealTimeButton->show(); + m_rewindButton->hide(); + } + + makeOpaque(); +} + +void MediaControlRootElement::playbackStarted() +{ + m_playButton->updateDisplayType(); + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + + if (m_mediaController->isFullscreen()) + startHideFullscreenControlsTimer(); +} + +void MediaControlRootElement::playbackProgressed() +{ + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + + if (!m_isMouseOverControls && m_mediaController->hasVideo()) + makeTransparent(); +} + +void MediaControlRootElement::playbackStopped() +{ + m_playButton->updateDisplayType(); + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + makeOpaque(); + + stopHideFullscreenControlsTimer(); +} + +void MediaControlRootElement::updateTimeDisplay() +{ + float now = m_mediaController->currentTime(); + float duration = m_mediaController->duration(); + + Page* page = document()->page(); + if (!page) + return; + + // Allow the theme to format the time. + ExceptionCode ec; + m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), ec); + m_currentTimeDisplay->setCurrentValue(now); + m_timeRemainingDisplay->setInnerText(page->theme()->formatMediaControlsRemainingTime(now, duration), ec); + m_timeRemainingDisplay->setCurrentValue(now - duration); +} + +void MediaControlRootElement::reportedError() +{ + Page* page = document()->page(); + if (!page) + return; + + if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaSliderPart)) + m_timelineContainer->hide(); + + if (!page->theme()->hasOwnDisabledStateHandlingFor(MediaMuteButtonPart)) + m_panelMuteButton->hide(); + + m_fullScreenButton->hide(); + + if (m_volumeSliderContainer) + m_volumeSliderContainer->hide(); + if (m_toggleClosedCaptionsButton && !page->theme()->hasOwnDisabledStateHandlingFor(MediaToggleClosedCaptionsButtonPart)) + m_toggleClosedCaptionsButton->hide(); +} + +void MediaControlRootElement::updateStatusDisplay() +{ + if (m_statusDisplay) + m_statusDisplay->update(); +} + +void MediaControlRootElement::loadedMetadata() +{ + if (m_statusDisplay && m_mediaController->isLiveStream()) + m_statusDisplay->hide(); + + reset(); +} + +void MediaControlRootElement::changedClosedCaptionsVisibility() +{ + if (m_toggleClosedCaptionsButton) + m_toggleClosedCaptionsButton->updateDisplayType(); +} + +void MediaControlRootElement::changedMute() +{ + m_panelMuteButton->changedMute(); + if (m_volumeSliderMuteButton) + m_volumeSliderMuteButton->changedMute(); +} + +void MediaControlRootElement::changedVolume() +{ + if (m_volumeSlider) + m_volumeSlider->setVolume(m_mediaController->volume()); +} + +void MediaControlRootElement::enteredFullscreen() +{ + if (m_mediaController->isLiveStream()) { + m_seekBackButton->hide(); + m_seekForwardButton->hide(); + m_rewindButton->show(); + m_returnToRealTimeButton->show(); + } else { + m_seekBackButton->show(); + m_seekForwardButton->show(); + m_rewindButton->hide(); + m_returnToRealTimeButton->hide(); + } + + m_panel->setCanBeDragged(true); + + if (Page* page = document()->page()) + page->chrome()->setCursorHiddenUntilMouseMoves(true); + + startHideFullscreenControlsTimer(); +} + +void MediaControlRootElement::exitedFullscreen() +{ + // "show" actually means removal of display:none style, so we are just clearing styles + // when exiting fullscreen. + // FIXME: Clarify naming of show/hide <http://webkit.org/b/58157> + m_rewindButton->show(); + m_seekBackButton->show(); + m_seekForwardButton->show(); + m_returnToRealTimeButton->show(); + + m_panel->setCanBeDragged(false); + + // We will keep using the panel, but we want it to go back to the standard position. + // This will matter right away because we use the panel even when not fullscreen. + // And if we reenter fullscreen we also want the panel in the standard position. + m_panel->resetPosition(); + + stopHideFullscreenControlsTimer(); +} + +void MediaControlRootElement::showVolumeSlider() +{ + if (!m_mediaController->hasAudio()) + return; + + if (m_volumeSliderContainer) + m_volumeSliderContainer->show(); +} + +bool MediaControlRootElement::shouldHideControls() +{ + return !m_panel->hovered(); +} + +bool MediaControlRootElement::containsRelatedTarget(Event* event) +{ + if (!event->isMouseEvent()) + return false; + EventTarget* relatedTarget = static_cast<MouseEvent*>(event)->relatedTarget(); + if (!relatedTarget) + return false; + return contains(relatedTarget->toNode()); +} + +void MediaControlRootElement::defaultEventHandler(Event* event) +{ + MediaControls::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent) { + if (!containsRelatedTarget(event)) { + m_isMouseOverControls = true; + if (!m_mediaController->canPlay()) { + makeOpaque(); + if (shouldHideControls()) + startHideFullscreenControlsTimer(); + } + } + } else if (event->type() == eventNames().mouseoutEvent) { + if (!containsRelatedTarget(event)) { + m_isMouseOverControls = false; + stopHideFullscreenControlsTimer(); + } + } else if (event->type() == eventNames().mousemoveEvent) { + if (m_mediaController->isFullscreen()) { + // When we get a mouse move in fullscreen mode, show the media controls, and start a timer + // that will hide the media controls after a 3 seconds without a mouse move. + makeOpaque(); + if (shouldHideControls()) + startHideFullscreenControlsTimer(); + } + } +} + +void MediaControlRootElement::startHideFullscreenControlsTimer() +{ + if (!m_mediaController->isFullscreen()) + return; + + m_hideFullscreenControlsTimer.startOneShot(timeWithoutMouseMovementBeforeHidingControls); +} + +void MediaControlRootElement::hideFullscreenControlsTimerFired(Timer<MediaControlRootElement>*) +{ + if (m_mediaController->paused()) + return; + + if (!m_mediaController->isFullscreen()) + return; + + if (!shouldHideControls()) + return; + + if (Page* page = document()->page()) + page->chrome()->setCursorHiddenUntilMouseMoves(true); + + makeTransparent(); +} + +void MediaControlRootElement::stopHideFullscreenControlsTimer() +{ + m_hideFullscreenControlsTimer.stop(); +} + +#if ENABLE(VIDEO_TRACK) +void MediaControlRootElement::createTextTrackDisplay() +{ + if (m_textDisplayContainer) + return; + + RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document()); + m_textDisplayContainer = textDisplayContainer.get(); + + RefPtr<MediaControlTextTrackDisplayElement> textDisplay = MediaControlTextTrackDisplayElement::create(document()); + m_textDisplayContainer->hide(); + m_textTrackDisplay = textDisplay.get(); + + ExceptionCode ec; + textDisplayContainer->appendChild(textDisplay.release(), ec, true); + if (ec) + return; + + // Insert it before the first controller element so it always displays behind the controls. + insertBefore(textDisplayContainer.release(), m_panel, ec, true); +} + +void MediaControlRootElement::showTextTrackDisplay() +{ + if (!m_textDisplayContainer) + createTextTrackDisplay(); + m_textDisplayContainer->show(); +} + +void MediaControlRootElement::hideTextTrackDisplay() +{ + if (!m_textDisplayContainer) + createTextTrackDisplay(); + m_textDisplayContainer->hide(); +} + +void MediaControlRootElement::updateTextTrackDisplay() +{ + if (!m_textDisplayContainer) + createTextTrackDisplay(); + + CueList activeCues = toParentMediaElement(m_textDisplayContainer)->currentlyActiveCues(); + m_textTrackDisplay->removeChildren(); + bool nothingToDisplay = true; + for (size_t i = 0; i < activeCues.size(); ++i) { + TextTrackCue* cue = activeCues[i].data(); + ASSERT(cue->isActive()); + if (!cue->track() || cue->track()->mode() != TextTrack::SHOWING) + continue; + + String cueText = cue->getCueAsSource(); + if (!cueText.isEmpty()) { + if (!nothingToDisplay) + m_textTrackDisplay->appendChild(document()->createElement(HTMLNames::brTag, false), ASSERT_NO_EXCEPTION); + m_textTrackDisplay->appendChild(document()->createTextNode(cueText), ASSERT_NO_EXCEPTION); + nothingToDisplay = false; + } + } + + if (!nothingToDisplay) + m_textDisplayContainer->show(); + else + m_textDisplayContainer->hide(); +} +#endif + +const AtomicString& MediaControlRootElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls")); + return id; +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/MediaControlRootElement.h b/Source/WebCore/html/shadow/MediaControlRootElement.h new file mode 100644 index 000000000..7010d02d4 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControlRootElement.h @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControlRootElement_h +#define MediaControlRootElement_h + +#if ENABLE(VIDEO) + +#include "MediaControls.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class HTMLInputElement; +class HTMLMediaElement; +class Event; +class MediaControlPanelMuteButtonElement; +class MediaControlPlayButtonElement; +class MediaControlSeekButtonElement; +class MediaControlRewindButtonElement; +class MediaControlReturnToRealtimeButtonElement; +class MediaControlToggleClosedCaptionsButtonElement; +class MediaControlCurrentTimeDisplayElement; +class MediaControlTimelineElement; +class MediaControlTimeRemainingDisplayElement; +class MediaControlVolumeSliderElement; +class MediaControlFullscreenButtonElement; +class MediaControlTimeDisplayElement; +class MediaControlStatusDisplayElement; +class MediaControlTimelineContainerElement; +class MediaControlSeekBackButtonElement; +class MediaControlSeekForwardButtonElement; +class MediaControlMuteButtonElement; +class MediaControlVolumeSliderElement; +class MediaControlVolumeSliderMuteButtonElement; +class MediaControlVolumeSliderContainerElement; +class MediaControlFullscreenVolumeMinButtonElement; +class MediaControlFullscreenVolumeSliderElement; +class MediaControlFullscreenVolumeMaxButtonElement; +class MediaControlPanelElement; +class MediaPlayer; + +class RenderBox; +class RenderMedia; + +#if ENABLE(VIDEO_TRACK) +class MediaControlTextTrackContainerElement; +class MediaControlTextTrackDisplayElement; +#endif + +class MediaControlRootElement : public MediaControls { +public: + static PassRefPtr<MediaControlRootElement> create(Document*); + + // MediaControls implementation. + void setMediaController(MediaControllerInterface*); + + void show(); + void hide(); + void makeOpaque(); + void makeTransparent(); + + void reset(); + + void playbackProgressed(); + void playbackStarted(); + void playbackStopped(); + + void changedMute(); + void changedVolume(); + + void enteredFullscreen(); + void exitedFullscreen(); + + void reportedError(); + void loadedMetadata(); + void changedClosedCaptionsVisibility(); + + void showVolumeSlider(); + void updateTimeDisplay(); + void updateStatusDisplay(); + +#if ENABLE(VIDEO_TRACK) + void createTextTrackDisplay(); + void showTextTrackDisplay(); + void hideTextTrackDisplay(); + void updateTextTrackDisplay(); +#endif + + virtual bool shouldHideControls(); + +private: + MediaControlRootElement(Document*); + + virtual void defaultEventHandler(Event*); + void hideFullscreenControlsTimerFired(Timer<MediaControlRootElement>*); + void startHideFullscreenControlsTimer(); + void stopHideFullscreenControlsTimer(); + + virtual const AtomicString& shadowPseudoId() const; + + bool containsRelatedTarget(Event*); + + MediaControllerInterface* m_mediaController; + + MediaControlRewindButtonElement* m_rewindButton; + MediaControlPlayButtonElement* m_playButton; + MediaControlReturnToRealtimeButtonElement* m_returnToRealTimeButton; + MediaControlStatusDisplayElement* m_statusDisplay; + MediaControlCurrentTimeDisplayElement* m_currentTimeDisplay; + MediaControlTimelineElement* m_timeline; + MediaControlTimeRemainingDisplayElement* m_timeRemainingDisplay; + MediaControlTimelineContainerElement* m_timelineContainer; + MediaControlSeekBackButtonElement* m_seekBackButton; + MediaControlSeekForwardButtonElement* m_seekForwardButton; + MediaControlToggleClosedCaptionsButtonElement* m_toggleClosedCaptionsButton; + MediaControlPanelMuteButtonElement* m_panelMuteButton; + MediaControlVolumeSliderElement* m_volumeSlider; + MediaControlVolumeSliderMuteButtonElement* m_volumeSliderMuteButton; + MediaControlVolumeSliderContainerElement* m_volumeSliderContainer; + MediaControlFullscreenButtonElement* m_fullScreenButton; + MediaControlFullscreenVolumeMinButtonElement* m_fullScreenMinVolumeButton; + MediaControlFullscreenVolumeSliderElement* m_fullScreenVolumeSlider; + MediaControlFullscreenVolumeMaxButtonElement* m_fullScreenMaxVolumeButton; + MediaControlPanelElement* m_panel; +#if ENABLE(VIDEO_TRACK) + MediaControlTextTrackContainerElement* m_textDisplayContainer; + MediaControlTextTrackDisplayElement* m_textTrackDisplay; +#endif + Timer<MediaControlRootElement> m_hideFullscreenControlsTimer; + bool m_isMouseOverControls; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp b/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp new file mode 100644 index 000000000..dea66fbfe --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControlRootElementChromium.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "MediaControlRootElementChromium.h" + +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "MediaControlElements.h" +#include "MouseEvent.h" +#include "Page.h" +#include "RenderTheme.h" +#include "Text.h" + +#if ENABLE(VIDEO_TRACK) +#include "TextTrackCue.h" +#endif + +using namespace std; + +namespace WebCore { + +MediaControlRootElementChromium::MediaControlRootElementChromium(Document* document) + : MediaControls(document) + , m_mediaController(0) + , m_playButton(0) + , m_currentTimeDisplay(0) + , m_timeline(0) + , m_timelineContainer(0) + , m_panelMuteButton(0) + , m_volumeSlider(0) + , m_volumeSliderContainer(0) + , m_panel(0) +#if ENABLE(VIDEO_TRACK) + , m_textDisplayContainer(0) + , m_textTrackDisplay(0) +#endif + , m_opaque(true) + , m_isMouseOverControls(false) +{ +} + +PassRefPtr<MediaControls> MediaControls::create(Document* document) +{ + return MediaControlRootElementChromium::create(document); +} + +PassRefPtr<MediaControlRootElementChromium> MediaControlRootElementChromium::create(Document* document) +{ + if (!document->page()) + return 0; + + RefPtr<MediaControlRootElementChromium> controls = adoptRef(new MediaControlRootElementChromium(document)); + + RefPtr<MediaControlPanelElement> panel = MediaControlPanelElement::create(document); + + ExceptionCode ec; + + RefPtr<MediaControlPlayButtonElement> playButton = MediaControlPlayButtonElement::create(document); + controls->m_playButton = playButton.get(); + panel->appendChild(playButton.release(), ec, true); + 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); + if (ec) + return 0; + + RefPtr<MediaControlCurrentTimeDisplayElement> currentTimeDisplay = MediaControlCurrentTimeDisplayElement::create(document); + controls->m_currentTimeDisplay = currentTimeDisplay.get(); + timelineContainer->appendChild(currentTimeDisplay.release(), ec, true); + if (ec) + return 0; + + controls->m_timelineContainer = timelineContainer.get(); + panel->appendChild(timelineContainer.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlPanelMuteButtonElement> panelMuteButton = MediaControlPanelMuteButtonElement::create(document, controls.get()); + controls->m_panelMuteButton = panelMuteButton.get(); + panel->appendChild(panelMuteButton.release(), ec, true); + if (ec) + return 0; + + RefPtr<MediaControlVolumeSliderContainerElement> volumeSliderContainer = MediaControlVolumeSliderContainerElement::create(document); + + RefPtr<MediaControlVolumeSliderElement> slider = MediaControlVolumeSliderElement::create(document); + controls->m_volumeSlider = slider.get(); + volumeSliderContainer->appendChild(slider.release(), ec, true); + if (ec) + return 0; + + controls->m_volumeSliderContainer = volumeSliderContainer.get(); + panel->appendChild(volumeSliderContainer.release(), ec, true); + if (ec) + return 0; + + controls->m_panel = panel.get(); + controls->appendChild(panel.release(), ec, true); + if (ec) + return 0; + + return controls.release(); +} + +void MediaControlRootElementChromium::setMediaController(MediaControllerInterface* controller) +{ + if (m_mediaController == controller) + return; + m_mediaController = controller; + + if (m_playButton) + m_playButton->setMediaController(controller); + if (m_currentTimeDisplay) + m_currentTimeDisplay->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 (m_panel) + m_panel->setMediaController(controller); +#if ENABLE(VIDEO_TRACK) + if (m_textDisplayContainer) + m_textDisplayContainer->setMediaController(controller); + if (m_textTrackDisplay) + m_textTrackDisplay->setMediaController(controller); +#endif + reset(); +} + +void MediaControlRootElementChromium::show() +{ + m_panel->show(); +} + +void MediaControlRootElementChromium::hide() +{ + m_panel->hide(); +} + +void MediaControlRootElementChromium::makeOpaque() +{ + m_panel->makeOpaque(); +} + +void MediaControlRootElementChromium::makeTransparent() +{ + m_panel->makeTransparent(); +} + +void MediaControlRootElementChromium::reset() +{ + Page* page = document()->page(); + if (!page) + return; + + updateStatusDisplay(); + + float duration = m_mediaController->duration(); + m_timeline->setDuration(duration); + m_timelineContainer->show(); + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + + m_panelMuteButton->show(); + + if (m_volumeSlider) + m_volumeSlider->setVolume(m_mediaController->volume()); + + makeOpaque(); +} + +void MediaControlRootElementChromium::playbackStarted() +{ + m_playButton->updateDisplayType(); + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); +} + +void MediaControlRootElementChromium::playbackProgressed() +{ + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + + if (!m_isMouseOverControls && m_mediaController->hasVideo()) + makeTransparent(); +} + +void MediaControlRootElementChromium::playbackStopped() +{ + m_playButton->updateDisplayType(); + m_timeline->setPosition(m_mediaController->currentTime()); + updateTimeDisplay(); + makeOpaque(); +} + +void MediaControlRootElementChromium::updateTimeDisplay() +{ + float now = m_mediaController->currentTime(); + float duration = m_mediaController->duration(); + + Page* page = document()->page(); + if (!page) + return; + + // Allow the theme to format the time. + ExceptionCode ec; + m_currentTimeDisplay->setInnerText(page->theme()->formatMediaControlsCurrentTime(now, duration), ec); + m_currentTimeDisplay->setCurrentValue(now); +} + +void MediaControlRootElementChromium::reportedError() +{ + Page* page = document()->page(); + if (!page) + return; + + m_timelineContainer->hide(); + m_panelMuteButton->hide(); + m_volumeSliderContainer->hide(); +} + +void MediaControlRootElementChromium::updateStatusDisplay() +{ +} + +bool MediaControlRootElementChromium::shouldHideControls() +{ + return !m_panel->hovered(); +} + +void MediaControlRootElementChromium::loadedMetadata() +{ + reset(); +} + +bool MediaControlRootElementChromium::containsRelatedTarget(Event* event) +{ + if (!event->isMouseEvent()) + return false; + EventTarget* relatedTarget = static_cast<MouseEvent*>(event)->relatedTarget(); + if (!relatedTarget) + return false; + return contains(relatedTarget->toNode()); +} + +void MediaControlRootElementChromium::defaultEventHandler(Event* event) +{ + MediaControls::defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent) { + if (!containsRelatedTarget(event)) { + m_isMouseOverControls = true; + if (!m_mediaController->canPlay()) + makeOpaque(); + } + } else if (event->type() == eventNames().mouseoutEvent) { + if (!containsRelatedTarget(event)) + m_isMouseOverControls = false; + } +} + +void MediaControlRootElementChromium::changedClosedCaptionsVisibility() +{ +} + +void MediaControlRootElementChromium::changedMute() +{ + m_panelMuteButton->changedMute(); +} + +void MediaControlRootElementChromium::changedVolume() +{ + m_volumeSlider->setVolume(m_mediaController->volume()); +} + +void MediaControlRootElementChromium::enteredFullscreen() +{ +} + +void MediaControlRootElementChromium::exitedFullscreen() +{ +} + +void MediaControlRootElementChromium::showVolumeSlider() +{ + if (!m_mediaController->hasAudio()) + return; + + m_volumeSliderContainer->show(); +} + +#if ENABLE(VIDEO_TRACK) +void MediaControlRootElementChromium::createTextTrackDisplay() +{ + if (m_textDisplayContainer) + return; + + RefPtr<MediaControlTextTrackContainerElement> textDisplayContainer = MediaControlTextTrackContainerElement::create(document()); + m_textDisplayContainer = textDisplayContainer.get(); + + RefPtr<MediaControlTextTrackDisplayElement> textDisplay = MediaControlTextTrackDisplayElement::create(document()); + m_textDisplayContainer->hide(); + m_textTrackDisplay = textDisplay.get(); + + ExceptionCode ec; + textDisplayContainer->appendChild(textDisplay.release(), ec, true); + if (ec) + return; + + // Insert it before the first controller element so it always displays behind the controls. + insertBefore(textDisplayContainer.release(), m_panel, ec, true); +} + +void MediaControlRootElementChromium::showTextTrackDisplay() +{ + if (!m_textDisplayContainer) + createTextTrackDisplay(); + m_textDisplayContainer->show(); +} + +void MediaControlRootElementChromium::hideTextTrackDisplay() +{ + if (!m_textDisplayContainer) + createTextTrackDisplay(); + m_textDisplayContainer->hide(); +} + +void MediaControlRootElementChromium::updateTextTrackDisplay() +{ + if (!m_textDisplayContainer) + createTextTrackDisplay(); + + CueList activeCues = toParentMediaElement(m_textDisplayContainer)->currentlyActiveCues(); + m_textTrackDisplay->removeChildren(); + bool nothingToDisplay = true; + for (size_t i = 0; i < activeCues.size(); ++i) { + TextTrackCue* cue = activeCues[i].data(); + ASSERT(cue->isActive()); + if (!cue->track() || cue->track()->mode() != TextTrack::SHOWING) + continue; + + String cueText = cue->getCueAsSource(); + if (!cueText.isEmpty()) { + if (!nothingToDisplay) + m_textTrackDisplay->appendChild(document()->createElement(HTMLNames::brTag, false), ASSERT_NO_EXCEPTION); + m_textTrackDisplay->appendChild(document()->createTextNode(cueText), ASSERT_NO_EXCEPTION); + nothingToDisplay = false; + } + } + + if (!nothingToDisplay) + m_textDisplayContainer->show(); + else + m_textDisplayContainer->hide(); +} +#endif + +const AtomicString& MediaControlRootElementChromium::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, id, ("-webkit-media-controls")); + return id; +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/MediaControlRootElementChromium.h b/Source/WebCore/html/shadow/MediaControlRootElementChromium.h new file mode 100644 index 000000000..d895c3bc3 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControlRootElementChromium.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControlRootElementChromium_h +#define MediaControlRootElementChromium_h + +#if ENABLE(VIDEO) + +#include "MediaControls.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Document; +class HTMLInputElement; +class Event; +class MediaControlPanelMuteButtonElement; +class MediaControlPlayButtonElement; +class MediaControlCurrentTimeDisplayElement; +class MediaControlTimelineElement; +class MediaControlVolumeSliderElement; +class MediaControlFullscreenButtonElement; +class MediaControlTimeDisplayElement; +class MediaControlTimelineContainerElement; +class MediaControlMuteButtonElement; +class MediaControlVolumeSliderElement; +class MediaControlVolumeSliderContainerElement; +class MediaControlPanelElement; +class MediaControllerInterface; +class MediaPlayer; + +class RenderBox; +class RenderMedia; + +#if ENABLE(VIDEO_TRACK) +class MediaControlTextTrackContainerElement; +class MediaControlTextTrackDisplayElement; +#endif + +class MediaControlRootElementChromium : public MediaControls { +public: + static PassRefPtr<MediaControlRootElementChromium> create(Document*); + + // MediaControls implementation. + void setMediaController(MediaControllerInterface*); + + void show(); + void hide(); + void makeOpaque(); + void makeTransparent(); + + void reset(); + + void playbackProgressed(); + void playbackStarted(); + void playbackStopped(); + + void changedMute(); + void changedVolume(); + + void enteredFullscreen(); + void exitedFullscreen(); + + void reportedError(); + void loadedMetadata(); + void changedClosedCaptionsVisibility(); + + void showVolumeSlider(); + void updateTimeDisplay(); + void updateStatusDisplay(); + +#if ENABLE(VIDEO_TRACK) + void createTextTrackDisplay(); + void showTextTrackDisplay(); + void hideTextTrackDisplay(); + void updateTextTrackDisplay(); +#endif + + virtual bool shouldHideControls(); + +private: + MediaControlRootElementChromium(Document*); + + virtual void defaultEventHandler(Event*); + + virtual const AtomicString& shadowPseudoId() const; + + bool containsRelatedTarget(Event*); + + MediaControllerInterface* m_mediaController; + + MediaControlPlayButtonElement* m_playButton; + MediaControlCurrentTimeDisplayElement* m_currentTimeDisplay; + MediaControlTimelineElement* m_timeline; + MediaControlTimelineContainerElement* m_timelineContainer; + MediaControlPanelMuteButtonElement* m_panelMuteButton; + MediaControlVolumeSliderElement* m_volumeSlider; + MediaControlVolumeSliderContainerElement* m_volumeSliderContainer; + MediaControlPanelElement* m_panel; +#if ENABLE(VIDEO_TRACK) + MediaControlTextTrackContainerElement* m_textDisplayContainer; + MediaControlTextTrackDisplayElement* m_textTrackDisplay; +#endif + + bool m_opaque; + bool m_isMouseOverControls; +}; + +} + +#endif + +#endif diff --git a/Source/WebCore/html/shadow/MediaControls.cpp b/Source/WebCore/html/shadow/MediaControls.cpp new file mode 100644 index 000000000..2d0079252 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControls.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "MediaControls.h" + +#include "HTMLDivElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +MediaControls::MediaControls(Document* document) + : HTMLDivElement(HTMLNames::divTag, document) +{ +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/MediaControls.h b/Source/WebCore/html/shadow/MediaControls.h new file mode 100644 index 000000000..a61e34e11 --- /dev/null +++ b/Source/WebCore/html/shadow/MediaControls.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControls_h +#define MediaControls_h + +#if ENABLE(VIDEO) + +#include "HTMLDivElement.h" + +namespace WebCore { + +class MediaControllerInterface; + +class MediaControls : public HTMLDivElement { + public: + virtual ~MediaControls() {} + + // This function is to be implemented in your port-specific media + // controls implementation. + static PassRefPtr<MediaControls> create(Document*); + + virtual void setMediaController(MediaControllerInterface*) = 0; + + virtual void show() = 0; + virtual void hide() = 0; + virtual void makeOpaque() = 0; + virtual void makeTransparent() = 0; + + virtual void reset() = 0; + + virtual void playbackProgressed() = 0; + virtual void playbackStarted() = 0; + virtual void playbackStopped() = 0; + + virtual void changedMute() = 0; + virtual void changedVolume() = 0; + + virtual void enteredFullscreen() = 0; + virtual void exitedFullscreen() = 0; + + virtual void reportedError() = 0; + virtual void loadedMetadata() = 0; + virtual void changedClosedCaptionsVisibility() = 0; + + virtual void showVolumeSlider() = 0; + virtual void updateTimeDisplay() = 0; + virtual void updateStatusDisplay() = 0; + + virtual bool shouldHideControls() = 0; + +#if ENABLE(VIDEO_TRACK) + virtual void showTextTrackDisplay() = 0; + virtual void hideTextTrackDisplay() = 0; + virtual void updateTextTrackDisplay() = 0; +#endif + +protected: + MediaControls(Document*); + +private: + MediaControls(); + + virtual bool isMediaControls() const { return true; } +}; + +inline MediaControls* toMediaControls(Node* node) +{ + ASSERT(!node || node->isMediaControls()); + return static_cast<MediaControls*>(node); +} + +// This will catch anyone doing an unneccessary cast. +void toMediaControls(const MediaControls*); + +} + +#endif + +#endif diff --git a/Source/WebCore/html/shadow/MeterShadowElement.cpp b/Source/WebCore/html/shadow/MeterShadowElement.cpp new file mode 100644 index 000000000..be1041f71 --- /dev/null +++ b/Source/WebCore/html/shadow/MeterShadowElement.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#if ENABLE(METER_TAG) +#include "MeterShadowElement.h" + +#include "CSSMutableStyleDeclaration.h" +#include "CSSPropertyNames.h" +#include "HTMLMeterElement.h" +#include "HTMLNames.h" +#include "RenderMeter.h" +#include "RenderTheme.h" + +namespace WebCore { + +using namespace HTMLNames; + +MeterShadowElement::MeterShadowElement(Document* document) + : HTMLDivElement(HTMLNames::divTag, document) +{ +} + +HTMLMeterElement* MeterShadowElement::meterElement() const +{ + Node* node = const_cast<MeterShadowElement*>(this)->shadowAncestorNode(); + ASSERT(!node || meterTag == toElement(node)->tagQName()); + return static_cast<HTMLMeterElement*>(node); +} + +bool MeterShadowElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + RenderMeter* meterRenderer = toRenderMeter(meterElement()->renderer()); + return meterRenderer && !meterRenderer->theme()->supportsMeter(meterRenderer->style()->appearance()) && HTMLDivElement::rendererIsNeeded(context); +} + +const AtomicString& MeterBarElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudId, ("-webkit-meter-bar")); + return pseudId; +} + + +const AtomicString& MeterValueElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, optimumPseudId, ("-webkit-meter-optimum-value")); + DEFINE_STATIC_LOCAL(AtomicString, suboptimumPseudId, ("-webkit-meter-suboptimum-value")); + DEFINE_STATIC_LOCAL(AtomicString, evenLessGoodPseudId, ("-webkit-meter-even-less-good-value")); + + HTMLMeterElement* meter = meterElement(); + if (!meter) + return optimumPseudId; + + switch (meter->gaugeRegion()) { + case HTMLMeterElement::GaugeRegionOptimum: + return optimumPseudId; + case HTMLMeterElement::GaugeRegionSuboptimal: + return suboptimumPseudId; + case HTMLMeterElement::GaugeRegionEvenLessGood: + return evenLessGoodPseudId; + default: + ASSERT_NOT_REACHED(); + return optimumPseudId; + } +} + +void MeterValueElement::setWidthPercentage(double width) +{ + ensureInlineStyleDecl()->setProperty(CSSPropertyWidth, width, CSSPrimitiveValue::CSS_PERCENTAGE); +} + + +} + +#endif + diff --git a/Source/WebCore/html/shadow/MeterShadowElement.h b/Source/WebCore/html/shadow/MeterShadowElement.h new file mode 100644 index 000000000..c36b01b24 --- /dev/null +++ b/Source/WebCore/html/shadow/MeterShadowElement.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MeterShadowElement_h +#define MeterShadowElement_h + +#include "HTMLDivElement.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class HTMLMeterElement; + +class MeterShadowElement : public HTMLDivElement { +public: + MeterShadowElement(Document*); + HTMLMeterElement* meterElement() const; + +private: + virtual bool rendererIsNeeded(const NodeRenderingContext&); +}; + +class MeterBarElement : public MeterShadowElement { +public: + MeterBarElement(Document* document) + : MeterShadowElement(document) + { + } + + static PassRefPtr<MeterBarElement> create(Document*); + virtual const AtomicString& shadowPseudoId() const; +}; + +inline PassRefPtr<MeterBarElement> MeterBarElement::create(Document* document) +{ + return adoptRef(new MeterBarElement(document)); +} + + +class MeterValueElement : public MeterShadowElement { +public: + MeterValueElement(Document* document) + : MeterShadowElement(document) + { + } + + virtual const AtomicString& shadowPseudoId() const; + static PassRefPtr<MeterValueElement> create(Document*); + void setWidthPercentage(double); +}; + +inline PassRefPtr<MeterValueElement> MeterValueElement::create(Document* document) +{ + return adoptRef(new MeterValueElement(document)); +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/ProgressShadowElement.cpp b/Source/WebCore/html/shadow/ProgressShadowElement.cpp new file mode 100644 index 000000000..ecd35544f --- /dev/null +++ b/Source/WebCore/html/shadow/ProgressShadowElement.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#if ENABLE(PROGRESS_TAG) +#include "ProgressShadowElement.h" + +#include "HTMLNames.h" +#include "HTMLProgressElement.h" +#include "RenderObject.h" + +namespace WebCore { + +using namespace HTMLNames; + +ProgressShadowElement::ProgressShadowElement(Document* document) + : HTMLDivElement(HTMLNames::divTag, document) +{ +} + +HTMLProgressElement* ProgressShadowElement::progressElement() const +{ + Node* node = const_cast<ProgressShadowElement*>(this)->shadowAncestorNode(); + ASSERT(!node || progressTag == toElement(node)->tagQName()); + return static_cast<HTMLProgressElement*>(node); +} + +bool ProgressShadowElement::rendererIsNeeded(const NodeRenderingContext& context) +{ + RenderObject* progressRenderer = progressElement()->renderer(); + return progressRenderer && !progressRenderer->style()->hasAppearance() && HTMLDivElement::rendererIsNeeded(context); +} + +const AtomicString& ProgressBarElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudId, ("-webkit-progress-bar")); + return pseudId; +} + + +const AtomicString& ProgressValueElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudId, ("-webkit-progress-value")); + return pseudId; +} + +void ProgressValueElement::setWidthPercentage(double width) +{ + ensureInlineStyleDecl()->setProperty(CSSPropertyWidth, width, CSSPrimitiveValue::CSS_PERCENTAGE); +} + +} +#endif + diff --git a/Source/WebCore/html/shadow/ProgressShadowElement.h b/Source/WebCore/html/shadow/ProgressShadowElement.h new file mode 100644 index 000000000..e135c71da --- /dev/null +++ b/Source/WebCore/html/shadow/ProgressShadowElement.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ProgressShadowElement_h +#define ProgressShadowElement_h + +#include "HTMLDivElement.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class HTMLProgressElement; + +class ProgressShadowElement : public HTMLDivElement { +public: + ProgressShadowElement(Document*); + HTMLProgressElement* progressElement() const; + +private: + virtual bool rendererIsNeeded(const NodeRenderingContext&); +}; + +class ProgressBarElement : public ProgressShadowElement { +public: + ProgressBarElement(Document* document) + : ProgressShadowElement(document) + { + } + + static PassRefPtr<ProgressBarElement> create(Document*); + virtual const AtomicString& shadowPseudoId() const; +}; + +inline PassRefPtr<ProgressBarElement> ProgressBarElement::create(Document* document) +{ + return adoptRef(new ProgressBarElement(document)); +} + + +class ProgressValueElement : public ProgressShadowElement { +public: + ProgressValueElement(Document* document) + : ProgressShadowElement(document) + { + } + + virtual const AtomicString& shadowPseudoId() const; + static PassRefPtr<ProgressValueElement> create(Document*); + void setWidthPercentage(double); +}; + +inline PassRefPtr<ProgressValueElement> ProgressValueElement::create(Document* document) +{ + return adoptRef(new ProgressValueElement(document)); +} + +} + +#endif diff --git a/Source/WebCore/html/shadow/SliderThumbElement.cpp b/Source/WebCore/html/shadow/SliderThumbElement.cpp new file mode 100644 index 000000000..1cf4de7d0 --- /dev/null +++ b/Source/WebCore/html/shadow/SliderThumbElement.cpp @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "config.h" +#include "SliderThumbElement.h" + +#include "CSSValueKeywords.h" +#include "Event.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLParserIdioms.h" +#include "MouseEvent.h" +#include "RenderDeprecatedFlexibleBox.h" +#include "RenderSlider.h" +#include "RenderTheme.h" +#include "ShadowRoot.h" +#include "StepRange.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +inline static double sliderPosition(HTMLInputElement* element) +{ + StepRange range(element); + return range.proportionFromValue(range.valueFromElement(element)); +} + +inline static bool hasVerticalAppearance(HTMLInputElement* input) +{ + ASSERT(input->renderer()); + RenderStyle* sliderStyle = input->renderer()->style(); + return sliderStyle->appearance() == SliderVerticalPart || sliderStyle->appearance() == MediaVolumeSliderPart; +} + +SliderThumbElement* sliderThumbElementOf(Node* node) +{ + ASSERT(node); + ShadowRoot* shadow = node->toInputElement()->shadowRoot(); + ASSERT(shadow); + Node* thumb = shadow->firstChild()->firstChild()->firstChild(); + ASSERT(thumb); + return toSliderThumbElement(thumb); +} + +// -------------------------------- + +RenderSliderThumb::RenderSliderThumb(Node* node) + : RenderBlock(node) +{ +} + +void RenderSliderThumb::updateAppearance(RenderStyle* parentStyle) +{ + if (parentStyle->appearance() == SliderVerticalPart) + style()->setAppearance(SliderThumbVerticalPart); + else if (parentStyle->appearance() == SliderHorizontalPart) + style()->setAppearance(SliderThumbHorizontalPart); + else if (parentStyle->appearance() == MediaSliderPart) + style()->setAppearance(MediaSliderThumbPart); + else if (parentStyle->appearance() == MediaVolumeSliderPart) + style()->setAppearance(MediaVolumeSliderThumbPart); + if (style()->hasAppearance()) + theme()->adjustSliderThumbSize(style()); +} + +bool RenderSliderThumb::isSliderThumb() const +{ + return true; +} + +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; + + double fraction = sliderPosition(input) * 100; + if (isVertical) + style()->setTop(Length(100 - fraction, Percent)); + else if (style()->isLeftToRightDirection()) + style()->setLeft(Length(fraction, Percent)); + else + style()->setRight(Length(fraction, Percent)); + + RenderBlock::layout(); +} + +// -------------------------------- + +// FIXME: Find a way to cascade appearance and adjust heights, and get rid of this class. +// http://webkit.org/b/62535 +class RenderSliderContainer : public RenderDeprecatedFlexibleBox { +public: + RenderSliderContainer(Node* node) + : RenderDeprecatedFlexibleBox(node) { } + +private: + virtual void layout(); +}; + +void RenderSliderContainer::layout() +{ + HTMLInputElement* input = node()->shadowAncestorNode()->toInputElement(); + bool isVertical = hasVerticalAppearance(input); + style()->setBoxOrient(isVertical ? VERTICAL : HORIZONTAL); + // Sets the concrete height if the height of the <input> is not fixed or a + // percentage value because a percentage height value of this box won't be + // based on the <input> height in such case. + Length inputHeight = input->renderer()->style()->height(); + RenderObject* trackRenderer = node()->firstChild()->renderer(); + if (!isVertical && input->renderer()->isSlider() && !inputHeight.isFixed() && !inputHeight.isPercent()) { + RenderObject* thumbRenderer = input->shadowRoot()->firstChild()->firstChild()->firstChild()->renderer(); + if (thumbRenderer) { + style()->setHeight(thumbRenderer->style()->height()); + if (trackRenderer) + trackRenderer->style()->setHeight(thumbRenderer->style()->height()); + } + } else { + style()->setHeight(Length(100, Percent)); + if (trackRenderer) + trackRenderer->style()->setHeight(Length()); + } + + RenderDeprecatedFlexibleBox::layout(); + + // Percentage 'top' for the thumb doesn't work if the parent style has no + // concrete height. + Node* track = node()->firstChild(); + if (track && track->renderer()->isBox()) { + RenderBox* trackBox = track->renderBox(); + trackBox->style()->setHeight(Length(trackBox->height() - trackBox->borderAndPaddingHeight(), Fixed)); + } +} + +// -------------------------------- + +void SliderThumbElement::setPositionFromValue() +{ + // Since the code to calculate position is in the RenderSliderThumb layout + // path, we don't actually update the value here. Instead, we poke at the + // renderer directly to trigger layout. + if (renderer()) + renderer()->setNeedsLayout(true); +} + +RenderObject* SliderThumbElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderSliderThumb(this); +} + +bool SliderThumbElement::isEnabledFormControl() const +{ + return hostInput()->isEnabledFormControl(); +} + +bool SliderThumbElement::isReadOnlyFormControl() const +{ + return hostInput()->isReadOnlyFormControl(); +} + +Node* SliderThumbElement::focusDelegate() +{ + return hostInput(); +} + +void SliderThumbElement::dragFrom(const LayoutPoint& point) +{ + setPositionFromPoint(point); + startDragging(); +} + +void SliderThumbElement::setPositionFromPoint(const LayoutPoint& point) +{ + HTMLInputElement* input = hostInput(); + + if (!input->renderer() || !renderer()) + return; + + LayoutPoint offset = roundedLayoutPoint(input->renderer()->absoluteToLocal(point, false, true)); + bool isVertical = hasVerticalAppearance(input); + LayoutUnit trackSize; + LayoutUnit position; + LayoutUnit currentPosition; + // We need to calculate currentPosition from absolute points becaue the + // renderer for this node is usually on a layer and renderBox()->x() and + // y() are unusable. + // FIXME: This should probably respect transforms. + LayoutPoint absoluteThumbOrigin = renderBox()->absoluteBoundingBoxRectIgnoringTransforms().location(); + LayoutPoint absoluteSliderContentOrigin = roundedLayoutPoint(input->renderer()->localToAbsolute()); + if (isVertical) { + trackSize = input->renderBox()->contentHeight() - renderBox()->height(); + position = offset.y() - renderBox()->height() / 2; + currentPosition = absoluteThumbOrigin.y() - absoluteSliderContentOrigin.y(); + } else { + trackSize = input->renderBox()->contentWidth() - renderBox()->width(); + position = offset.x() - renderBox()->width() / 2; + currentPosition = absoluteThumbOrigin.x() - absoluteSliderContentOrigin.x(); + } + position = max<LayoutUnit>(0, min(position, trackSize)); + if (position == currentPosition) + return; + + StepRange range(input); + double fraction = static_cast<double>(position) / trackSize; + if (isVertical || !renderBox()->style()->isLeftToRightDirection()) + fraction = 1 - fraction; + double value = range.clampValue(range.valueFromProportion(fraction)); + + // FIXME: This is no longer being set from renderer. Consider updating the method name. + input->setValueFromRenderer(serializeForNumberType(value)); + renderer()->setNeedsLayout(true); + input->dispatchFormControlChangeEvent(); +} + +void SliderThumbElement::startDragging() +{ + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(this); + m_inDragMode = true; + } +} + +void SliderThumbElement::stopDragging() +{ + if (!m_inDragMode) + return; + + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_inDragMode = false; + if (renderer()) + renderer()->setNeedsLayout(true); +} + +void SliderThumbElement::defaultEventHandler(Event* event) +{ + if (!event->isMouseEvent()) { + HTMLDivElement::defaultEventHandler(event); + return; + } + + // FIXME: Should handle this readonly/disabled check in more general way. + // Missing this kind of check is likely to occur elsewhere if adding it in each shadow element. + HTMLInputElement* input = hostInput(); + if (!input || input->isReadOnlyFormControl() || !input->isEnabledFormControl()) { + HTMLDivElement::defaultEventHandler(event); + return; + } + + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + bool isLeftButton = mouseEvent->button() == LeftButton; + const AtomicString& eventType = event->type(); + + // We intentionally do not call event->setDefaultHandled() here because + // MediaControlTimelineElement::defaultEventHandler() wants to handle these + // mouse events. + if (eventType == eventNames().mousedownEvent && isLeftButton) { + startDragging(); + return; + } else if (eventType == eventNames().mouseupEvent && isLeftButton) { + stopDragging(); + return; + } else if (eventType == eventNames().mousemoveEvent) { + if (m_inDragMode) + setPositionFromPoint(mouseEvent->absoluteLocation()); + return; + } + + HTMLDivElement::defaultEventHandler(event); +} + +void SliderThumbElement::detach() +{ + if (m_inDragMode) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + HTMLDivElement::detach(); +} + +HTMLInputElement* SliderThumbElement::hostInput() const +{ + // Only HTMLInputElement creates SliderThumbElement instances as its shadow nodes. + // So, shadowAncestorNode() must be an HTMLInputElement. + return shadowAncestorNode()->toInputElement(); +} + +const AtomicString& SliderThumbElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb")); + return sliderThumb; +} + +// -------------------------------- + +inline TrackLimiterElement::TrackLimiterElement(Document* document) + : HTMLDivElement(HTMLNames::divTag, document) +{ +} + +PassRefPtr<TrackLimiterElement> TrackLimiterElement::create(Document* document) +{ + RefPtr<TrackLimiterElement> element = adoptRef(new TrackLimiterElement(document)); + + CSSInlineStyleDeclaration* style = element->ensureInlineStyleDecl(); + style->setProperty(CSSPropertyVisibility, CSSValueHidden); + style->setProperty(CSSPropertyPosition, CSSValueStatic); + + return element.release(); +} + +RenderObject* TrackLimiterElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderSliderThumb(this); +} + +const AtomicString& TrackLimiterElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-thumb")); + return sliderThumb; +} + +TrackLimiterElement* trackLimiterElementOf(Node* node) +{ + ASSERT(node); + ShadowRoot* shadow = node->toInputElement()->shadowRoot(); + ASSERT(shadow); + Node* limiter = shadow->firstChild()->lastChild(); + ASSERT(limiter); + return static_cast<TrackLimiterElement*>(limiter); +} + +// -------------------------------- + +inline SliderContainerElement::SliderContainerElement(Document* document) + : HTMLDivElement(HTMLNames::divTag, document) +{ +} + +PassRefPtr<SliderContainerElement> SliderContainerElement::create(Document* document) +{ + return adoptRef(new SliderContainerElement(document)); +} + +RenderObject* SliderContainerElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderSliderContainer(this); +} + +const AtomicString& SliderContainerElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, sliderThumb, ("-webkit-slider-container")); + return sliderThumb; +} + +} diff --git a/Source/WebCore/html/shadow/SliderThumbElement.h b/Source/WebCore/html/shadow/SliderThumbElement.h new file mode 100644 index 000000000..e2d8b66eb --- /dev/null +++ b/Source/WebCore/html/shadow/SliderThumbElement.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SliderThumbElement_h +#define SliderThumbElement_h + +#include "FloatPoint.h" +#include "HTMLDivElement.h" +#include "HTMLNames.h" +#include "RenderBlock.h" +#include "RenderStyleConstants.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class HTMLElement; +class HTMLInputElement; +class Event; +class FloatPoint; + +class SliderThumbElement : public HTMLDivElement { +public: + static PassRefPtr<SliderThumbElement> create(Document*); + + void setPositionFromValue(); + + void dragFrom(const LayoutPoint&); + virtual void defaultEventHandler(Event*); + virtual void detach(); + virtual const AtomicString& shadowPseudoId() const; + HTMLInputElement* hostInput() const; + +private: + SliderThumbElement(Document*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual PassRefPtr<Element> cloneElementWithoutAttributesAndChildren(); + virtual bool isEnabledFormControl() const; + virtual bool isReadOnlyFormControl() const; + virtual Node* focusDelegate(); + void startDragging(); + void stopDragging(); + void setPositionFromPoint(const LayoutPoint&); + + bool m_inDragMode; +}; + +inline SliderThumbElement::SliderThumbElement(Document* document) + : HTMLDivElement(HTMLNames::divTag, document) + , m_inDragMode(false) +{ +} + +inline PassRefPtr<SliderThumbElement> SliderThumbElement::create(Document* document) +{ + return adoptRef(new SliderThumbElement(document)); +} + +inline PassRefPtr<Element> SliderThumbElement::cloneElementWithoutAttributesAndChildren() +{ + return create(document()); +} + +inline SliderThumbElement* toSliderThumbElement(Node* node) +{ + ASSERT(!node || node->isHTMLElement()); + return static_cast<SliderThumbElement*>(node); +} + +// This always return a valid pointer. +// An assertion fails if the specified node is not a range input. +SliderThumbElement* sliderThumbElementOf(Node*); + +// -------------------------------- + +class RenderSliderThumb : public RenderBlock { +public: + RenderSliderThumb(Node*); + void updateAppearance(RenderStyle* parentStyle); + +private: + virtual bool isSliderThumb() const; + virtual void layout(); +}; + +// -------------------------------- + +class TrackLimiterElement : public HTMLDivElement { +public: + static PassRefPtr<TrackLimiterElement> create(Document*); + +private: + TrackLimiterElement(Document*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual const AtomicString& shadowPseudoId() const; +}; + +// This always return a valid pointer. +// An assertion fails if the specified node is not a range input. +TrackLimiterElement* trackLimiterElementOf(Node*); + +// -------------------------------- + +class SliderContainerElement : public HTMLDivElement { +public: + static PassRefPtr<SliderContainerElement> create(Document*); + +private: + SliderContainerElement(Document*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual const AtomicString& shadowPseudoId() const; +}; + +} + +#endif diff --git a/Source/WebCore/html/shadow/TextControlInnerElements.cpp b/Source/WebCore/html/shadow/TextControlInnerElements.cpp new file mode 100644 index 000000000..c24931f2c --- /dev/null +++ b/Source/WebCore/html/shadow/TextControlInnerElements.cpp @@ -0,0 +1,566 @@ +/* + * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextControlInnerElements.h" + +#include "BeforeTextInsertedEvent.h" +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLTextAreaElement.h" +#include "MouseEvent.h" +#include "Page.h" +#include "RenderLayer.h" +#include "RenderTextControlSingleLine.h" +#include "ScriptController.h" +#include "ScrollbarTheme.h" +#include "SpeechInput.h" +#include "SpeechInputEvent.h" +#include "TextEvent.h" +#include "TextEventInputType.h" + +namespace WebCore { + +using namespace HTMLNames; + +TextControlInnerElement::TextControlInnerElement(Document* document) + : HTMLDivElement(divTag, document) +{ + setHasCustomStyleForRenderer(); +} + +PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(Document* document) +{ + return adoptRef(new TextControlInnerElement(document)); +} + +PassRefPtr<RenderStyle> TextControlInnerElement::customStyleForRenderer() +{ + RenderTextControlSingleLine* parentRenderer = toRenderTextControlSingleLine(shadowAncestorNode()->renderer()); + return parentRenderer->createInnerBlockStyle(parentRenderer->style()); +} + +// ---------------------------- + +inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document) + : HTMLDivElement(divTag, document) +{ + setHasCustomStyleForRenderer(); +} + +PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document) +{ + return adoptRef(new TextControlInnerTextElement(document)); +} + +void TextControlInnerTextElement::defaultEventHandler(Event* event) +{ + // FIXME: In the future, we should add a way to have default event listeners. + // Then we would add one to the text field's inner div, and we wouldn't need this subclass. + // Or possibly we could just use a normal event listener. + if (event->isBeforeTextInsertedEvent() || event->type() == eventNames().webkitEditableContentChangedEvent) { + Node* shadowAncestor = shadowAncestorNode(); + // A TextControlInnerTextElement can be its own shadow ancestor if its been detached, but kept alive by an EditCommand. + // In this case, an undo/redo can cause events to be sent to the TextControlInnerTextElement. + // To prevent an infinite loop, we must check for this case before sending the event up the chain. + if (shadowAncestor && shadowAncestor != this) + shadowAncestor->defaultEventHandler(event); + } + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); +} + +RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + bool multiLine = false; + Node* shadowAncestor = shadowAncestorNode(); + if (shadowAncestor && shadowAncestor->renderer()) { + ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea()); + multiLine = shadowAncestor->renderer()->isTextArea(); + } + return new (arena) RenderTextControlInnerBlock(this, multiLine); +} + +PassRefPtr<RenderStyle> TextControlInnerTextElement::customStyleForRenderer() +{ + RenderTextControl* parentRenderer = toRenderTextControl(shadowAncestorNode()->renderer()); + return parentRenderer->createInnerTextStyle(parentRenderer->style()); +} + +// ---------------------------- + +inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document) + : HTMLDivElement(divTag, document) +{ +} + +PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document) +{ + return adoptRef(new SearchFieldResultsButtonElement(document)); +} + +const AtomicString& SearchFieldResultsButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, resultsId, ("-webkit-search-results-button")); + DEFINE_STATIC_LOCAL(AtomicString, resultsDecorationId, ("-webkit-search-results-decoration")); + DEFINE_STATIC_LOCAL(AtomicString, decorationId, ("-webkit-search-decoration")); + Node* host = shadowAncestorNode(); + if (!host) + return resultsId; + if (HTMLInputElement* input = host->toInputElement()) { + if (input->maxResults() < 0) + return decorationId; + if (input->maxResults() > 0) + return resultsId; + return resultsDecorationId; + } + return resultsId; +} + +void SearchFieldResultsButtonElement::defaultEventHandler(Event* event) +{ + // On mousedown, bring up a menu, if needed + HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode()); + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + input->focus(); + input->select(); + RenderTextControlSingleLine* renderer = toRenderTextControlSingleLine(input->renderer()); + if (renderer->popupIsVisible()) + renderer->hidePopup(); + else if (input->maxResults() > 0) + renderer->showPopup(); + event->setDefaultHandled(); + } + + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); +} + +// ---------------------------- + +inline SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* document) + : HTMLDivElement(divTag, document) + , m_capturing(false) +{ +} + +PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document) +{ + return adoptRef(new SearchFieldCancelButtonElement(document)); +} + +const AtomicString& SearchFieldCancelButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-search-cancel-button")); + return pseudoId; +} + +void SearchFieldCancelButtonElement::detach() +{ + if (m_capturing) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + HTMLDivElement::detach(); +} + + +void SearchFieldCancelButtonElement::defaultEventHandler(Event* event) +{ + // If the element is visible, on mouseup, clear the value, and set selection + RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode())); + if (input->disabled() || input->isReadOnlyFormControl()) { + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); + return; + } + + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + if (renderer() && renderer()->visibleToHitTesting()) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(this); + m_capturing = true; + } + } + input->focus(); + input->select(); + event->setDefaultHandled(); + } + if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + if (m_capturing) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_capturing = false; + } + if (hovered()) { + String oldValue = input->value(); + input->setValueForUser(""); + input->onSearch(); + event->setDefaultHandled(); + } + } + } + + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); +} + +// ---------------------------- + +inline SpinButtonElement::SpinButtonElement(Document* document) + : HTMLDivElement(divTag, document) + , m_capturing(false) + , m_upDownState(Indeterminate) + , m_pressStartingState(Indeterminate) + , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired) +{ +} + +PassRefPtr<SpinButtonElement> SpinButtonElement::create(Document* document) +{ + return adoptRef(new SpinButtonElement(document)); +} + +const AtomicString& SpinButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, innerPseudoId, ("-webkit-inner-spin-button")); + return innerPseudoId; +} + +void SpinButtonElement::detach() +{ + releaseCapture(); + HTMLDivElement::detach(); +} + +void SpinButtonElement::defaultEventHandler(Event* event) +{ + if (!event->isMouseEvent()) { + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); + return; + } + + RenderBox* box = renderBox(); + if (!box) { + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); + return; + } + + RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode())); + if (input->disabled() || input->isReadOnlyFormControl()) { + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); + return; + } + + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + IntPoint local = roundedIntPoint(box->absoluteToLocal(mouseEvent->absoluteLocation(), false, true)); + if (mouseEvent->type() == eventNames().mousedownEvent && mouseEvent->button() == LeftButton) { + if (box->borderBoxRect().contains(local)) { + // The following functions of HTMLInputElement may run JavaScript + // code which detaches this shadow node. We need to take a reference + // and check renderer() after such function calls. + RefPtr<Node> protector(this); + input->focus(); + input->select(); + if (renderer()) { + ASSERT(m_upDownState != Indeterminate); + input->stepUpFromRenderer(m_upDownState == Up ? 1 : -1); + if (renderer()) + startRepeatingTimer(); + } + event->setDefaultHandled(); + } + } else if (mouseEvent->type() == eventNames().mouseupEvent && mouseEvent->button() == LeftButton) + stopRepeatingTimer(); + else if (event->type() == eventNames().mousemoveEvent) { + if (box->borderBoxRect().contains(local)) { + if (!m_capturing) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(this); + m_capturing = true; + } + } + UpDownState oldUpDownState = m_upDownState; + m_upDownState = local.y() < box->height() / 2 ? Up : Down; + if (m_upDownState != oldUpDownState) + renderer()->repaint(); + } else { + releaseCapture(); + m_upDownState = Indeterminate; + } + } + + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); +} + +void SpinButtonElement::releaseCapture() +{ + stopRepeatingTimer(); + if (m_capturing) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_capturing = false; + } + } +} + +void SpinButtonElement::startRepeatingTimer() +{ + m_pressStartingState = m_upDownState; + ScrollbarTheme* theme = ScrollbarTheme::theme(); + m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay()); +} + +void SpinButtonElement::stopRepeatingTimer() +{ + m_repeatingTimer.stop(); +} + +void SpinButtonElement::step(int amount) +{ + HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode()); + if (input->disabled() || input->isReadOnlyFormControl()) + return; + // On Mac OS, NSStepper updates the value for the button under the mouse + // cursor regardless of the button pressed at the beginning. So the + // following check is not needed for Mac OS. +#if !OS(MAC_OS_X) + if (m_upDownState != m_pressStartingState) + return; +#endif + input->stepUpFromRenderer(amount); +} + +void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*) +{ + step(m_upDownState == Up ? 1 : -1); +} + +void SpinButtonElement::setHovered(bool flag) +{ + if (!flag) + m_upDownState = Indeterminate; + HTMLDivElement::setHovered(flag); +} + + +// ---------------------------- + +#if ENABLE(INPUT_SPEECH) + +inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(Document* document) + : HTMLDivElement(divTag, document) + , m_capturing(false) + , m_state(Idle) + , m_listenerId(0) +{ +} + +InputFieldSpeechButtonElement::~InputFieldSpeechButtonElement() +{ + SpeechInput* speech = speechInput(); + if (speech && m_listenerId) { // Could be null when page is unloading. + if (m_state != Idle) + speech->cancelRecognition(m_listenerId); + speech->unregisterListener(m_listenerId); + } +} + +PassRefPtr<InputFieldSpeechButtonElement> InputFieldSpeechButtonElement::create(Document* document) +{ + return adoptRef(new InputFieldSpeechButtonElement(document)); +} + +void InputFieldSpeechButtonElement::defaultEventHandler(Event* event) +{ + // For privacy reasons, only allow clicks directly coming from the user. + if (!ScriptController::processingUserGesture()) { + HTMLDivElement::defaultEventHandler(event); + return; + } + + // The call to focus() below dispatches a focus event, and an event handler in the page might + // remove the input element from DOM. To make sure it remains valid until we finish our work + // here, we take a temporary reference. + RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode())); + + if (input->disabled() || input->isReadOnlyFormControl()) { + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); + return; + } + + // On mouse down, select the text and set focus. + if (event->type() == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + if (renderer() && renderer()->visibleToHitTesting()) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(this); + m_capturing = true; + } + } + RefPtr<InputFieldSpeechButtonElement> holdRefButton(this); + input->focus(); + input->select(); + event->setDefaultHandled(); + } + // On mouse up, release capture cleanly. + if (event->type() == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + if (m_capturing && renderer() && renderer()->visibleToHitTesting()) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_capturing = false; + } + } + } + + if (event->type() == eventNames().clickEvent && m_listenerId) { + switch (m_state) { + case Idle: + startSpeechInput(); + break; + case Recording: + stopSpeechInput(); + break; + case Recognizing: + // Nothing to do here, we will continue to wait for results. + break; + } + event->setDefaultHandled(); + } + + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); +} + +void InputFieldSpeechButtonElement::setState(SpeechInputState state) +{ + if (m_state != state) { + m_state = state; + shadowAncestorNode()->renderer()->repaint(); + } +} + +SpeechInput* InputFieldSpeechButtonElement::speechInput() +{ + return document()->page() ? document()->page()->speechInput() : 0; +} + +void InputFieldSpeechButtonElement::didCompleteRecording(int) +{ + setState(Recognizing); +} + +void InputFieldSpeechButtonElement::didCompleteRecognition(int) +{ + setState(Idle); +} + +void InputFieldSpeechButtonElement::setRecognitionResult(int, const SpeechInputResultArray& results) +{ + m_results = results; + + // The call to setValue() below dispatches an event, and an event handler in the page might + // remove the input element from DOM. To make sure it remains valid until we finish our work + // here, we take a temporary reference. + RefPtr<HTMLInputElement> input(static_cast<HTMLInputElement*>(shadowAncestorNode())); + if (input->disabled() || input->isReadOnlyFormControl()) + return; + + RefPtr<InputFieldSpeechButtonElement> holdRefButton(this); + if (document() && document()->domWindow()) + input->dispatchEvent(TextEvent::create(document()->domWindow(), results.isEmpty() ? "" : results[0]->utterance(), TextEventInputOther)); + + // This event is sent after the text event so the website can perform actions using the input field content immediately. + // It provides alternative recognition hypotheses and notifies that the results come from speech input. + input->dispatchEvent(SpeechInputEvent::create(eventNames().webkitspeechchangeEvent, results)); + + // Check before accessing the renderer as the above event could have potentially turned off + // speech in the input element, hence removing this button and renderer from the hierarchy. + if (renderer()) + renderer()->repaint(); +} + +void InputFieldSpeechButtonElement::attach() +{ + ASSERT(!m_listenerId); + m_listenerId = document()->page()->speechInput()->registerListener(this); + HTMLDivElement::attach(); +} + +void InputFieldSpeechButtonElement::detach() +{ + if (m_capturing) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + + if (m_listenerId) { + if (m_state != Idle) + speechInput()->cancelRecognition(m_listenerId); + speechInput()->unregisterListener(m_listenerId); + m_listenerId = 0; + } + + HTMLDivElement::detach(); +} + +void InputFieldSpeechButtonElement::startSpeechInput() +{ + if (m_state != Idle) + return; + + RefPtr<HTMLInputElement> input = static_cast<HTMLInputElement*>(shadowAncestorNode()); + AtomicString language = input->computeInheritedLanguage(); + String grammar = input->getAttribute(webkitgrammarAttr); + // FIXME: this should probably respect transforms + IntRect rect = renderer()->absoluteBoundingBoxRectIgnoringTransforms(); + if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin())) + setState(Recording); +} + +void InputFieldSpeechButtonElement::stopSpeechInput() +{ + if (m_state == Recording) + speechInput()->stopRecording(m_listenerId); +} + +const AtomicString& InputFieldSpeechButtonElement::shadowPseudoId() const +{ + DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-input-speech-button")); + return pseudoId; +} + +#endif // ENABLE(INPUT_SPEECH) + +} diff --git a/Source/WebCore/html/shadow/TextControlInnerElements.h b/Source/WebCore/html/shadow/TextControlInnerElements.h new file mode 100644 index 000000000..c231f88dc --- /dev/null +++ b/Source/WebCore/html/shadow/TextControlInnerElements.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2006, 2008, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextControlInnerElements_h +#define TextControlInnerElements_h + +#include "HTMLDivElement.h" +#include "SpeechInputListener.h" +#include "Timer.h" +#include <wtf/Forward.h> + +namespace WebCore { + +class SpeechInput; + +class TextControlInnerElement : public HTMLDivElement { +public: + static PassRefPtr<TextControlInnerElement> create(Document*); + +protected: + TextControlInnerElement(Document*); + virtual PassRefPtr<RenderStyle> customStyleForRenderer(); + +private: + virtual bool isMouseFocusable() const { return false; } +}; + +class TextControlInnerTextElement : public HTMLDivElement { +public: + static PassRefPtr<TextControlInnerTextElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + TextControlInnerTextElement(Document*); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual PassRefPtr<RenderStyle> customStyleForRenderer(); + virtual bool isMouseFocusable() const { return false; } +}; + +class SearchFieldResultsButtonElement : public HTMLDivElement { +public: + static PassRefPtr<SearchFieldResultsButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + SearchFieldResultsButtonElement(Document*); + virtual const AtomicString& shadowPseudoId() const; + virtual bool isMouseFocusable() const { return false; } +}; + +class SearchFieldCancelButtonElement : public HTMLDivElement { +public: + static PassRefPtr<SearchFieldCancelButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + SearchFieldCancelButtonElement(Document*); + virtual const AtomicString& shadowPseudoId() const; + virtual void detach(); + virtual bool isMouseFocusable() const { return false; } + + bool m_capturing; +}; + +class SpinButtonElement : public HTMLDivElement { +public: + enum UpDownState { + Indeterminate, // Hovered, but the event is not handled. + Down, + Up, + }; + + static PassRefPtr<SpinButtonElement> create(Document*); + UpDownState upDownState() const { return m_upDownState; } + virtual void releaseCapture(); + + void step(int amount); + +private: + SpinButtonElement(Document*); + + virtual const AtomicString& shadowPseudoId() const; + virtual void detach(); + virtual bool isSpinButtonElement() const { return true; } + virtual bool isEnabledFormControl() const { return static_cast<Element*>(shadowAncestorNode())->isEnabledFormControl(); } + virtual bool isReadOnlyFormControl() const { return static_cast<Element*>(shadowAncestorNode())->isReadOnlyFormControl(); } + virtual void defaultEventHandler(Event*); + void startRepeatingTimer(); + void stopRepeatingTimer(); + void repeatingTimerFired(Timer<SpinButtonElement>*); + virtual void setHovered(bool = true); + virtual bool isMouseFocusable() const { return false; } + + bool m_capturing; + UpDownState m_upDownState; + UpDownState m_pressStartingState; + Timer<SpinButtonElement> m_repeatingTimer; +}; + +#if ENABLE(INPUT_SPEECH) + +class InputFieldSpeechButtonElement + : public HTMLDivElement, + public SpeechInputListener { +public: + enum SpeechInputState { + Idle, + Recording, + Recognizing, + }; + + static PassRefPtr<InputFieldSpeechButtonElement> create(Document*); + virtual ~InputFieldSpeechButtonElement(); + + virtual void detach(); + virtual void defaultEventHandler(Event*); + virtual bool isInputFieldSpeechButtonElement() const { return true; } + SpeechInputState state() const { return m_state; } + void startSpeechInput(); + void stopSpeechInput(); + + // SpeechInputListener methods. + void didCompleteRecording(int); + void didCompleteRecognition(int); + void setRecognitionResult(int, const SpeechInputResultArray&); + +private: + InputFieldSpeechButtonElement(Document*); + SpeechInput* speechInput(); + void setState(SpeechInputState state); + virtual const AtomicString& shadowPseudoId() const; + virtual bool isMouseFocusable() const { return false; } + virtual void attach(); + + bool m_capturing; + SpeechInputState m_state; + int m_listenerId; + SpeechInputResultArray m_results; +}; + +inline InputFieldSpeechButtonElement* toInputFieldSpeechButtonElement(Element* element) +{ + ASSERT(!element || element->isInputFieldSpeechButtonElement()); + return static_cast<InputFieldSpeechButtonElement*>(element); +} + +#endif // ENABLE(INPUT_SPEECH) + +} // namespace + +#endif diff --git a/Source/WebCore/html/track/TextTrackList.cpp b/Source/WebCore/html/track/TextTrackList.cpp new file mode 100644 index 000000000..77d9673d4 --- /dev/null +++ b/Source/WebCore/html/track/TextTrackList.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "TextTrackList.h" + +#include "EventNames.h" +#include "LoadableTextTrack.h" +#include "ScriptExecutionContext.h" +#include "TextTrack.h" +#include "TrackEvent.h" + +using namespace WebCore; + +TextTrackList::TextTrackList(HTMLMediaElement* owner, ScriptExecutionContext* context) + : m_context(context) + , m_owner(owner) + , m_pendingEventTimer(this, &TextTrackList::asyncEventTimerFired) + , m_dispatchingEvents(0) +{ + ASSERT(context->isDocument()); +} + +TextTrackList::~TextTrackList() +{ +} + +unsigned TextTrackList::length() const +{ + return m_addTrackTracks.size() + m_elementTracks.size(); +} + +TextTrack* TextTrackList::item(unsigned index) +{ + // 4.8.10.12.1 Text track model + // The text tracks are sorted as follows: + // 1. The text tracks corresponding to track element children of the media element, in tree order. + // 2. Any text tracks added using the addTextTrack() method, in the order they were added, oldest first. + // 3. Any media-resource-specific text tracks (text tracks corresponding to data in the media + // resource), in the order defined by the media resource's format specification. + + if (index < m_elementTracks.size()) + return m_elementTracks[index].get(); + + index -= m_elementTracks.size(); + if (index < m_addTrackTracks.size()) + return m_addTrackTracks[index].get(); + + return 0; +} + +void TextTrackList::append(PassRefPtr<TextTrack> prpTrack) +{ + RefPtr<TextTrack> track = prpTrack; + + if (track->trackType() == TextTrack::AddTrack) + m_addTrackTracks.append(track); + else if (track->trackType() == TextTrack::TrackElement) { + // Insert tracks added for <track> element in tree order. + size_t index = static_cast<LoadableTextTrack*>(track.get())->trackElementIndex(); + m_elementTracks.insert(index, track); + } else + ASSERT_NOT_REACHED(); + + ASSERT(!track->mediaElement() || track->mediaElement() == owner()); + track->setMediaElement(owner()); + + scheduleAddTrackEvent(track.release()); +} + +void TextTrackList::remove(TextTrack* track) +{ + Vector<RefPtr<TextTrack> >* tracks = 0; + + if (track->trackType() == TextTrack::TrackElement) + tracks = &m_elementTracks; + else if (track->trackType() == TextTrack::AddTrack) + tracks = &m_addTrackTracks; + else + ASSERT_NOT_REACHED(); + + size_t index = tracks->find(track); + if (index == notFound) + return; + + ASSERT(track->mediaElement() == owner()); + track->setMediaElement(0); + + tracks->remove(index); +} + +const AtomicString& TextTrackList::interfaceName() const +{ + return eventNames().interfaceForTextTrackList; +} + +void TextTrackList::scheduleAddTrackEvent(PassRefPtr<TextTrack> track) +{ + // 4.8.10.12.3 Sourcing out-of-band text tracks + // 4.8.10.12.3 Sourcing out-of-band text tracks + // 4.8.10.12.4 Text track API + // ... then queue a task to fire an event with the name addtrack, that does not + // bubble and is not cancelable, and that uses the TrackEvent interface, with + // the track attribute initialized to the text track's TextTrack object, at + // the media element's textTracks attribute's TextTrackList object. + + RefPtr<TextTrack> trackRef = track; + TrackEventInit initializer; + initializer.track = trackRef; + initializer.bubbles = false; + initializer.cancelable = false; + + m_pendingEvents.append(TrackEvent::create(eventNames().addtrackEvent, initializer)); + if (!m_pendingEventTimer.isActive()) + m_pendingEventTimer.startOneShot(0); +} + +void TextTrackList::asyncEventTimerFired(Timer<TextTrackList>*) +{ + Vector<RefPtr<Event> > pendingEvents; + ExceptionCode ec = 0; + + ++m_dispatchingEvents; + m_pendingEvents.swap(pendingEvents); + size_t count = pendingEvents.size(); + for (size_t index = 0; index < count; ++index) + dispatchEvent(pendingEvents[index].release(), ec); + --m_dispatchingEvents; +} + +#endif diff --git a/Source/WebCore/html/track/TextTrackList.h b/Source/WebCore/html/track/TextTrackList.h new file mode 100644 index 000000000..88d9af62e --- /dev/null +++ b/Source/WebCore/html/track/TextTrackList.h @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextTrackList_h +#define TextTrackList_h + +#if ENABLE(VIDEO_TRACK) + +#include "EventListener.h" +#include "EventTarget.h" +#include "Timer.h" +#include <algorithm> +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class HTMLMediaElement; +class TextTrack; +class TextTrackList; + +class TextTrackList : public RefCounted<TextTrackList>, public EventTarget { +public: + static PassRefPtr<TextTrackList> create(HTMLMediaElement* owner, ScriptExecutionContext* context) + { + return adoptRef(new TextTrackList(owner, context)); + } + ~TextTrackList(); + + unsigned length() const; + TextTrack* item(unsigned index); + void append(PassRefPtr<TextTrack>); + void remove(TextTrack*); + + // EventTarget + virtual const AtomicString& interfaceName() const; + using RefCounted<TextTrackList>::ref; + using RefCounted<TextTrackList>::deref; + virtual ScriptExecutionContext* scriptExecutionContext() const { return m_context; } + + DEFINE_ATTRIBUTE_EVENT_LISTENER(addtrack); + + void clearOwner() { m_owner = 0; } + HTMLMediaElement* owner() { return m_owner; } + + bool isFiringEventListeners() { return m_dispatchingEvents; } + +private: + TextTrackList(HTMLMediaElement*, ScriptExecutionContext*); + + // EventTarget + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + virtual EventTargetData* eventTargetData() { return &m_eventTargetData; } + virtual EventTargetData* ensureEventTargetData() { return &m_eventTargetData; } + + void scheduleAddTrackEvent(PassRefPtr<TextTrack>); + void asyncEventTimerFired(Timer<TextTrackList>*); + + ScriptExecutionContext* m_context; + HTMLMediaElement* m_owner; + + Vector<RefPtr<Event> > m_pendingEvents; + Timer<TextTrackList> m_pendingEventTimer; + + EventTargetData m_eventTargetData; + Vector<RefPtr<TextTrack> > m_addTrackTracks; + Vector<RefPtr<TextTrack> > m_elementTracks; + + int m_dispatchingEvents; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/track/TextTrackList.idl b/Source/WebCore/html/track/TextTrackList.idl new file mode 100644 index 000000000..f1460b6da --- /dev/null +++ b/Source/WebCore/html/track/TextTrackList.idl @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=VIDEO_TRACK, + EnabledAtRuntime=webkitVideoTrack, + HasIndexGetter, + EventTarget, + CustomMarkFunction, + CustomIsReachable + ] TextTrackList { + readonly attribute unsigned long length; + TextTrack item(in unsigned long index); + + attribute EventListener onaddtrack; + + void addEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + void removeEventListener(in DOMString type, + in EventListener listener, + in [Optional] boolean useCapture); + boolean dispatchEvent(in Event evt) + raises(EventException); + }; + +} diff --git a/Source/WebCore/html/track/TrackBase.cpp b/Source/WebCore/html/track/TrackBase.cpp new file mode 100644 index 000000000..6a2c35823 --- /dev/null +++ b/Source/WebCore/html/track/TrackBase.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TrackBase.h" + +#if ENABLE(VIDEO_TRACK) + +namespace WebCore { + +TrackBase::TrackBase(ScriptExecutionContext* context, Type type) + : m_scriptExecutionContext(context) +{ + ASSERT(type != BaseTrack); + m_type = type; +} + +TrackBase::~TrackBase() +{ +} + +const AtomicString& TrackBase::interfaceName() const +{ + return eventNames().interfaceForTextTrack; +} + +ScriptExecutionContext* TrackBase::scriptExecutionContext() const +{ + return m_scriptExecutionContext; +} + +EventTargetData* TrackBase::eventTargetData() +{ + return &m_eventTargetData; +} + +EventTargetData* TrackBase::ensureEventTargetData() +{ + return &m_eventTargetData; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/TrackBase.h b/Source/WebCore/html/track/TrackBase.h new file mode 100644 index 000000000..c00660f05 --- /dev/null +++ b/Source/WebCore/html/track/TrackBase.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TrackBase_h +#define TrackBase_h + +#if ENABLE(VIDEO_TRACK) + +#include "EventTarget.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +class ScriptExecutionContext; + +class TrackBase : public RefCounted<TrackBase>, public EventTarget { +public: + virtual ~TrackBase(); + + enum Type { BaseTrack, TextTrack, AudioTrack, VideoTrack }; + Type type() const { return m_type; } + + virtual const AtomicString& interfaceName() const; + virtual ScriptExecutionContext* scriptExecutionContext() const; + + using RefCounted<TrackBase>::ref; + using RefCounted<TrackBase>::deref; + +protected: + TrackBase(ScriptExecutionContext*, Type); + + virtual EventTargetData* eventTargetData(); + virtual EventTargetData* ensureEventTargetData(); + +private: + Type m_type; + + virtual void refEventTarget() { ref(); } + virtual void derefEventTarget() { deref(); } + + ScriptExecutionContext* m_scriptExecutionContext; + EventTargetData m_eventTargetData; +}; + +} // namespace WebCore + +#endif +#endif // TrackBase_h diff --git a/Source/WebCore/html/track/TrackEvent.cpp b/Source/WebCore/html/track/TrackEvent.cpp new file mode 100644 index 000000000..0bb56a5f8 --- /dev/null +++ b/Source/WebCore/html/track/TrackEvent.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "TrackEvent.h" + +#include "EventNames.h" + +namespace WebCore { + +TrackEventInit::TrackEventInit() +{ +} + + +TrackEvent::TrackEvent() +{ +} + +TrackEvent::TrackEvent(const AtomicString& type, const TrackEventInit& initializer) + : Event(type, initializer) + , m_track(initializer.track) +{ +} + +TrackEvent::~TrackEvent() +{ +} + +const AtomicString& TrackEvent::interfaceName() const +{ + return eventNames().interfaceForTrackEvent; +} + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/html/track/TrackEvent.h b/Source/WebCore/html/track/TrackEvent.h new file mode 100644 index 000000000..32913748d --- /dev/null +++ b/Source/WebCore/html/track/TrackEvent.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TrackEvent_h +#define TrackEvent_h + +#if ENABLE(VIDEO_TRACK) + +#include "Event.h" +#include "TrackBase.h" + +namespace WebCore { + +struct TrackEventInit : public EventInit { + TrackEventInit(); + + RefPtr<TrackBase> track; +}; + +class TrackEvent : public Event { +public: + virtual ~TrackEvent(); + + static PassRefPtr<TrackEvent> create() + { + return adoptRef(new TrackEvent); + } + + static PassRefPtr<TrackEvent> create(const AtomicString& type, const TrackEventInit& initializer) + { + return adoptRef(new TrackEvent(type, initializer)); + } + + virtual const AtomicString& interfaceName() const; + + TrackBase* track() const { return m_track.get(); } + +private: + TrackEvent(); + TrackEvent(const AtomicString& type, const TrackEventInit& initializer); + + RefPtr<TrackBase> m_track; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/track/TrackEvent.idl b/Source/WebCore/html/track/TrackEvent.idl new file mode 100644 index 000000000..5d28f3b53 --- /dev/null +++ b/Source/WebCore/html/track/TrackEvent.idl @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +module html { + + interface [ + Conditional=VIDEO_TRACK, + EnabledAtRuntime=webkitVideoTrack, + ConstructorTemplate=Event + ] TrackEvent : Event { + readonly attribute [InitializedByConstructor, CustomGetter] object track; + }; + +} diff --git a/Source/WebCore/html/track/WebVTTParser.cpp b/Source/WebCore/html/track/WebVTTParser.cpp new file mode 100644 index 000000000..9d619527c --- /dev/null +++ b/Source/WebCore/html/track/WebVTTParser.cpp @@ -0,0 +1,422 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "WebVTTParser.h" + +#include "HTMLElement.h" +#include "ProcessingInstruction.h" +#include "SegmentedString.h" +#include "Text.h" +#include <wtf/text/WTFString.h> + +namespace WebCore { + +const int secondsPerHour = 3600; +const int secondsPerMinute = 60; +const double malformedTime = -1; +const unsigned bomLength = 3; +const unsigned fileIdentiferLength = 6; + +unsigned WebVTTParser::fileIdentifierMaximumLength() +{ + return bomLength + fileIdentiferLength; +} + +inline bool hasLongWebVTTIdentifier(String line) +{ + // If line is more than six characters ... + if (line.length() < fileIdentiferLength) + return false; + + // but the first six characters do not exactly equal "WEBVTT" ... + if (line.substring(0, fileIdentiferLength) != "WEBVTT") + return false; + + // or the seventh character is neither a space nor a tab character, then abort. + if (line.length() > fileIdentiferLength && line[fileIdentiferLength] != ' ' && line[fileIdentiferLength] != '\t') + return false; + + return true; +} + +bool WebVTTParser::hasRequiredFileIdentifier(const char* data, unsigned length) +{ + // A WebVTT file identifier consists of an optional BOM character, + // the string "WEBVTT" followed by an optional space or tab character, + // and any number of characters that are not line terminators ... + unsigned position = 0; + if (length >= bomLength && data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') + position += bomLength; + String line = collectNextLine(data, length, &position); + + if (line.length() < fileIdentiferLength) + return false; + if (line.length() == fileIdentiferLength && line != "WEBVTT") + return false; + if (!hasLongWebVTTIdentifier(line)) + return false; + + return true; +} + +String WebVTTParser::collectDigits(const String& input, unsigned* position) +{ + StringBuilder digits; + while (*position < input.length() && isASCIIDigit(input[*position])) + digits.append(input[(*position)++]); + return digits.toString(); +} + +String WebVTTParser::collectWord(const String& input, unsigned* position) +{ + StringBuilder string; + while (*position < input.length() && !isASpace(input[*position])) + string.append(input[(*position)++]); + return string.toString(); +} + +WebVTTParser::WebVTTParser(WebVTTParserClient* client, ScriptExecutionContext* context) + : m_scriptExecutionContext(context) + , m_state(Initial) + , m_tokenizer(WebVTTTokenizer::create()) + , m_client(client) +{ +} + +void WebVTTParser::getNewCues(Vector<RefPtr<TextTrackCue> >& outputCues) +{ + outputCues = m_cuelist; + m_cuelist.clear(); +} + +void WebVTTParser::parseBytes(const char* data, unsigned length) +{ + // 4.8.10.13.3 WHATWG WebVTT Parser algorithm. + // 1-3 - Initial setup. + unsigned position = 0; + + while (position < length) { + String line = collectNextLine(data, length, &position); + + switch (m_state) { + case Initial: + // 4-12 - Collect the first line and check for "WEBVTT". + if (!hasRequiredFileIdentifier(data, length)) + return; + m_state = Header; + break; + + case Header: + // 13-18 - Allow a header (comment area) under the WEBVTT line. + if (line.isEmpty()) + m_state = Id; + break; + + case Id: + // 19-29 - Allow any number of line terminators, then initialize new cue values. + if (line.isEmpty()) + break; + resetCueValues(); + + // 30-39 - Check if this line contains an optional identifier or timing data. + m_state = collectCueId(line); + break; + + case TimingsAndSettings: + // 40 - Collect cue timings and settings. + m_state = collectTimingsAndSettings(line); + break; + + case CueText: + // 41-53 - Collect the cue text, create a cue, and add it to the output. + m_state = collectCueText(line, length, position); + break; + + case BadCue: + // 54-62 - Collect and discard the remaining cue. + m_state = ignoreBadCue(line); + break; + } + } +} + +WebVTTParser::ParseState WebVTTParser::collectCueId(const String& line) +{ + if (line.contains("-->")) + return collectTimingsAndSettings(line); + m_currentId = line; + return TimingsAndSettings; +} + +WebVTTParser::ParseState WebVTTParser::collectTimingsAndSettings(const String& line) +{ + // 4.8.10.13.3 Collect WebVTT cue timings and settings. + // 1-3 - Let input be the string being parsed and position be a pointer into input + unsigned position = 0; + skipWhiteSpace(line, &position); + + // 4-5 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue start time be the collected time. + m_currentStartTime = collectTimeStamp(line, &position); + if (m_currentStartTime == malformedTime) + return BadCue; + if (position >= line.length()) + return BadCue; + char nextChar = line[position++]; + if (nextChar != ' ' && nextChar != '\t') + return BadCue; + skipWhiteSpace(line, &position); + + // 6-9 - If the next three characters are not "-->", abort and return failure. + if (line.find("-->", position) == notFound) + return BadCue; + position += 3; + if (position >= line.length()) + return BadCue; + nextChar = line[position++]; + if (nextChar != ' ' && nextChar != '\t') + return BadCue; + skipWhiteSpace(line, &position); + + // 10-11 - Collect a WebVTT timestamp. If that fails, then abort and return failure. Otherwise, let cue's text track cue end time be the collected time. + m_currentEndTime = collectTimeStamp(line, &position); + if (m_currentEndTime == malformedTime) + return BadCue; + skipWhiteSpace(line, &position); + + // 12 - Parse the WebVTT settings for the cue (conducted in TextTrackCue). + m_currentSettings = line.substring(position, line.length()-1); + return CueText; +} + +WebVTTParser::ParseState WebVTTParser::collectCueText(const String& line, unsigned length, unsigned position) +{ + if (line.isEmpty()) { + processCueText(); + return Id; + } + if (!m_currentContent.isEmpty()) + m_currentContent.append("\n"); + m_currentContent.append(line); + + if (position >= length) + processCueText(); + + return CueText; +} + +WebVTTParser::ParseState WebVTTParser::ignoreBadCue(const String& line) +{ + if (!line.isEmpty()) + return BadCue; + return Id; +} + +void WebVTTParser::processCueText() +{ + // 51 - Cue text processing based on + // 4.8.10.13.4 WebVTT cue text parsing rules and + // 4.8.10.13.5 WebVTT cue text DOM construction rules. + if (m_currentContent.length() <= 0) + return; + + ASSERT(m_scriptExecutionContext->isDocument()); + Document* document = static_cast<Document*>(m_scriptExecutionContext); + + m_attachmentRoot = DocumentFragment::create(document); + m_currentNode = m_attachmentRoot; + m_tokenizer->reset(); + m_token.clear(); + + SegmentedString content(m_currentContent.toString()); + while (m_tokenizer->nextToken(content, m_token)) + constructTreeFromToken(document); + + RefPtr<TextTrackCue> cue = TextTrackCue::create(m_scriptExecutionContext, m_currentId, m_currentStartTime, m_currentEndTime, m_currentContent.toString(), m_currentSettings, false); + cue->setCueHTML(m_attachmentRoot); + m_cuelist.append(cue); + m_client->newCuesParsed(); +} + +void WebVTTParser::resetCueValues() +{ + m_currentId = emptyString(); + m_currentSettings = emptyString(); + m_currentStartTime = 0; + m_currentEndTime = 0; + m_currentContent.clear(); +} + +double WebVTTParser::collectTimeStamp(const String& line, unsigned* position) +{ + // 4.8.10.13.3 Collect a WebVTT timestamp. + // 1-4 - Initial checks, let most significant units be minutes. + enum Mode { minutes, hours }; + Mode mode = minutes; + if (*position >= line.length() || !isASCIIDigit(line[*position])) + return malformedTime; + + // 5-6 - Collect a sequence of characters that are 0-9. + String digits1 = collectDigits(line, position); + int value1 = digits1.toInt(); + + // 7 - If not 2 characters or value is greater than 59, interpret as hours. + if (digits1.length() != 2 || value1 > 59) + mode = hours; + + // 8-12 - Collect the next sequence of 0-9 after ':' (must be 2 chars). + if (*position >= line.length() || line[(*position)++] != ':') + return malformedTime; + if (*position >= line.length() || !isASCIIDigit(line[(*position)])) + return malformedTime; + String digits2 = collectDigits(line, position); + int value2 = digits2.toInt(); + if (digits2.length() != 2) + return malformedTime; + + // 13 - Detect whether this timestamp includes hours. + int value3; + if (mode == hours || (*position < line.length() && line[*position] == ':')) { + if (*position >= line.length() || line[(*position)++] != ':') + return malformedTime; + if (*position >= line.length() || !isASCIIDigit(line[*position])) + return malformedTime; + String digits3 = collectDigits(line, position); + if (digits3.length() != 2) + return malformedTime; + value3 = digits3.toInt(); + } else { + value3 = value2; + value2 = value1; + value1 = 0; + } + + // 14-19 - Collect next sequence of 0-9 after '.' (must be 3 chars). + if (*position >= line.length() || line[(*position)++] != '.') + return malformedTime; + if (*position >= line.length() || !isASCIIDigit(line[*position])) + return malformedTime; + String digits4 = collectDigits(line, position); + if (digits4.length() != 3) + return malformedTime; + int value4 = digits4.toInt(); + if (value2 > 59 || value3 > 59) + return malformedTime; + + // 20-21 - Calculate result. + return (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + ((double)value4 / 1000); +} + +void WebVTTParser::constructTreeFromToken(Document* document) +{ + AtomicString tokenTagName(m_token.name().data(), m_token.name().size()); + QualifiedName tagName(nullAtom, tokenTagName, xhtmlNamespaceURI); + + switch (m_token.type()) { + case WebVTTTokenTypes::Character: { + String content(m_token.characters().data(), m_token.characters().size()); + RefPtr<Text> child = Text::create(document, content); + m_currentNode->parserAddChild(child); + break; + } + case WebVTTTokenTypes::StartTag: { + RefPtr<HTMLElement> child; + if (isRecognizedTag(tokenTagName)) + child = HTMLElement::create(tagName, document); + else if (m_token.name().size() == 1 && m_token.name()[0] == 'c') + child = HTMLElement::create(spanTag, document); + else if (m_token.name().size() == 1 && m_token.name()[0] == 'v') + child = HTMLElement::create(qTag, document); + + if (child) { + if (m_token.classes().size() > 0) + child->setAttribute(classAttr, AtomicString(m_token.classes().data(), m_token.classes().size())); + if (child->hasTagName(qTag)) + child->setAttribute(titleAttr, AtomicString(m_token.annotation().data(), m_token.annotation().size())); + m_currentNode->parserAddChild(child); + m_currentNode = child; + } + break; + } + case WebVTTTokenTypes::EndTag: + if (isRecognizedTag(tokenTagName) + || (m_token.name().size() == 1 && m_token.name()[0] == 'c') + || (m_token.name().size() == 1 && m_token.name()[0] == 'v')) { + if (m_currentNode->parentNode()) + m_currentNode = m_currentNode->parentNode(); + } + break; + case WebVTTTokenTypes::TimestampTag: { + unsigned position = 0; + double time = collectTimeStamp(m_token.characters().data(), &position); + if (time != malformedTime) + m_currentNode->parserAddChild(ProcessingInstruction::create(document, "timestamp", String(m_token.characters().data(), m_token.characters().size()))); + break; + } + default: + break; + } + m_token.clear(); +} + +void WebVTTParser::skipWhiteSpace(const String& line, unsigned* position) +{ + while (*position < line.length() && isASpace(line[*position])) + (*position)++; +} + +void WebVTTParser::skipLineTerminator(const char* data, unsigned length, unsigned* position) +{ + if (*position >= length) + return; + if (data[*position] == '\r') + (*position)++; + if (*position >= length) + return; + if (data[*position] == '\n') + (*position)++; +} + +String WebVTTParser::collectNextLine(const char* data, unsigned length, unsigned* position) +{ + unsigned oldPosition = *position; + while (*position < length && data[*position] != '\r' && data[*position] != '\n') + (*position)++; + String line = String::fromUTF8(data + oldPosition, *position - oldPosition); + skipLineTerminator(data, length, position); + return line; +} + +} + +#endif diff --git a/Source/WebCore/html/track/WebVTTParser.h b/Source/WebCore/html/track/WebVTTParser.h new file mode 100644 index 000000000..244ee8f32 --- /dev/null +++ b/Source/WebCore/html/track/WebVTTParser.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebVTTParser_h +#define WebVTTParser_h + +#if ENABLE(VIDEO_TRACK) + +#include "Document.h" +#include "DocumentFragment.h" +#include "HTMLNames.h" +#include "TextTrackCue.h" +#include "WebVTTTokenizer.h" +#include <wtf/PassOwnPtr.h> +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +using namespace HTMLNames; + +class WebVTTParserClient { +public: + virtual ~WebVTTParserClient() { } + + virtual void newCuesParsed() = 0; +}; + +class WebVTTParser { +public: + virtual ~WebVTTParser() { } + + enum ParseState { Initial, Header, Id, TimingsAndSettings, CueText, BadCue }; + + static PassOwnPtr<WebVTTParser> create(WebVTTParserClient* client, ScriptExecutionContext* context) + { + return adoptPtr(new WebVTTParser(client, context)); + } + + static unsigned fileIdentifierMaximumLength(); + static bool hasRequiredFileIdentifier(const char* data, unsigned length); + + static inline bool isRecognizedTag(const AtomicString& tagName) + { + return tagName == iTag + || tagName == bTag + || tagName == uTag + || tagName == rubyTag + || tagName == rtTag; + } + + static inline bool isASpace(char c) + { + // WebVTT space characters are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR). + return c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r'; + } + static String collectDigits(const String&, unsigned*); + static String collectWord(const String&, unsigned*); + + // Input data to the parser to parse. + virtual void parseBytes(const char* data, unsigned length); + + // Transfers ownership of last parsed cues to caller. + virtual void getNewCues(Vector<RefPtr<TextTrackCue> >&); + +protected: + WebVTTParser(WebVTTParserClient*, ScriptExecutionContext*); + + ScriptExecutionContext* m_scriptExecutionContext; + ParseState m_state; + +private: + ParseState collectCueId(const String&); + ParseState collectTimingsAndSettings(const String&); + ParseState collectCueText(const String&, unsigned length, unsigned); + ParseState ignoreBadCue(const String&); + + void processCueText(); + 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*); + + void constructTreeFromToken(Document*); + + String m_currentId; + double m_currentStartTime; + double m_currentEndTime; + StringBuilder m_currentContent; + String m_currentSettings; + + WebVTTToken m_token; + OwnPtr<WebVTTTokenizer> m_tokenizer; + + RefPtr<DocumentFragment> m_attachmentRoot; + RefPtr<ContainerNode> m_currentNode; + + WebVTTParserClient* m_client; + + Vector<RefPtr<TextTrackCue> > m_cuelist; +}; + +} // namespace WebCore + +#endif +#endif diff --git a/Source/WebCore/html/track/WebVTTToken.h b/Source/WebCore/html/track/WebVTTToken.h new file mode 100644 index 000000000..5868fceb3 --- /dev/null +++ b/Source/WebCore/html/track/WebVTTToken.h @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebVTTToken_h +#define WebVTTToken_h + +#if ENABLE(VIDEO_TRACK) + +#include "MarkupTokenBase.h" + +namespace WebCore { + +class WebVTTTokenTypes { +public: + enum Type { + Uninitialized, + Character, + StartTag, + EndTag, + TimestampTag, + EndOfFile, + }; +}; + +class WebVTTToken : public MarkupTokenBase<WebVTTTokenTypes> { +public: + void appendToName(UChar character) + { + ASSERT(m_type == WebVTTTokenTypes::StartTag || m_type == WebVTTTokenTypes::EndTag); + MarkupTokenBase<WebVTTTokenTypes>::appendToName(character); + } + + const DataVector& name() const + { + return MarkupTokenBase<WebVTTTokenTypes>::name(); + } + + const DataVector& characters() const + { + ASSERT(m_type == Type::Character || m_type == Type::TimestampTag); + return m_data; + } + + void beginEmptyStartTag() + { + ASSERT(m_type == Type::Uninitialized); + m_type = Type::StartTag; + m_currentAttribute = 0; + m_attributes.clear(); + m_data.clear(); + } + + void beginTimestampTag(UChar character) + { + ASSERT(character); + ASSERT(m_type == Type::Uninitialized); + m_type = Type::TimestampTag; + m_selfClosing = true; + m_data.append(character); + } + + void appendToTimestamp(UChar character) + { + ASSERT(character); + ASSERT(m_type == Type::TimestampTag); + m_data.append(character); + } + + void appendToClass(UChar character) + { + appendToStartType(character); + } + + void addNewClass() + { + ASSERT(m_type == Type::StartTag); + if (!m_classes.isEmpty()) + m_classes.append(' '); + m_classes.append(m_currentBuffer); + m_currentBuffer.clear(); + } + + const DataVector& classes() const + { + return m_classes; + } + + void appendToAnnotation(UChar character) + { + appendToStartType(character); + } + + void addNewAnnotation() + { + ASSERT(m_type == Type::StartTag); + m_annotation.clear(); + m_annotation.append(m_currentBuffer); + m_currentBuffer.clear(); + } + + const DataVector& annotation() const + { + return m_annotation; + } + + void clear() + { + m_annotation.clear(); + m_classes.clear(); + m_currentBuffer.clear(); + MarkupTokenBase<WebVTTTokenTypes>::clear(); + } + +private: + void appendToStartType(UChar character) + { + ASSERT(character); + ASSERT(m_type == Type::StartTag); + m_currentBuffer.append(character); + } + + DataVector m_annotation; + DataVector m_classes; + DataVector m_currentBuffer; +}; + +} + +#endif +#endif diff --git a/Source/WebCore/html/track/WebVTTTokenizer.cpp b/Source/WebCore/html/track/WebVTTTokenizer.cpp new file mode 100644 index 000000000..94618ab63 --- /dev/null +++ b/Source/WebCore/html/track/WebVTTTokenizer.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO_TRACK) + +#include "WebVTTTokenizer.h" + +#include "MarkupTokenizerInlineMethods.h" + +namespace WebCore { + +#define WEBVTT_BEGIN_STATE(stateName) BEGIN_STATE(WebVTTTokenizerState, stateName) +#define WEBVTT_ADVANCE_TO(stateName) ADVANCE_TO(WebVTTTokenizerState, stateName) + +WebVTTTokenizer::WebVTTTokenizer() +{ + reset(); +} + +template<> +inline bool MarkupTokenizerBase<WebVTTToken, WebVTTTokenizerState>::shouldSkipNullCharacters() const +{ + return true; +} + +inline bool vectorEqualsString(const Vector<UChar, 32>& vector, const String& string) +{ + if (vector.size() != string.length()) + return false; + const UChar* stringData = string.characters(); + const UChar* vectorData = vector.data(); + return !memcmp(stringData, vectorData, vector.size() * sizeof(UChar)); +} + +void WebVTTTokenizer::reset() +{ + m_state = WebVTTTokenizerState::DataState; + m_token = 0; + m_lineNumber = 0; + m_buffer.clear(); +} + +bool WebVTTTokenizer::nextToken(SegmentedString& source, WebVTTToken& token) +{ + // If we have a token in progress, then we're supposed to be called back + // with the same token so we can finish it. + ASSERT(!m_token || m_token == &token || token.type() == WebVTTTokenTypes::Uninitialized); + m_token = &token; + + if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source, m_lineNumber)) + return haveBufferedCharacterToken(); + + UChar cc = m_inputStreamPreprocessor.nextInputCharacter(); + + // 4.8.10.13.4 WebVTT cue text tokenizer + switch (m_state) { + WEBVTT_BEGIN_STATE(DataState) { + if (cc == '&') { + m_buffer.append(cc); + WEBVTT_ADVANCE_TO(EscapeState); + } else if (cc == '<') { + if (m_token->type() == WebVTTTokenTypes::Uninitialized + || vectorEqualsString(m_token->characters(), emptyString())) + WEBVTT_ADVANCE_TO(TagState); + else + return emitAndResumeIn(source, WebVTTTokenizerState::TagState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) + return emitEndOfFile(source); + else { + bufferCharacter(cc); + WEBVTT_ADVANCE_TO(DataState); + } + } + END_STATE() + + WEBVTT_BEGIN_STATE(EscapeState) { + if (cc == ';') { + if (vectorEqualsString(m_buffer, "&")) + bufferCharacter('&'); + else if (vectorEqualsString(m_buffer, "<")) + bufferCharacter('<'); + else if (vectorEqualsString(m_buffer, ">")) + bufferCharacter('>'); + else { + m_buffer.append(cc); + m_token->appendToCharacter(m_buffer); + } + m_buffer.clear(); + WEBVTT_ADVANCE_TO(DataState); + } else if (isASCIIAlphanumeric(cc)) { + m_buffer.append(cc); + WEBVTT_ADVANCE_TO(EscapeState); + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { + m_token->appendToCharacter(m_buffer); + return emitEndOfFile(source); + } else { + if (!vectorEqualsString(m_buffer, "&")) + m_token->appendToCharacter(m_buffer); + m_buffer.clear(); + WEBVTT_ADVANCE_TO(DataState); + } + } + END_STATE() + + WEBVTT_BEGIN_STATE(TagState) { + if (isTokenizerWhitespace(cc)) { + m_token->beginEmptyStartTag(); + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + } else if (cc == '.') { + m_token->beginEmptyStartTag(); + WEBVTT_ADVANCE_TO(StartTagClassState); + } else if (cc == '/') { + WEBVTT_ADVANCE_TO(EndTagOpenState); + } else if (WTF::isASCIIDigit(cc)) { + m_token->beginTimestampTag(cc); + WEBVTT_ADVANCE_TO(TimestampTagState); + } else if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) { + m_token->beginEmptyStartTag(); + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + } else { + m_token->beginStartTag(cc); + WEBVTT_ADVANCE_TO(StartTagState); + } + } + END_STATE() + + WEBVTT_BEGIN_STATE(StartTagState) { + if (isTokenizerWhitespace(cc)) + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + else if (cc == '.') + WEBVTT_ADVANCE_TO(StartTagClassState); + else if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + else { + m_token->appendToName(cc); + WEBVTT_ADVANCE_TO(StartTagState); + } + } + END_STATE() + + WEBVTT_BEGIN_STATE(StartTagClassState) { + if (isTokenizerWhitespace(cc)) { + m_token->addNewClass(); + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + } else if (cc == '.') { + m_token->addNewClass(); + WEBVTT_ADVANCE_TO(StartTagClassState); + } else if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) { + m_token->addNewClass(); + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + } else { + m_token->appendToClass(cc); + WEBVTT_ADVANCE_TO(StartTagClassState); + } + + } + END_STATE() + + WEBVTT_BEGIN_STATE(StartTagAnnotationState) { + if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) { + m_token->addNewAnnotation(); + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + } + m_token->appendToAnnotation(cc); + WEBVTT_ADVANCE_TO(StartTagAnnotationState); + } + END_STATE() + + WEBVTT_BEGIN_STATE(EndTagOpenState) { + if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) { + m_token->beginEndTag('\0'); + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + } + m_token->beginEndTag(cc); + WEBVTT_ADVANCE_TO(EndTagState); + } + END_STATE() + + WEBVTT_BEGIN_STATE(EndTagState) { + if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + m_token->appendToName(cc); + WEBVTT_ADVANCE_TO(EndTagState); + } + END_STATE() + + WEBVTT_BEGIN_STATE(TimestampTagState) { + if (cc == '>' || cc == InputStreamPreprocessor::endOfFileMarker) + return emitAndResumeIn(source, WebVTTTokenizerState::DataState); + m_token->appendToTimestamp(cc); + WEBVTT_ADVANCE_TO(TimestampTagState); + } + END_STATE() + + } + + ASSERT_NOT_REACHED(); + return false; +} + +} + +#endif diff --git a/Source/WebCore/html/track/WebVTTTokenizer.h b/Source/WebCore/html/track/WebVTTTokenizer.h new file mode 100644 index 000000000..e6d45f7ff --- /dev/null +++ b/Source/WebCore/html/track/WebVTTTokenizer.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebVTTTokenizer_h +#define WebVTTTokenizer_h + +#if ENABLE(VIDEO_TRACK) + +#include "MarkupTokenizerBase.h" +#include "SegmentedString.h" +#include "WebVTTToken.h" + +namespace WebCore { + +class WebVTTTokenizerState { +public: + enum State { + DataState, + EscapeState, + TagState, + StartTagState, + StartTagClassState, + StartTagAnnotationState, + EndTagState, + EndTagOpenState, + TimestampTagState, + }; +}; + +class WebVTTTokenizer : MarkupTokenizerBase<WebVTTToken, WebVTTTokenizerState> { + WTF_MAKE_NONCOPYABLE(WebVTTTokenizer); + WTF_MAKE_FAST_ALLOCATED; +public: + static PassOwnPtr<WebVTTTokenizer> create() { return adoptPtr(new WebVTTTokenizer); } + + void reset(); + + bool nextToken(SegmentedString&, WebVTTToken&); + +private: + WebVTTTokenizer(); + + Vector<UChar, 32> m_buffer; +}; + +} + +#endif +#endif |