diff options
author | Ben Murdoch <benm@google.com> | 2011-05-24 11:24:40 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-06-02 09:53:15 +0100 |
commit | 81bc750723a18f21cd17d1b173cd2a4dda9cea6e (patch) | |
tree | 7a9e5ed86ff429fd347a25153107221543909b19 /Source/WebCore/html | |
parent | 94088a6d336c1dd80a1e734af51e96abcbb689a7 (diff) | |
download | external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.zip external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.gz external_webkit-81bc750723a18f21cd17d1b173cd2a4dda9cea6e.tar.bz2 |
Merge WebKit at r80534: Intial merge by Git
Change-Id: Ia7a83357124c9e1cdb1debf55d9661ec0bd09a61
Diffstat (limited to 'Source/WebCore/html')
69 files changed, 2112 insertions, 590 deletions
diff --git a/Source/WebCore/html/HTMLAttributeNames.in b/Source/WebCore/html/HTMLAttributeNames.in index d195a6f..956812d 100644 --- a/Source/WebCore/html/HTMLAttributeNames.in +++ b/Source/WebCore/html/HTMLAttributeNames.in @@ -216,6 +216,7 @@ onseeked onseeking onselect onselectstart +onselectionchange onwebkitspeechchange onstalled onstorage @@ -240,6 +241,7 @@ open optimum pattern placeholder +pluginspage pluginurl ping poster diff --git a/Source/WebCore/html/HTMLBodyElement.cpp b/Source/WebCore/html/HTMLBodyElement.cpp index 84f81c3..3cc4cd8 100644 --- a/Source/WebCore/html/HTMLBodyElement.cpp +++ b/Source/WebCore/html/HTMLBodyElement.cpp @@ -171,6 +171,8 @@ void HTMLBodyElement::parseMappedAttribute(Attribute* attr) 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) @@ -286,7 +288,8 @@ void HTMLBodyElement::setVLink(const String& value) static int adjustForZoom(int value, Document* document) { - float zoomFactor = document->frame()->pageZoomFactor(); + Frame* frame = document->frame(); + float zoomFactor = frame->pageZoomFactor() * frame->pageScaleFactor(); if (zoomFactor == 1) return value; // Needed because of truncation (rather than rounding) when scaling up. @@ -314,7 +317,7 @@ void HTMLBodyElement::setScrollLeft(int scrollLeft) FrameView* view = frame->view(); if (!view) return; - view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor()), view->scrollY())); + view->setScrollPosition(IntPoint(static_cast<int>(scrollLeft * frame->pageZoomFactor() * frame->pageScaleFactor()), view->scrollY())); } int HTMLBodyElement::scrollTop() const @@ -336,7 +339,7 @@ void HTMLBodyElement::setScrollTop(int scrollTop) FrameView* view = frame->view(); if (!view) return; - view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor()))); + view->setScrollPosition(IntPoint(view->scrollX(), static_cast<int>(scrollTop * frame->pageZoomFactor() * frame->pageScaleFactor()))); } int HTMLBodyElement::scrollHeight() const diff --git a/Source/WebCore/html/HTMLCanvasElement.cpp b/Source/WebCore/html/HTMLCanvasElement.cpp index 09e8d04..79ebdad 100644 --- a/Source/WebCore/html/HTMLCanvasElement.cpp +++ b/Source/WebCore/html/HTMLCanvasElement.cpp @@ -369,7 +369,7 @@ IntSize HTMLCanvasElement::convertToValidDeviceSize(float width, float height) c if (width < 1 || height < 1 || width * height > MaxCanvasArea) return IntSize(); -#if PLATFORM(SKIA) +#if USE(SKIA) if (width > MaxSkiaDim || height > MaxSkiaDim) return IntSize(); #endif diff --git a/Source/WebCore/html/HTMLElement.cpp b/Source/WebCore/html/HTMLElement.cpp index 22fc2f2..b3981c8 100644 --- a/Source/WebCore/html/HTMLElement.cpp +++ b/Source/WebCore/html/HTMLElement.cpp @@ -124,7 +124,7 @@ bool HTMLElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry } if (attrName == dirAttr) { result = hasLocalName(bdoTag) ? eBDO : eUniversal; - return false; + return true; } return StyledElement::mapToEntry(attrName, result); @@ -148,14 +148,18 @@ void HTMLElement::parseMappedAttribute(Attribute* attr) } else if (attr->name() == tabindexAttr) { indexstring = getAttribute(tabindexAttr); int tabindex = 0; - if (parseHTMLInteger(indexstring, tabindex)) { + 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() == langAttr) { // FIXME: Implement } else if (attr->name() == dirAttr) { - addCSSProperty(attr, CSSPropertyDirection, attr->value()); + if (!equalIgnoringCase(attr->value(), "auto")) + addCSSProperty(attr, CSSPropertyDirection, attr->value()); + dirAttributeChanged(attr); addCSSProperty(attr, CSSPropertyUnicodeBidi, hasLocalName(bdoTag) ? CSSValueBidiOverride : CSSValueEmbed); } else if (attr->name() == draggableAttr) { const AtomicString& value = attr->value(); @@ -655,7 +659,7 @@ bool HTMLElement::supportsFocus() const bool HTMLElement::isContentEditable() const { - if (document()->frame() && document()->frame()->isContentEditable()) + if (document()->inDesignMode()) return true; // Ideally we'd call ASSERT!needsStyleRecalc()) here, but @@ -674,7 +678,7 @@ bool HTMLElement::isContentEditable() const bool HTMLElement::isContentRichlyEditable() const { - if (document()->frame() && document()->frame()->isContentEditable()) + if (document()->inDesignMode()) return true; if (!renderer()) { @@ -878,6 +882,147 @@ void HTMLElement::dispatchInputEvents() ownerForm->dispatchFormInput(); } +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 +{ + Node* node = firstChild(); + while (node) { + // Skip bdi, script and style elements + if (equalIgnoringCase(node->nodeName(), "bdi") || node->hasTagName(scriptTag) || node->hasTagName(styleTag)) { + 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; + } + } +} + } // namespace WebCore #ifndef NDEBUG diff --git a/Source/WebCore/html/HTMLElement.h b/Source/WebCore/html/HTMLElement.h index 2f6bc41..73517df 100644 --- a/Source/WebCore/html/HTMLElement.h +++ b/Source/WebCore/html/HTMLElement.h @@ -87,6 +87,8 @@ public: virtual void dispatchChangeEvents(); virtual void dispatchInputEvents(); + TextDirection directionalityIfhasDirAutoAttribute(bool& isAuto) const; + protected: HTMLElement(const QualifiedName& tagName, Document*); @@ -95,6 +97,8 @@ protected: virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; virtual void parseMappedAttribute(Attribute*); + virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); + private: virtual String nodeName() const; @@ -106,6 +110,12 @@ private: PassRefPtr<DocumentFragment> textToFragment(const String&, ExceptionCode&); HTMLFormElement* shadowAncestorOwnerForm(); + + void dirAttributeChanged(Attribute*); + void adjustDirectionalityIfNeededAfterChildAttributeChanged(Element* child); + void calculateAndAdjustDirectionality(); + void adjustDirectionalityIfNeededAfterChildrenChanged(Node* beforeChange, int childCountDelta); + TextDirection directionality(Node** strongDirectionalityTextNode= 0) const; }; inline HTMLElement* toHTMLElement(Node* node) diff --git a/Source/WebCore/html/HTMLElementsAllInOne.cpp b/Source/WebCore/html/HTMLElementsAllInOne.cpp index 785a94b..e4499c0 100644 --- a/Source/WebCore/html/HTMLElementsAllInOne.cpp +++ b/Source/WebCore/html/HTMLElementsAllInOne.cpp @@ -97,6 +97,7 @@ #include "HTMLSelectElement.cpp" #include "HTMLSourceElement.cpp" #include "HTMLStyleElement.cpp" +#include "HTMLSummaryElement.cpp" #include "HTMLTableCaptionElement.cpp" #include "HTMLTableCellElement.cpp" #include "HTMLTableColElement.cpp" diff --git a/Source/WebCore/html/HTMLFormControlElement.cpp b/Source/WebCore/html/HTMLFormControlElement.cpp index 2f3db08..4b3c48a 100644 --- a/Source/WebCore/html/HTMLFormControlElement.cpp +++ b/Source/WebCore/html/HTMLFormControlElement.cpp @@ -65,6 +65,7 @@ HTMLFormControlElement::HTMLFormControlElement(const QualifiedName& tagName, Doc , m_willValidateInitialized(false) , m_willValidate(true) , m_isValid(true) + , m_wasChangedSinceLastFormControlChangeEvent(false) { if (!this->form()) setForm(findFormAncestor()); @@ -130,7 +131,7 @@ void HTMLFormControlElement::attach() // on the renderer. if (renderer()) renderer()->updateFromElement(); - + // Focus the element if it should honour its autofocus attribute. // We have to determine if the element is a TextArea/Input/Button/Select, // if input type hidden ignore autofocus. So if disabled or readonly. @@ -188,13 +189,25 @@ 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::dispatchChangeEvents(); + setChangedSinceLastFormControlChangeEvent(false); } void HTMLFormControlElement::dispatchFormControlInputEvent() { + setChangedSinceLastFormControlChangeEvent(true); HTMLElement::dispatchInputEvents(); } @@ -251,7 +264,7 @@ bool HTMLFormControlElement::isKeyboardFocusable(KeyboardEvent* event) const { if (isFocusable()) if (document()->frame()) - return document()->frame()->eventHandler()->tabsToAllControls(event); + return document()->frame()->eventHandler()->tabsToAllFormControls(event); return false; } @@ -451,13 +464,13 @@ PassRefPtr<NodeList> HTMLFormControlElement::labels() return 0; if (!document()) return 0; - + NodeRareData* data = Node::ensureRareData(); if (!data->nodeLists()) { data->setNodeLists(NodeListsNodeData::create()); document()->addNodeListCache(); } - + return LabelsNodeList::create(this); } @@ -582,6 +595,7 @@ bool HTMLTextFormControlElement::placeholderShouldBeVisible() const { return supportsPlaceholder() && isEmptyValue() + && isEmptySuggestedValue() && !isPlaceholderEmpty() && (document()->focusedNode() != this || (renderer() && renderer()->theme()->shouldShowPlaceholderWhenFocused())); } diff --git a/Source/WebCore/html/HTMLFormControlElement.h b/Source/WebCore/html/HTMLFormControlElement.h index 368dcfa..120313d 100644 --- a/Source/WebCore/html/HTMLFormControlElement.h +++ b/Source/WebCore/html/HTMLFormControlElement.h @@ -52,6 +52,9 @@ public: 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(); @@ -161,6 +164,8 @@ private: // Cache of validity()->valid(). // But "candidate for constraint validation" doesn't affect m_isValid. bool m_isValid : 1; + + bool m_wasChangedSinceLastFormControlChangeEvent : 1; }; // FIXME: Give this class its own header file. @@ -223,6 +228,8 @@ private: // 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(). diff --git a/Source/WebCore/html/HTMLImageLoader.cpp b/Source/WebCore/html/HTMLImageLoader.cpp index ab4ae29..77ee1e7 100644 --- a/Source/WebCore/html/HTMLImageLoader.cpp +++ b/Source/WebCore/html/HTMLImageLoader.cpp @@ -49,6 +49,10 @@ 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. diff --git a/Source/WebCore/html/HTMLInputElement.cpp b/Source/WebCore/html/HTMLInputElement.cpp index 14dd149..c5f1ebc 100644 --- a/Source/WebCore/html/HTMLInputElement.cpp +++ b/Source/WebCore/html/HTMLInputElement.cpp @@ -501,6 +501,8 @@ void HTMLInputElement::updateType() updateFocusAppearance(true); } + setChangedSinceLastFormControlChangeEvent(false); + checkedRadioButtons().addButton(this); setNeedsValidityCheck(); @@ -905,10 +907,14 @@ void HTMLInputElement::setValue(const String& value, bool sendChangeEvent) } m_inputType->valueChanged(); - // Don't dispatch the change event when focused, it will be dispatched - // when the control loses focus. - if (sendChangeEvent && document()->focusedNode() != this) - dispatchFormControlChangeEvent(); + if (sendChangeEvent) { + // 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 (isTextField() && focused()) + dispatchFormControlInputEvent(); + else + dispatchFormControlChangeEvent(); + } InputElement::notifyFormStateChanged(this); } @@ -977,6 +983,10 @@ void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths) 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) @@ -1045,15 +1055,10 @@ void HTMLInputElement::defaultEventHandler(Event* evt) addSearchResult(); onSearch(); } - // Fire onChange for text fields. - RenderObject* r = renderer(); - if (r && r->isTextField() && toRenderTextControl(r)->wasChangedSinceLastChangeEvent()) { + // Form submission finishes editing, just as loss of focus does. + // If there was a change, send the event now. + if (wasChangedSinceLastFormControlChangeEvent()) dispatchFormControlChangeEvent(); - // Refetch the renderer since arbitrary JS code run during onchange can do anything, including destroying it. - r = renderer(); - if (r && r->isTextField()) - toRenderTextControl(r)->setChangedSinceLastChangeEvent(false); - } RefPtr<HTMLFormElement> formForSubmission = m_inputType->formForSubmission(); // Form may never have been present, or may have been destroyed by code responding to the change event. @@ -1170,6 +1175,11 @@ 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); @@ -1430,8 +1440,6 @@ void HTMLInputElement::stepUpFromRenderer(int n) } if (currentStringValue != value()) { - if (renderer() && renderer()->isTextField()) - toRenderTextControl(renderer())->setChangedSinceLastChangeEvent(true); if (m_inputType->isRangeControl()) dispatchFormControlChangeEvent(); else diff --git a/Source/WebCore/html/HTMLInputElement.h b/Source/WebCore/html/HTMLInputElement.h index 757992a..27d556b 100644 --- a/Source/WebCore/html/HTMLInputElement.h +++ b/Source/WebCore/html/HTMLInputElement.h @@ -196,7 +196,7 @@ public: CheckedRadioButtons& checkedRadioButtons() const; void handleBeforeTextInsertedEvent(Event*); void updateCheckedRadioButtons(); - + protected: HTMLInputElement(const QualifiedName&, Document*, HTMLFormElement*, bool createdByParser); @@ -217,11 +217,11 @@ private: virtual bool shouldUseInputMethod() const; virtual const AtomicString& formControlName() const; - + // isChecked is used by the rendering tree/CSS while checked() is used by JS to determine checked state virtual bool isChecked() const; virtual bool isIndeterminate() const { return indeterminate(); } - + virtual bool isTextFormControl() const { return isTextField(); } virtual bool hasSpinButton() const; @@ -235,7 +235,7 @@ private: virtual void restoreFormControlState(const String&); virtual bool canStartSelection() const; - + virtual void accessKeyAction(bool sendToAnyElement); virtual bool mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const; @@ -260,6 +260,7 @@ private: virtual void cacheSelection(int start, int end); virtual String visibleValue() const; + virtual String convertFromVisibleValue(const String&) const; virtual bool isAcceptableValue(const String&) const; virtual String sanitizeValue(const String&) const; virtual bool hasUnacceptableValue() const; @@ -280,6 +281,7 @@ private: virtual bool supportsPlaceholder() const; virtual bool isEmptyValue() const { return value().isEmpty(); } + virtual bool isEmptySuggestedValue() const { return suggestedValue().isEmpty(); } virtual void handleFocusEvent(); virtual void handleBlurEvent(); virtual int cachedSelectionStart() const { return m_data.cachedSelectionStart(); } diff --git a/Source/WebCore/html/HTMLMediaElement.cpp b/Source/WebCore/html/HTMLMediaElement.cpp index 04d0a1d..a2506d0 100644 --- a/Source/WebCore/html/HTMLMediaElement.cpp +++ b/Source/WebCore/html/HTMLMediaElement.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007, 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 @@ -139,7 +139,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) , m_proxyWidget(0) #endif - , m_restrictions(NoRestrictions) + , m_restrictions(RequireUserGestureForFullScreenRestriction) , m_preload(MediaPlayer::Auto) , m_displayMode(Unknown) , m_processingMediaPlayerCallback(0) @@ -171,10 +171,14 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* docum LOG(Media, "HTMLMediaElement::HTMLMediaElement"); document->registerForDocumentActivationCallbacks(this); document->registerForMediaVolumeCallbacks(this); +<<<<<<< HEAD #if PLATFORM(ANDROID) && ENABLE(TOUCH_EVENTS) // Enable the Media Element to listen to all the touch events document->addListenerTypeIfNeeded(eventNames().touchstartEvent); #endif +======= + document->registerForPrivateBrowsingStateChangedCallbacks(this); +>>>>>>> WebKit at r80534 } HTMLMediaElement::~HTMLMediaElement() @@ -185,6 +189,7 @@ HTMLMediaElement::~HTMLMediaElement() setShouldDelayLoadEvent(false); document()->unregisterForDocumentActivationCallbacks(this); document()->unregisterForMediaVolumeCallbacks(this); + document()->unregisterForPrivateBrowsingStateChangedCallbacks(this); } void HTMLMediaElement::willMoveToNewOwnerDocument() @@ -700,6 +705,10 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content if (m_sendProgressEvents) startProgressEventTimer(); + Settings* settings = document()->settings(); + bool privateMode = !settings || settings->privateBrowsingEnabled(); + m_player->setPrivateBrowsingMode(privateMode); + if (!autoplay()) m_player->setPreload(m_preload); m_player->setPreservesPitch(m_webkitPreservesPitch); @@ -1480,6 +1489,10 @@ bool HTMLMediaElement::controls() const 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; + return hasAttribute(controlsAttr); } @@ -1527,8 +1540,7 @@ void HTMLMediaElement::setMuted(bool muted) m_player->setMuted(m_muted); if (renderer()) renderer()->updateFromElement(); - } else - updateVolume(); + } } scheduleEvent(eventNames().volumechangeEvent); } @@ -1868,9 +1880,14 @@ void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged"); beginProcessingMediaPlayerCallback(); - if (m_player) - m_volume = m_player->volume(); - updateVolume(); + if (m_player) { + float vol = m_player->volume(); + if (vol != m_volume) { + m_volume = vol; + updateVolume(); + scheduleEvent(eventNames().volumechangeEvent); + } + } endProcessingMediaPlayerCallback(); } @@ -2496,18 +2513,18 @@ bool HTMLMediaElement::webkitHasClosedCaptions() const } #if ENABLE(MEDIA_STATISTICS) -unsigned long HTMLMediaElement::webkitAudioBytesDecoded() const +unsigned HTMLMediaElement::webkitAudioDecodedByteCount() const { if (!m_player) return 0; - return m_player->audioBytesDecoded(); + return m_player->audioDecodedByteCount(); } -unsigned long HTMLMediaElement::webkitVideoBytesDecoded() const +unsigned HTMLMediaElement::webkitVideoDecodedByteCount() const { if (!m_player) return 0; - return m_player->videoBytesDecoded(); + return m_player->videoDecodedByteCount(); } #endif @@ -2539,6 +2556,33 @@ void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay) 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); +} + } #endif diff --git a/Source/WebCore/html/HTMLMediaElement.h b/Source/WebCore/html/HTMLMediaElement.h index 9778fd4..f870586 100644 --- a/Source/WebCore/html/HTMLMediaElement.h +++ b/Source/WebCore/html/HTMLMediaElement.h @@ -131,8 +131,8 @@ public: #if ENABLE(MEDIA_STATISTICS) // Statistics - unsigned long webkitAudioBytesDecoded() const; - unsigned long webkitVideoBytesDecoded() const; + unsigned webkitAudioDecodedByteCount() const; + unsigned webkitVideoDecodedByteCount() const; #endif // controls @@ -176,6 +176,27 @@ public: void sourceWillBeRemoved(HTMLSourceElement*); void sourceWasAdded(HTMLSourceElement*); + void privateBrowsingStateDidChange(); + + // Restrictions to change default behaviors. + enum BehaviorRestrictions { + NoRestrictions = 0, + RequireUserGestureForLoadRestriction = 1 << 0, + RequireUserGestureForRateChangeRestriction = 1 << 1, + RequireUserGestureForFullScreenRestriction = 1 << 2 + }; + + bool requireUserGestureForLoad() const { return m_restrictions & RequireUserGestureForLoadRestriction; } + bool requireUserGestureForRateChange() const { return m_restrictions & RequireUserGestureForRateChangeRestriction; } + bool requireUserGestureForFullScreen() const { return m_restrictions & RequireUserGestureForFullScreenRestriction; } + + void setBehaviorRestrictions(BehaviorRestrictions restrictions) { m_restrictions = restrictions; } + + // Media cache management. + static void getSitesInMediaCache(Vector<String>&); + static void clearMediaCache(); + static void clearMediaCacheForSite(const String&); + protected: HTMLMediaElement(const QualifiedName&, Document*); virtual ~HTMLMediaElement(); @@ -298,14 +319,6 @@ private: void invalidateCachedTime(); void refreshCachedTime() const; - // Restrictions to change default behaviors. This is effectively a compile time choice at the moment - // because there are no accessor functions. - enum BehaviorRestrictions { - NoRestrictions = 0, - RequireUserGestureForLoadRestriction = 1 << 0, - RequireUserGestureForRateChangeRestriction = 1 << 1, - }; - Timer<HTMLMediaElement> m_loadTimer; Timer<HTMLMediaElement> m_asyncEventTimer; Timer<HTMLMediaElement> m_progressEventTimer; diff --git a/Source/WebCore/html/HTMLMediaElement.idl b/Source/WebCore/html/HTMLMediaElement.idl index 9d4602d..c7e6b07 100644 --- a/Source/WebCore/html/HTMLMediaElement.idl +++ b/Source/WebCore/html/HTMLMediaElement.idl @@ -84,8 +84,8 @@ interface [Conditional=VIDEO] HTMLMediaElement : HTMLElement { #if defined(ENABLE_MEDIA_STATISTICS) && ENABLE_MEDIA_STATISTICS // The number of bytes consumed by the media decoder. - readonly attribute unsigned long webkitAudioBytesDecoded; - readonly attribute unsigned long webkitVideoBytesDecoded; + readonly attribute unsigned long webkitAudioDecodedByteCount; + readonly attribute unsigned long webkitVideoDecodedByteCount; #endif }; } diff --git a/Source/WebCore/html/HTMLScriptElement.cpp b/Source/WebCore/html/HTMLScriptElement.cpp index 603b2a8..8e708d1 100644 --- a/Source/WebCore/html/HTMLScriptElement.cpp +++ b/Source/WebCore/html/HTMLScriptElement.cpp @@ -35,9 +35,9 @@ namespace WebCore { using namespace HTMLNames; -inline HTMLScriptElement::HTMLScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool wasAlreadyStarted) +inline HTMLScriptElement::HTMLScriptElement(const QualifiedName& tagName, Document* document, bool wasInsertedByParser, bool alreadyStarted) : HTMLElement(tagName, document) - , ScriptElement(this, wasInsertedByParser, wasAlreadyStarted) + , ScriptElement(this, wasInsertedByParser, alreadyStarted) { ASSERT(hasTagName(scriptTag)); } @@ -74,16 +74,10 @@ void HTMLScriptElement::parseMappedAttribute(Attribute* attr) HTMLElement::parseMappedAttribute(attr); } -void HTMLScriptElement::finishParsingChildren() -{ - ScriptElement::finishParsingChildren(sourceAttributeValue()); - HTMLElement::finishParsingChildren(); -} - void HTMLScriptElement::insertedIntoDocument() { HTMLElement::insertedIntoDocument(); - ScriptElement::insertedIntoDocument(sourceAttributeValue()); + ScriptElement::insertedIntoDocument(); } void HTMLScriptElement::removedFromDocument() @@ -160,6 +154,11 @@ bool HTMLScriptElement::deferAttributeValue() const return fastHasAttribute(deferAttr); } +bool HTMLScriptElement::hasSourceAttribute() const +{ + return fastHasAttribute(srcAttr); +} + void HTMLScriptElement::dispatchLoadEvent() { ASSERT(!haveFiredLoadEvent()); @@ -175,7 +174,7 @@ void HTMLScriptElement::dispatchErrorEvent() PassRefPtr<Element> HTMLScriptElement::cloneElementWithoutAttributesAndChildren() const { - return adoptRef(new HTMLScriptElement(tagQName(), document(), false, wasAlreadyStarted())); + return adoptRef(new HTMLScriptElement(tagQName(), document(), false, alreadyStarted())); } } diff --git a/Source/WebCore/html/HTMLScriptElement.h b/Source/WebCore/html/HTMLScriptElement.h index 09773c5..efe6a6a 100644 --- a/Source/WebCore/html/HTMLScriptElement.h +++ b/Source/WebCore/html/HTMLScriptElement.h @@ -39,7 +39,7 @@ public: KURL src() const; private: - HTMLScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool wasAlreadyStarted); + HTMLScriptElement(const QualifiedName&, Document*, bool wasInsertedByParser, bool alreadyStarted); virtual void parseMappedAttribute(Attribute*); virtual void insertedIntoDocument(); @@ -47,7 +47,6 @@ private: virtual void childrenChanged(bool changedByParser = false, Node* beforeChange = 0, Node* afterChange = 0, int childCountDelta = 0); virtual bool isURLAttribute(Attribute*) const; - virtual void finishParsingChildren(); virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; @@ -59,6 +58,7 @@ private: virtual String eventAttributeValue() const; virtual bool asyncAttributeValue() const; virtual bool deferAttributeValue() const; + virtual bool hasSourceAttribute() const; virtual void dispatchLoadEvent(); virtual void dispatchErrorEvent(); diff --git a/Source/WebCore/html/HTMLSummaryElement.cpp b/Source/WebCore/html/HTMLSummaryElement.cpp new file mode 100644 index 0000000..96e3d74 --- /dev/null +++ b/Source/WebCore/html/HTMLSummaryElement.cpp @@ -0,0 +1,41 @@ +/* + * 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" + +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +PassRefPtr<HTMLSummaryElement> HTMLSummaryElement::create(const QualifiedName& tagName, Document* document) +{ + return adoptRef(new HTMLSummaryElement(tagName, document)); +} + +HTMLSummaryElement::HTMLSummaryElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) +{ + ASSERT(hasTagName(summaryTag)); +} + +} diff --git a/Source/WebCore/html/HTMLSummaryElement.h b/Source/WebCore/html/HTMLSummaryElement.h new file mode 100644 index 0000000..1b24c67 --- /dev/null +++ b/Source/WebCore/html/HTMLSummaryElement.h @@ -0,0 +1,38 @@ +/* + * 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 HTMLSummaryElement : public HTMLElement { +public: + static PassRefPtr<HTMLSummaryElement> create(const QualifiedName&, Document*); + +private: + HTMLSummaryElement(const QualifiedName&, Document*); +}; + +} + +#endif // HTMLSummaryElement_h diff --git a/Source/WebCore/html/HTMLTagNames.in b/Source/WebCore/html/HTMLTagNames.in index 83d41de..bea63e4 100644 --- a/Source/WebCore/html/HTMLTagNames.in +++ b/Source/WebCore/html/HTMLTagNames.in @@ -115,7 +115,7 @@ strike interfaceName=HTMLElement strong interfaceName=HTMLElement style constructorNeedsCreatedByParser sub interfaceName=HTMLElement -summary interfaceName=HTMLElement +summary interfaceName=HTMLSummaryElement, JSInterfaceName=HTMLElement sup interfaceName=HTMLElement table tbody interfaceName=HTMLTableSectionElement diff --git a/Source/WebCore/html/HTMLVideoElement.cpp b/Source/WebCore/html/HTMLVideoElement.cpp index 2004e48..4ddcdfe 100644 --- a/Source/WebCore/html/HTMLVideoElement.cpp +++ b/Source/WebCore/html/HTMLVideoElement.cpp @@ -232,7 +232,7 @@ void HTMLVideoElement::webkitEnterFullscreen(bool isUserGesture, ExceptionCode& // Generate an exception if this isn't called in response to a user gesture, or if the // element does not support fullscreen. - if (!isUserGesture || !supportsFullscreen()) { + if ((requireUserGestureForFullScreen() && !isUserGesture) || !supportsFullscreen()) { ec = INVALID_STATE_ERR; return; } @@ -264,20 +264,20 @@ void HTMLVideoElement::willMoveToNewOwnerDocument() } #if ENABLE(MEDIA_STATISTICS) -unsigned long HTMLVideoElement::webkitDecodedFrames() const +unsigned HTMLVideoElement::webkitDecodedFrameCount() const { if (!player()) return 0; - return player()->decodedFrames(); + return player()->decodedFrameCount(); } -unsigned long HTMLVideoElement::webkitDroppedFrames() const +unsigned HTMLVideoElement::webkitDroppedFrameCount() const { if (!player()) return 0; - return player()->droppedFrames(); + return player()->droppedFrameCount(); } #endif diff --git a/Source/WebCore/html/HTMLVideoElement.h b/Source/WebCore/html/HTMLVideoElement.h index dd38a59..714fcaa 100644 --- a/Source/WebCore/html/HTMLVideoElement.h +++ b/Source/WebCore/html/HTMLVideoElement.h @@ -57,8 +57,8 @@ public: #if ENABLE(MEDIA_STATISTICS) // Statistics - unsigned long webkitDecodedFrames() const; - unsigned long webkitDroppedFrames() const; + unsigned webkitDecodedFrameCount() const; + unsigned webkitDroppedFrameCount() const; #endif // Used by canvas to gain raw pixel access diff --git a/Source/WebCore/html/HTMLVideoElement.idl b/Source/WebCore/html/HTMLVideoElement.idl index 50513d4..68c1aa7 100644 --- a/Source/WebCore/html/HTMLVideoElement.idl +++ b/Source/WebCore/html/HTMLVideoElement.idl @@ -46,11 +46,11 @@ module html { #if defined(ENABLE_MEDIA_STATISTICS) && ENABLE_MEDIA_STATISTICS // The number of frames that have been decoded and made available for // playback. - readonly attribute unsigned long webkitDecodedFrames; + readonly attribute unsigned long webkitDecodedFrameCount; // The number of decoded frames that have been dropped by the player // for performance reasons during playback. - readonly attribute unsigned long webkitDroppedFrames; + readonly attribute unsigned long webkitDroppedFrameCount; #endif }; } diff --git a/Source/WebCore/html/HTMLViewSourceDocument.cpp b/Source/WebCore/html/HTMLViewSourceDocument.cpp index 27eaf51..b0633eb 100644 --- a/Source/WebCore/html/HTMLViewSourceDocument.cpp +++ b/Source/WebCore/html/HTMLViewSourceDocument.cpp @@ -52,6 +52,8 @@ HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, co , m_type(mimeType) { setUsesBeforeAfterRules(true); + setUsesViewSourceStyles(true); + setCompatibilityMode(QuirksMode); lockCompatibilityMode(); } diff --git a/Source/WebCore/html/ImageDocument.cpp b/Source/WebCore/html/ImageDocument.cpp index a42ccc8..d03e37e 100644 --- a/Source/WebCore/html/ImageDocument.cpp +++ b/Source/WebCore/html/ImageDocument.cpp @@ -134,7 +134,7 @@ void ImageDocumentParser::appendBytes(DocumentWriter*, const char*, int, bool) CachedImage* cachedImage = document()->cachedImage(); cachedImage->data(frame->loader()->documentLoader()->mainResourceData(), false); - document()->imageChanged(); + document()->imageUpdated(); } void ImageDocumentParser::finish() @@ -165,7 +165,7 @@ void ImageDocumentParser::finish() document()->setTitle(imageTitle(fileName, size)); } - document()->imageChanged(); + document()->imageUpdated(); } document()->finishedParsing(); @@ -282,7 +282,7 @@ void ImageDocument::imageClicked(int x, int y) } } -void ImageDocument::imageChanged() +void ImageDocument::imageUpdated() { ASSERT(m_imageElement); diff --git a/Source/WebCore/html/ImageDocument.h b/Source/WebCore/html/ImageDocument.h index 5d00bd6..9d281f1 100644 --- a/Source/WebCore/html/ImageDocument.h +++ b/Source/WebCore/html/ImageDocument.h @@ -43,7 +43,7 @@ public: void disconnectImageElement() { m_imageElement = 0; } void windowSizeChanged(); - void imageChanged(); + void imageUpdated(); void imageClicked(int x, int y); private: diff --git a/Source/WebCore/html/InputType.cpp b/Source/WebCore/html/InputType.cpp index 21ba7c2..d944909 100644 --- a/Source/WebCore/html/InputType.cpp +++ b/Source/WebCore/html/InputType.cpp @@ -521,6 +521,11 @@ String InputType::visibleValue() const return element()->value(); } +String InputType::convertFromVisibleValue(const String& visibleValue) const +{ + return visibleValue; +} + bool InputType::isAcceptableValue(const String&) { return true; diff --git a/Source/WebCore/html/InputType.h b/Source/WebCore/html/InputType.h index 8c35af3..1b34717 100644 --- a/Source/WebCore/html/InputType.h +++ b/Source/WebCore/html/InputType.h @@ -153,7 +153,9 @@ public: 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." virtual String sanitizeValue(const String&); virtual bool hasUnacceptableValue(); diff --git a/Source/WebCore/html/NumberInputType.cpp b/Source/WebCore/html/NumberInputType.cpp index 0011115..3397248 100644 --- a/Source/WebCore/html/NumberInputType.cpp +++ b/Source/WebCore/html/NumberInputType.cpp @@ -53,18 +53,6 @@ using namespace std; static const double numberDefaultStep = 1.0; static const double numberStepScaleFactor = 1.0; -// Returns true if the specified character can be a part of 'valid floating -// point number' of HTML5. -static bool isHTMLNumberCharacter(UChar ch) -{ - return ch == '+' || ch == '-' || ch == '.' || ch == 'e' || ch == 'E' || isASCIIDigit(ch); -} - -static bool isNumberCharacter(UChar ch) -{ - return isLocalizedNumberCharacter(ch) || isHTMLNumberCharacter(ch); -} - PassOwnPtr<InputType> NumberInputType::create(HTMLInputElement* element) { return adoptPtr(new NumberInputType(element)); @@ -183,30 +171,6 @@ void NumberInputType::handleKeydownEvent(KeyboardEvent* event) TextFieldInputType::handleKeydownEvent(event); } -void NumberInputType::handleBeforeTextInsertedEvent(BeforeTextInsertedEvent* event) -{ - unsigned length = event->text().length(); - bool hasInvalidChar = false; - for (unsigned i = 0; i < length; ++i) { - if (!isNumberCharacter(event->text()[i])) { - hasInvalidChar = true; - break; - } - } - if (hasInvalidChar) { - Vector<UChar> stripped; - stripped.reserveCapacity(length); - for (unsigned i = 0; i < length; ++i) { - UChar ch = event->text()[i]; - if (!isNumberCharacter(ch)) - continue; - stripped.append(ch); - } - event->setText(String::adopt(stripped)); - } - TextFieldInputType::handleBeforeTextInsertedEvent(event); -} - void NumberInputType::handleWheelEvent(WheelEvent* event) { handleWheelEventForSpinButton(event); @@ -264,6 +228,14 @@ String NumberInputType::visibleValue() const 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); @@ -271,12 +243,9 @@ bool NumberInputType::isAcceptableValue(const String& proposedValue) String NumberInputType::sanitizeValue(const String& proposedValue) { - // Try to parse the value as a localized number, then try to parse it as - // the standard format. - double parsedValue = parseLocalizedNumber(proposedValue); - if (isfinite(parsedValue)) - return serializeForNumberType(parsedValue); - return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : String(); + if (proposedValue.isEmpty()) + return proposedValue; + return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : emptyAtom.string(); } bool NumberInputType::hasUnacceptableValue() diff --git a/Source/WebCore/html/NumberInputType.h b/Source/WebCore/html/NumberInputType.h index 9d97134..ea5dc10 100644 --- a/Source/WebCore/html/NumberInputType.h +++ b/Source/WebCore/html/NumberInputType.h @@ -57,7 +57,6 @@ private: virtual double defaultStep() const; virtual double stepScaleFactor() const; virtual void handleKeydownEvent(KeyboardEvent*); - virtual void handleBeforeTextInsertedEvent(BeforeTextInsertedEvent*); virtual void handleWheelEvent(WheelEvent*); virtual double parseToDouble(const String&, double) const; virtual double parseToDoubleWithDecimalPlaces(const String&, double, unsigned*) const; @@ -65,6 +64,7 @@ private: virtual double acceptableError(double) const; virtual void handleBlurEvent(); virtual String visibleValue() const; + virtual String convertFromVisibleValue(const String&) const; virtual bool isAcceptableValue(const String&); virtual String sanitizeValue(const String&); virtual bool hasUnacceptableValue(); diff --git a/Source/WebCore/html/TextFieldInputType.cpp b/Source/WebCore/html/TextFieldInputType.cpp index 1d06be3..ffea2f4 100644 --- a/Source/WebCore/html/TextFieldInputType.cpp +++ b/Source/WebCore/html/TextFieldInputType.cpp @@ -85,7 +85,7 @@ void TextFieldInputType::handleKeydownEventForSpinButton(KeyboardEvent* event) void TextFieldInputType::handleWheelEventForSpinButton(WheelEvent* event) { - if (element()->disabled() || element()->readOnly()) + if (element()->disabled() || element()->readOnly() || !element()->focused()) return; int step = 0; if (event->wheelDeltaY() > 0) diff --git a/Source/WebCore/html/ValidationMessage.cpp b/Source/WebCore/html/ValidationMessage.cpp index f772b92..70aa02d 100644 --- a/Source/WebCore/html/ValidationMessage.cpp +++ b/Source/WebCore/html/ValidationMessage.cpp @@ -121,11 +121,8 @@ void ValidationMessage::buildBubbleTree(Timer<ValidationMessage>*) // to inherit an existing shadow tree. if (host->shadowRoot()) host->shadowRoot()->appendChild(m_bubble.get(), ec); - else { + else host->setShadowRoot(m_bubble); - // FIXME: The following attach() should be unnecessary. - m_bubble->attach(); - } m_bubble->appendChild(ElementWithPseudoId::create(doc, "-webkit-validation-bubble-top-outer-arrow"), ec); m_bubble->appendChild(ElementWithPseudoId::create(doc, "-webkit-validation-bubble-top-inner-arrow"), ec); diff --git a/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp index 9ef2dba..a549782 100644 --- a/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp +++ b/Source/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -174,6 +174,11 @@ void CanvasRenderingContext2D::reset() if (m_context3D && m_drawingBuffer) { m_drawingBuffer->reset(IntSize(canvas()->width(), canvas()->height())); c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas()->width(), canvas()->height())); +#if USE(ACCELERATED_COMPOSITING) + RenderBox* renderBox = canvas()->renderBox(); + if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) + renderBox->layer()->contentChanged(RenderLayer::CanvasChanged); +#endif } } #endif @@ -446,7 +451,7 @@ void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati if (!c) return; c->setCompositeOperation(op); -#if ENABLE(ACCELERATED_2D_CANVAS) +#if ENABLE(ACCELERATED_2D_CANVAS) && !ENABLE(SKIA_GPU) if (isAccelerated() && op != CompositeSourceOver) { c->setSharedGraphicsContext3D(0, 0, IntSize()); m_drawingBuffer.clear(); @@ -877,6 +882,8 @@ bool CanvasRenderingContext2D::isPointInPath(const float x, const float y) 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); } @@ -1771,7 +1778,7 @@ void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo unsigned length = text.length(); const UChar* string = text.characters(); - TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, rtl, override, false, false); + TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, rtl, override); // Draw the item text at the correct point. FloatPoint location(x, y); diff --git a/Source/WebCore/html/canvas/Int32Array.h b/Source/WebCore/html/canvas/Int32Array.h index c657300..3a98eed 100644 --- a/Source/WebCore/html/canvas/Int32Array.h +++ b/Source/WebCore/html/canvas/Int32Array.h @@ -38,8 +38,8 @@ public: static PassRefPtr<Int32Array> create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length); // Can’t use "using" here due to a bug in the RVCT compiler. - void set(TypedArrayBase<int>* array, unsigned offset, ExceptionCode& ec) { return TypedArrayBase<int>::set(array, offset, ec); } - void set(unsigned index, double value) { return IntegralTypedArrayBase<int>::set(index, value); } + void set(TypedArrayBase<int>* array, unsigned offset, ExceptionCode& ec) { TypedArrayBase<int>::set(array, offset, ec); } + void set(unsigned index, double value) { IntegralTypedArrayBase<int>::set(index, value); } PassRefPtr<Int32Array> subarray(int start) const; PassRefPtr<Int32Array> subarray(int start, int end) const; diff --git a/Source/WebCore/html/canvas/Int8Array.h b/Source/WebCore/html/canvas/Int8Array.h index aabdb97..2d3fc89 100644 --- a/Source/WebCore/html/canvas/Int8Array.h +++ b/Source/WebCore/html/canvas/Int8Array.h @@ -40,8 +40,8 @@ public: static PassRefPtr<Int8Array> create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length); // Can’t use "using" here due to a bug in the RVCT compiler. - void set(TypedArrayBase<signed char>* array, unsigned offset, ExceptionCode& ec) { return TypedArrayBase<signed char>::set(array, offset, ec); } - void set(unsigned index, double value) { return IntegralTypedArrayBase<signed char>::set(index, value); } + void set(TypedArrayBase<signed char>* array, unsigned offset, ExceptionCode& ec) { TypedArrayBase<signed char>::set(array, offset, ec); } + void set(unsigned index, double value) { IntegralTypedArrayBase<signed char>::set(index, value); } PassRefPtr<Int8Array> subarray(int start) const; PassRefPtr<Int8Array> subarray(int start, int end) const; diff --git a/Source/WebCore/html/canvas/IntegralTypedArrayBase.h b/Source/WebCore/html/canvas/IntegralTypedArrayBase.h index b87d832..0b26844 100644 --- a/Source/WebCore/html/canvas/IntegralTypedArrayBase.h +++ b/Source/WebCore/html/canvas/IntegralTypedArrayBase.h @@ -45,11 +45,9 @@ class IntegralTypedArrayBase : public TypedArrayBase<T> { return; if (isnan(value)) // Clamp NaN to 0 value = 0; - if (value < std::numeric_limits<T>::min()) - value = std::numeric_limits<T>::min(); - else if (value > std::numeric_limits<T>::max()) - value = std::numeric_limits<T>::max(); - TypedArrayBase<T>::data()[index] = static_cast<T>(value); + // The double cast is necessary to get the correct wrapping + // for out-of-range values with Int32Array and Uint32Array. + TypedArrayBase<T>::data()[index] = static_cast<T>(static_cast<int64_t>(value)); } // Invoked by the indexed getter. Does not perform range checks; caller diff --git a/Source/WebCore/html/canvas/OESVertexArrayObject.cpp b/Source/WebCore/html/canvas/OESVertexArrayObject.cpp new file mode 100644 index 0000000..5fb71c8 --- /dev/null +++ b/Source/WebCore/html/canvas/OESVertexArrayObject.cpp @@ -0,0 +1,116 @@ +/* + * 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() + , m_context(context) +{ +} + +OESVertexArrayObject::~OESVertexArrayObject() +{ +} + +WebGLExtension::ExtensionName OESVertexArrayObject::getName() const +{ + return OESVertexArrayObjectName; +} + +PassRefPtr<OESVertexArrayObject> OESVertexArrayObject::create(WebGLRenderingContext* context) +{ + return adoptRef(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 0000000..d9792da --- /dev/null +++ b/Source/WebCore/html/canvas/OESVertexArrayObject.h @@ -0,0 +1,63 @@ +/* + * 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 "ExceptionCode.h" +#include "GraphicsTypes3D.h" +#include "WebGLExtension.h" +#include "WebGLVertexArrayObjectOES.h" + +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/UnusedParam.h> + +namespace WebCore { + +class WebGLRenderingContext; +class WebGLVertexArrayObjectOES; + +class OESVertexArrayObject : public WebGLExtension { +public: + static PassRefPtr<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*); + + WebGLRenderingContext* m_context; +}; + +} // 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 0000000..911ebbc --- /dev/null +++ b/Source/WebCore/html/canvas/OESVertexArrayObject.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 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, OmitConstructor, DontCheckEnums] OESVertexArrayObject { + const unsigned int VERTEX_ARRAY_BINDING_OES = 0x85B5; + + [StrictTypeChecking] WebGLVertexArrayObjectOES createVertexArrayOES(); + [StrictTypeChecking] void deleteVertexArrayOES(in WebGLVertexArrayObjectOES arrayObject); + [StrictTypeChecking] boolean isVertexArrayOES(in WebGLVertexArrayObjectOES arrayObject); + [StrictTypeChecking] void bindVertexArrayOES(in WebGLVertexArrayObjectOES arrayObject) raises(DOMException); + }; +} diff --git a/Source/WebCore/html/canvas/Uint16Array.h b/Source/WebCore/html/canvas/Uint16Array.h index 8ef04a4..0b02d09 100644 --- a/Source/WebCore/html/canvas/Uint16Array.h +++ b/Source/WebCore/html/canvas/Uint16Array.h @@ -40,8 +40,8 @@ public: static PassRefPtr<Uint16Array> create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length); // Can’t use "using" here due to a bug in the RVCT compiler. - void set(TypedArrayBase<unsigned short>* array, unsigned offset, ExceptionCode& ec) { return TypedArrayBase<unsigned short>::set(array, offset, ec); } - void set(unsigned index, double value) { return IntegralTypedArrayBase<unsigned short>::set(index, value); } + void set(TypedArrayBase<unsigned short>* array, unsigned offset, ExceptionCode& ec) { TypedArrayBase<unsigned short>::set(array, offset, ec); } + void set(unsigned index, double value) { IntegralTypedArrayBase<unsigned short>::set(index, value); } PassRefPtr<Uint16Array> subarray(int start) const; PassRefPtr<Uint16Array> subarray(int start, int end) const; diff --git a/Source/WebCore/html/canvas/Uint32Array.h b/Source/WebCore/html/canvas/Uint32Array.h index 196a67f..15a7ab6 100644 --- a/Source/WebCore/html/canvas/Uint32Array.h +++ b/Source/WebCore/html/canvas/Uint32Array.h @@ -40,8 +40,8 @@ public: static PassRefPtr<Uint32Array> create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length); // Can’t use "using" here due to a bug in the RVCT compiler. - void set(TypedArrayBase<unsigned int>* array, unsigned offset, ExceptionCode& ec) { return TypedArrayBase<unsigned int>::set(array, offset, ec); } - void set(unsigned index, double value) { return IntegralTypedArrayBase<unsigned int>::set(index, value); } + void set(TypedArrayBase<unsigned int>* array, unsigned offset, ExceptionCode& ec) { TypedArrayBase<unsigned int>::set(array, offset, ec); } + void set(unsigned index, double value) { IntegralTypedArrayBase<unsigned int>::set(index, value); } PassRefPtr<Uint32Array> subarray(int start) const; PassRefPtr<Uint32Array> subarray(int start, int end) const; diff --git a/Source/WebCore/html/canvas/Uint8Array.h b/Source/WebCore/html/canvas/Uint8Array.h index a3a42dc..3da1eaa 100644 --- a/Source/WebCore/html/canvas/Uint8Array.h +++ b/Source/WebCore/html/canvas/Uint8Array.h @@ -40,8 +40,8 @@ public: static PassRefPtr<Uint8Array> create(PassRefPtr<ArrayBuffer> buffer, unsigned byteOffset, unsigned length); // Can’t use "using" here due to a bug in the RVCT compiler. - void set(TypedArrayBase<unsigned char>* array, unsigned offset, ExceptionCode& ec) { return TypedArrayBase<unsigned char>::set(array, offset, ec); } - void set(unsigned index, double value) { return IntegralTypedArrayBase<unsigned char>::set(index, value); } + void set(TypedArrayBase<unsigned char>* array, unsigned offset, ExceptionCode& ec) { TypedArrayBase<unsigned char>::set(array, offset, ec); } + void set(unsigned index, double value) { IntegralTypedArrayBase<unsigned char>::set(index, value); } PassRefPtr<Uint8Array> subarray(int start) const; PassRefPtr<Uint8Array> subarray(int start, int end) const; diff --git a/Source/WebCore/html/canvas/WebGLExtension.h b/Source/WebCore/html/canvas/WebGLExtension.h index d135c18..9a6753f 100644 --- a/Source/WebCore/html/canvas/WebGLExtension.h +++ b/Source/WebCore/html/canvas/WebGLExtension.h @@ -37,6 +37,7 @@ public: WebKitLoseContextName, OESTextureFloatName, OESStandardDerivativesName, + OESVertexArrayObjectName, }; virtual ~WebGLExtension(); diff --git a/Source/WebCore/html/canvas/WebGLGetInfo.cpp b/Source/WebCore/html/canvas/WebGLGetInfo.cpp index d0c8c65..17be974 100644 --- a/Source/WebCore/html/canvas/WebGLGetInfo.cpp +++ b/Source/WebCore/html/canvas/WebGLGetInfo.cpp @@ -38,6 +38,7 @@ #include "WebGLProgram.h" #include "WebGLRenderbuffer.h" #include "WebGLTexture.h" +#include "WebGLVertexArrayObjectOES.h" namespace WebCore { @@ -134,6 +135,12 @@ WebGLGetInfo::WebGLGetInfo(PassRefPtr<Uint8Array> value) { } +WebGLGetInfo::WebGLGetInfo(PassRefPtr<WebGLVertexArrayObjectOES> value) + : m_type(kTypeWebGLVertexArrayObjectOES) + , m_webglVertexArrayObject(value) +{ +} + WebGLGetInfo::~WebGLGetInfo() { } @@ -227,6 +234,12 @@ PassRefPtr<Uint8Array> WebGLGetInfo::getWebGLUnsignedByteArray() const 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 index 91f9233..bc2aacc 100644 --- a/Source/WebCore/html/canvas/WebGLGetInfo.h +++ b/Source/WebCore/html/canvas/WebGLGetInfo.h @@ -36,6 +36,7 @@ #include "WebGLProgram.h" #include "WebGLRenderbuffer.h" #include "WebGLTexture.h" +#include "WebGLVertexArrayObjectOES.h" #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> @@ -65,7 +66,8 @@ public: kTypeWebGLProgram, kTypeWebGLRenderbuffer, kTypeWebGLTexture, - kTypeWebGLUnsignedByteArray + kTypeWebGLUnsignedByteArray, + kTypeWebGLVertexArrayObjectOES, }; WebGLGetInfo(bool value); @@ -86,6 +88,7 @@ public: WebGLGetInfo(PassRefPtr<WebGLRenderbuffer> value); WebGLGetInfo(PassRefPtr<WebGLTexture> value); WebGLGetInfo(PassRefPtr<Uint8Array> value); + WebGLGetInfo(PassRefPtr<WebGLVertexArrayObjectOES> value); virtual ~WebGLGetInfo(); @@ -107,6 +110,7 @@ public: PassRefPtr<WebGLRenderbuffer> getWebGLRenderbuffer() const; PassRefPtr<WebGLTexture> getWebGLTexture() const; PassRefPtr<Uint8Array> getWebGLUnsignedByteArray() const; + PassRefPtr<WebGLVertexArrayObjectOES> getWebGLVertexArrayObjectOES() const; private: Type m_type; @@ -126,6 +130,7 @@ private: RefPtr<WebGLRenderbuffer> m_webglRenderbuffer; RefPtr<WebGLTexture> m_webglTexture; RefPtr<Uint8Array> m_webglUnsignedByteArray; + RefPtr<WebGLVertexArrayObjectOES> m_webglVertexArrayObject; }; } // namespace WebCore diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp index bd155c9..cc7a23b 100644 --- a/Source/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -46,6 +46,7 @@ #include "NotImplemented.h" #include "OESStandardDerivatives.h" #include "OESTextureFloat.h" +#include "OESVertexArrayObject.h" #include "RenderBox.h" #include "RenderLayer.h" #include "Settings.h" @@ -66,6 +67,10 @@ #include <wtf/PassOwnArrayPtr.h> #include <wtf/text/StringBuilder.h> +#if PLATFORM(QT) +#undef emit +#endif + namespace WebCore { const double secondsBetweenRestoreAttempts = 1.0; @@ -398,7 +403,6 @@ void WebGLRenderingContext::initializeNewContext() m_unpackPremultiplyAlpha = false; m_unpackColorspaceConversion = GraphicsContext3D::BROWSER_DEFAULT_WEBGL; m_boundArrayBuffer = 0; - m_boundElementArrayBuffer = 0; m_currentProgram = 0; m_framebufferBinding = 0; m_renderbufferBinding = 0; @@ -408,7 +412,6 @@ void WebGLRenderingContext::initializeNewContext() m_stencilFuncRefBack = 0; m_stencilFuncMask = 0xFFFFFFFF; m_stencilFuncMaskBack = 0xFFFFFFFF; - m_vertexAttribState.clear(); GC3Dint numCombinedTextureImageUnits = 0; m_context->getIntegerv(GraphicsContext3D::MAX_COMBINED_TEXTURE_IMAGE_UNITS, &numCombinedTextureImageUnits); @@ -418,13 +421,19 @@ void WebGLRenderingContext::initializeNewContext() 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_defaultVertexArrayObject = WebGLVertexArrayObjectOES::create(this, WebGLVertexArrayObjectOES::VaoTypeDefault); + addObject(m_defaultVertexArrayObject.get()); + m_boundVertexArrayObject = m_defaultVertexArrayObject; + + m_vertexAttribValue.resize(m_maxVertexAttribs); if (!isGLES2NPOTStrict()) createFallbackBlackTextures1x1(); @@ -457,6 +466,8 @@ WebGLRenderingContext::~WebGLRenderingContext() { detachAndRemoveAllObjects(); m_context->setContextLostCallback(0); + if (m_webkitLoseContext) + m_webkitLoseContext->contextDestroyed(); } void WebGLRenderingContext::markContextChanged() @@ -596,7 +607,7 @@ void WebGLRenderingContext::bindBuffer(GC3Denum target, WebGLBuffer* buffer, Exc if (target == GraphicsContext3D::ARRAY_BUFFER) m_boundArrayBuffer = buffer; else if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) - m_boundElementArrayBuffer = buffer; + m_boundVertexArrayObject->setElementArrayBuffer(buffer); else { m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return; @@ -1112,10 +1123,11 @@ void WebGLRenderingContext::deleteBuffer(WebGLBuffer* buffer) return; if (m_boundArrayBuffer == buffer) m_boundArrayBuffer = 0; - if (m_boundElementArrayBuffer == buffer) - m_boundElementArrayBuffer = 0; + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); + if (elementArrayBuffer == buffer) + m_boundVertexArrayObject->setElementArrayBuffer(0); if (!isGLES2Compliant()) { - VertexAttribState& state = m_vertexAttribState[0]; + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0); if (buffer == state.bufferBinding) { state.bufferBinding = m_vertexAttrib0Buffer; state.bytesPerElement = 0; @@ -1236,8 +1248,8 @@ void WebGLRenderingContext::disableVertexAttribArray(GC3Duint index, ExceptionCo return; } - if (index < m_vertexAttribState.size()) - m_vertexAttribState[index].enabled = false; + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index); + state.enabled = false; if (index > 0 || isGLES2Compliant()) { m_context->disableVertexAttribArray(index); @@ -1247,7 +1259,9 @@ void WebGLRenderingContext::disableVertexAttribArray(GC3Duint index, ExceptionCo bool WebGLRenderingContext::validateElementArraySize(GC3Dsizei count, GC3Denum type, GC3Dintptr offset) { - if (!m_boundElementArrayBuffer) + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); + + if (!elementArrayBuffer) return false; if (offset < 0) @@ -1261,11 +1275,11 @@ bool WebGLRenderingContext::validateElementArraySize(GC3Dsizei count, GC3Denum t // Make uoffset an element offset. offset /= 2; - GC3Dsizeiptr n = m_boundElementArrayBuffer->byteLength() / 2; + GC3Dsizeiptr n = elementArrayBuffer->byteLength() / 2; if (offset > n || count > n - offset) return false; } else if (type == GraphicsContext3D::UNSIGNED_BYTE) { - GC3Dsizeiptr n = m_boundElementArrayBuffer->byteLength(); + GC3Dsizeiptr n = elementArrayBuffer->byteLength(); if (offset > n || count > n - offset) return false; } @@ -1279,18 +1293,20 @@ bool WebGLRenderingContext::validateIndexArrayConservative(GC3Denum type, int& n // 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 (!m_boundElementArrayBuffer) + if (!elementArrayBuffer) return false; - GC3Dsizeiptr numElements = m_boundElementArrayBuffer->byteLength(); + GC3Dsizeiptr numElements = elementArrayBuffer->byteLength(); // The case count==0 is already dealt with in drawElements before validateIndexArrayConservative. if (!numElements) return false; - const ArrayBuffer* buffer = m_boundElementArrayBuffer->elementArrayBuffer(); + const ArrayBuffer* buffer = elementArrayBuffer->elementArrayBuffer(); ASSERT(buffer); - int maxIndex = m_boundElementArrayBuffer->getCachedMaxIndex(type); + int maxIndex = elementArrayBuffer->getCachedMaxIndex(type); if (maxIndex < 0) { // Compute the maximum index in the entire buffer for the given type of index. switch (type) { @@ -1310,7 +1326,7 @@ bool WebGLRenderingContext::validateIndexArrayConservative(GC3Denum type, int& n default: return false; } - m_boundElementArrayBuffer->setCachedMaxIndex(type, maxIndex); + elementArrayBuffer->setCachedMaxIndex(type, maxIndex); } if (maxIndex >= 0) { @@ -1327,8 +1343,10 @@ bool WebGLRenderingContext::validateIndexArrayPrecise(GC3Dsizei count, GC3Denum { ASSERT(count >= 0 && offset >= 0); int lastIndex = -1; + + RefPtr<WebGLBuffer> elementArrayBuffer = m_boundVertexArrayObject->getElementArrayBuffer(); - if (!m_boundElementArrayBuffer) + if (!elementArrayBuffer) return false; if (!count) { @@ -1336,7 +1354,7 @@ bool WebGLRenderingContext::validateIndexArrayPrecise(GC3Dsizei count, GC3Denum return true; } - if (!m_boundElementArrayBuffer->elementArrayBuffer()) + if (!elementArrayBuffer->elementArrayBuffer()) return false; unsigned long uoffset = offset; @@ -1345,14 +1363,14 @@ bool WebGLRenderingContext::validateIndexArrayPrecise(GC3Dsizei count, GC3Denum if (type == GraphicsContext3D::UNSIGNED_SHORT) { // Make uoffset an element offset. uoffset /= sizeof(GC3Dushort); - const GC3Dushort* p = static_cast<const GC3Dushort*>(m_boundElementArrayBuffer->elementArrayBuffer()->data()) + uoffset; + 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*>(m_boundElementArrayBuffer->elementArrayBuffer()->data()) + uoffset; + const GC3Dubyte* p = static_cast<const GC3Dubyte*>(elementArrayBuffer->elementArrayBuffer()->data()) + uoffset; while (n-- > 0) { if (*p > lastIndex) lastIndex = *p; @@ -1370,12 +1388,11 @@ bool WebGLRenderingContext::validateRenderingState(int numElementsRequired) if (!m_currentProgram) return false; - int numAttribStates = static_cast<int>(m_vertexAttribState.size()); - // Look in each enabled vertex attrib and check if they've been bound to a buffer. - for (int i = 0; i < numAttribStates; ++i) { - if (m_vertexAttribState[i].enabled - && (!m_vertexAttribState[i].bufferBinding || !m_vertexAttribState[i].bufferBinding->object())) + 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; } @@ -1387,8 +1404,8 @@ bool WebGLRenderingContext::validateRenderingState(int numElementsRequired) int numActiveAttribLocations = m_currentProgram->numActiveAttribLocations(); for (int i = 0; i < numActiveAttribLocations; ++i) { int loc = m_currentProgram->getActiveAttribLocation(i); - if (loc >=0 && loc < numAttribStates) { - const VertexAttribState& state = m_vertexAttribState[loc]; + 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 @@ -1502,7 +1519,7 @@ void WebGLRenderingContext::drawElements(GC3Denum mode, GC3Dsizei count, GC3Denu if (!count) return; - if (!m_boundElementArrayBuffer) { + if (!m_boundVertexArrayObject->getElementArrayBuffer()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } @@ -1568,10 +1585,8 @@ void WebGLRenderingContext::enableVertexAttribArray(GC3Duint index, ExceptionCod return; } - if (index >= m_vertexAttribState.size()) - m_vertexAttribState.resize(index + 1); - - m_vertexAttribState[index].enabled = true; + WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(index); + state.enabled = true; m_context->enableVertexAttribArray(index); cleanupAfterGraphicsCall(false); @@ -1845,6 +1860,14 @@ WebGLExtension* WebGLRenderingContext::getExtension(const String& name) } 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_lose_context")) { if (!m_webkitLoseContext) m_webkitLoseContext = WebKitLoseContext::create(this); @@ -1965,7 +1988,7 @@ WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& case GraphicsContext3D::DITHER: return getBooleanParameter(pname); case GraphicsContext3D::ELEMENT_ARRAY_BUFFER_BINDING: - return WebGLGetInfo(PassRefPtr<WebGLBuffer>(m_boundElementArrayBuffer)); + return WebGLGetInfo(PassRefPtr<WebGLBuffer>(m_boundVertexArrayObject->getElementArrayBuffer())); case GraphicsContext3D::FRAMEBUFFER_BINDING: return WebGLGetInfo(PassRefPtr<WebGLFramebuffer>(m_framebufferBinding)); case GraphicsContext3D::FRONT_FACE: @@ -2091,6 +2114,14 @@ WebGLGetInfo WebGLRenderingContext::getParameter(GC3Denum pname, ExceptionCode& return getUnsignedIntParameter(Extensions3D::FRAGMENT_SHADER_DERIVATIVE_HINT_OES); 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(); @@ -2252,6 +2283,8 @@ Vector<String> WebGLRenderingContext::getSupportedExtensions() 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_lose_context"); return result; } @@ -2447,40 +2480,26 @@ WebGLGetInfo WebGLRenderingContext::getVertexAttrib(GC3Duint index, GC3Denum pna 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_vertexAttribState[0].bufferBinding == m_vertexAttrib0Buffer) - || index >= m_vertexAttribState.size() - || !m_vertexAttribState[index].bufferBinding - || !m_vertexAttribState[index].bufferBinding->object()) + if ((!isGLES2Compliant() && !index && m_boundVertexArrayObject->getVertexAttribState(0).bufferBinding == m_vertexAttrib0Buffer) + || !state.bufferBinding + || !state.bufferBinding->object()) return WebGLGetInfo(); - return WebGLGetInfo(PassRefPtr<WebGLBuffer>(m_vertexAttribState[index].bufferBinding)); + return WebGLGetInfo(PassRefPtr<WebGLBuffer>(state.bufferBinding)); case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_ENABLED: - if (index >= m_vertexAttribState.size()) - return WebGLGetInfo(false); - return WebGLGetInfo(m_vertexAttribState[index].enabled); + return WebGLGetInfo(state.enabled); case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_NORMALIZED: - if (index >= m_vertexAttribState.size()) - return WebGLGetInfo(false); - return WebGLGetInfo(m_vertexAttribState[index].normalized); + return WebGLGetInfo(state.normalized); case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_SIZE: - if (index >= m_vertexAttribState.size()) - return WebGLGetInfo(static_cast<int>(4)); - return WebGLGetInfo(m_vertexAttribState[index].size); + return WebGLGetInfo(state.size); case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_STRIDE: - if (index >= m_vertexAttribState.size()) - return WebGLGetInfo(static_cast<int>(0)); - return WebGLGetInfo(m_vertexAttribState[index].originalStride); + return WebGLGetInfo(state.originalStride); case GraphicsContext3D::VERTEX_ATTRIB_ARRAY_TYPE: - if (index >= m_vertexAttribState.size()) - return WebGLGetInfo(static_cast<unsigned int>(GraphicsContext3D::FLOAT)); - return WebGLGetInfo(m_vertexAttribState[index].type); + return WebGLGetInfo(state.type); case GraphicsContext3D::CURRENT_VERTEX_ATTRIB: - if (index >= m_vertexAttribState.size()) { - float value[4] = { 0.0f, 0.0f, 0.0f, 1.0f }; - return WebGLGetInfo(Float32Array::create(value, 4)); - } - return WebGLGetInfo(Float32Array::create(m_vertexAttribState[index].value, 4)); + return WebGLGetInfo(Float32Array::create(m_vertexAttribValue[index].value, 4)); default: m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return WebGLGetInfo(); @@ -3718,19 +3737,17 @@ void WebGLRenderingContext::vertexAttribPointer(GC3Duint index, GC3Dint size, GC } GC3Dsizei bytesPerElement = size * typeSize; - if (index >= m_vertexAttribState.size()) - m_vertexAttribState.resize(index + 1); - GC3Dsizei validatedStride = stride ? stride : bytesPerElement; - m_vertexAttribState[index].bufferBinding = m_boundArrayBuffer; - m_vertexAttribState[index].bytesPerElement = bytesPerElement; - m_vertexAttribState[index].size = size; - m_vertexAttribState[index].type = type; - m_vertexAttribState[index].normalized = normalized; - m_vertexAttribState[index].stride = validatedStride; - m_vertexAttribState[index].originalStride = stride; - m_vertexAttribState[index].offset = offset; + 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); } @@ -4467,7 +4484,7 @@ WebGLBuffer* WebGLRenderingContext::validateBufferDataParameters(GC3Denum target WebGLBuffer* buffer = 0; switch (target) { case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: - buffer = m_boundElementArrayBuffer.get(); + buffer = m_boundVertexArrayObject->getElementArrayBuffer().get(); break; case GraphicsContext3D::ARRAY_BUFFER: buffer = m_boundArrayBuffer.get(); @@ -4516,12 +4533,11 @@ void WebGLRenderingContext::vertexAttribfImpl(GC3Duint index, GC3Dsizei expected } cleanupAfterGraphicsCall(false); } - if (index >= m_vertexAttribState.size()) - m_vertexAttribState.resize(index + 1); - m_vertexAttribState[index].value[0] = v0; - m_vertexAttribState[index].value[1] = v1; - m_vertexAttribState[index].value[2] = v2; - m_vertexAttribState[index].value[3] = v3; + 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) @@ -4569,21 +4585,21 @@ void WebGLRenderingContext::vertexAttribfvImpl(GC3Duint index, GC3Dfloat* v, GC3 } cleanupAfterGraphicsCall(false); } - if (index >= m_vertexAttribState.size()) - m_vertexAttribState.resize(index + 1); - m_vertexAttribState[index].initValue(); + VertexAttribValue& attribValue = m_vertexAttribValue[index]; + attribValue.initValue(); for (int ii = 0; ii < expectedSize; ++ii) - m_vertexAttribState[index].value[ii] = v[ii]; + attribValue.value[ii] = v[ii]; } void WebGLRenderingContext::initVertexAttrib0() { - m_vertexAttribState.resize(1); + 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); - m_vertexAttribState[0].bufferBinding = m_vertexAttrib0Buffer; + state.bufferBinding = m_vertexAttrib0Buffer; m_context->bindBuffer(GraphicsContext3D::ARRAY_BUFFER, 0); m_context->enableVertexAttribArray(0); m_vertexAttrib0BufferSize = 0; @@ -4597,7 +4613,8 @@ void WebGLRenderingContext::initVertexAttrib0() bool WebGLRenderingContext::simulateVertexAttrib0(GC3Dsizei numVertex) { - const VertexAttribState& state = m_vertexAttribState[0]; + const WebGLVertexArrayObjectOES::VertexAttribState& state = m_boundVertexArrayObject->getVertexAttribState(0); + const VertexAttribValue& attribValue = m_vertexAttribValue[0]; if (!m_currentProgram) return false; bool usingVertexAttrib0 = m_currentProgram->isUsingVertexAttrib0(); @@ -4617,21 +4634,21 @@ bool WebGLRenderingContext::simulateVertexAttrib0(GC3Dsizei numVertex) } if (usingVertexAttrib0 && (m_forceAttrib0BufferRefill - || state.value[0] != m_vertexAttrib0BufferValue[0] - || state.value[1] != m_vertexAttrib0BufferValue[1] - || state.value[2] != m_vertexAttrib0BufferValue[2] - || state.value[3] != m_vertexAttrib0BufferValue[3])) { + || 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] = state.value[0]; - bufferData[ii * 4 + 1] = state.value[1]; - bufferData[ii * 4 + 2] = state.value[2]; - bufferData[ii * 4 + 3] = state.value[3]; + 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] = state.value[0]; - m_vertexAttrib0BufferValue[1] = state.value[1]; - m_vertexAttrib0BufferValue[2] = state.value[2]; - m_vertexAttrib0BufferValue[3] = state.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()); } @@ -4641,7 +4658,7 @@ bool WebGLRenderingContext::simulateVertexAttrib0(GC3Dsizei numVertex) void WebGLRenderingContext::restoreStatesAfterVertexAttrib0Simulation() { - const VertexAttribState& state = m_vertexAttribState[0]; + 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); @@ -4651,11 +4668,16 @@ void WebGLRenderingContext::restoreStatesAfterVertexAttrib0Simulation() int WebGLRenderingContext::getNumberOfExtensions() { - return (m_oesStandardDerivatives ? 1 : 0) + (m_webkitLoseContext ? 1 : 0) + (m_oesTextureFloat ? 1 : 0); + return (m_oesVertexArrayObject ? 1 : 0) + (m_oesStandardDerivatives ? 1 : 0) + (m_webkitLoseContext ? 1 : 0) + (m_oesTextureFloat ? 1 : 0); } WebGLExtension* WebGLRenderingContext::getExtensionNumber(int i) { + if (m_oesVertexArrayObject) { + if (!i) + return m_oesVertexArrayObject.get(); + --i; + } if (m_oesStandardDerivatives) { if (!i) return m_oesStandardDerivatives.get(); diff --git a/Source/WebCore/html/canvas/WebGLRenderingContext.h b/Source/WebCore/html/canvas/WebGLRenderingContext.h index dd71620..13145c8 100644 --- a/Source/WebCore/html/canvas/WebGLRenderingContext.h +++ b/Source/WebCore/html/canvas/WebGLRenderingContext.h @@ -59,6 +59,8 @@ class ImageData; class IntSize; class OESStandardDerivatives; class OESTextureFloat; +class OESVertexArrayObject; +class WebGLVertexArrayObjectOES; class WebGLRenderingContext : public CanvasRenderingContext { public: @@ -286,6 +288,8 @@ public: virtual void paintRenderingResultsToCanvas(); void removeObject(WebGLObject*); + + unsigned getMaxVertexAttribs() const { return m_maxVertexAttribs; } // Helpers for JSC bindings. int getNumberOfExtensions(); @@ -293,6 +297,7 @@ public: private: friend class WebGLObject; + friend class OESVertexArrayObject; WebGLRenderingContext(HTMLCanvasElement*, PassRefPtr<GraphicsContext3D>, GraphicsContext3D::Attributes); void initializeNewContext(); @@ -362,24 +367,24 @@ public: // 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<WebGLBuffer> m_boundElementArrayBuffer; - - // Cached values for vertex attrib range checks - class VertexAttribState { + + 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: - VertexAttribState() - : enabled(false) - , bytesPerElement(0) - , size(4) - , type(GraphicsContext3D::FLOAT) - , normalized(false) - , stride(16) - , originalStride(0) - , offset(0) + VertexAttribValue() { initValue(); } - + void initValue() { value[0] = 0.0f; @@ -387,20 +392,10 @@ public: value[2] = 0.0f; value[3] = 1.0f; } - - bool enabled; - RefPtr<WebGLBuffer> bufferBinding; - GC3Dsizei bytesPerElement; - GC3Dint size; - GC3Denum type; - bool normalized; - GC3Dsizei stride; - GC3Dsizei originalStride; - GC3Dintptr offset; + GC3Dfloat value[4]; }; - - Vector<VertexAttribState> m_vertexAttribState; + Vector<VertexAttribValue> m_vertexAttribValue; unsigned m_maxVertexAttribs; RefPtr<WebGLBuffer> m_vertexAttrib0Buffer; long m_vertexAttrib0BufferSize; @@ -462,6 +457,7 @@ public: // Enabled extension objects. RefPtr<OESTextureFloat> m_oesTextureFloat; RefPtr<OESStandardDerivatives> m_oesStandardDerivatives; + RefPtr<OESVertexArrayObject> m_oesVertexArrayObject; RefPtr<WebKitLoseContext> m_webkitLoseContext; // Helpers for getParameter and others diff --git a/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.cpp b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.cpp new file mode 100644 index 0000000..d14c96c --- /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 0000000..f49a780 --- /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 0000000..8582b36 --- /dev/null +++ b/Source/WebCore/html/canvas/WebGLVertexArrayObjectOES.idl @@ -0,0 +1,29 @@ +/* + * 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/canvas/WebKitLoseContext.cpp b/Source/WebCore/html/canvas/WebKitLoseContext.cpp index 05e82f4..c594e32 100644 --- a/Source/WebCore/html/canvas/WebKitLoseContext.cpp +++ b/Source/WebCore/html/canvas/WebKitLoseContext.cpp @@ -55,7 +55,8 @@ PassRefPtr<WebKitLoseContext> WebKitLoseContext::create(WebGLRenderingContext* c void WebKitLoseContext::loseContext() { - m_context->forceLostContext(); + if (m_context) + m_context->forceLostContext(); } } // namespace WebCore diff --git a/Source/WebCore/html/canvas/WebKitLoseContext.h b/Source/WebCore/html/canvas/WebKitLoseContext.h index e25e084..b713bef 100644 --- a/Source/WebCore/html/canvas/WebKitLoseContext.h +++ b/Source/WebCore/html/canvas/WebKitLoseContext.h @@ -42,6 +42,7 @@ public: virtual ExtensionName getName() const; void loseContext(); + void contextDestroyed() { m_context = 0; } private: WebKitLoseContext(WebGLRenderingContext*); diff --git a/Source/WebCore/html/parser/HTMLConstructionSite.cpp b/Source/WebCore/html/parser/HTMLConstructionSite.cpp index a026ef9..2be6039 100644 --- a/Source/WebCore/html/parser/HTMLConstructionSite.cpp +++ b/Source/WebCore/html/parser/HTMLConstructionSite.cpp @@ -1,5 +1,6 @@ /* * 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 @@ -58,16 +59,16 @@ using namespace HTMLNames; namespace { -bool hasImpliedEndTag(Element* element) +bool hasImpliedEndTag(ContainerNode* node) { - return element->hasTagName(ddTag) - || element->hasTagName(dtTag) - || element->hasTagName(liTag) - || element->hasTagName(optionTag) - || element->hasTagName(optgroupTag) - || element->hasTagName(pTag) - || element->hasTagName(rpTag) - || element->hasTagName(rtTag); + 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) @@ -204,6 +205,12 @@ void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& 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()); } @@ -236,7 +243,7 @@ void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token) void HTMLConstructionSite::insertComment(AtomicHTMLToken& token) { ASSERT(token.type() == HTMLToken::Comment); - attach(currentElement(), Comment::create(currentElement()->document(), token.comment())); + attach(currentNode(), Comment::create(currentNode()->document(), token.comment())); } void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token) @@ -248,13 +255,13 @@ void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token) void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token) { ASSERT(token.type() == HTMLToken::Comment); - Element* parent = m_openElements.htmlElement(); + ContainerNode* parent = m_openElements.rootNode(); attach(parent, Comment::create(parent->document(), token.comment())); } PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child) { - return attach(currentElement(), child); + return attach(currentNode(), child); } void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token) @@ -310,7 +317,7 @@ void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token) void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token) { - RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, currentElement()->document(), true); + RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, currentNode()->document(), true); if (m_fragmentScriptingPermission == FragmentScriptingAllowed) element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); m_openElements.push(attachToCurrent(element.release())); @@ -329,27 +336,40 @@ void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const At void HTMLConstructionSite::insertTextNode(const String& characters) { AttachmentSite site; - site.parent = currentElement(); + site.parent = currentNode(); site.nextChild = 0; if (shouldFosterParent()) findFosterSite(site); + 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 = site.nextChild ? site.nextChild->previousSibling() : site.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); - textNode->parserAppendData(characters); - return; + currentPosition = textNode->parserAppendData(characters.characters(), characters.length(), Text::defaultLengthLimit); } - attachAtSite(site, Text::create(site.parent->document(), characters)); + while (currentPosition < characters.length()) { + RefPtr<Text> textNode = Text::createWithLengthLimit(site.parent->document(), 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()) + textNode = Text::create(site.parent->document(), characters.substring(currentPosition)); + + currentPosition += textNode->length(); + ASSERT(currentPosition <= characters.length()); + attachAtSite(site, textNode.release()); + } } PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI) { QualifiedName tagName(nullAtom, token.name(), namespaceURI); - RefPtr<Element> element = currentElement()->document()->createElement(tagName, true); + RefPtr<Element> element = currentNode()->document()->createElement(tagName, true); element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); return element.release(); } @@ -360,7 +380,7 @@ PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& tok // 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, currentElement()->document(), form(), true); + RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, currentNode()->document(), form(), true); element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); ASSERT(element->isHTMLElement()); return element.release(); @@ -439,13 +459,13 @@ void HTMLConstructionSite::reconstructTheActiveFormattingElements() void HTMLConstructionSite::generateImpliedEndTagsWithExclusion(const AtomicString& tagName) { - while (hasImpliedEndTag(currentElement()) && !currentElement()->hasLocalName(tagName)) + while (hasImpliedEndTag(currentNode()) && !currentNode()->hasLocalName(tagName)) m_openElements.pop(); } void HTMLConstructionSite::generateImpliedEndTags() { - while (hasImpliedEndTag(currentElement())) + while (hasImpliedEndTag(currentNode())) m_openElements.pop(); } @@ -464,13 +484,14 @@ void HTMLConstructionSite::findFosterSite(AttachmentSite& site) return; } // Fragment case - site.parent = m_openElements.bottom(); // <html> element + site.parent = m_openElements.rootNode(); // DocumentFragment site.nextChild = 0; } bool HTMLConstructionSite::shouldFosterParent() const { return m_redirectAttachToFosterParent + && currentNode()->isElementNode() && causesFosterParenting(currentElement()->tagQName()); } diff --git a/Source/WebCore/html/parser/HTMLConstructionSite.h b/Source/WebCore/html/parser/HTMLConstructionSite.h index 0298503..380e487 100644 --- a/Source/WebCore/html/parser/HTMLConstructionSite.h +++ b/Source/WebCore/html/parser/HTMLConstructionSite.h @@ -1,5 +1,6 @@ /* * 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 @@ -80,6 +81,7 @@ public: void generateImpliedEndTagsWithExclusion(const AtomicString& tagName); 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; } diff --git a/Source/WebCore/html/parser/HTMLDocumentParser.cpp b/Source/WebCore/html/parser/HTMLDocumentParser.cpp index 2fe9486..843df45 100644 --- a/Source/WebCore/html/parser/HTMLDocumentParser.cpp +++ b/Source/WebCore/html/parser/HTMLDocumentParser.cpp @@ -86,7 +86,7 @@ HTMLDocumentParser::HTMLDocumentParser(HTMLDocument* document, bool reportErrors , m_parserScheduler(HTMLParserScheduler::create(this)) , m_xssFilter(this) , m_endWasDelayed(false) - , m_writeNestingLevel(0) + , m_pumpSessionNestingLevel(0) { } @@ -98,7 +98,7 @@ HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* cont , m_treeBuilder(HTMLTreeBuilder::create(this, fragment, contextElement, scriptingPermission, usePreHTML5ParserQuirks(fragment->document()))) , m_xssFilter(this) , m_endWasDelayed(false) - , m_writeNestingLevel(0) + , m_pumpSessionNestingLevel(0) { bool reportErrors = false; // For now document fragment parsing never reports errors. m_tokenizer->setState(tokenizerStateForContextElement(contextElement, reportErrors)); @@ -107,7 +107,7 @@ HTMLDocumentParser::HTMLDocumentParser(DocumentFragment* fragment, Element* cont HTMLDocumentParser::~HTMLDocumentParser() { ASSERT(!m_parserScheduler); - ASSERT(!m_writeNestingLevel); + ASSERT(!m_pumpSessionNestingLevel); ASSERT(!m_preloadScanner); } @@ -155,9 +155,14 @@ void HTMLDocumentParser::prepareToStopParsing() attemptToRunDeferredScriptsAndEnd(); } +bool HTMLDocumentParser::isParsingFragment() const +{ + return m_treeBuilder->isParsingFragment(); +} + bool HTMLDocumentParser::processingData() const { - return isScheduledForResume() || inWrite(); + return isScheduledForResume() || inPumpSession(); } void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) @@ -204,6 +209,36 @@ bool HTMLDocumentParser::runScriptsForPausedTreeBuilder() 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 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()); @@ -212,6 +247,8 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) // 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 @@ -219,53 +256,35 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) // much we parsed as part of didWriteHTML instead of willWriteHTML. InspectorInstrumentationCookie cookie = InspectorInstrumentation::willWriteHTML(document(), m_input.current().length(), m_tokenizer->lineNumber()); - HTMLParserScheduler::PumpSession session; - // FIXME: This loop body has is now too long and needs cleanup. - while (mode == ForceSynchronous || m_parserScheduler->shouldContinueParsing(session)) { - // 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 (!m_treeBuilder->isParsingFragment() - && document()->frame() && document()->frame()->navigationScheduler()->locationChangePending()) - break; + while (canTakeNextToken(mode, session) && !session.needsYield) { + if (!isParsingFragment()) + m_sourceTracker.start(m_input, m_token); - m_sourceTracker.start(m_input, m_token); if (!m_tokenizer->nextToken(m_input.current(), m_token)) break; - m_sourceTracker.end(m_input, m_token); - m_xssFilter.filterToken(m_token); + if (!isParsingFragment()) { + m_sourceTracker.end(m_input, m_token); + + // We do not XSS filter innerHTML, which means we (intentionally) fail + // http/tests/security/xssAuditor/dom-write-innerHTML.html + m_xssFilter.filterToken(m_token); + } m_treeBuilder->constructTreeFromToken(m_token); m_token.clear(); - - // JavaScript may have stopped or detached the parser. - if (isStopped()) - return; - - // The parser will pause itself when waiting on a script to load or run. - if (!m_treeBuilder->isPaused()) - continue; - - // If we're paused waiting for a script, we try to execute scripts before continuing. - bool shouldContinueParsing = runScriptsForPausedTreeBuilder(); - m_treeBuilder->setPaused(!shouldContinueParsing); - - // JavaScript may have stopped or detached the parser. - if (isStopped()) - return; - - if (!shouldContinueParsing) - break; } // 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() == HTMLTokenizer::DataState); if (!m_preloadScanner) { @@ -301,14 +320,10 @@ void HTMLDocumentParser::insert(const SegmentedString& source) // but we need to ensure it isn't deleted yet. RefPtr<HTMLDocumentParser> protect(this); - { - NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel); - - SegmentedString excludedLineNumberSource(source); - excludedLineNumberSource.setExcludeLineNumbers(); - m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); - pumpTokenizerIfPossible(ForceSynchronous); - } + SegmentedString excludedLineNumberSource(source); + excludedLineNumberSource.setExcludeLineNumbers(); + m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); + pumpTokenizerIfPossible(ForceSynchronous); endIfDelayed(); } @@ -322,13 +337,11 @@ void HTMLDocumentParser::append(const SegmentedString& source) // but we need to ensure it isn't deleted yet. RefPtr<HTMLDocumentParser> protect(this); - { - NestingLevelIncrementer nestingLevelIncrementer(m_writeNestingLevel); - - m_input.appendToEnd(source); - if (m_preloadScanner) - m_preloadScanner->appendToEnd(source); + m_input.appendToEnd(source); + if (m_preloadScanner) + m_preloadScanner->appendToEnd(source); +<<<<<<< HEAD if (m_writeNestingLevel > 1) { // 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 @@ -340,8 +353,17 @@ void HTMLDocumentParser::append(const SegmentedString& source) } pumpTokenizerIfPossible(AllowYield); +======= + 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; +>>>>>>> WebKit at r80534 } + pumpTokenizerIfPossible(AllowYield); + endIfDelayed(); #ifdef ANDROID_INSTRUMENT android::TimeCounter::record(android::TimeCounter::ParsingTimeCounter, __FUNCTION__); diff --git a/Source/WebCore/html/parser/HTMLDocumentParser.h b/Source/WebCore/html/parser/HTMLDocumentParser.h index be2ca1b..4bc33e4 100644 --- a/Source/WebCore/html/parser/HTMLDocumentParser.h +++ b/Source/WebCore/html/parser/HTMLDocumentParser.h @@ -51,6 +51,8 @@ class HTMLPreloadScanner; class ScriptController; class ScriptSourceCode; +class PumpSession; + class HTMLDocumentParser : public ScriptableDocumentParser, HTMLScriptRunnerHost, CachedResourceClient { WTF_MAKE_FAST_ALLOCATED; public: @@ -116,6 +118,7 @@ private: AllowYield, ForceSynchronous, }; + bool canTakeNextToken(SynchronousMode, PumpSession&); void pumpTokenizer(SynchronousMode); void pumpTokenizerIfPossible(SynchronousMode); @@ -128,10 +131,11 @@ private: void attemptToRunDeferredScriptsAndEnd(); void end(); + bool isParsingFragment() const; bool isScheduledForResume() const; bool inScriptExecution() const; - bool inWrite() const { return m_writeNestingLevel > 0; } - bool shouldDelayEnd() const { return inWrite() || isWaitingForScripts() || inScriptExecution() || isScheduledForResume(); } + bool inPumpSession() const { return m_pumpSessionNestingLevel > 0; } + bool shouldDelayEnd() const { return inPumpSession() || isWaitingForScripts() || inScriptExecution() || isScheduledForResume(); } ScriptController* script() const; @@ -149,7 +153,7 @@ private: XSSFilter m_xssFilter; bool m_endWasDelayed; - unsigned m_writeNestingLevel; + unsigned m_pumpSessionNestingLevel; }; } diff --git a/Source/WebCore/html/parser/HTMLElementStack.cpp b/Source/WebCore/html/parser/HTMLElementStack.cpp index 6aab0f7..6f5f9ed 100644 --- a/Source/WebCore/html/parser/HTMLElementStack.cpp +++ b/Source/WebCore/html/parser/HTMLElementStack.cpp @@ -1,5 +1,6 @@ /* * 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 @@ -26,6 +27,7 @@ #include "config.h" #include "HTMLElementStack.h" +#include "DocumentFragment.h" #include "Element.h" #include "HTMLNames.h" #include "MathMLNames.h" @@ -38,62 +40,68 @@ using namespace HTMLNames; namespace { -inline bool isNumberedHeaderElement(Element* element) +inline bool isNumberedHeaderElement(ContainerNode* node) { - return element->hasTagName(h1Tag) - || element->hasTagName(h2Tag) - || element->hasTagName(h3Tag) - || element->hasTagName(h4Tag) - || element->hasTagName(h5Tag) - || element->hasTagName(h6Tag); + return node->hasTagName(h1Tag) + || node->hasTagName(h2Tag) + || node->hasTagName(h3Tag) + || node->hasTagName(h4Tag) + || node->hasTagName(h5Tag) + || node->hasTagName(h6Tag); +} + +inline bool isRootMarker(ContainerNode* node) +{ + return node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE + || node->hasTagName(htmlTag); } -inline bool isScopeMarker(Element* element) +inline bool isScopeMarker(ContainerNode* node) { - return element->hasTagName(appletTag) - || element->hasTagName(captionTag) - || element->hasTagName(htmlTag) - || element->hasTagName(marqueeTag) - || element->hasTagName(objectTag) - || element->hasTagName(tableTag) - || element->hasTagName(tdTag) - || element->hasTagName(thTag) - || element->hasTagName(MathMLNames::miTag) - || element->hasTagName(MathMLNames::moTag) - || element->hasTagName(MathMLNames::mnTag) - || element->hasTagName(MathMLNames::msTag) - || element->hasTagName(MathMLNames::mtextTag) - || element->hasTagName(MathMLNames::annotation_xmlTag) - || element->hasTagName(SVGNames::foreignObjectTag) - || element->hasTagName(SVGNames::descTag) - || element->hasTagName(SVGNames::titleTag); + 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) + || isRootMarker(node); } -inline bool isListItemScopeMarker(Element* element) +inline bool isListItemScopeMarker(ContainerNode* node) { - return isScopeMarker(element) - || element->hasTagName(olTag) - || element->hasTagName(ulTag); + return isScopeMarker(node) + || node->hasTagName(olTag) + || node->hasTagName(ulTag); } -inline bool isTableScopeMarker(Element* element) +inline bool isTableScopeMarker(ContainerNode* node) { - return element->hasTagName(tableTag) - || element->hasTagName(htmlTag); + return node->hasTagName(tableTag) + || isRootMarker(node); } -inline bool isTableBodyScopeMarker(Element* element) +inline bool isTableBodyScopeMarker(ContainerNode* node) { - return element->hasTagName(tbodyTag) - || element->hasTagName(tfootTag) - || element->hasTagName(theadTag) - || element->hasTagName(htmlTag); + return node->hasTagName(tbodyTag) + || node->hasTagName(tfootTag) + || node->hasTagName(theadTag) + || isRootMarker(node); } -inline bool isTableRowScopeMarker(Element* element) +inline bool isTableRowScopeMarker(ContainerNode* node) { - return element->hasTagName(trTag) - || element->hasTagName(htmlTag); + return node->hasTagName(trTag) + || isRootMarker(node); } inline bool isForeignContentScopeMarker(Element* element) @@ -109,25 +117,25 @@ inline bool isForeignContentScopeMarker(Element* element) || element->namespaceURI() == HTMLNames::xhtmlNamespaceURI; } -inline bool isButtonScopeMarker(Element* element) +inline bool isButtonScopeMarker(ContainerNode* node) { - return isScopeMarker(element) - || element->hasTagName(buttonTag); + return isScopeMarker(node) + || node->hasTagName(buttonTag); } -inline bool isSelectScopeMarker(Element* element) +inline bool isSelectScopeMarker(ContainerNode* node) { - return !element->hasTagName(optgroupTag) - && !element->hasTagName(optionTag); + return !node->hasTagName(optgroupTag) + && !node->hasTagName(optionTag); } } -HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<Element> element, PassOwnPtr<ElementRecord> next) - : m_element(element) +HTMLElementStack::ElementRecord::ElementRecord(PassRefPtr<ContainerNode> node, PassOwnPtr<ElementRecord> next) + : m_node(node) , m_next(next) { - ASSERT(m_element); + ASSERT(m_node); } HTMLElementStack::ElementRecord::~ElementRecord() @@ -137,8 +145,9 @@ HTMLElementStack::ElementRecord::~ElementRecord() void HTMLElementStack::ElementRecord::replaceElement(PassRefPtr<Element> element) { ASSERT(element); + ASSERT(!m_node || m_node->isElementNode()); // FIXME: Should this call finishParsingChildren? - m_element = element; + m_node = element; } bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const @@ -151,7 +160,7 @@ bool HTMLElementStack::ElementRecord::isAbove(ElementRecord* other) const } HTMLElementStack::HTMLElementStack() - : m_htmlElement(0) + : m_rootNode(0) , m_headElement(0) , m_bodyElement(0) { @@ -171,7 +180,7 @@ 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_htmlElement); + 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. @@ -194,11 +203,11 @@ void HTMLElementStack::popHTMLBodyElement() void HTMLElementStack::popAll() { - m_htmlElement = 0; + m_rootNode = 0; m_headElement = 0; m_bodyElement = 0; while (m_top) { - top()->finishParsingChildren(); + topNode()->finishParsingChildren(); m_top = m_top->releaseNext(); } } @@ -226,7 +235,7 @@ void HTMLElementStack::popUntilPopped(const AtomicString& tagName) void HTMLElementStack::popUntilNumberedHeaderElementPopped() { - while (!isNumberedHeaderElement(top())) + while (!isNumberedHeaderElement(topNode())) pop(); pop(); } @@ -246,21 +255,21 @@ void HTMLElementStack::popUntilPopped(Element* element) 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(top())) + 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(top())) + 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(top())) + while (!isTableRowScopeMarker(topNode())) pop(); } @@ -269,14 +278,25 @@ void HTMLElementStack::popUntilForeignContentScopeMarker() while (!isForeignContentScopeMarker(top())) pop(); } + +void HTMLElementStack::pushRootNode(PassRefPtr<ContainerNode> rootNode) +{ + ASSERT(rootNode->nodeType() == Node::DOCUMENT_FRAGMENT_NODE); + pushRootNodeCommon(rootNode); +} void HTMLElementStack::pushHTMLHtmlElement(PassRefPtr<Element> element) { - ASSERT(!m_top); // <html> should always be the bottom of the stack. ASSERT(element->hasTagName(HTMLNames::htmlTag)); - ASSERT(!m_htmlElement); - m_htmlElement = element.get(); - pushCommon(element); + 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) @@ -300,7 +320,7 @@ void HTMLElementStack::push(PassRefPtr<Element> element) ASSERT(!element->hasTagName(HTMLNames::htmlTag)); ASSERT(!element->hasTagName(HTMLNames::headTag)); ASSERT(!element->hasTagName(HTMLNames::bodyTag)); - ASSERT(m_htmlElement); + ASSERT(m_rootNode); pushCommon(element); } @@ -312,7 +332,7 @@ void HTMLElementStack::insertAbove(PassRefPtr<Element> element, ElementRecord* r ASSERT(!element->hasTagName(HTMLNames::htmlTag)); ASSERT(!element->hasTagName(HTMLNames::headTag)); ASSERT(!element->hasTagName(HTMLNames::bodyTag)); - ASSERT(m_htmlElement); + ASSERT(m_rootNode); if (recordBelow == m_top) { push(element); return; @@ -372,7 +392,7 @@ void HTMLElementStack::remove(Element* element) HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const { for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) { - if (pos->element() == element) + if (pos->node() == element) return pos; } return 0; @@ -381,7 +401,7 @@ HTMLElementStack::ElementRecord* HTMLElementStack::find(Element* element) const HTMLElementStack::ElementRecord* HTMLElementStack::topmost(const AtomicString& tagName) const { for (ElementRecord* pos = m_top.get(); pos; pos = pos->next()) { - if (pos->element()->hasLocalName(tagName)) + if (pos->node()->hasLocalName(tagName)) return pos; } return 0; @@ -397,14 +417,14 @@ bool HTMLElementStack::contains(const AtomicString& tagName) const return !!topmost(tagName); } -template <bool isMarker(Element*)> +template <bool isMarker(ContainerNode*)> bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag) { for (HTMLElementStack::ElementRecord* pos = top; pos; pos = pos->next()) { - Element* element = pos->element(); - if (element->hasLocalName(targetTag)) + ContainerNode* node = pos->node(); + if (node->hasLocalName(targetTag)) return true; - if (isMarker(element)) + if (isMarker(node)) return false; } ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker. @@ -427,10 +447,10 @@ bool HTMLElementStack::hasOnlyHTMLElementsInScope() const bool HTMLElementStack::hasNumberedHeaderElementInScope() const { for (ElementRecord* record = m_top.get(); record; record = record->next()) { - Element* element = record->element(); - if (isNumberedHeaderElement(element)) + ContainerNode* node = record->node(); + if (isNumberedHeaderElement(node)) return true; - if (isScopeMarker(element)) + if (isScopeMarker(node)) return false; } ASSERT_NOT_REACHED(); // <html> is always on the stack and is a scope marker. @@ -507,8 +527,8 @@ bool HTMLElementStack::inSelectScope(const QualifiedName& tagName) const Element* HTMLElementStack::htmlElement() const { - ASSERT(m_htmlElement); - return m_htmlElement; + ASSERT(m_rootNode); + return toElement(m_rootNode); } Element* HTMLElementStack::headElement() const @@ -522,12 +542,18 @@ Element* HTMLElementStack::bodyElement() const ASSERT(m_bodyElement); return m_bodyElement; } + +ContainerNode* HTMLElementStack::rootNode() const +{ + ASSERT(m_rootNode); + return m_rootNode; +} -void HTMLElementStack::pushCommon(PassRefPtr<Element> element) +void HTMLElementStack::pushCommon(PassRefPtr<ContainerNode> node) { - ASSERT(m_htmlElement); - m_top = adoptPtr(new ElementRecord(element, m_top.release())); - top()->beginParsingChildren(); + ASSERT(m_rootNode); + m_top = adoptPtr(new ElementRecord(node, m_top.release())); + topNode()->beginParsingChildren(); } void HTMLElementStack::popCommon() diff --git a/Source/WebCore/html/parser/HTMLElementStack.h b/Source/WebCore/html/parser/HTMLElementStack.h index ad8b941..a710932 100644 --- a/Source/WebCore/html/parser/HTMLElementStack.h +++ b/Source/WebCore/html/parser/HTMLElementStack.h @@ -1,5 +1,6 @@ /* * 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 @@ -26,6 +27,7 @@ #ifndef HTMLElementStack_h #define HTMLElementStack_h +#include "Element.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> @@ -34,6 +36,8 @@ namespace WebCore { +class ContainerNode; +class DocumentFragment; class Element; class QualifiedName; @@ -50,7 +54,8 @@ public: public: ~ElementRecord(); // Public for ~PassOwnPtr() - Element* element() const { return m_element.get(); } + Element* element() const { return toElement(m_node.get()); } + ContainerNode* node() const { return m_node.get(); } void replaceElement(PassRefPtr<Element>); bool isAbove(ElementRecord*) const; @@ -60,12 +65,12 @@ public: private: friend class HTMLElementStack; - ElementRecord(PassRefPtr<Element>, PassOwnPtr<ElementRecord>); + ElementRecord(PassRefPtr<ContainerNode>, PassOwnPtr<ElementRecord>); PassOwnPtr<ElementRecord> releaseNext() { return m_next.release(); } void setNext(PassOwnPtr<ElementRecord> next) { m_next = next; } - RefPtr<Element> m_element; + RefPtr<ContainerNode> m_node; OwnPtr<ElementRecord> m_next; }; @@ -76,6 +81,12 @@ public: 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; @@ -86,6 +97,7 @@ public: 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>); @@ -131,24 +143,27 @@ public: Element* htmlElement() const; Element* headElement() const; Element* bodyElement() const; + + ContainerNode* rootNode() const; #ifndef NDEBUG void show(); #endif private: - void pushCommon(PassRefPtr<Element>); + void pushCommon(PassRefPtr<ContainerNode>); + void pushRootNodeCommon(PassRefPtr<ContainerNode>); void popCommon(); void removeNonTopCommon(Element*); OwnPtr<ElementRecord> m_top; - // We remember <html>, <head> and <body> as they are pushed. Their - // ElementRecords keep them alive. <html> is never popped. + // 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. - Element* m_htmlElement; + ContainerNode* m_rootNode; Element* m_headElement; Element* m_bodyElement; }; diff --git a/Source/WebCore/html/parser/HTMLParserScheduler.cpp b/Source/WebCore/html/parser/HTMLParserScheduler.cpp index 56db1aa..c4525c8 100644 --- a/Source/WebCore/html/parser/HTMLParserScheduler.cpp +++ b/Source/WebCore/html/parser/HTMLParserScheduler.cpp @@ -74,25 +74,24 @@ HTMLParserScheduler::~HTMLParserScheduler() m_continueNextChunkTimer.stop(); } -// FIXME: This belongs on Document. -static bool isLayoutTimerActive(Document* doc) -{ - ASSERT(doc); - return doc->view() && doc->view()->layoutPending() && !doc->minimumLayoutDelay(); -} - 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 (isLayoutTimerActive(m_parser->document())) { + if (m_parser->document()->isLayoutTimerActive()) { m_continueNextChunkTimer.startOneShot(0); return; } m_parser->resumeParsingAfterYield(); } +void HTMLParserScheduler::scheduleForResume() +{ + m_continueNextChunkTimer.startOneShot(0); +} + + void HTMLParserScheduler::suspend() { ASSERT(!m_isSuspendedWithActiveTimer); diff --git a/Source/WebCore/html/parser/HTMLParserScheduler.h b/Source/WebCore/html/parser/HTMLParserScheduler.h index c415c62..9aa12eb 100644 --- a/Source/WebCore/html/parser/HTMLParserScheduler.h +++ b/Source/WebCore/html/parser/HTMLParserScheduler.h @@ -26,6 +26,7 @@ #ifndef HTMLParserScheduler_h #define HTMLParserScheduler_h +#include "NestingLevelIncrementer.h" #include "Timer.h" #include <wtf/CurrentTime.h> #include <wtf/PassOwnPtr.h> @@ -34,6 +35,21 @@ namespace WebCore { class HTMLDocumentParser; +class PumpSession : public NestingLevelIncrementer { +public: + PumpSession(unsigned& nestingLevel) + : NestingLevelIncrementer(nestingLevel) + , processedTokens(0) + , startTime(currentTime()) + , needsYield(false) + { + } + + int processedTokens; + double startTime; + bool needsYield; +}; + class HTMLParserScheduler { WTF_MAKE_NONCOPYABLE(HTMLParserScheduler); WTF_MAKE_FAST_ALLOCATED; public: @@ -43,34 +59,19 @@ public: } ~HTMLParserScheduler(); - struct PumpSession { - PumpSession() - : processedTokens(0) - , startTime(currentTime()) - { - } - - int processedTokens; - double startTime; - }; - // Inline as this is called after every token in the parser. - bool shouldContinueParsing(PumpSession& session) + void checkForYieldBeforeToken(PumpSession& session) { if (session.processedTokens > m_parserChunkSize) { session.processedTokens = 0; double elapsedTime = currentTime() - session.startTime; - if (elapsedTime > m_parserTimeLimit) { - // Schedule the parser to continue and yield from the parser. - m_continueNextChunkTimer.startOneShot(0); - return false; - } + if (elapsedTime > m_parserTimeLimit) + session.needsYield = true; } - ++session.processedTokens; - return true; } + void scheduleForResume(); bool isScheduledForResume() const { return m_isSuspendedWithActiveTimer || m_continueNextChunkTimer.isActive(); } void suspend(); diff --git a/Source/WebCore/html/parser/HTMLScriptRunner.cpp b/Source/WebCore/html/parser/HTMLScriptRunner.cpp index c99858d..99fff5e 100644 --- a/Source/WebCore/html/parser/HTMLScriptRunner.cpp +++ b/Source/WebCore/html/parser/HTMLScriptRunner.cpp @@ -262,17 +262,9 @@ void HTMLScriptRunner::requestDeferredScript(Element* element) bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const { ASSERT(!pendingScript.element()); - const AtomicString& srcValue = script->getAttribute(srcAttr); - // Allow the host to disllow script loads (using the XSSAuditor, etc.) - // FIXME: this check should be performed on the final URL in a redirect chain. - if (!m_host->shouldLoadExternalScriptFromSrc(srcValue)) - return false; - // FIXME: We need to resolve the url relative to the element. - if (!script->dispatchBeforeLoadEvent(srcValue)) - return false; pendingScript.setElement(script); // This should correctly return 0 for empty or invalid srcValues. - CachedScript* cachedScript = m_document->cachedResourceLoader()->requestScript(srcValue, toScriptElement(script)->scriptCharset()); + CachedScript* cachedScript = toScriptElement(script)->cachedScript().get(); if (!cachedScript) { notImplemented(); // Dispatch error event. return false; @@ -293,29 +285,24 @@ void HTMLScriptRunner::runScript(Element* script, const TextPosition1& scriptSta ScriptElement* scriptElement = toScriptElement(script); ASSERT(scriptElement); - if (!scriptElement->shouldExecuteAsJavaScript()) + + scriptElement->prepareScript(scriptStartPosition); + + if (!scriptElement->willBeParserExecuted()) return; - - if (script->hasAttribute(srcAttr)) { - if (script->hasAttribute(asyncAttr)) // Async takes precendence over defer. - return; // Asynchronous scripts handle themselves. - - if (script->hasAttribute(deferAttr)) - requestDeferredScript(script); - else - requestParsingBlockingScript(script); - } else if (!m_document->haveStylesheetsLoaded() && m_scriptNestingLevel == 1) { - // Block inline script execution on stylesheet load, unless we are in document.write(). - // The latter case can only happen if a script both triggers a stylesheet load - // and writes an inline script. Since write is blocking we have to execute the - // written script immediately, ignoring the pending sheets. - m_parsingBlockingScript.setElement(script); - m_parsingBlockingScript.setStartingPosition(scriptStartPosition); - } else { - ASSERT(isExecutingScript()); - ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition); - scriptElement->executeScript(sourceCode); - } + + 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/HTMLTreeBuilder.cpp b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp index d2931ac..8f9e3e1 100644 --- a/Source/WebCore/html/parser/HTMLTreeBuilder.cpp +++ b/Source/WebCore/html/parser/HTMLTreeBuilder.cpp @@ -1,5 +1,6 @@ /* * 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 @@ -122,6 +123,8 @@ bool isSpecialNode(Node* node) || node->hasTagName(SVGNames::descTag) || node->hasTagName(SVGNames::titleTag)) return true; + if (node->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) + return true; if (node->namespaceURI() != xhtmlNamespaceURI) return false; const AtomicString& tagName = node->localName(); @@ -375,7 +378,9 @@ HTMLTreeBuilder::HTMLTreeBuilder(HTMLDocumentParser* parser, DocumentFragment* f 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 - processFakeStartTag(htmlTag); + // 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)); } @@ -410,19 +415,6 @@ HTMLTreeBuilder::FragmentParsingContext::FragmentParsingContext(DocumentFragment ASSERT(!fragment->hasChildNodes()); } -void HTMLTreeBuilder::FragmentParsingContext::finished() -{ - if (!m_contextElement) - return; - - // The HTML5 spec says to return the children of the fragment's document - // element when there is a context element (10.4.7). - RefPtr<ContainerNode> documentElement = firstElementChild(m_fragment); - m_fragment->removeChildren(); - ASSERT(documentElement); - m_fragment->takeAllChildrenFrom(documentElement.get()); -} - HTMLTreeBuilder::FragmentParsingContext::~FragmentParsingContext() { } @@ -453,7 +445,7 @@ void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token) // the U+0000 characters into replacement characters has compatibility // problems. m_parser->tokenizer()->setForceNullCharacterReplacement(m_insertionMode == TextMode || m_insertionMode == InForeignContentMode); - m_parser->tokenizer()->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI); + m_parser->tokenizer()->setShouldAllowCDATA(m_insertionMode == InForeignContentMode && m_tree.currentNode()->namespaceURI() != xhtmlNamespaceURI); } void HTMLTreeBuilder::processToken(AtomicHTMLToken& token) @@ -574,12 +566,12 @@ void HTMLTreeBuilder::processIsindexStartTagForInBody(AtomicHTMLToken& token) namespace { -bool isLi(const Element* element) +bool isLi(const ContainerNode* element) { return element->hasTagName(liTag); } -bool isDdOrDt(const Element* element) +bool isDdOrDt(const ContainerNode* element) { return element->hasTagName(ddTag) || element->hasTagName(dtTag); @@ -587,15 +579,16 @@ bool isDdOrDt(const Element* element) } -template <bool shouldClose(const Element*)> +template <bool shouldClose(const ContainerNode*)> void HTMLTreeBuilder::processCloseWhenNestedTag(AtomicHTMLToken& token) { m_framesetOk = false; HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); while (1) { - Element* node = nodeRecord->element(); + ContainerNode* node = nodeRecord->node(); if (shouldClose(node)) { - processFakeEndTag(node->tagQName()); + ASSERT(node->isElementNode()); + processFakeEndTag(toElement(node)->tagQName()); break; } if (isSpecialNode(node) && !node->hasTagName(addressTag) && !node->hasTagName(divTag) && !node->hasTagName(pTag)) @@ -786,7 +779,7 @@ void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) } if (isNumberedHeaderTag(token.name())) { processFakePEndTagIfPInButtonScope(); - if (isNumberedHeaderTag(m_tree.currentElement()->localName())) { + if (isNumberedHeaderTag(m_tree.currentNode()->localName())) { parseError(token); m_tree.openElements()->pop(); } @@ -978,7 +971,7 @@ void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) if (token.name() == rpTag || token.name() == rtTag) { if (m_tree.openElements()->inScope(rubyTag.localName())) { m_tree.generateImpliedEndTags(); - if (!m_tree.currentElement()->hasTagName(rubyTag)) { + if (!m_tree.currentNode()->hasTagName(rubyTag)) { parseError(token); m_tree.openElements()->popUntil(rubyTag.localName()); } @@ -1019,7 +1012,7 @@ void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup() { - if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { + if (m_tree.currentNode() == m_tree.openElements()->rootNode()) { ASSERT(isParsingFragment()); // FIXME: parse error return false; @@ -1115,7 +1108,7 @@ void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token) namespace { -bool shouldProcessForeignContentUsingInBodyInsertionMode(AtomicHTMLToken& token, Element* currentElement) +bool shouldProcessForeignContentUsingInBodyInsertionMode(AtomicHTMLToken& token, ContainerNode* currentElement) { ASSERT(token.type() == HTMLToken::StartTag); if (currentElement->hasTagName(MathMLNames::miTag) @@ -1409,7 +1402,7 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) return; } if (token.name() == optionTag) { - if (m_tree.currentElement()->hasTagName(optionTag)) { + if (m_tree.currentNode()->hasTagName(optionTag)) { AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); processEndTag(endOption); } @@ -1417,11 +1410,11 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) return; } if (token.name() == optgroupTag) { - if (m_tree.currentElement()->hasTagName(optionTag)) { + if (m_tree.currentNode()->hasTagName(optionTag)) { AtomicHTMLToken endOption(HTMLToken::EndTag, optionTag.localName()); processEndTag(endOption); } - if (m_tree.currentElement()->hasTagName(optgroupTag)) { + if (m_tree.currentNode()->hasTagName(optgroupTag)) { AtomicHTMLToken endOptgroup(HTMLToken::EndTag, optgroupTag.localName()); processEndTag(endOptgroup); } @@ -1543,20 +1536,24 @@ void HTMLTreeBuilder::processAnyOtherEndTagForInBody(AtomicHTMLToken& token) ASSERT(token.type() == HTMLToken::EndTag); HTMLElementStack::ElementRecord* record = m_tree.openElements()->topRecord(); while (1) { - Element* node = record->element(); + ContainerNode* node = record->node(); if (node->hasLocalName(token.name())) { m_tree.generateImpliedEndTags(); - if (!m_tree.currentElement()->hasLocalName(token.name())) { + // 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(node)) + if (!m_tree.openElements()->contains(toElement(node))) return; } - m_tree.openElements()->popUntilPopped(node); + m_tree.openElements()->popUntilPopped(toElement(node)); return; } if (isSpecialNode(node)) { @@ -1616,7 +1613,7 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) } // 4. ASSERT(furthestBlock->isAbove(formattingElementRecord)); - Element* commonAncestor = formattingElementRecord->next()->element(); + ContainerNode* commonAncestor = formattingElementRecord->next()->node(); // 5. HTMLFormattingElementList::Bookmark bookmark = m_tree.activeFormattingElements()->bookmarkFor(formattingElement); // 6. @@ -1668,7 +1665,9 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) m_tree.fosterParent(lastNode->element()); else { commonAncestor->parserAddChild(lastNode->element()); - if (lastNode->element()->parentElement()->attached() && !lastNode->element()->attached()) + ASSERT(lastNode->node()->isElementNode()); + ASSERT(lastNode->element()->parentNode()); + if (lastNode->element()->parentNode()->attached() && !lastNode->element()->attached()) lastNode->element()->lazyAttach(); } // 8 @@ -1700,8 +1699,8 @@ void HTMLTreeBuilder::resetInsertionModeAppropriately() bool last = false; HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); while (1) { - Element* node = nodeRecord->element(); - if (node == m_tree.openElements()->bottom()) { + ContainerNode* node = nodeRecord->node(); + if (node == m_tree.openElements()->rootNode()) { ASSERT(isParsingFragment()); last = true; node = m_fragmentContext.contextElement(); @@ -1831,7 +1830,7 @@ void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTags(); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilPopped(token.name()); m_tree.activeFormattingElements()->clearToLastMarker(); @@ -1901,7 +1900,7 @@ void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTags(); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilPopped(token.name()); return; @@ -1926,7 +1925,7 @@ void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTagsWithExclusion(token.name()); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilPopped(token.name()); return; @@ -1937,7 +1936,7 @@ void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTagsWithExclusion(token.name()); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilPopped(token.name()); return; @@ -1949,7 +1948,7 @@ void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTagsWithExclusion(token.name()); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilPopped(token.name()); return; @@ -1960,7 +1959,7 @@ void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTags(); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilNumberedHeaderElementPopped(); return; @@ -1977,7 +1976,7 @@ void HTMLTreeBuilder::processEndTagForInBody(AtomicHTMLToken& token) return; } m_tree.generateImpliedEndTags(); - if (!m_tree.currentElement()->hasLocalName(token.name())) + if (!m_tree.currentNode()->hasLocalName(token.name())) parseError(token); m_tree.openElements()->popUntilPopped(token.name()); m_tree.activeFormattingElements()->clearToLastMarker(); @@ -2221,7 +2220,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) case InFramesetMode: ASSERT(insertionMode() == InFramesetMode); if (token.name() == framesetTag) { - if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { + if (m_tree.currentNode() == m_tree.openElements()->rootNode()) { parseError(token); return; } @@ -2261,9 +2260,9 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) case InSelectMode: ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode); if (token.name() == optgroupTag) { - if (m_tree.currentElement()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag)) + if (m_tree.currentNode()->hasTagName(optionTag) && m_tree.oneBelowTop()->hasTagName(optgroupTag)) processFakeEndTag(optionTag); - if (m_tree.currentElement()->hasTagName(optgroupTag)) { + if (m_tree.currentNode()->hasTagName(optgroupTag)) { m_tree.openElements()->pop(); return; } @@ -2271,7 +2270,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) return; } if (token.name() == optionTag) { - if (m_tree.currentElement()->hasTagName(optionTag)) { + if (m_tree.currentNode()->hasTagName(optionTag)) { m_tree.openElements()->pop(); return; } @@ -2294,23 +2293,29 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) processEndTag(token); break; case InForeignContentMode: - if (token.name() == SVGNames::scriptTag && m_tree.currentElement()->hasTagName(SVGNames::scriptTag)) { + if (token.name() == SVGNames::scriptTag && m_tree.currentNode()->hasTagName(SVGNames::scriptTag)) { notImplemented(); return; } - if (m_tree.currentElement()->namespaceURI() != xhtmlNamespaceURI) { + if (m_tree.currentNode()->namespaceURI() != xhtmlNamespaceURI) { // FIXME: This code just wants an Element* iterator, instead of an ElementRecord* HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); - if (!nodeRecord->element()->hasLocalName(token.name())) + if (!nodeRecord->node()->hasLocalName(token.name())) parseError(token); while (1) { - if (nodeRecord->element()->hasLocalName(token.name())) { + if (nodeRecord->node()->hasLocalName(token.name())) { m_tree.openElements()->popUntilPopped(nodeRecord->element()); resetForeignInsertionMode(); return; } nodeRecord = nodeRecord->next(); - if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI) + + if (nodeRecord->node()->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) { + ASSERT(isParsingFragment()); + break; + } + + if (nodeRecord->node()->namespaceURI() == xhtmlNamespaceURI) break; } } @@ -2609,11 +2614,11 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) case InSelectInTableMode: case InSelectMode: ASSERT(insertionMode() == InSelectMode || insertionMode() == InSelectInTableMode || insertionMode() == InTableMode || insertionMode() == InFramesetMode || insertionMode() == InTableBodyMode); - if (m_tree.currentElement() != m_tree.openElements()->htmlElement()) + if (m_tree.currentNode() != m_tree.openElements()->rootNode()) parseError(token); break; case InColumnGroupMode: - if (m_tree.currentElement() == m_tree.openElements()->htmlElement()) { + if (m_tree.currentNode() == m_tree.openElements()->rootNode()) { ASSERT(isParsingFragment()); return; // FIXME: Should we break here instead of returning? } @@ -2634,7 +2639,7 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) return; case TextMode: parseError(token); - if (m_tree.currentElement()->hasTagName(scriptTag)) + if (m_tree.currentNode()->hasTagName(scriptTag)) notImplemented(); // mark the script element as "already started". m_tree.openElements()->pop(); setInsertionMode(m_originalInsertionMode); @@ -2642,7 +2647,7 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) processEndOfFile(token); return; } - ASSERT(m_tree.openElements()->top()); + ASSERT(m_tree.currentNode()); m_tree.openElements()->popAll(); } @@ -2794,28 +2799,16 @@ void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token) void HTMLTreeBuilder::finished() { - ASSERT(m_document); - if (isParsingFragment()) { - m_fragmentContext.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&) { - DEFINE_STATIC_LOCAL(String, parseErrorMessage, ("HTML parse error (recovered gracefully)")); - - if (!m_reportErrors) - return; - - DOMWindow* domWindow = m_document->domWindow(); - if (!domWindow) - return; - - domWindow->console()->addMessage(HTMLMessageSource, LogMessageType, WarningMessageLevel, parseErrorMessage, m_parser->lineNumber(), m_document->url().string()); } bool HTMLTreeBuilder::scriptEnabled(Frame* frame) diff --git a/Source/WebCore/html/parser/HTMLTreeBuilder.h b/Source/WebCore/html/parser/HTMLTreeBuilder.h index 0cec667..5bdc44b 100644 --- a/Source/WebCore/html/parser/HTMLTreeBuilder.h +++ b/Source/WebCore/html/parser/HTMLTreeBuilder.h @@ -1,5 +1,6 @@ /* * 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 @@ -178,7 +179,7 @@ private: void closeTheCell(); - template <bool shouldClose(const Element*)> + template <bool shouldClose(const ContainerNode*)> void processCloseWhenNestedTag(AtomicHTMLToken&); bool m_framesetOk; @@ -215,8 +216,6 @@ private: Element* contextElement() const { ASSERT(m_fragment); return m_contextElement; } FragmentScriptingPermission scriptingPermission() const { ASSERT(m_fragment); return m_scriptingPermission; } - void finished(); - private: DocumentFragment* m_fragment; Element* m_contextElement; diff --git a/Source/WebCore/html/parser/NestingLevelIncrementer.h b/Source/WebCore/html/parser/NestingLevelIncrementer.h index 8155635..bf08425 100644 --- a/Source/WebCore/html/parser/NestingLevelIncrementer.h +++ b/Source/WebCore/html/parser/NestingLevelIncrementer.h @@ -26,6 +26,8 @@ #ifndef NestingLevelIncrementer_h #define NestingLevelIncrementer_h +#include <wtf/Noncopyable.h> + namespace WebCore { class NestingLevelIncrementer { diff --git a/Source/WebCore/html/parser/XSSFilter.cpp b/Source/WebCore/html/parser/XSSFilter.cpp index de31f76..ddc3318 100644 --- a/Source/WebCore/html/parser/XSSFilter.cpp +++ b/Source/WebCore/html/parser/XSSFilter.cpp @@ -245,10 +245,14 @@ bool XSSFilter::filterTokenInitial(HTMLToken& token) 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; } @@ -351,6 +355,15 @@ bool XSSFilter::filterAppletToken(HTMLToken& token) return didBlockScript; } +bool XSSFilter::filterIframeToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLToken::StartTag); + ASSERT(hasName(token, iframeTag)); + + return eraseAttributeIfInjected(token, srcAttr); +} + bool XSSFilter::filterMetaToken(HTMLToken& token) { ASSERT(m_state == Initial); @@ -369,6 +382,15 @@ bool XSSFilter::filterBaseToken(HTMLToken& token) return eraseAttributeIfInjected(token, hrefAttr); } +bool XSSFilter::filterFormToken(HTMLToken& token) +{ + ASSERT(m_state == Initial); + ASSERT(token.type() == HTMLToken::StartTag); + ASSERT(hasName(token, formTag)); + + return eraseAttributeIfInjected(token, actionAttr); +} + bool XSSFilter::eraseDangerousAttributesIfInjected(HTMLToken& token) { DEFINE_STATIC_LOCAL(String, safeJavaScriptURL, ("javascript:void(0)")); diff --git a/Source/WebCore/html/parser/XSSFilter.h b/Source/WebCore/html/parser/XSSFilter.h index 2c7d428..c9ba12e 100644 --- a/Source/WebCore/html/parser/XSSFilter.h +++ b/Source/WebCore/html/parser/XSSFilter.h @@ -58,8 +58,10 @@ private: 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()); diff --git a/Source/WebCore/html/parser/create-html-entity-table b/Source/WebCore/html/parser/create-html-entity-table index e6132bc..92fb39c 100755 --- a/Source/WebCore/html/parser/create-html-entity-table +++ b/Source/WebCore/html/parser/create-html-entity-table @@ -58,7 +58,8 @@ def offset_table_entry(offset): program_name = os.path.basename(__file__) if len(sys.argv) < 4 or sys.argv[1] != "-o": - print >> sys.stderr, "Usage: %s -o OUTPUT_FILE INPUT_FILE" % program_name + # 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] @@ -68,12 +69,12 @@ html_entity_names_file = open(input_path) entries = list(csv.reader(html_entity_names_file)) html_entity_names_file.close() -entries.sort(lambda a, b: cmp(a[ENTITY], b[ENTITY])) +entries.sort(key = lambda entry: entry[ENTITY]) entity_count = len(entries) output_file = open(output_path, "w") -print >> output_file, """/* +output_file.write("""/* * Copyright (C) 2010 Google, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without @@ -107,15 +108,15 @@ print >> output_file, """/* namespace WebCore { namespace { -""" +""") for entry in entries: - print >> output_file, "const UChar %sEntityName[] = %s;" % ( + output_file.write("const UChar %sEntityName[] = %s;" % ( convert_entity_to_cpp_name(entry[ENTITY]), - convert_entity_to_uchar_array(entry[ENTITY])) + convert_entity_to_uchar_array(entry[ENTITY]))) -print >> output_file, """ -HTMLEntityTableEntry staticEntityTable[%s] = {""" % entity_count +output_file.write(""" +HTMLEntityTableEntry staticEntityTable[%s] = {""" % entity_count) index = {} offset = 0 @@ -123,26 +124,26 @@ for entry in entries: letter = entry[ENTITY][0] if not index.get(letter): index[letter] = offset - print >> output_file, ' { %sEntityName, %s, %s },' % ( + output_file.write(' { %sEntityName, %s, %s },' % ( convert_entity_to_cpp_name(entry[ENTITY]), len(entry[ENTITY]), - convert_value_to_int(entry[VALUE])) + convert_value_to_int(entry[VALUE]))) offset += 1 -print >> output_file, """}; -""" +output_file.write("""}; +""") -print >> output_file, "const HTMLEntityTableEntry* uppercaseOffset[] = {" -for letter in string.uppercase: - print >> output_file, offset_table_entry(index[letter]) -print >> output_file, offset_table_entry(index['a']) -print >> output_file, """}; +output_file.write("const HTMLEntityTableEntry* uppercaseOffset[] = {") +for letter in string.ascii_uppercase: + output_file.write(offset_table_entry(index[letter])) +output_file.write(offset_table_entry(index['a'])) +output_file.write("""}; -const HTMLEntityTableEntry* lowercaseOffset[] = {""" -for letter in string.lowercase: - print >> output_file, offset_table_entry(index[letter]) -print >> output_file, offset_table_entry(entity_count) -print >> output_file, """}; +const HTMLEntityTableEntry* lowercaseOffset[] = {""") +for letter in string.ascii_lowercase: + output_file.write(offset_table_entry(index[letter])) +output_file.write(offset_table_entry(entity_count)) +output_file.write("""}; } @@ -175,4 +176,4 @@ const HTMLEntityTableEntry* HTMLEntityTable::lastEntry() } } -""" % entity_count +""" % entity_count) diff --git a/Source/WebCore/html/shadow/MediaControls.cpp b/Source/WebCore/html/shadow/MediaControls.cpp index a374e49..61a3684 100644 --- a/Source/WebCore/html/shadow/MediaControls.cpp +++ b/Source/WebCore/html/shadow/MediaControls.cpp @@ -254,7 +254,7 @@ void MediaControls::createPanel() void MediaControls::createMuteButton() { ASSERT(!m_muteButton); - m_muteButton = MediaControlMuteButtonElement::create(m_mediaElement); + m_muteButton = MediaControlPanelMuteButtonElement::create(m_mediaElement); m_muteButton->attachToParent(m_panel.get()); } @@ -482,7 +482,7 @@ void MediaControls::updateVolumeSliderContainer(bool visible) RefPtr<RenderStyle> s = m_volumeSliderContainer->styleForElement(); int height = s->height().isPercent() ? 0 : s->height().value(); int width = s->width().isPercent() ? 0 : s->width().value(); - IntPoint offset = m_mediaElement->document()->page()->theme()->volumeSliderOffsetFromMuteButton(m_muteButton->renderer()->node(), IntSize(width, height)); + IntPoint offset = m_mediaElement->document()->page()->theme()->volumeSliderOffsetFromMuteButton(m_muteButton->renderBox(), IntSize(width, height)); int x = offset.x() + m_muteButton->renderBox()->offsetLeft(); int y = offset.y() + m_muteButton->renderBox()->offsetTop(); diff --git a/Source/WebCore/html/shadow/TextControlInnerElements.cpp b/Source/WebCore/html/shadow/TextControlInnerElements.cpp new file mode 100644 index 0000000..968c5e4 --- /dev/null +++ b/Source/WebCore/html/shadow/TextControlInnerElements.cpp @@ -0,0 +1,503 @@ +/* + * 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 "ScrollbarTheme.h" +#include "SpeechInput.h" +#include "SpeechInputEvent.h" + +namespace WebCore { + +using namespace HTMLNames; + +TextControlInnerElement::TextControlInnerElement(Document* document, HTMLElement* shadowParent) + : HTMLDivElement(divTag, document) +{ + setShadowHost(shadowParent); +} + +PassRefPtr<TextControlInnerElement> TextControlInnerElement::create(HTMLElement* shadowParent) +{ + return adoptRef(new TextControlInnerElement(shadowParent->document(), shadowParent)); +} + +void TextControlInnerElement::attachInnerElement(Node* parent, PassRefPtr<RenderStyle> style, RenderArena* arena) +{ + // When adding these elements, create the renderer & style first before adding to the DOM. + // Otherwise, the render tree will create some anonymous blocks that will mess up our layout. + + // Create the renderer with the specified style + RenderObject* renderer = createRenderer(arena, style.get()); + if (renderer) { + setRenderer(renderer); + renderer->setStyle(style); + } + + // Set these explicitly since this normally happens during an attach() + setAttached(); + setInDocument(); + + // For elements not yet in shadow DOM, add the node to the DOM normally. + if (!isShadowRoot()) { + // FIXME: This code seems very wrong. Why are we magically adding |this| to the DOM here? + // We shouldn't be calling parser API methods outside of the parser! + parent->deprecatedParserAddChild(this); + } + + // Add the renderer to the render tree + if (renderer) + parent->renderer()->addChild(renderer); +} + +void TextControlInnerElement::detach() +{ + HTMLDivElement::detach(); + // FIXME: Remove once shadow DOM uses Element::setShadowRoot(). + if (shadowHost()) + setShadowHost(0); +} + +// ---------------------------- + +inline TextControlInnerTextElement::TextControlInnerTextElement(Document* document, HTMLElement* shadowParent) + : TextControlInnerElement(document, shadowParent) +{ +} + +PassRefPtr<TextControlInnerTextElement> TextControlInnerTextElement::create(Document* document, HTMLElement* shadowParent) +{ + return adoptRef(new TextControlInnerTextElement(document, shadowParent)); +} + +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); +} + +// ---------------------------- + +inline SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* document) + : TextControlInnerElement(document) +{ +} + +PassRefPtr<SearchFieldResultsButtonElement> SearchFieldResultsButtonElement::create(Document* document) +{ + return adoptRef(new SearchFieldResultsButtonElement(document)); +} + +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) + : TextControlInnerElement(document) + , m_capturing(false) +{ +} + +PassRefPtr<SearchFieldCancelButtonElement> SearchFieldCancelButtonElement::create(Document* document) +{ + return adoptRef(new SearchFieldCancelButtonElement(document)); +} + +void SearchFieldCancelButtonElement::detach() +{ + if (m_capturing) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + TextControlInnerElement::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 (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(HTMLElement* shadowParent) + : TextControlInnerElement(shadowParent->document(), shadowParent) + , m_capturing(false) + , m_upDownState(Indeterminate) + , m_pressStartingState(Indeterminate) + , m_repeatingTimer(this, &SpinButtonElement::repeatingTimerFired) +{ +} + +PassRefPtr<SpinButtonElement> SpinButtonElement::create(HTMLElement* shadowParent) +{ + return adoptRef(new SpinButtonElement(shadowParent)); +} + +void SpinButtonElement::detach() +{ + stopRepeatingTimer(); + if (m_capturing) { + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_capturing = false; + } + } + TextControlInnerElement::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()) { + 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 { + if (m_capturing) { + stopRepeatingTimer(); + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_capturing = false; + } + } + } + } + + if (!event->defaultHandled()) + HTMLDivElement::defaultEventHandler(event); +} + +void SpinButtonElement::startRepeatingTimer() +{ + m_pressStartingState = m_upDownState; + ScrollbarTheme* theme = ScrollbarTheme::nativeTheme(); + m_repeatingTimer.start(theme->initialAutoscrollTimerDelay(), theme->autoscrollTimerDelay()); +} + +void SpinButtonElement::stopRepeatingTimer() +{ + m_repeatingTimer.stop(); +} + +void SpinButtonElement::repeatingTimerFired(Timer<SpinButtonElement>*) +{ + 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(m_upDownState == Up ? 1 : -1); +} + +void SpinButtonElement::setHovered(bool flag) +{ + if (!hovered() && flag) + m_upDownState = Indeterminate; + TextControlInnerElement::setHovered(flag); +} + + +// ---------------------------- + +#if ENABLE(INPUT_SPEECH) + +inline InputFieldSpeechButtonElement::InputFieldSpeechButtonElement(HTMLElement* shadowParent) + : TextControlInnerElement(shadowParent->document(), shadowParent) + , m_capturing(false) + , m_state(Idle) + , m_listenerId(document()->page()->speechInput()->registerListener(this)) +{ +} + +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(HTMLElement* shadowParent) +{ + return adoptRef(new InputFieldSpeechButtonElement(shadowParent)); +} + +void InputFieldSpeechButtonElement::defaultEventHandler(Event* event) +{ + // For privacy reasons, only allow clicks directly coming from the user. + if (!event->fromUserGesture()) { + 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())); + + // 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) { + switch (m_state) { + case Idle: { + AtomicString language = input->computeInheritedLanguage(); + String grammar = input->getAttribute(webkitgrammarAttr); + IntRect rect = input->renderer()->absoluteBoundingBoxRect(); + if (speechInput()->startRecognition(m_listenerId, rect, language, grammar, document()->securityOrigin())) + setState(Recording); + } + break; + case Recording: + speechInput()->stopRecording(m_listenerId); + 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())); + RefPtr<InputFieldSpeechButtonElement> holdRefButton(this); + input->setValue(results.isEmpty() ? "" : results[0]->utterance()); + 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::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; + } + + TextControlInnerElement::detach(); +} + +#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 0000000..4ba7857 --- /dev/null +++ b/Source/WebCore/html/shadow/TextControlInnerElements.h @@ -0,0 +1,158 @@ +/* + * 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(HTMLElement* shadowParent); + virtual void detach(); + + void attachInnerElement(Node*, PassRefPtr<RenderStyle>, RenderArena*); + +protected: + TextControlInnerElement(Document*, HTMLElement* shadowParent = 0); + +private: + virtual bool isMouseFocusable() const { return false; } +}; + +class TextControlInnerTextElement : public TextControlInnerElement { +public: + static PassRefPtr<TextControlInnerTextElement> create(Document*, HTMLElement* shadowParent); + + virtual void defaultEventHandler(Event*); + +private: + TextControlInnerTextElement(Document*, HTMLElement* shadowParent); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); +}; + +class SearchFieldResultsButtonElement : public TextControlInnerElement { +public: + static PassRefPtr<SearchFieldResultsButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + SearchFieldResultsButtonElement(Document*); +}; + +class SearchFieldCancelButtonElement : public TextControlInnerElement { +public: + static PassRefPtr<SearchFieldCancelButtonElement> create(Document*); + + virtual void defaultEventHandler(Event*); + +private: + SearchFieldCancelButtonElement(Document*); + + virtual void detach(); + + bool m_capturing; +}; + +class SpinButtonElement : public TextControlInnerElement { +public: + enum UpDownState { + Indeterminate, // Hovered, but the event is not handled. + Down, + Up, + }; + + static PassRefPtr<SpinButtonElement> create(HTMLElement*); + UpDownState upDownState() const { return m_upDownState; } + +private: + SpinButtonElement(HTMLElement*); + + virtual void detach(); + virtual bool isSpinButtonElement() const { return true; } + // FIXME: shadowAncestorNode() should be const. + virtual bool isEnabledFormControl() const { return static_cast<Element*>(const_cast<SpinButtonElement*>(this)->shadowAncestorNode())->isEnabledFormControl(); } + virtual bool isReadOnlyFormControl() const { return static_cast<Element*>(const_cast<SpinButtonElement*>(this)->shadowAncestorNode())->isReadOnlyFormControl(); } + virtual void defaultEventHandler(Event*); + void startRepeatingTimer(); + void stopRepeatingTimer(); + void repeatingTimerFired(Timer<SpinButtonElement>*); + virtual void setHovered(bool = true); + + bool m_capturing; + UpDownState m_upDownState; + UpDownState m_pressStartingState; + Timer<SpinButtonElement> m_repeatingTimer; +}; + +#if ENABLE(INPUT_SPEECH) + +class InputFieldSpeechButtonElement + : public TextControlInnerElement, + public SpeechInputListener { +public: + enum SpeechInputState { + Idle, + Recording, + Recognizing, + }; + + static PassRefPtr<InputFieldSpeechButtonElement> create(HTMLElement*); + virtual ~InputFieldSpeechButtonElement(); + + virtual void detach(); + virtual void defaultEventHandler(Event*); + SpeechInputState state() const { return m_state; } + + // SpeechInputListener methods. + void didCompleteRecording(int); + void didCompleteRecognition(int); + void setRecognitionResult(int, const SpeechInputResultArray&); + +private: + InputFieldSpeechButtonElement(HTMLElement*); + SpeechInput* speechInput(); + void setState(SpeechInputState state); + + bool m_capturing; + SpeechInputState m_state; + int m_listenerId; + SpeechInputResultArray m_results; +}; + +#endif // ENABLE(INPUT_SPEECH) + +} // namespace + +#endif |