diff options
Diffstat (limited to 'WebCore/html')
53 files changed, 1215 insertions, 492 deletions
diff --git a/WebCore/html/Blob.cpp b/WebCore/html/Blob.cpp index baf3d49..0680d82 100644 --- a/WebCore/html/Blob.cpp +++ b/WebCore/html/Blob.cpp @@ -43,6 +43,11 @@ Blob::Blob(const String& type, const BlobItemList& items) m_items.append(items[i]); } +Blob::Blob(const PassRefPtr<BlobItem>& item) +{ + m_items.append(item); +} + Blob::Blob(const String& path) { // Note: this doesn't initialize the type unlike File(path). diff --git a/WebCore/html/Blob.h b/WebCore/html/Blob.h index ad6c7a6..f5526c7 100644 --- a/WebCore/html/Blob.h +++ b/WebCore/html/Blob.h @@ -71,6 +71,7 @@ public: protected: Blob(const String& type, const BlobItemList&); + Blob(const PassRefPtr<BlobItem>&); // FIXME: Deprecated constructor. See also the comment for Blob::create(path). Blob(const String& path); diff --git a/WebCore/html/BlobBuilder.cpp b/WebCore/html/BlobBuilder.cpp index cda1568..b2684fc 100644 --- a/WebCore/html/BlobBuilder.cpp +++ b/WebCore/html/BlobBuilder.cpp @@ -35,35 +35,34 @@ #include "AtomicString.h" #include "Blob.h" #include "ExceptionCode.h" +#include "LineEnding.h" #include "TextEncoding.h" namespace WebCore { -static bool getLineEndingTypeFromString(const AtomicString& typeString, LineEnding& endingType) +static CString convertToCString(const String& text, const String& endingType, ExceptionCode& ec) { DEFINE_STATIC_LOCAL(AtomicString, transparent, ("transparent")); DEFINE_STATIC_LOCAL(AtomicString, native, ("native")); - if (typeString.isEmpty() || typeString == transparent) { - endingType = EndingTransparent; - return true; - } - if (typeString == native) { - endingType = EndingNative; - return true; - } - return false; + ec = 0; + + if (endingType.isEmpty() || endingType == transparent) + return UTF8Encoding().encode(text.characters(), text.length(), EntitiesForUnencodables); + if (endingType == native) + return normalizeLineEndingsToNative(UTF8Encoding().encode(text.characters(), text.length(), EntitiesForUnencodables)); + + ec = SYNTAX_ERR; + return CString(); } -bool BlobBuilder::append(const String& text, const String& type, ExceptionCode& ec) +bool BlobBuilder::append(const String& text, const String& endingType, ExceptionCode& ec) { - ec = 0; - LineEnding endingType; - if (!getLineEndingTypeFromString(type, endingType)) { - ec = SYNTAX_ERR; + CString cstr = convertToCString(text, endingType, ec); + if (ec) return false; - } - m_items.append(StringBlobItem::create(text, endingType, UTF8Encoding())); + + m_items.append(StringBlobItem::create(cstr)); return true; } diff --git a/WebCore/html/File.cpp b/WebCore/html/File.cpp index 2c9ce53..4000dcb 100644 --- a/WebCore/html/File.cpp +++ b/WebCore/html/File.cpp @@ -34,6 +34,19 @@ namespace WebCore { File::File(const String& path) : Blob(path) { + Init(); +} + +#if ENABLE(DIRECTORY_UPLOAD) +File::File(const String& relativePath, const String& filePath) + : Blob(FileBlobItem::create(filePath, relativePath)) +{ + Init(); +} +#endif + +void File::Init() +{ // We don't use MIMETypeRegistry::getMIMETypeForPath() because it returns "application/octet-stream" upon failure. const String& fileName = name(); int index = fileName.reverseFind('.'); @@ -46,4 +59,11 @@ const String& File::name() const return items().at(0)->toFileBlobItem()->name(); } +#if ENABLE(DIRECTORY_UPLOAD) +const String& File::webkitRelativePath() const +{ + return items().at(0)->toFileBlobItem()->relativePath(); +} +#endif + } // namespace WebCore diff --git a/WebCore/html/File.h b/WebCore/html/File.h index 582db28..d685472 100644 --- a/WebCore/html/File.h +++ b/WebCore/html/File.h @@ -39,9 +39,20 @@ public: return adoptRef(new File(path)); } +#if ENABLE(DIRECTORY_UPLOAD) + static PassRefPtr<File> create(const String& relativePath, const String& path) + { + return adoptRef(new File(relativePath, path)); + } +#endif + virtual bool isFile() const { return true; } const String& name() const; +#if ENABLE(DIRECTORY_UPLOAD) + // Returns the relative path of this file in the context of a directory selection. + const String& webkitRelativePath() const; +#endif // FIXME: obsolete attributes. To be removed. const String& fileName() const { return name(); } @@ -49,6 +60,11 @@ public: private: File(const String& path); + void Init(); + +#if ENABLE(DIRECTORY_UPLOAD) + File(const String& relativePath, const String& path); +#endif }; } // namespace WebCore diff --git a/WebCore/html/File.idl b/WebCore/html/File.idl index 2632a4d..5626c8e 100644 --- a/WebCore/html/File.idl +++ b/WebCore/html/File.idl @@ -30,6 +30,9 @@ module html { GenerateToJS ] File : Blob { readonly attribute DOMString name; +#if defined(ENABLE_DIRECTORY_UPLOAD) && ENABLE_DIRECTORY_UPLOAD + readonly attribute DOMString webkitRelativePath; +#endif // FIXME: obsolete attributes. To be removed. readonly attribute DOMString fileName; diff --git a/WebCore/html/FileError.idl b/WebCore/html/FileError.idl index adc25e8..555d103 100644 --- a/WebCore/html/FileError.idl +++ b/WebCore/html/FileError.idl @@ -30,7 +30,8 @@ module html { interface [ - Conditional=FILE_READER|FILE_WRITER + Conditional=FILE_READER|FILE_WRITER, + DontCheckEnums ] FileError { #if !defined(LANGUAGE_OBJECTIVE_C) const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; diff --git a/WebCore/html/FileReader.idl b/WebCore/html/FileReader.idl index b933f51..ad0e187 100644 --- a/WebCore/html/FileReader.idl +++ b/WebCore/html/FileReader.idl @@ -34,7 +34,8 @@ module html { CanBeConstructed, CallWith=ScriptExecutionContext, EventTarget, - NoStaticTables + NoStaticTables, + DontCheckEnums ] FileReader { // ready states const unsigned short EMPTY = 0; diff --git a/WebCore/html/FormDataList.cpp b/WebCore/html/FormDataList.cpp index 0f62595..94d0031 100644 --- a/WebCore/html/FormDataList.cpp +++ b/WebCore/html/FormDataList.cpp @@ -21,6 +21,8 @@ #include "config.h" #include "FormDataList.h" +#include "LineEnding.h" + namespace WebCore { FormDataList::FormDataList(const TextEncoding& c) @@ -30,7 +32,8 @@ FormDataList::FormDataList(const TextEncoding& c) void FormDataList::appendString(const String& s) { - m_items.append(StringBlobItem::create(s, EndingCRLF, m_encoding)); + CString cstr = m_encoding.encode(s.characters(), s.length(), EntitiesForUnencodables); + m_items.append(StringBlobItem::create(normalizeLineEndingsToCRLF(cstr))); } void FormDataList::appendString(const CString& s) diff --git a/WebCore/html/HTMLAttributeNames.in b/WebCore/html/HTMLAttributeNames.in index 36770e2..b4cdb02 100644 --- a/WebCore/html/HTMLAttributeNames.in +++ b/WebCore/html/HTMLAttributeNames.in @@ -281,5 +281,6 @@ version viewsource vlink vspace +webkitdirectory width wrap diff --git a/WebCore/html/HTMLCanvasElement.cpp b/WebCore/html/HTMLCanvasElement.cpp index a14cbef..6b5a3a3 100644 --- a/WebCore/html/HTMLCanvasElement.cpp +++ b/WebCore/html/HTMLCanvasElement.cpp @@ -66,6 +66,9 @@ static const int DefaultHeight = 150; // in exchange for a smaller maximum canvas size. static const float MaxCanvasArea = 32768 * 8192; // Maximum canvas area in CSS pixels +//In Skia, we will also limit width/height to 32767. +static const float MaxSkiaDim = 32767.0F; // Maximum width/height in CSS pixels. + HTMLCanvasElement::HTMLCanvasElement(const QualifiedName& tagName, Document* document) : HTMLElement(tagName, document) , m_observer(0) @@ -275,7 +278,7 @@ void HTMLCanvasElement::paint(GraphicsContext* context, const IntRect& r) if (hasCreatedImageBuffer()) { ImageBuffer* imageBuffer = buffer(); if (imageBuffer) { - Image* image = imageBuffer->image(); + Image* image = imageBuffer->imageForRendering(); if (image) context->drawImage(image, DeviceColorSpace, r); } @@ -294,6 +297,16 @@ bool HTMLCanvasElement::is3D() const } #endif +void HTMLCanvasElement::makeRenderingResultsAvailable() +{ +#if ENABLE(3D_CANVAS) + if (is3D()) { + WebGLRenderingContext* context3d = reinterpret_cast<WebGLRenderingContext*>(renderingContext()); + context3d->paintRenderingResultsToCanvas(); + } +#endif +} + void HTMLCanvasElement::recalcStyle(StyleChange change) { HTMLElement::recalcStyle(change); @@ -324,6 +337,8 @@ String HTMLCanvasElement::toDataURL(const String& mimeType, const double* qualit String lowercaseMimeType = mimeType.lower(); + makeRenderingResultsAvailable(); + // FIXME: Make isSupportedImageMIMETypeForEncoding threadsafe (to allow this method to be used on a worker thread). if (mimeType.isNull() || !MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(lowercaseMimeType)) return buffer()->toDataURL("image/png"); @@ -344,6 +359,11 @@ IntSize HTMLCanvasElement::convertLogicalToDevice(const FloatSize& logicalSize) if (!(wf >= 1 && hf >= 1 && wf * hf <= MaxCanvasArea)) return IntSize(); +#if PLATFORM(SKIA) + if (wf > MaxSkiaDim || hf > MaxSkiaDim) + return IntSize(); +#endif + return IntSize(static_cast<unsigned>(wf), static_cast<unsigned>(hf)); } diff --git a/WebCore/html/HTMLCanvasElement.h b/WebCore/html/HTMLCanvasElement.h index d399d5f..ef5d289 100644 --- a/WebCore/html/HTMLCanvasElement.h +++ b/WebCore/html/HTMLCanvasElement.h @@ -32,10 +32,6 @@ #include "HTMLElement.h" #include "IntSize.h" -#if ENABLE(3D_CANVAS) -#include "GraphicsContext3D.h" -#endif - namespace WebCore { class CanvasContextAttributes; @@ -114,6 +110,7 @@ public: bool is3D() const; #endif +<<<<<<< HEAD:WebCore/html/HTMLCanvasElement.h #ifdef ANDROID_INSTRUMENT void* operator new(size_t size) { return HTMLElement::operator new(size); @@ -129,6 +126,9 @@ public: HTMLElement::operator delete[](p, size); } #endif +======= + void makeRenderingResultsAvailable(); +>>>>>>> webkit.org at r63859:WebCore/html/HTMLCanvasElement.h private: HTMLCanvasElement(const QualifiedName&, Document*); diff --git a/WebCore/html/HTMLConstructionSite.cpp b/WebCore/html/HTMLConstructionSite.cpp index 5745625..e9bb762 100644 --- a/WebCore/html/HTMLConstructionSite.cpp +++ b/WebCore/html/HTMLConstructionSite.cpp @@ -32,6 +32,8 @@ #include "Element.h" #include "Frame.h" #include "HTMLDocument.h" +#include "HTMLElementFactory.h" +#include "HTMLFormElement.h" #include "HTMLHtmlElement.h" #include "HTMLNames.h" #include "HTMLScriptElement.h" @@ -70,6 +72,15 @@ bool hasImpliedEndTag(Element* element) || element->hasTagName(rtTag); } +bool causesFosterParenting(const QualifiedName& tagName) +{ + return tagName == tableTag + || tagName == tbodyTag + || tagName == tfootTag + || tagName == theadTag + || tagName == trTag; +} + } // namespace template<typename ChildType> @@ -80,7 +91,7 @@ PassRefPtr<ChildType> HTMLConstructionSite::attach(Node* parent, PassRefPtr<Chil // FIXME: It's confusing that HTMLConstructionSite::attach does the magic // redirection to the foster parent but HTMLConstructionSite::attachAtSite // doesn't. It feels like we're missing a concept somehow. - if (m_redirectAttachToFosterParent) { + if (shouldFosterParent()) { fosterParent(child.get()); ASSERT(child->attached()); return child.release(); @@ -92,9 +103,10 @@ PassRefPtr<ChildType> HTMLConstructionSite::attach(Node* parent, PassRefPtr<Chil // |parent| to hold a ref at this point. In the common case (at least // for elements), however, we'll get to use this ref in the stack of // open elements. - ASSERT(parent->attached()); - ASSERT(!child->attached()); - child->attach(); + if (parent->attached()) { + ASSERT(!child->attached()); + child->attach(); + } return child.release(); } @@ -107,8 +119,7 @@ void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr<N ExceptionCode ec = 0; site.parent->insertBefore(child, site.nextChild, ec); ASSERT(!ec); - ASSERT(site.parent->attached()); - if (!child->attached()) + if (site.parent->attached() && !child->attached()) child->attach(); return; } @@ -116,14 +127,14 @@ void HTMLConstructionSite::attachAtSite(const AttachmentSite& site, PassRefPtr<N // It's slightly unfortunate that we need to hold a reference to child // here to call attach(). We should investigate whether we can rely on // |site.parent| to hold a ref at this point. - ASSERT(site.parent->attached()); - if (!child->attached()) + if (site.parent->attached() && !child->attached()) child->attach(); } -HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission) +HTMLConstructionSite::HTMLConstructionSite(Document* document, FragmentScriptingPermission scriptingPermission, bool isParsingFragment) : m_document(document) , m_fragmentScriptingPermission(scriptingPermission) + , m_isParsingFragment(isParsingFragment) , m_redirectAttachToFosterParent(false) { } @@ -132,11 +143,23 @@ HTMLConstructionSite::~HTMLConstructionSite() { } +PassRefPtr<HTMLFormElement> HTMLConstructionSite::takeForm() +{ + return m_form.release(); +} + +void HTMLConstructionSite::dispatchDocumentElementAvailableIfNeeded() +{ + if (m_document->frame() && !m_isParsingFragment) + m_document->frame()->loader()->dispatchDocumentElementAvailable(); +} + void HTMLConstructionSite::insertHTMLHtmlStartTagBeforeHTML(AtomicHTMLToken& token) { RefPtr<Element> element = HTMLHtmlElement::create(m_document); element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); m_openElements.pushHTMLHtmlElement(attach(m_document, element.release())); + dispatchDocumentElementAvailableIfNeeded(); } void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken& token, Element* element) @@ -193,40 +216,52 @@ void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment())); } -PassRefPtr<Element> HTMLConstructionSite::createHTMLElementAndAttachToCurrent(AtomicHTMLToken& token) +PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child) { - ASSERT(token.type() == HTMLToken::StartTag); - return attach(currentElement(), createHTMLElement(token)); + return attach(currentElement(), child); } void HTMLConstructionSite::insertHTMLHtmlElement(AtomicHTMLToken& token) { - ASSERT(!m_redirectAttachToFosterParent); - m_openElements.pushHTMLHtmlElement(createHTMLElementAndAttachToCurrent(token)); + ASSERT(!shouldFosterParent()); + m_openElements.pushHTMLHtmlElement(attachToCurrent(createHTMLElement(token))); + dispatchDocumentElementAvailableIfNeeded(); } void HTMLConstructionSite::insertHTMLHeadElement(AtomicHTMLToken& token) { - ASSERT(!m_redirectAttachToFosterParent); - m_head = createHTMLElementAndAttachToCurrent(token); + ASSERT(!shouldFosterParent()); + m_head = attachToCurrent(createHTMLElement(token)); m_openElements.pushHTMLHeadElement(m_head); } void HTMLConstructionSite::insertHTMLBodyElement(AtomicHTMLToken& token) { - ASSERT(!m_redirectAttachToFosterParent); - m_openElements.pushHTMLBodyElement(createHTMLElementAndAttachToCurrent(token)); + ASSERT(!shouldFosterParent()); + m_openElements.pushHTMLBodyElement(attachToCurrent(createHTMLElement(token))); +} + +void HTMLConstructionSite::insertHTMLFormElement(AtomicHTMLToken& token) +{ + insertHTMLElement(token); + ASSERT(currentElement()->isHTMLElement()); + ASSERT(currentElement()->hasTagName(formTag)); + m_form = static_cast<HTMLFormElement*>(currentElement()); } void HTMLConstructionSite::insertHTMLElement(AtomicHTMLToken& token) { - m_openElements.push(createHTMLElementAndAttachToCurrent(token)); + m_openElements.push(attachToCurrent(createHTMLElement(token))); } void HTMLConstructionSite::insertSelfClosingHTMLElement(AtomicHTMLToken& token) { ASSERT(token.type() == HTMLToken::StartTag); - createHTMLElementAndAttachToCurrent(token); + RefPtr<Element> element = attachToCurrent(createHTMLElement(token)); + // Normally HTMLElementStack is responsible for calling finishParsingChildren, + // but self-closing elements are never in the element stack so the stack + // doesn't get a chance to tell them that we're done parsing their children. + element->finishParsingChildren(); // FIXME: Do we want to acknowledge the token's self-closing flag? // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#acknowledge-self-closing-flag } @@ -244,7 +279,7 @@ void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token) { RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, m_document, true); element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); - m_openElements.push(attach(currentElement(), element.release())); + m_openElements.push(attachToCurrent(element.release())); } void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const AtomicString& namespaceURI) @@ -252,7 +287,7 @@ void HTMLConstructionSite::insertForeignElement(AtomicHTMLToken& token, const At ASSERT(token.type() == HTMLToken::StartTag); notImplemented(); // parseError when xmlns or xmlns:xlink are wrong. - RefPtr<Element> element = attach(currentElement(), createElement(token, namespaceURI)); + RefPtr<Element> element = attachToCurrent(createElement(token, namespaceURI)); if (!token.selfClosing()) m_openElements.push(element); } @@ -262,7 +297,7 @@ void HTMLConstructionSite::insertTextNode(const String& characters) AttachmentSite site; site.parent = currentElement(); site.nextChild = 0; - if (m_redirectAttachToFosterParent) + if (shouldFosterParent()) findFosterSite(site); Node* previousChild = site.nextChild ? site.nextChild->previousSibling() : site.parent->lastChild(); @@ -287,11 +322,54 @@ PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& token) { - RefPtr<Element> element = createElement(token, xhtmlNamespaceURI); + QualifiedName tagName(nullAtom, token.name(), xhtmlNamespaceURI); + // FIXME: This can't use HTMLConstructionSite::createElement because we + // have to pass the current form element. We should rework form association + // to occur after construction to allow better code sharing here. + RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, m_document, form(), true); + element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); ASSERT(element->isHTMLElement()); return element.release(); } +PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord* record) +{ + return createHTMLElementFromSavedElement(record->element()); +} + +namespace { + +PassRefPtr<NamedNodeMap> cloneAttributes(Element* element) +{ + NamedNodeMap* attributes = element->attributes(true); + if (!attributes) + return 0; + + RefPtr<NamedNodeMap> newAttributes = NamedNodeMap::create(); + for (size_t i = 0; i < attributes->length(); ++i) { + Attribute* attribute = attributes->attributeItem(i); + RefPtr<Attribute> clone = Attribute::createMapped(attribute->name(), attribute->value()); + newAttributes->addAttribute(clone); + } + return newAttributes.release(); +} + +} + +PassRefPtr<Element> HTMLConstructionSite::createHTMLElementFromSavedElement(Element* element) +{ + // FIXME: This method is wrong. We should be using the original token. + // Using an Element* causes us to fail examples like this: + // <b id="1"><p><script>document.getElementById("1").id = "2"</script></p>TEXT</b> + // When reconstructTheActiveFormattingElements calls this method to open + // a second <b> tag to wrap TEXT, it will have id "2", even though the HTML5 + // spec implies it should be "1". Minefield matches the HTML5 spec here. + + ASSERT(element->isHTMLElement()); // otherwise localName() might be wrong. + AtomicHTMLToken fakeToken(HTMLToken::StartTag, element->localName(), cloneAttributes(element)); + return createHTMLElement(fakeToken); +} + bool HTMLConstructionSite::indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const { if (m_activeFormattingElements.isEmpty()) @@ -319,9 +397,8 @@ void HTMLConstructionSite::reconstructTheActiveFormattingElements() ASSERT(unopenEntryIndex < m_activeFormattingElements.size()); for (; unopenEntryIndex < m_activeFormattingElements.size(); ++unopenEntryIndex) { HTMLFormattingElementList::Entry& unopenedEntry = m_activeFormattingElements.at(unopenEntryIndex); - // FIXME: We're supposed to save the original token in the entry. - AtomicHTMLToken fakeToken(HTMLToken::StartTag, unopenedEntry.element()->localName()); - insertHTMLElement(fakeToken); + RefPtr<Element> reconstructed = createHTMLElementFromSavedElement(unopenedEntry.element()); + m_openElements.push(attachToCurrent(reconstructed.release())); unopenedEntry.replaceElement(currentElement()); } } @@ -357,6 +434,12 @@ void HTMLConstructionSite::findFosterSite(AttachmentSite& site) site.nextChild = 0; } +bool HTMLConstructionSite::shouldFosterParent() const +{ + return m_redirectAttachToFosterParent + && causesFosterParenting(currentElement()->tagQName()); +} + void HTMLConstructionSite::fosterParent(Node* node) { AttachmentSite site; diff --git a/WebCore/html/HTMLConstructionSite.h b/WebCore/html/HTMLConstructionSite.h index c0af9b3..d9c8ac5 100644 --- a/WebCore/html/HTMLConstructionSite.h +++ b/WebCore/html/HTMLConstructionSite.h @@ -42,7 +42,7 @@ class Element; class HTMLConstructionSite : public Noncopyable { public: - HTMLConstructionSite(Document*, FragmentScriptingPermission); + HTMLConstructionSite(Document*, FragmentScriptingPermission, bool isParsingFragment); ~HTMLConstructionSite(); void insertDoctype(AtomicHTMLToken&); @@ -55,6 +55,7 @@ public: void insertHTMLHtmlElement(AtomicHTMLToken&); void insertHTMLHeadElement(AtomicHTMLToken&); void insertHTMLBodyElement(AtomicHTMLToken&); + void insertHTMLFormElement(AtomicHTMLToken&); void insertScriptElement(AtomicHTMLToken&); void insertTextNode(const String&); void insertForeignElement(AtomicHTMLToken&, const AtomicString& namespaceURI); @@ -64,7 +65,9 @@ public: void insertHTMLBodyStartTagInBody(AtomicHTMLToken&); PassRefPtr<Element> createHTMLElement(AtomicHTMLToken&); + PassRefPtr<Element> createHTMLElementFromElementRecord(HTMLElementStack::ElementRecord*); + bool shouldFosterParent() const; void fosterParent(Node*); bool indexOfFirstUnopenFormattingElement(unsigned& firstUnopenElementIndex) const; @@ -81,18 +84,16 @@ public: Element* head() const { return m_head.get(); } - Element* form() const { return m_form.get(); } - PassRefPtr<Element> takeForm() { return m_form.release(); } - - void setForm(PassRefPtr<Element> form) { m_form = form; } + HTMLFormElement* form() const { return m_form.get(); } + PassRefPtr<HTMLFormElement> takeForm(); class RedirectToFosterParentGuard : public Noncopyable { public: - RedirectToFosterParentGuard(HTMLConstructionSite& tree, bool shouldRedirect) + RedirectToFosterParentGuard(HTMLConstructionSite& tree) : m_tree(tree) , m_wasRedirectingBefore(tree.m_redirectAttachToFosterParent) { - m_tree.m_redirectAttachToFosterParent = shouldRedirect; + m_tree.m_redirectAttachToFosterParent = true; } ~RedirectToFosterParentGuard() @@ -113,21 +114,25 @@ private: template<typename ChildType> PassRefPtr<ChildType> attach(Node* parent, PassRefPtr<ChildType> child); + PassRefPtr<Element> attachToCurrent(PassRefPtr<Element>); void attachAtSite(const AttachmentSite&, PassRefPtr<Node> child); void findFosterSite(AttachmentSite&); + PassRefPtr<Element> createHTMLElementFromSavedElement(Element*); PassRefPtr<Element> createElement(AtomicHTMLToken&, const AtomicString& namespaceURI); - PassRefPtr<Element> createHTMLElementAndAttachToCurrent(AtomicHTMLToken&); void mergeAttributesFromTokenIntoElement(AtomicHTMLToken&, Element*); + void dispatchDocumentElementAvailableIfNeeded(); Document* m_document; RefPtr<Element> m_head; - RefPtr<Element> m_form; + RefPtr<HTMLFormElement> m_form; mutable HTMLElementStack m_openElements; mutable HTMLFormattingElementList m_activeFormattingElements; + FragmentScriptingPermission m_fragmentScriptingPermission; + bool m_isParsingFragment; // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-intable // In the "in table" insertion mode, we sometimes get into a state where diff --git a/WebCore/html/HTMLElementStack.cpp b/WebCore/html/HTMLElementStack.cpp index d1a1752..5c325e5 100644 --- a/WebCore/html/HTMLElementStack.cpp +++ b/WebCore/html/HTMLElementStack.cpp @@ -137,6 +137,17 @@ void HTMLElementStack::popHTMLBodyElement() popCommon(); } +void HTMLElementStack::popAll() +{ + m_htmlElement = 0; + m_headElement = 0; + m_bodyElement = 0; + while (m_top) { + top()->finishParsingChildren(); + m_top = m_top->releaseNext(); + } +} + void HTMLElementStack::pop() { ASSERT(!top()->hasTagName(HTMLNames::headTag)); @@ -325,6 +336,11 @@ bool HTMLElementStack::contains(Element* element) const return !!find(element); } +bool HTMLElementStack::contains(const AtomicString& tagName) const +{ + return !!topmost(tagName); +} + template <bool isMarker(Element*)> bool inScopeCommon(HTMLElementStack::ElementRecord* top, const AtomicString& targetTag) { diff --git a/WebCore/html/HTMLElementStack.h b/WebCore/html/HTMLElementStack.h index 8be4422..830faf0 100644 --- a/WebCore/html/HTMLElementStack.h +++ b/WebCore/html/HTMLElementStack.h @@ -92,11 +92,13 @@ public: void popUntilTableRowScopeMarker(); // "clear the stack back to a table row context" in the spec. void popHTMLHeadElement(); void popHTMLBodyElement(); + void popAll(); void remove(Element*); void removeHTMLHeadElement(Element*); bool contains(Element*) const; + bool contains(const AtomicString& tagName) const; bool inScope(Element*) const; bool inScope(const AtomicString& tagName) const; diff --git a/WebCore/html/HTMLFormElement.cpp b/WebCore/html/HTMLFormElement.cpp index 2eface2..4d6b603 100644 --- a/WebCore/html/HTMLFormElement.cpp +++ b/WebCore/html/HTMLFormElement.cpp @@ -74,6 +74,7 @@ static int64_t generateFormDataIdentifier() HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* document) : HTMLElement(tagName, document) + , m_submissionTrigger(NotSubmittedByJavaScript) , m_autocomplete(true) , m_insubmit(false) , m_doingsubmit(false) @@ -370,6 +371,7 @@ void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool lockH } m_insubmit = true; + m_submissionTrigger = formSubmissionTrigger; HTMLFormControlElement* firstSuccessfulSubmitButton = 0; bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button? @@ -559,6 +561,11 @@ String HTMLFormElement::target() const return getAttribute(targetAttr); } +FormSubmissionTrigger HTMLFormElement::submissionTrigger() const +{ + return m_submissionTrigger; +} + HTMLFormControlElement* HTMLFormElement::defaultButton() const { for (unsigned i = 0; i < m_associatedElements.size(); ++i) { diff --git a/WebCore/html/HTMLFormElement.h b/WebCore/html/HTMLFormElement.h index a2a5897..5aa9a5c 100644 --- a/WebCore/html/HTMLFormElement.h +++ b/WebCore/html/HTMLFormElement.h @@ -98,6 +98,8 @@ public: virtual String target() const; + FormSubmissionTrigger submissionTrigger() const; + HTMLFormControlElement* defaultButton() const; bool checkValidity(); @@ -153,6 +155,8 @@ private: Vector<HTMLFormControlElement*> m_associatedElements; Vector<HTMLImageElement*> m_imageElements; + FormSubmissionTrigger m_submissionTrigger; + bool m_autocomplete : 1; bool m_insubmit : 1; bool m_doingsubmit : 1; diff --git a/WebCore/html/HTMLFormattingElementList.cpp b/WebCore/html/HTMLFormattingElementList.cpp index d71cc8c..22bf03e 100644 --- a/WebCore/html/HTMLFormattingElementList.cpp +++ b/WebCore/html/HTMLFormattingElementList.cpp @@ -70,36 +70,22 @@ HTMLFormattingElementList::Bookmark HTMLFormattingElementList::bookmarkFor(Eleme { size_t index = m_entries.reverseFind(element); ASSERT(index != notFound); - Element* elementBefore = (index > 1) ? m_entries[index - 1].element() : 0; - Element* elementAfter = (index < m_entries.size() - 1) ? m_entries[index + 1].element() : 0; - return Bookmark(elementBefore, elementAfter); + return Bookmark(&at(index)); } -void HTMLFormattingElementList::insertAt(Element* element, const Bookmark& bookmark) +void HTMLFormattingElementList::swapTo(Element* oldElement, Element* newElement, const Bookmark& bookmark) { - size_t beforeIndex = notFound; - if (bookmark.elementBefore()) { - beforeIndex = m_entries.reverseFind(bookmark.elementBefore()); - ASSERT(beforeIndex != notFound); - } - size_t afterIndex = notFound; - if (bookmark.elementAfter()) { - afterIndex = m_entries.reverseFind(bookmark.elementAfter()); - ASSERT(afterIndex != notFound); - } - - if (!bookmark.elementBefore()) { - if (bookmark.elementAfter()) - ASSERT(!afterIndex); - m_entries.prepend(element); - } else { - if (bookmark.elementAfter()) { - // Bookmarks are not general purpose. They're only for the Adoption - // Agency. Assume the bookmarked element was already removed. - ASSERT(beforeIndex + 1 == afterIndex); - } - m_entries.insert(beforeIndex + 1, element); + ASSERT(contains(oldElement)); + ASSERT(!contains(newElement)); + if (!bookmark.hasBeenMoved()) { + ASSERT(bookmark.mark()->element() == oldElement); + bookmark.mark()->replaceElement(newElement); + return; } + size_t index = bookmark.mark() - first(); + ASSERT(index < size()); + m_entries.insert(index + 1, newElement); + remove(oldElement); } void HTMLFormattingElementList::append(Element* element) @@ -121,8 +107,13 @@ void HTMLFormattingElementList::appendMarker() void HTMLFormattingElementList::clearToLastMarker() { - while (m_entries.size() && !m_entries.last().isMarker()) + // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#clear-the-list-of-active-formatting-elements-up-to-the-last-marker + while (m_entries.size()) { + bool shouldStop = m_entries.last().isMarker(); m_entries.removeLast(); + if (shouldStop) + break; + } } #ifndef NDEBUG diff --git a/WebCore/html/HTMLFormattingElementList.h b/WebCore/html/HTMLFormattingElementList.h index 6b41da4..135163c 100644 --- a/WebCore/html/HTMLFormattingElementList.h +++ b/WebCore/html/HTMLFormattingElementList.h @@ -80,24 +80,24 @@ public: class Bookmark { public: - Bookmark(Element* before, Element* after) - : m_before(before) - , m_after(after) + Bookmark(Entry* entry) + : m_hasBeenMoved(false) + , m_mark(entry) { } - void moveToAfter(Element* before) + void moveToAfter(Entry* before) { - m_before = before; - m_after = 0; + m_hasBeenMoved = true; + m_mark = before; } - Element* elementBefore() const { return m_before; } - Element* elementAfter() const { return m_after; } + bool hasBeenMoved() const { return m_hasBeenMoved; } + Entry* mark() const { return m_mark; } private: - Element* m_before; - Element* m_after; + bool m_hasBeenMoved; + Entry* m_mark; }; bool isEmpty() const { return !size(); } @@ -111,9 +111,10 @@ public: void remove(Element*); Bookmark bookmarkFor(Element*); - void insertAt(Element*, const Bookmark&); + void swapTo(Element* oldElement, Element* newElement, const Bookmark&); void appendMarker(); + // clearToLastMarker also clears the marker (per the HTML5 spec). void clearToLastMarker(); const Entry& at(size_t i) const { return m_entries[i]; } @@ -124,6 +125,8 @@ public: #endif private: + Entry* first() { return &at(0); } + Vector<Entry> m_entries; }; diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp index 8502814..49b6f01 100644 --- a/WebCore/html/HTMLInputElement.cpp +++ b/WebCore/html/HTMLInputElement.cpp @@ -29,6 +29,7 @@ #include "AXObjectCache.h" #include "Attribute.h" +#include "BeforeTextInsertedEvent.h" #include "CSSPropertyNames.h" #include "ChromeClient.h" #include "DateComponents.h" @@ -40,6 +41,7 @@ #include "ExceptionCode.h" #include "File.h" #include "FileList.h" +#include "FileSystem.h" #include "FocusController.h" #include "FormDataList.h" #include "Frame.h" @@ -108,6 +110,12 @@ static const double weekDefaultStepBase = -259200000.0; // The first day of 1970 static const double msecPerMinute = 60 * 1000; static const double msecPerSecond = 1000; +static bool isNumberCharacter(UChar ch) +{ + return ch == '+' || ch == '-' || ch == '.' || ch == 'e' || ch == 'E' + || ch >= '0' && ch <= '9'; +} + HTMLInputElement::HTMLInputElement(const QualifiedName& tagName, Document* document, HTMLFormElement* form) : HTMLTextFormControlElement(tagName, document, form) , m_xPos(0) @@ -1187,6 +1195,13 @@ void HTMLInputElement::parseMappedAttribute(Attribute* attr) m_hasNonEmptyList = !attr->isEmpty(); // FIXME: we need to tell this change to a renderer if the attribute affects the appearance. #endif +#if ENABLE(INPUT_SPEECH) + else if (attr->name() == speechAttr) { + if (renderer()) + renderer()->updateFromElement(); + setNeedsStyleRecalc(); + } +#endif else HTMLTextFormControlElement::parseMappedAttribute(attr); } @@ -1978,8 +1993,30 @@ void HTMLInputElement::setFileListFromRenderer(const Vector<String>& paths) { m_fileList->clear(); int size = paths.size(); + +#if ENABLE(DIRECTORY_UPLOAD) + // If a directory is being selected, the UI allows a directory to be chosen + // and the paths provided here share a root directory somewhere up the tree; + // we want to store only the relative paths from that point. + if (webkitdirectory() && size > 0) { + String rootPath = directoryName(paths[0]); + // Find the common root path. + for (int i = 1; i < size; i++) { + while (!paths[i].startsWith(rootPath)) + rootPath = directoryName(rootPath); + } + rootPath = directoryName(rootPath); + ASSERT(rootPath.length()); + for (int i = 0; i < size; i++) + m_fileList->append(File::create(paths[i].substring(1 + rootPath.length()), paths[i])); + } else { + for (int i = 0; i < size; i++) + m_fileList->append(File::create(paths[i])); + } +#else for (int i = 0; i < size; i++) m_fileList->append(File::create(paths[i])); +#endif setFormControlValueMatchesRenderer(true); InputElement::notifyFormStateChanged(this); @@ -2126,7 +2163,21 @@ void HTMLInputElement::defaultEventHandler(Event* evt) } } - if (isTextField() + if (hasSpinButton() && evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent()) { + String key = static_cast<KeyboardEvent*>(evt)->keyIdentifier(); + int step = 0; + if (key == "Up") + step = 1; + else if (key == "Down") + step = -1; + if (step) { + stepUpFromRenderer(step); + evt->setDefaultHandled(); + return; + } + } + + if (isTextField() && evt->type() == eventNames().keydownEvent && evt->isKeyboardEvent() && focused() @@ -2382,7 +2433,7 @@ void HTMLInputElement::defaultEventHandler(Event* evt) } if (evt->isBeforeTextInsertedEvent()) - InputElement::handleBeforeTextInsertedEvent(m_data, this, this, evt); + handleBeforeTextInsertedEvent(evt); if (isTextField() && renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == eventNames().blurEvent || evt->type() == eventNames().focusEvent)) toRenderTextControlSingleLine(renderer())->forwardEvent(evt); @@ -2394,6 +2445,33 @@ void HTMLInputElement::defaultEventHandler(Event* evt) HTMLFormControlElementWithState::defaultEventHandler(evt); } +void HTMLInputElement::handleBeforeTextInsertedEvent(Event* event) +{ + if (inputType() == NUMBER) { + BeforeTextInsertedEvent* textEvent = static_cast<BeforeTextInsertedEvent*>(event); + unsigned length = textEvent->text().length(); + bool hasInvalidChar = false; + for (unsigned i = 0; i < length; ++i) { + if (!isNumberCharacter(textEvent->text()[i])) { + hasInvalidChar = true; + break; + } + } + if (hasInvalidChar) { + Vector<UChar> stripped; + stripped.reserveCapacity(length); + for (unsigned i = 0; i < length; ++i) { + UChar ch = textEvent->text()[i]; + if (!isNumberCharacter(ch)) + continue; + stripped.append(ch); + } + textEvent->setText(String::adopt(stripped)); + } + } + InputElement::handleBeforeTextInsertedEvent(m_data, this, this, event); +} + PassRefPtr<HTMLFormElement> HTMLInputElement::createTemporaryFormForIsIndex() { RefPtr<HTMLFormElement> form = HTMLFormElement::create(document()); @@ -2460,6 +2538,13 @@ bool HTMLInputElement::multiple() const return !getAttribute(multipleAttr).isNull(); } +#if ENABLE(DIRECTORY_UPLOAD) +bool HTMLInputElement::webkitdirectory() const +{ + return !getAttribute(webkitdirectoryAttr).isNull(); +} +#endif + void HTMLInputElement::setSize(unsigned size) { setAttribute(sizeAttr, String::number(size)); diff --git a/WebCore/html/HTMLInputElement.h b/WebCore/html/HTMLInputElement.h index b055e73..3b9ba88 100644 --- a/WebCore/html/HTMLInputElement.h +++ b/WebCore/html/HTMLInputElement.h @@ -180,6 +180,10 @@ public: bool multiple() const; +#if ENABLE(DIRECTORY_UPLOAD) + bool webkitdirectory() const; +#endif + virtual bool isAutofilled() const { return m_autofilled; } void setAutofilled(bool value = true); @@ -299,6 +303,7 @@ private: void updateCheckedRadioButtons(); + void handleBeforeTextInsertedEvent(Event*); PassRefPtr<HTMLFormElement> createTemporaryFormForIsIndex(); // Helper for getAllowedValueStep(); bool getStepParameters(double* defaultStep, double* stepScaleFactor) const; diff --git a/WebCore/html/HTMLInputElement.idl b/WebCore/html/HTMLInputElement.idl index b93f05c..c66e775 100644 --- a/WebCore/html/HTMLInputElement.idl +++ b/WebCore/html/HTMLInputElement.idl @@ -40,6 +40,9 @@ module html { attribute long maxLength setter raises(DOMException); attribute [Reflect] DOMString min; attribute [Reflect] boolean multiple; +#if defined(ENABLE_DIRECTORY_UPLOAD) && ENABLE_DIRECTORY_UPLOAD + attribute [Reflect] boolean webkitdirectory; +#endif attribute [Reflect] DOMString name; attribute [Reflect] DOMString pattern; attribute [Reflect] DOMString placeholder; @@ -95,6 +98,10 @@ module html { readonly attribute FileList files; readonly attribute NodeList labels; + +#if defined(ENABLE_INPUT_SPEECH) && ENABLE_INPUT_SPEECH + attribute [Reflect] boolean speech; +#endif }; } diff --git a/WebCore/html/HTMLLinkElement.cpp b/WebCore/html/HTMLLinkElement.cpp index eff250a..cce991a 100644 --- a/WebCore/html/HTMLLinkElement.cpp +++ b/WebCore/html/HTMLLinkElement.cpp @@ -200,6 +200,7 @@ void HTMLLinkElement::process() if (m_relAttribute.m_isIcon && m_url.isValid() && !m_url.isEmpty()) document()->setIconURL(m_url.string(), type); +<<<<<<< HEAD:WebCore/html/HTMLLinkElement.cpp #ifdef ANDROID_APPLE_TOUCH_ICON if ((m_relAttribute.m_isTouchIcon || m_relAttribute.m_isPrecomposedTouchIcon) && m_url.isValid() && !m_url.isEmpty() && document()->frame()) @@ -209,6 +210,9 @@ void HTMLLinkElement::process() #endif if (m_relAttribute.m_isDNSPrefetch && m_url.isValid() && !m_url.isEmpty()) +======= + if (m_relAttribute.m_isDNSPrefetch && document()->isDNSPrefetchEnabled() && m_url.isValid() && !m_url.isEmpty()) +>>>>>>> webkit.org at r63859:WebCore/html/HTMLLinkElement.cpp ResourceHandle::prepareForURL(m_url); #if ENABLE(LINK_PREFETCH) diff --git a/WebCore/html/HTMLLinkElement.h b/WebCore/html/HTMLLinkElement.h index efdfde2..057cccc 100644 --- a/WebCore/html/HTMLLinkElement.h +++ b/WebCore/html/HTMLLinkElement.h @@ -31,7 +31,6 @@ namespace WebCore { class CachedCSSStyleSheet; -class CachedLinkPrefetch; class KURL; class HTMLLinkElement : public HTMLElement, public CachedResourceClient { diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp index b83d1cb..bf24cf9 100644 --- a/WebCore/html/HTMLMediaElement.cpp +++ b/WebCore/html/HTMLMediaElement.cpp @@ -130,6 +130,7 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc) #endif , m_dispatchingCanPlayEvent(false) , m_loadInitiatedByUserGesture(false) + , m_completelyLoaded(false) { document()->registerForDocumentActivationCallbacks(this); document()->registerForMediaVolumeCallbacks(this); @@ -177,16 +178,19 @@ void HTMLMediaElement::attributeChanged(Attribute* attr, bool preserveDecls) if (!getAttribute(srcAttr).isEmpty()) scheduleLoad(); } -#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) else if (attrName == controlsAttr) { +#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) if (!isVideo() && attached() && (controls() != (renderer() != 0))) { detach(); attach(); } if (renderer()) renderer()->updateFromElement(); - } +#else + if (m_player) + m_player->setControls(controls()); #endif + } } void HTMLMediaElement::parseMappedAttribute(Attribute* attr) @@ -226,8 +230,6 @@ void HTMLMediaElement::parseMappedAttribute(Attribute* attr) setAttributeEventListener(eventNames().endedEvent, createAttributeEventListener(this, attr)); else if (attrName == onerrorAttr) setAttributeEventListener(eventNames().errorEvent, createAttributeEventListener(this, attr)); - else if (attrName == onloadAttr) - setAttributeEventListener(eventNames().loadEvent, createAttributeEventListener(this, attr)); else if (attrName == onloadeddataAttr) setAttributeEventListener(eventNames().loadeddataEvent, createAttributeEventListener(this, attr)); else if (attrName == onloadedmetadataAttr) @@ -285,8 +287,14 @@ RenderObject* HTMLMediaElement::createRenderer(RenderArena* arena, RenderStyle*) #if ENABLE(PLUGIN_PROXY_FOR_VIDEO) // Setup the renderer if we already have a proxy widget. RenderEmbeddedObject* mediaRenderer = new (arena) RenderEmbeddedObject(this); - if (m_proxyWidget) + if (m_proxyWidget) { mediaRenderer->setWidget(m_proxyWidget); + + Frame* frame = document()->frame(); + FrameLoader* loader = frame ? frame->loader() : 0; + if (loader) + loader->showMediaPlayerProxyPlugin(m_proxyWidget.get()); + } return mediaRenderer; #else return new (arena) RenderMedia(this); @@ -321,6 +329,14 @@ void HTMLMediaElement::attach() if (renderer()) renderer()->updateFromElement(); +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + else if (m_proxyWidget) { + Frame* frame = document()->frame(); + FrameLoader* loader = frame ? frame->loader() : 0; + if (loader) + loader->hideMediaPlayerProxyPlugin(m_proxyWidget.get()); + } +#endif } void HTMLMediaElement::recalcStyle(StyleChange change) @@ -482,6 +498,7 @@ void HTMLMediaElement::prepareForLoad() m_loadTimer.stop(); m_sentStalledEvent = false; m_haveFiredLoadedData = false; + m_completelyLoaded = false; // 1 - Abort any already-running instance of the resource selection algorithm for this element. m_currentSourceNode = 0; @@ -842,25 +859,15 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) } if (state == MediaPlayer::Loaded) { - NetworkState oldState = m_networkState; - - m_networkState = NETWORK_LOADED; - if (oldState < NETWORK_LOADED || oldState == NETWORK_NO_SOURCE) { + if (m_networkState != NETWORK_IDLE) { m_progressEventTimer.stop(); // Schedule one last progress event so we guarantee that at least one is fired // for files that load very quickly. scheduleEvent(eventNames().progressEvent); - - // Check to see if readyState changes need to be dealt with before sending the - // 'load' event so we report 'canplaythrough' first. This is necessary because a - // media engine reports readyState and networkState changes separately - MediaPlayer::ReadyState currentState = m_player->readyState(); - if (static_cast<ReadyState>(currentState) != m_readyState) - setReadyState(currentState); - - scheduleEvent(eventNames().loadEvent); } + m_networkState = NETWORK_IDLE; + m_completelyLoaded = true; } } @@ -973,7 +980,7 @@ void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) { ASSERT(m_player); - if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) + if (m_networkState != NETWORK_LOADING) return; unsigned progress = m_player->bytesLoaded(); @@ -1021,49 +1028,55 @@ bool HTMLMediaElement::supportsSave() const void HTMLMediaElement::seek(float time, ExceptionCode& ec) { - // 4.8.10.10. Seeking - // 1 + // 4.8.9.9 Seeking + + // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception. if (m_readyState == HAVE_NOTHING || !m_player) { ec = INVALID_STATE_ERR; return; } - // 2 + // Get the current time before setting m_seeking, m_lastSeekTime is returned once it is set. + float now = currentTime(); + + // 3 - Set the seeking IDL attribute to true. + // The flag will be cleared when the engine tells is the time has actually changed + m_seeking = true; + + // 4 - Queue a task to fire a simple event named timeupdate at the element. + scheduleTimeupdateEvent(false); + + // 6 - If the new playback position is later than the end of the media resource, then let it be the end + // of the media resource instead. time = min(time, duration()); - // 3 - time = max(time, 0.0f); + // 7 - If the new playback position is less than the earliest possible position, let it be that position instead. + float earliestTime = m_player->startTime(); + time = max(time, earliestTime); - // 4 + // 8 - If the (possibly now changed) new playback position is not in one of the ranges given in the + // seekable attribute, then let it be the position in one of the ranges given in the seekable attribute + // that is the nearest to the new playback position. ... If there are no ranges given in the seekable + // attribute then set the seeking IDL attribute to false and abort these steps. RefPtr<TimeRanges> seekableRanges = seekable(); - if (!seekableRanges->contain(time)) { - ec = INDEX_SIZE_ERR; + if (!seekableRanges->length() || time == now) { + m_seeking = false; return; } - - // avoid generating events when the time won't actually change - float now = currentTime(); - if (time == now) - return; + time = seekableRanges->nearest(time); - // 5 if (m_playing) { if (m_lastSeekTime < now) addPlayedRange(m_lastSeekTime, now); } m_lastSeekTime = time; + m_sentEndEvent = false; - // 6 - set the seeking flag, it will be cleared when the engine tells is the time has actually changed - m_seeking = true; - - // 7 - scheduleTimeupdateEvent(false); + // 9 - Set the current playback position to the given new playback position + m_player->seek(time); - // 8 - this is covered, if necessary, when the engine signals a readystate change + // 10-15 are handled, if necessary, when the engine signals a readystate change. - // 10 - m_player->seek(time); - m_sentEndEvent = false; } void HTMLMediaElement::finishSeek() @@ -1809,7 +1822,7 @@ void HTMLMediaElement::stopPeriodicTimers() void HTMLMediaElement::userCancelledLoad() { - if (m_networkState == NETWORK_EMPTY || m_networkState >= NETWORK_LOADED) + if (m_networkState == NETWORK_EMPTY || m_completelyLoaded) return; // If the media data fetching process is aborted by the user: @@ -1992,9 +2005,7 @@ void HTMLMediaElement::createMediaPlayerProxy() { ensureMediaPlayer(); - if (!inDocument() && m_proxyWidget) - return; - if (inDocument() && !m_needWidgetUpdate) + if (m_proxyWidget || (inDocument() && !m_needWidgetUpdate)) return; Frame* frame = document()->frame(); diff --git a/WebCore/html/HTMLMediaElement.h b/WebCore/html/HTMLMediaElement.h index 7d83f94..051629e 100644 --- a/WebCore/html/HTMLMediaElement.h +++ b/WebCore/html/HTMLMediaElement.h @@ -88,7 +88,7 @@ public: void setSrc(const String&); String currentSrc() const; - enum NetworkState { NETWORK_EMPTY, NETWORK_IDLE, NETWORK_LOADING, NETWORK_LOADED, NETWORK_NO_SOURCE }; + enum NetworkState { NETWORK_EMPTY, NETWORK_IDLE, NETWORK_LOADING, NETWORK_NO_SOURCE }; NetworkState networkState() const; String preload() const; @@ -366,6 +366,7 @@ private: bool m_dispatchingCanPlayEvent : 1; bool m_loadInitiatedByUserGesture : 1; + bool m_completelyLoaded : 1; }; } //namespace diff --git a/WebCore/html/HTMLMediaElement.idl b/WebCore/html/HTMLMediaElement.idl index baea577..28a0fbe 100644 --- a/WebCore/html/HTMLMediaElement.idl +++ b/WebCore/html/HTMLMediaElement.idl @@ -36,8 +36,7 @@ interface [Conditional=VIDEO] HTMLMediaElement : HTMLElement { const unsigned short NETWORK_EMPTY = 0; const unsigned short NETWORK_IDLE = 1; const unsigned short NETWORK_LOADING = 2; - const unsigned short NETWORK_LOADED = 3; - const unsigned short NETWORK_NO_SOURCE = 4; + const unsigned short NETWORK_NO_SOURCE = 3; readonly attribute unsigned short networkState; attribute DOMString preload; diff --git a/WebCore/html/HTMLToken.h b/WebCore/html/HTMLToken.h index c2db87d..d2987f4 100644 --- a/WebCore/html/HTMLToken.h +++ b/WebCore/html/HTMLToken.h @@ -339,7 +339,7 @@ public: m_data = String(token.comment().data(), token.comment().size()); break; case HTMLToken::Character: - m_data = String(token.characters().data(), token.characters().size()); + m_externalCharacters = &token.characters(); break; } } @@ -352,12 +352,6 @@ public: ASSERT(usesName()); } - explicit AtomicHTMLToken(const String& characters) - : m_type(HTMLToken::Character) - , m_data(characters) - { - } - HTMLToken::Type type() const { return m_type; } const AtomicString& name() const @@ -398,10 +392,10 @@ public: return m_attributes.release(); } - const String& characters() const + const HTMLToken::DataVector& characters() const { ASSERT(m_type == HTMLToken::Character); - return m_data; + return *m_externalCharacters; } const String& comment() const @@ -446,10 +440,19 @@ private: // "name" for DOCTYPE, StartTag, and EndTag AtomicString m_name; - // "characters" for Character // "data" for Comment String m_data; + // "characters" for Character + // + // We don't want to copy the the characters out of the HTMLToken, so we + // keep a pointer to its buffer instead. This buffer is owned by the + // HTMLToken and causes a lifetime dependence between these objects. + // + // FIXME: Add a mechanism for "internalizing" the characters when the + // HTMLToken is destructed. + const HTMLToken::DataVector* m_externalCharacters; + // For DOCTYPE OwnPtr<HTMLToken::DoctypeData> m_doctypeData; diff --git a/WebCore/html/HTMLTreeBuilder.cpp b/WebCore/html/HTMLTreeBuilder.cpp index 80d4530..93da3f7 100644 --- a/WebCore/html/HTMLTreeBuilder.cpp +++ b/WebCore/html/HTMLTreeBuilder.cpp @@ -33,6 +33,7 @@ #include "Frame.h" #include "HTMLDocument.h" #include "HTMLElementFactory.h" +#include "HTMLFormElement.h" #include "HTMLHtmlElement.h" #include "HTMLNames.h" #include "HTMLScriptElement.h" @@ -65,77 +66,6 @@ inline bool isTreeBuilderWhitepace(UChar cc) return cc == '\t' || cc == '\x0A' || cc == '\x0C' || cc == '\x0D' || cc == ' '; } -class ExternalCharacterTokenBuffer : public Noncopyable { -public: - explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token) - : m_current(token.characters().characters()) - , m_end(m_current + token.characters().length()) - { - ASSERT(!isEmpty()); - } - - ~ExternalCharacterTokenBuffer() - { - ASSERT(isEmpty()); - } - - bool isEmpty() const { return m_current == m_end; } - - void skipLeadingWhitespace() - { - ASSERT(!isEmpty()); - while (isTreeBuilderWhitepace(*m_current)) { - if (++m_current == m_end) - return; - } - } - - String takeLeadingWhitespace() - { - ASSERT(!isEmpty()); - const UChar* start = m_current; - skipLeadingWhitespace(); - if (start == m_current) - return String(); - return String(start, m_current - start); - } - - String takeRemaining() - { - ASSERT(!isEmpty()); - const UChar* start = m_current; - m_current = m_end; - return String(start, m_current - start); - } - - void giveRemainingTo(Vector<UChar>& recipient) - { - recipient.append(m_current, m_end - m_current); - m_current = m_end; - } - - String takeRemainingWhitespace() - { - ASSERT(!isEmpty()); - Vector<UChar> whitespace; - do { - UChar cc = *m_current++; - if (isTreeBuilderWhitepace(cc)) - whitespace.append(cc); - } while (m_current < m_end); - // Returning the null string when there aren't any whitespace - // characters is slightly cleaner semantically because we don't want - // to insert a text node (as opposed to inserting an empty text node). - if (whitespace.isEmpty()) - return String(); - return String::adopt(whitespace); - } - -private: - const UChar* m_current; - const UChar* m_end; -}; - inline bool hasNonWhitespace(const String& string) { const UChar* characters = string.characters(); @@ -285,13 +215,6 @@ bool isNonAnchorFormattingTag(const AtomicString& tagName) || isNonAnchorNonNobrFormattingTag(tagName); } -bool requiresRedirectToFosterParent(Element* element) -{ - return element->hasTagName(tableTag) - || isTableBodyContextTag(element->localName()) - || element->hasTagName(trTag); -} - // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#formatting bool isFormattingTag(const AtomicString& tagName) { @@ -316,10 +239,89 @@ bool isNotFormattingAndNotPhrasing(const Element* element) } // namespace +class HTMLTreeBuilder::ExternalCharacterTokenBuffer : public Noncopyable { +public: + explicit ExternalCharacterTokenBuffer(AtomicHTMLToken& token) + : m_current(token.characters().data()) + , m_end(m_current + token.characters().size()) + { + ASSERT(!isEmpty()); + } + + explicit ExternalCharacterTokenBuffer(const String& string) + : m_current(string.characters()) + , m_end(m_current + string.length()) + { + ASSERT(!isEmpty()); + } + + ~ExternalCharacterTokenBuffer() + { + ASSERT(isEmpty()); + } + + bool isEmpty() const { return m_current == m_end; } + + void skipLeadingWhitespace() + { + ASSERT(!isEmpty()); + while (isTreeBuilderWhitepace(*m_current)) { + if (++m_current == m_end) + return; + } + } + + String takeLeadingWhitespace() + { + ASSERT(!isEmpty()); + const UChar* start = m_current; + skipLeadingWhitespace(); + if (start == m_current) + return String(); + return String(start, m_current - start); + } + + String takeRemaining() + { + ASSERT(!isEmpty()); + const UChar* start = m_current; + m_current = m_end; + return String(start, m_current - start); + } + + void giveRemainingTo(Vector<UChar>& recipient) + { + recipient.append(m_current, m_end - m_current); + m_current = m_end; + } + + String takeRemainingWhitespace() + { + ASSERT(!isEmpty()); + Vector<UChar> whitespace; + do { + UChar cc = *m_current++; + if (isTreeBuilderWhitepace(cc)) + whitespace.append(cc); + } while (m_current < m_end); + // Returning the null string when there aren't any whitespace + // characters is slightly cleaner semantically because we don't want + // to insert a text node (as opposed to inserting an empty text node). + if (whitespace.isEmpty()) + return String(); + return String::adopt(whitespace); + } + +private: + const UChar* m_current; + const UChar* m_end; +}; + + HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, HTMLDocument* document, bool reportErrors) : m_framesetOk(true) , m_document(document) - , m_tree(document, FragmentScriptingAllowed) + , m_tree(document, FragmentScriptingAllowed, false) , m_reportErrors(reportErrors) , m_isPaused(false) , m_insertionMode(InitialMode) @@ -339,7 +341,7 @@ HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, HTMLDocument* documen HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission) : m_framesetOk(true) , m_document(fragment->document()) - , m_tree(fragment->document(), scriptingPermission) + , m_tree(fragment->document(), scriptingPermission, true) , m_reportErrors(false) // FIXME: Why not report errors in fragments? , m_isPaused(false) , m_insertionMode(InitialMode) @@ -386,7 +388,7 @@ static void convertToOldStyle(AtomicHTMLToken& token, Token& oldStyleToken) break; case HTMLToken::Character: oldStyleToken.tagName = textAtom; - oldStyleToken.text = token.characters().impl(); + oldStyleToken.text = StringImpl::create(token.characters().data(), token.characters().size()); break; } } @@ -542,7 +544,7 @@ void HTMLTreeBuilder::processDoctypeToken(AtomicHTMLToken& token) return; } if (m_insertionMode == InTableTextMode) { - processDefaultForInTableTextMode(token); + defaultForInTableText(); processDoctypeToken(token); return; } @@ -565,8 +567,9 @@ void HTMLTreeBuilder::processFakeEndTag(const QualifiedName& tagName) void HTMLTreeBuilder::processFakeCharacters(const String& characters) { - AtomicHTMLToken fakeToken(characters); - processCharacter(fakeToken); + ASSERT(!characters.isEmpty()); + ExternalCharacterTokenBuffer buffer(characters); + processCharacterBuffer(buffer); } void HTMLTreeBuilder::processFakePEndTagIfPInScope() @@ -843,8 +846,7 @@ void HTMLTreeBuilder::processStartTagForInBody(AtomicHTMLToken& token) return; } processFakePEndTagIfPInScope(); - m_tree.insertHTMLElement(token); - m_tree.setForm(m_tree.currentElement()); + m_tree.insertHTMLFormElement(token); return; } if (token.name() == liTag) { @@ -1069,12 +1071,12 @@ bool HTMLTreeBuilder::processColgroupEndTagForInColumnGroup() void HTMLTreeBuilder::closeTheCell() { ASSERT(insertionMode() == InCellMode); - if (m_tree.openElements()->inScope(tdTag)) { - ASSERT(!m_tree.openElements()->inScope(thTag)); + if (m_tree.openElements()->inTableScope(tdTag)) { + ASSERT(!m_tree.openElements()->inTableScope(thTag)); processFakeEndTag(tdTag); return; } - ASSERT(m_tree.openElements()->inScope(thTag)); + ASSERT(m_tree.openElements()->inTableScope(thTag)); processFakeEndTag(thTag); ASSERT(insertionMode() == InRowMode); } @@ -1129,7 +1131,7 @@ void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token) } if (token.name() == inputTag) { Attribute* typeAttribute = token.getAttributeItem(typeAttr); - if (!typeAttribute || equalIgnoringCase(typeAttribute->value(), "hidden")) { + if (typeAttribute && equalIgnoringCase(typeAttribute->value(), "hidden")) { parseError(token); m_tree.insertSelfClosingHTMLElement(token); return; @@ -1140,11 +1142,14 @@ void HTMLTreeBuilder::processStartTagForInTable(AtomicHTMLToken& token) parseError(token); if (m_tree.form()) return; - m_tree.insertSelfClosingHTMLElement(token); + // FIXME: This deviates from the spec: + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10216 + m_tree.insertHTMLFormElement(token); + m_tree.openElements()->pop(); return; } parseError(token); - HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree, requiresRedirectToFosterParent(m_tree.currentElement())); + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); processStartTagForInBody(token); } @@ -1178,7 +1183,7 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) switch (insertionMode()) { case InitialMode: ASSERT(insertionMode() == InitialMode); - processDefaultForInitialMode(token); + defaultForInitial(); // Fall through. case BeforeHTMLMode: ASSERT(insertionMode() == BeforeHTMLMode); @@ -1187,7 +1192,7 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) setInsertionMode(BeforeHeadMode); return; } - processDefaultForBeforeHTMLMode(token); + defaultForBeforeHTML(); // Fall through. case BeforeHeadMode: ASSERT(insertionMode() == BeforeHeadMode); @@ -1200,13 +1205,13 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) setInsertionMode(InHeadMode); return; } - processDefaultForBeforeHeadMode(token); + defaultForBeforeHead(); // Fall through. case InHeadMode: ASSERT(insertionMode() == InHeadMode); if (processStartTagForInHead(token)) return; - processDefaultForInHeadMode(token); + defaultForInHead(); // Fall through. case AfterHeadMode: ASSERT(insertionMode() == AfterHeadMode); @@ -1243,7 +1248,7 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForAfterHeadMode(token); + defaultForAfterHead(); // Fall through case InBodyMode: ASSERT(insertionMode() == InBodyMode); @@ -1383,7 +1388,7 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForInHeadNoscriptMode(token); + defaultForInHeadNoscript(); processToken(token); break; case InFramesetMode: @@ -1482,7 +1487,7 @@ void HTMLTreeBuilder::processStartTag(AtomicHTMLToken& token) } break; case InTableTextMode: - processDefaultForInTableTextMode(token); + defaultForInTableText(); processStartTag(token); break; case InForeignContentMode: { @@ -1561,7 +1566,7 @@ bool HTMLTreeBuilder::processBodyEndTagForInBody(AtomicHTMLToken& token) parseError(token); return false; } - notImplemented(); + notImplemented(); // Emit a more specific parse error based on stack contents. setInsertionMode(AfterBodyMode); return true; } @@ -1630,7 +1635,7 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) while (1) { // 1. Element* formattingElement = m_tree.activeFormattingElements()->closestElementInScopeWithName(token.name()); - if (!formattingElement || (m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement)) { + if (!formattingElement || ((m_tree.openElements()->contains(formattingElement)) && !m_tree.openElements()->inScope(formattingElement))) { parseError(token); notImplemented(); // Check the stack of open elements for a more specific parse error. return; @@ -1675,11 +1680,7 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) if (node == formattingElementRecord) break; // 6.5 - // FIXME: We're supposed to save the original token in the entry. - AtomicHTMLToken fakeToken(HTMLToken::StartTag, node->element()->localName()); - // Is createHTMLElement correct? (instead of insertHTMLElement) - // Does this code ever leave newElement unattached? - RefPtr<Element> newElement = m_tree.createHTMLElement(fakeToken); + RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(node); HTMLFormattingElementList::Entry* nodeEntry = m_tree.activeFormattingElements()->find(node->element()); nodeEntry->replaceElement(newElement.get()); node->replaceElement(newElement.release()); @@ -1687,7 +1688,7 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) // was replaced in 6.5. // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10096 if (lastNode == furthestBlock) - bookmark.moveToAfter(node->element()); + bookmark.moveToAfter(nodeEntry); // 6.6 // Use appendChild instead of parserAddChild to handle possible reparenting. ExceptionCode ec; @@ -1698,6 +1699,8 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) } // 7 const AtomicString& commonAncestorTag = commonAncestor->localName(); + // FIXME: If this moves to HTMLConstructionSite, this check should use + // causesFosterParenting(tagName) instead. if (commonAncestorTag == tableTag || commonAncestorTag == trTag || isTableBodyContextTag(commonAncestorTag)) @@ -1708,16 +1711,21 @@ void HTMLTreeBuilder::callTheAdoptionAgency(AtomicHTMLToken& token) ASSERT(!ec); } // 8 - // FIXME: We're supposed to save the original token in the entry. - AtomicHTMLToken fakeToken(HTMLToken::StartTag, formattingElement->localName()); - RefPtr<Element> newElement = m_tree.createHTMLElement(fakeToken); + RefPtr<Element> newElement = m_tree.createHTMLElementFromElementRecord(formattingElementRecord); // 9 reparentChildren(furthestBlock->element(), newElement.get()); // 10 - furthestBlock->element()->parserAddChild(newElement); + Element* furthestBlockElement = furthestBlock->element(); + // FIXME: All this creation / parserAddChild / attach business should + // be in HTMLConstructionSite. My guess is that steps 8--12 + // should all be in some HTMLConstructionSite function. + furthestBlockElement->parserAddChild(newElement); + if (furthestBlockElement->attached()) { + ASSERT(!newElement->attached()); + newElement->attach(); + } // 11 - m_tree.activeFormattingElements()->remove(formattingElement); - m_tree.activeFormattingElements()->insertAt(newElement.get(), bookmark); + m_tree.activeFormattingElements()->swapTo(formattingElement, newElement.get(), bookmark); // 12 m_tree.openElements()->remove(formattingElement); m_tree.openElements()->insertAbove(newElement, furthestBlock); @@ -2100,7 +2108,7 @@ void HTMLTreeBuilder::processEndTagForInTable(AtomicHTMLToken& token) return; } // Is this redirection necessary here? - HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree, requiresRedirectToFosterParent(m_tree.currentElement())); + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); processEndTagForInBody(token); } @@ -2110,7 +2118,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) switch (insertionMode()) { case InitialMode: ASSERT(insertionMode() == InitialMode); - processDefaultForInitialMode(token); + defaultForInitial(); // Fall through. case BeforeHTMLMode: ASSERT(insertionMode() == BeforeHTMLMode); @@ -2118,7 +2126,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForBeforeHTMLMode(token); + defaultForBeforeHTML(); // Fall through. case BeforeHeadMode: ASSERT(insertionMode() == BeforeHeadMode); @@ -2126,7 +2134,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForBeforeHeadMode(token); + defaultForBeforeHead(); // Fall through. case InHeadMode: ASSERT(insertionMode() == InHeadMode); @@ -2139,7 +2147,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForInHeadMode(token); + defaultForInHead(); // Fall through. case AfterHeadMode: ASSERT(insertionMode() == AfterHeadMode); @@ -2147,7 +2155,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForAfterHeadMode(token); + defaultForAfterHead(); // Fall through case InBodyMode: ASSERT(insertionMode() == InBodyMode); @@ -2242,7 +2250,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) parseError(token); return; } - processDefaultForInHeadNoscriptMode(token); + defaultForInHeadNoscript(); processToken(token); break; case TextMode: @@ -2251,6 +2259,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) m_isPaused = true; ASSERT(m_tree.currentElement()->hasTagName(scriptTag)); m_scriptToProcess = m_tree.currentElement(); + m_scriptToProcessStartLine = m_lastScriptElementStartLine + 1; m_tree.openElements()->pop(); setInsertionMode(m_originalInsertionMode); return; @@ -2326,7 +2335,7 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) } break; case InTableTextMode: - processDefaultForInTableTextMode(token); + defaultForInTableText(); processEndTag(token); break; case InForeignContentMode: @@ -2339,9 +2348,11 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) HTMLElementStack::ElementRecord* nodeRecord = m_tree.openElements()->topRecord(); if (!nodeRecord->element()->hasLocalName(token.name())) { parseError(token); - // FIXME: This return is not in the spec but appears to be needed. + // FIXME: This return is not in the spec but it needed for now + // to prevent walking off the bottom of the stack. // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10118 - return; + if (!m_tree.openElements()->contains(token.name())) + return; } while (1) { if (nodeRecord->element()->hasLocalName(token.name())) { @@ -2349,8 +2360,13 @@ void HTMLTreeBuilder::processEndTag(AtomicHTMLToken& token) return; } nodeRecord = nodeRecord->next(); - if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI) + if (nodeRecord->element()->namespaceURI() == xhtmlNamespaceURI) { processUsingSecondaryInsertionModeAndAdjustInsertionMode(token); + // FIXME: This is a hack around a spec bug and is likely wrong. + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=9581 + if (nodeRecord != m_tree.openElements()->topRecord()) + return; + } } return; } @@ -2408,7 +2424,7 @@ void HTMLTreeBuilder::processComment(AtomicHTMLToken& token) return; } if (m_insertionMode == InTableTextMode) { - processDefaultForInTableTextMode(token); + defaultForInTableText(); processComment(token); return; } @@ -2418,13 +2434,16 @@ void HTMLTreeBuilder::processComment(AtomicHTMLToken& token) void HTMLTreeBuilder::processCharacter(AtomicHTMLToken& token) { ASSERT(token.type() == HTMLToken::Character); - // FIXME: Currently this design has an extra memcpy because we copy the // characters out of the HTMLTokenizer's buffer into the AtomicHTMLToken // and then into the text node. What we'd really like is to copy directly // from the HTMLTokenizer's buffer into the text node. ExternalCharacterTokenBuffer buffer(token); + processCharacterBuffer(buffer); +} +void HTMLTreeBuilder::processCharacterBuffer(ExternalCharacterTokenBuffer& buffer) +{ ReprocessBuffer: switch (insertionMode()) { case InitialMode: { @@ -2432,7 +2451,7 @@ ReprocessBuffer: buffer.skipLeadingWhitespace(); if (buffer.isEmpty()) return; - processDefaultForInitialMode(token); + defaultForInitial(); // Fall through. } case BeforeHTMLMode: { @@ -2440,7 +2459,7 @@ ReprocessBuffer: buffer.skipLeadingWhitespace(); if (buffer.isEmpty()) return; - processDefaultForBeforeHTMLMode(token); + defaultForBeforeHTML(); // Fall through. } case BeforeHeadMode: { @@ -2448,7 +2467,7 @@ ReprocessBuffer: buffer.skipLeadingWhitespace(); if (buffer.isEmpty()) return; - processDefaultForBeforeHeadMode(token); + defaultForBeforeHead(); // Fall through. } case InHeadMode: { @@ -2458,7 +2477,7 @@ ReprocessBuffer: m_tree.insertTextNode(leadingWhitespace); if (buffer.isEmpty()) return; - processDefaultForInHeadMode(token); + defaultForInHead(); // Fall through. } case AfterHeadMode: { @@ -2468,7 +2487,7 @@ ReprocessBuffer: m_tree.insertTextNode(leadingWhitespace); if (buffer.isEmpty()) return; - processDefaultForAfterHeadMode(token); + defaultForAfterHead(); // Fall through. } case InBodyMode: @@ -2511,7 +2530,7 @@ ReprocessBuffer: case AfterBodyMode: case AfterAfterBodyMode: { ASSERT(insertionMode() == AfterBodyMode || insertionMode() == AfterAfterBodyMode); - parseError(token); + // FIXME: parse error setInsertionMode(InBodyMode); goto ReprocessBuffer; break; @@ -2528,7 +2547,7 @@ ReprocessBuffer: m_tree.insertTextNode(leadingWhitespace); if (buffer.isEmpty()) return; - processDefaultForInHeadNoscriptMode(token); + defaultForInHeadNoscript(); goto ReprocessBuffer; break; } @@ -2575,28 +2594,28 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) switch (insertionMode()) { case InitialMode: ASSERT(insertionMode() == InitialMode); - processDefaultForInitialMode(token); + defaultForInitial(); // Fall through. case BeforeHTMLMode: ASSERT(insertionMode() == BeforeHTMLMode); - processDefaultForBeforeHTMLMode(token); + defaultForBeforeHTML(); // Fall through. case BeforeHeadMode: ASSERT(insertionMode() == BeforeHeadMode); - processDefaultForBeforeHeadMode(token); + defaultForBeforeHead(); // Fall through. case InHeadMode: ASSERT(insertionMode() == InHeadMode); - processDefaultForInHeadMode(token); + defaultForInHead(); // Fall through. case AfterHeadMode: ASSERT(insertionMode() == AfterHeadMode); - processDefaultForAfterHeadMode(token); + defaultForAfterHead(); // Fall through case InBodyMode: case InCellMode: ASSERT(insertionMode() == InBodyMode || insertionMode() == InCellMode); - notImplemented(); + notImplemented(); // Emit parse error based on what elemtns are still open. break; case AfterBodyMode: case AfterAfterBodyMode: @@ -2605,9 +2624,9 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) break; case InHeadNoscriptMode: ASSERT(insertionMode() == InHeadNoscriptMode); - processDefaultForInHeadNoscriptMode(token); - processToken(token); - break; + defaultForInHeadNoscript(); + processEndOfFile(token); + return; case AfterFramesetMode: case AfterAfterFramesetMode: ASSERT(insertionMode() == AfterFramesetMode || insertionMode() == AfterAfterFramesetMode); @@ -2631,7 +2650,7 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) return; } processEndOfFile(token); - break; + return; case InForeignContentMode: parseError(token); // FIXME: Following the spec would infinitely recurse on <svg><svg> @@ -2639,64 +2658,66 @@ void HTMLTreeBuilder::processEndOfFile(AtomicHTMLToken& token) m_tree.openElements()->popUntilElementWithNamespace(xhtmlNamespaceURI); setInsertionMode(m_secondaryInsertionMode); processEndOfFile(token); - break; + return; case InTableTextMode: - processDefaultForInTableTextMode(token); + defaultForInTableText(); processEndOfFile(token); - break; + return; case TextMode: case InCaptionMode: case InRowMode: notImplemented(); break; } + ASSERT(m_tree.openElements()->top()); + m_tree.openElements()->popAll(); } -void HTMLTreeBuilder::processDefaultForInitialMode(AtomicHTMLToken& token) +void HTMLTreeBuilder::defaultForInitial() { notImplemented(); - parseError(token); + // FIXME: parse error setInsertionMode(BeforeHTMLMode); } -void HTMLTreeBuilder::processDefaultForBeforeHTMLMode(AtomicHTMLToken&) +void HTMLTreeBuilder::defaultForBeforeHTML() { AtomicHTMLToken startHTML(HTMLToken::StartTag, htmlTag.localName()); m_tree.insertHTMLHtmlStartTagBeforeHTML(startHTML); setInsertionMode(BeforeHeadMode); } -void HTMLTreeBuilder::processDefaultForBeforeHeadMode(AtomicHTMLToken&) +void HTMLTreeBuilder::defaultForBeforeHead() { AtomicHTMLToken startHead(HTMLToken::StartTag, headTag.localName()); processStartTag(startHead); } -void HTMLTreeBuilder::processDefaultForInHeadMode(AtomicHTMLToken&) +void HTMLTreeBuilder::defaultForInHead() { AtomicHTMLToken endHead(HTMLToken::EndTag, headTag.localName()); processEndTag(endHead); } -void HTMLTreeBuilder::processDefaultForInHeadNoscriptMode(AtomicHTMLToken&) +void HTMLTreeBuilder::defaultForInHeadNoscript() { AtomicHTMLToken endNoscript(HTMLToken::EndTag, noscriptTag.localName()); processEndTag(endNoscript); } -void HTMLTreeBuilder::processDefaultForAfterHeadMode(AtomicHTMLToken&) +void HTMLTreeBuilder::defaultForAfterHead() { AtomicHTMLToken startBody(HTMLToken::StartTag, bodyTag.localName()); processStartTag(startBody); m_framesetOk = true; } -void HTMLTreeBuilder::processDefaultForInTableTextMode(AtomicHTMLToken& token) +void HTMLTreeBuilder::defaultForInTableText() { String characters = String::adopt(m_pendingTableCharacters); if (hasNonWhitespace(characters)) { - parseError(token); - HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree, requiresRedirectToFosterParent(m_tree.currentElement())); + // FIXME: parse error + HTMLConstructionSite::RedirectToFosterParentGuard redirecter(m_tree); m_tree.reconstructTheActiveFormattingElements(); m_tree.insertTextNode(characters); m_framesetOk = false; @@ -2775,6 +2796,7 @@ void HTMLTreeBuilder::processScriptStartTag(AtomicHTMLToken& token) m_tree.insertScriptElement(token); m_tokenizer->setState(HTMLTokenizer::ScriptDataState); m_originalInsertionMode = m_insertionMode; + m_lastScriptElementStartLine = m_tokenizer->lineNumber(); setInsertionMode(TextMode); } diff --git a/WebCore/html/HTMLTreeBuilder.h b/WebCore/html/HTMLTreeBuilder.h index 74e3398..0f87cb0 100644 --- a/WebCore/html/HTMLTreeBuilder.h +++ b/WebCore/html/HTMLTreeBuilder.h @@ -76,6 +76,7 @@ public: private: class FakeInsertionMode; + class ExternalCharacterTokenBuffer; // Represents HTML5 "insertion mode" // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#insertion-mode enum InsertionMode { @@ -134,6 +135,8 @@ private: // needs to assert which tokens it can be called with. void processAnyOtherEndTagForInBody(AtomicHTMLToken&); + void processCharacterBuffer(ExternalCharacterTokenBuffer&); + void processFakeStartTag(const QualifiedName&, PassRefPtr<NamedNodeMap> attributes = 0); void processFakeEndTag(const QualifiedName&); void processFakeCharacters(const String&); @@ -144,14 +147,13 @@ private: void processScriptStartTag(AtomicHTMLToken&); // Default processing for the different insertion modes. - // FIXME: These functions need to be renamed to remove "process" from their names. - void processDefaultForInitialMode(AtomicHTMLToken&); - void processDefaultForBeforeHTMLMode(AtomicHTMLToken&); - void processDefaultForBeforeHeadMode(AtomicHTMLToken&); - void processDefaultForInHeadMode(AtomicHTMLToken&); - void processDefaultForInHeadNoscriptMode(AtomicHTMLToken&); - void processDefaultForAfterHeadMode(AtomicHTMLToken&); - void processDefaultForInTableTextMode(AtomicHTMLToken&); + void defaultForInitial(); + void defaultForBeforeHTML(); + void defaultForBeforeHead(); + void defaultForInHead(); + void defaultForInHeadNoscript(); + void defaultForAfterHead(); + void defaultForInTableText(); void processUsingSecondaryInsertionModeAndAdjustInsertionMode(AtomicHTMLToken&); diff --git a/WebCore/html/LegacyHTMLTreeBuilder.cpp b/WebCore/html/LegacyHTMLTreeBuilder.cpp index afd79b6..ee0bcfc 100644 --- a/WebCore/html/LegacyHTMLTreeBuilder.cpp +++ b/WebCore/html/LegacyHTMLTreeBuilder.cpp @@ -277,7 +277,30 @@ PassRefPtr<Node> LegacyHTMLTreeBuilder::parseToken(Token* t) if (m_inBody && !skipMode() && m_current->localName() != styleTag && m_current->localName() != titleTag && !t->text->containsOnlyWhitespace()) m_haveContent = true; - + + // HTML5 requires text node coalescing. + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#insert-a-character + Node* previousChild = m_current->lastChild(); + if (previousChild && previousChild->isTextNode()) { + // Only coalesce text nodes if the text node wouldn't be foster parented. + if (!m_current->hasTagName(htmlTag) + && !m_current->hasTagName(tableTag) + && !m_current->hasTagName(trTag) + && !m_current->hasTagName(theadTag) + && !m_current->hasTagName(tbodyTag) + && !m_current->hasTagName(tfootTag) + && !m_current->hasTagName(titleTag)) { + // Technically we're only supposed to merge into the previous + // text node if it was the last node inserted by the parser. + // (This was a spec modification made to make it easier for + // mozilla to run their parser in a thread.) + // In practice it does not seem to matter. + CharacterData* textNode = static_cast<CharacterData*>(previousChild); + textNode->parserAppendData(t->text); + return textNode; + } + } + RefPtr<Node> n; String text = t->text.get(); unsigned charsLeft = text.length(); @@ -566,8 +589,17 @@ bool LegacyHTMLTreeBuilder::handleError(Node* n, bool flat, const AtomicString& } else { if (n->isTextNode()) { Text* t = static_cast<Text*>(n); - if (t->containsOnlyWhitespace()) + if (t->containsOnlyWhitespace()) { + if (m_head && !m_inBody) { + // We're between </head> and <body>. According to + // the HTML5 parsing algorithm, we're supposed to + // insert whitespace text nodes into the HTML element. + ExceptionCode ec; + m_current->appendChild(n, ec); + return true; + } return false; + } } if (!m_haveFrameSet) { // Ensure that head exists. @@ -862,6 +894,15 @@ bool LegacyHTMLTreeBuilder::nestedStyleCreateErrorCheck(Token* t, RefPtr<Node>&) return allowNestedRedundantTag(t->tagName); } +bool LegacyHTMLTreeBuilder::colCreateErrorCheck(Token*, RefPtr<Node>&) +{ + if (!m_current->hasTagName(tableTag)) + return true; + RefPtr<Element> implicitColgroup = HTMLElementFactory::createHTMLElement(colgroupTag, m_document, 0, true); + insertNode(implicitColgroup.get()); + return true; +} + bool LegacyHTMLTreeBuilder::tableCellCreateErrorCheck(Token*, RefPtr<Node>&) { popBlock(tdTag); @@ -955,6 +996,7 @@ PassRefPtr<Node> LegacyHTMLTreeBuilder::getNode(Token* t) mapTagsToFunc(gFunctionMap, pCloserCreateErrorTags, &LegacyHTMLTreeBuilder::pCloserCreateErrorCheck); mapTagToFunc(gFunctionMap, bodyTag, &LegacyHTMLTreeBuilder::bodyCreateErrorCheck); + mapTagToFunc(gFunctionMap, colTag, &LegacyHTMLTreeBuilder::colCreateErrorCheck); mapTagToFunc(gFunctionMap, ddTag, &LegacyHTMLTreeBuilder::ddCreateErrorCheck); mapTagToFunc(gFunctionMap, dtTag, &LegacyHTMLTreeBuilder::dtCreateErrorCheck); mapTagToFunc(gFunctionMap, formTag, &LegacyHTMLTreeBuilder::formCreateErrorCheck); diff --git a/WebCore/html/LegacyHTMLTreeBuilder.h b/WebCore/html/LegacyHTMLTreeBuilder.h index cfd9519..4ac8413 100644 --- a/WebCore/html/LegacyHTMLTreeBuilder.h +++ b/WebCore/html/LegacyHTMLTreeBuilder.h @@ -85,6 +85,7 @@ private: PassRefPtr<Node> getNode(Token*); bool bodyCreateErrorCheck(Token*, RefPtr<Node>&); bool canvasCreateErrorCheck(Token*, RefPtr<Node>&); + bool colCreateErrorCheck(Token*, RefPtr<Node>&); bool commentCreateErrorCheck(Token*, RefPtr<Node>&); bool ddCreateErrorCheck(Token*, RefPtr<Node>&); bool dtCreateErrorCheck(Token*, RefPtr<Node>&); diff --git a/WebCore/html/TimeRanges.cpp b/WebCore/html/TimeRanges.cpp index e5b070d..1e18306 100644 --- a/WebCore/html/TimeRanges.cpp +++ b/WebCore/html/TimeRanges.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,6 +27,8 @@ #include "TimeRanges.h" +#include <math.h> + using namespace WebCore; TimeRanges::TimeRanges(float start, float end) @@ -115,3 +117,21 @@ bool TimeRanges::contain(float time) const } return false; } + +float TimeRanges::nearest(float time) const +{ + ExceptionCode unused; + float closest = 0; + unsigned count = length(); + for (unsigned ndx = 0; ndx < count; ndx++) { + float startTime = start(ndx, unused); + float endTime = end(ndx, unused); + if (time >= startTime && time <= endTime) + return time; + if (fabs(startTime - time) < closest) + closest = fabsf(startTime - time); + else if (fabs(endTime - time) < closest) + closest = fabsf(endTime - time); + } + return closest; +} diff --git a/WebCore/html/TimeRanges.h b/WebCore/html/TimeRanges.h index 6be8c4e..2d7af25 100644 --- a/WebCore/html/TimeRanges.h +++ b/WebCore/html/TimeRanges.h @@ -55,6 +55,8 @@ public: void add(float start, float end); bool contain(float time) const; + + float nearest(float time) const; private: TimeRanges() { } diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/WebCore/html/canvas/CanvasRenderingContext2D.cpp index b3d212a..acd15d2 100644 --- a/WebCore/html/canvas/CanvasRenderingContext2D.cpp +++ b/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -172,6 +172,9 @@ void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) if (!style) return; + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(*style)) + return; + if (canvas()->originClean()) { if (CanvasPattern* pattern = style->canvasPattern()) { if (!pattern->originClean()) @@ -184,6 +187,7 @@ void CanvasRenderingContext2D::setStrokeStyle(PassRefPtr<CanvasStyle> style) if (!c) return; state().m_strokeStyle->applyStrokeColor(c); + state().m_unparsedStrokeColor = String(); } CanvasStyle* CanvasRenderingContext2D::fillStyle() const @@ -195,6 +199,9 @@ void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) { if (!style) return; + + if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(*style)) + return; if (canvas()->originClean()) { if (CanvasPattern* pattern = style->canvasPattern()) { @@ -208,6 +215,7 @@ void CanvasRenderingContext2D::setFillStyle(PassRefPtr<CanvasStyle> style) if (!c) return; state().m_fillStyle->applyFillColor(c); + state().m_unparsedFillColor = String(); } float CanvasRenderingContext2D::lineWidth() const @@ -478,7 +486,10 @@ void CanvasRenderingContext2D::setTransform(float m11, float m12, float m21, flo void CanvasRenderingContext2D::setStrokeColor(const String& color) { + if (color == state().m_unparsedStrokeColor) + return; setStrokeStyle(CanvasStyle::create(color)); + state().m_unparsedStrokeColor = color; } void CanvasRenderingContext2D::setStrokeColor(float grayLevel) @@ -508,7 +519,10 @@ void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k void CanvasRenderingContext2D::setFillColor(const String& color) { + if (color == state().m_unparsedFillColor) + return; setFillStyle(CanvasStyle::create(color)); + state().m_unparsedFillColor = color; } void CanvasRenderingContext2D::setFillColor(float grayLevel) @@ -543,7 +557,12 @@ void CanvasRenderingContext2D::beginPath() void CanvasRenderingContext2D::closePath() { - m_path.closeSubpath(); + if (m_path.isEmpty()) + return; + + FloatRect boundRect = m_path.boundingRect(); + if (boundRect.width() || boundRect.height()) + m_path.closeSubpath(); } void CanvasRenderingContext2D::moveTo(float x, float y) @@ -561,9 +580,11 @@ void CanvasRenderingContext2D::lineTo(float x, float y) return; if (!state().m_invertibleCTM) return; + + FloatPoint p1 = FloatPoint(x, y); if (!m_path.hasCurrentPoint()) - m_path.moveTo(FloatPoint(x, y)); - else + m_path.moveTo(p1); + else if (p1 != m_path.currentPoint()) m_path.addLineTo(FloatPoint(x, y)); } @@ -575,7 +596,10 @@ void CanvasRenderingContext2D::quadraticCurveTo(float cpx, float cpy, float x, f return; if (!m_path.hasCurrentPoint()) m_path.moveTo(FloatPoint(cpx, cpy)); - m_path.addQuadCurveTo(FloatPoint(cpx, cpy), FloatPoint(x, y)); + + FloatPoint p1 = FloatPoint(x, y); + if (p1 != m_path.currentPoint()) + m_path.addQuadCurveTo(FloatPoint(cpx, cpy), p1); } void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) @@ -586,22 +610,35 @@ void CanvasRenderingContext2D::bezierCurveTo(float cp1x, float cp1y, float cp2x, return; if (!m_path.hasCurrentPoint()) m_path.moveTo(FloatPoint(cp1x, cp1y)); - m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), FloatPoint(x, y)); + + FloatPoint p1 = FloatPoint(x, y); + if (p1 != m_path.currentPoint()) + m_path.addBezierCurveTo(FloatPoint(cp1x, cp1y), FloatPoint(cp2x, cp2y), p1); } -void CanvasRenderingContext2D::arcTo(float x0, float y0, float x1, float y1, float r, ExceptionCode& ec) +void CanvasRenderingContext2D::arcTo(float x1, float y1, float x2, float y2, float r, ExceptionCode& ec) { ec = 0; - if (!isfinite(x0) | !isfinite(y0) | !isfinite(x1) | !isfinite(y1) | !isfinite(r)) + if (!isfinite(x1) | !isfinite(y1) | !isfinite(x2) | !isfinite(y2) | !isfinite(r)) return; if (r < 0) { ec = INDEX_SIZE_ERR; return; } + if (!state().m_invertibleCTM) return; - m_path.addArcTo(FloatPoint(x0, y0), FloatPoint(x1, y1), r); + + FloatPoint p1 = FloatPoint(x1, y1); + FloatPoint p2 = FloatPoint(x2, y2); + + if (!m_path.hasCurrentPoint()) + m_path.moveTo(p1); + else if (p1 == m_path.currentPoint() || p1 == p2 || !r) + lineTo(x1, y1); + else + m_path.addArcTo(p1, p2, r); } void CanvasRenderingContext2D::arc(float x, float y, float r, float sa, float ea, bool anticlockwise, ExceptionCode& ec) @@ -646,10 +683,27 @@ static bool validateRectForCanvas(float& x, float& y, float& width, float& heigh void CanvasRenderingContext2D::rect(float x, float y, float width, float height) { - if (!validateRectForCanvas(x, y, width, height)) - return; if (!state().m_invertibleCTM) return; + + if (!isfinite(x) || !isfinite(y) || !isfinite(width) || !isfinite(height)) + return; + + if (!width && !height) { + m_path.moveTo(FloatPoint(x, y)); + return; + } + + if (width < 0) { + width = -width; + x -= width; + } + + if (height < 0) { + height = -height; + y -= height; + } + m_path.addRect(FloatRect(x, y, width, height)); } @@ -1126,6 +1180,8 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const if (!sourceCanvas->originClean()) canvas()->setOriginTainted(); + sourceCanvas->makeRenderingResultsAvailable(); + c->drawImage(buffer->image(), DeviceColorSpace, destRect, sourceRect, state().m_globalComposite); willDraw(destRect); // This call comes after drawImage, since the buffer we draw into may be our own, and we need to make sure it is dirty. // FIXME: Arguably willDraw should become didDraw and occur after drawing calls and not before them to avoid problems like this. @@ -1344,7 +1400,7 @@ void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options) dirtyRect = ctm.mapRect(r); } - if (options & CanvasWillDrawApplyShadow) { + if (options & CanvasWillDrawApplyShadow && alphaChannel(state().m_shadowColor)) { // The shadow gets applied after transformation FloatRect shadowRect(dirtyRect); shadowRect.move(state().m_shadowOffset); diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.h b/WebCore/html/canvas/CanvasRenderingContext2D.h index 43f3b35..2eac88d 100644 --- a/WebCore/html/canvas/CanvasRenderingContext2D.h +++ b/WebCore/html/canvas/CanvasRenderingContext2D.h @@ -214,6 +214,8 @@ namespace WebCore { struct State { State(); + String m_unparsedStrokeColor; + String m_unparsedFillColor; RefPtr<CanvasStyle> m_strokeStyle; RefPtr<CanvasStyle> m_fillStyle; float m_lineWidth; diff --git a/WebCore/html/canvas/CanvasStyle.cpp b/WebCore/html/canvas/CanvasStyle.cpp index 67e8201..1ae5236 100644 --- a/WebCore/html/canvas/CanvasStyle.cpp +++ b/WebCore/html/canvas/CanvasStyle.cpp @@ -33,6 +33,7 @@ #include "CanvasGradient.h" #include "CanvasPattern.h" #include "GraphicsContext.h" +#include <wtf/Assertions.h> #include <wtf/PassRefPtr.h> #if PLATFORM(CG) @@ -120,6 +121,29 @@ PassRefPtr<CanvasStyle> CanvasStyle::create(PassRefPtr<CanvasPattern> pattern) return adoptRef(new CanvasStyle(pattern)); } +bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const +{ + if (m_type != other.m_type) + return false; + + switch (m_type) { + case CanvasStyle::RGBA: + return m_rgba == other.m_rgba; + case CanvasStyle::CMYKA: + return m_cmyka.c == other.m_cmyka.c + && m_cmyka.m == other.m_cmyka.m + && m_cmyka.y == other.m_cmyka.y + && m_cmyka.k == other.m_cmyka.k + && m_cmyka.a == other.m_cmyka.a; + case CanvasStyle::Gradient: + case CanvasStyle::ImagePattern: + return false; + } + + ASSERT_NOT_REACHED(); + return false; +} + void CanvasStyle::applyStrokeColor(GraphicsContext* context) { if (!context) diff --git a/WebCore/html/canvas/CanvasStyle.h b/WebCore/html/canvas/CanvasStyle.h index 18e55cf..76ba6ef 100644 --- a/WebCore/html/canvas/CanvasStyle.h +++ b/WebCore/html/canvas/CanvasStyle.h @@ -55,6 +55,8 @@ namespace WebCore { void applyFillColor(GraphicsContext*); void applyStrokeColor(GraphicsContext*); + bool isEquivalentColor(const CanvasStyle&) const; + private: CanvasStyle(RGBA32 rgba); CanvasStyle(float grayLevel); diff --git a/WebCore/html/canvas/Float32Array.idl b/WebCore/html/canvas/Float32Array.idl index 651e74f..5a939ca 100644 --- a/WebCore/html/canvas/Float32Array.idl +++ b/WebCore/html/canvas/Float32Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Float32Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 4; diff --git a/WebCore/html/canvas/Int16Array.idl b/WebCore/html/canvas/Int16Array.idl index 095611b..02417f8 100644 --- a/WebCore/html/canvas/Int16Array.idl +++ b/WebCore/html/canvas/Int16Array.idl @@ -32,7 +32,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Int16Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 2; diff --git a/WebCore/html/canvas/Int32Array.idl b/WebCore/html/canvas/Int32Array.idl index 9d1e8ee..6977d00 100644 --- a/WebCore/html/canvas/Int32Array.idl +++ b/WebCore/html/canvas/Int32Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Int32Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 4; diff --git a/WebCore/html/canvas/Int8Array.idl b/WebCore/html/canvas/Int8Array.idl index 43c83f5..4dba9e4 100644 --- a/WebCore/html/canvas/Int8Array.idl +++ b/WebCore/html/canvas/Int8Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Int8Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 1; diff --git a/WebCore/html/canvas/Uint16Array.idl b/WebCore/html/canvas/Uint16Array.idl index 4c369a0..de1e5e0 100644 --- a/WebCore/html/canvas/Uint16Array.idl +++ b/WebCore/html/canvas/Uint16Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Uint16Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 2; diff --git a/WebCore/html/canvas/Uint32Array.idl b/WebCore/html/canvas/Uint32Array.idl index 25f5b71..ce632dd 100644 --- a/WebCore/html/canvas/Uint32Array.idl +++ b/WebCore/html/canvas/Uint32Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Uint32Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 4; diff --git a/WebCore/html/canvas/Uint8Array.idl b/WebCore/html/canvas/Uint8Array.idl index 76b8cdd..c520844 100644 --- a/WebCore/html/canvas/Uint8Array.idl +++ b/WebCore/html/canvas/Uint8Array.idl @@ -33,7 +33,8 @@ module html { HasNumericIndexGetter, HasCustomIndexSetter, GenerateNativeConverter, - CustomToJS + CustomToJS, + DontCheckEnums ] Uint8Array : ArrayBufferView { const unsigned int BYTES_PER_ELEMENT = 1; diff --git a/WebCore/html/canvas/WebGLBuffer.cpp b/WebCore/html/canvas/WebGLBuffer.cpp index e71a12b..e449052 100644 --- a/WebCore/html/canvas/WebGLBuffer.cpp +++ b/WebCore/html/canvas/WebGLBuffer.cpp @@ -74,6 +74,35 @@ bool WebGLBuffer::associateBufferData(int size) return false; } +bool WebGLBuffer::associateBufferData(ArrayBuffer* array) +{ + if (!m_target) + return false; + if (!array) + return false; + + if (m_target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) { + clearCachedMaxIndices(); + m_byteLength = array->byteLength(); + // We must always clone the incoming data because client-side + // modifications without calling bufferData or bufferSubData + // must never be able to change the validation results. + m_elementArrayBuffer = ArrayBuffer::create(array); + if (!m_elementArrayBuffer) { + m_byteLength = 0; + return false; + } + return true; + } + + if (m_target == GraphicsContext3D::ARRAY_BUFFER) { + m_byteLength = array->byteLength(); + return true; + } + + return false; +} + bool WebGLBuffer::associateBufferData(ArrayBufferView* array) { if (!m_target) @@ -103,6 +132,39 @@ bool WebGLBuffer::associateBufferData(ArrayBufferView* array) return false; } +bool WebGLBuffer::associateBufferSubData(long offset, ArrayBuffer* array) +{ + if (!m_target) + return false; + if (!array) + return false; + + if (m_target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER) { + clearCachedMaxIndices(); + + // We need to protect against integer overflow with these tests + if (offset < 0) + return false; + + unsigned long uoffset = static_cast<unsigned long>(offset); + if (uoffset > m_byteLength || array->byteLength() > m_byteLength - uoffset) + return false; + + if (!m_elementArrayBuffer) + return false; + + memcpy(static_cast<unsigned char*>(m_elementArrayBuffer->data()) + offset, + static_cast<unsigned char*>(array->data()), + array->byteLength()); + return true; + } + + if (m_target == GraphicsContext3D::ARRAY_BUFFER) + return array->byteLength() + offset <= m_byteLength; + + return false; +} + bool WebGLBuffer::associateBufferSubData(long offset, ArrayBufferView* array) { if (!m_target) diff --git a/WebCore/html/canvas/WebGLBuffer.h b/WebCore/html/canvas/WebGLBuffer.h index e1fec47..1280cf9 100644 --- a/WebCore/html/canvas/WebGLBuffer.h +++ b/WebCore/html/canvas/WebGLBuffer.h @@ -41,7 +41,9 @@ namespace WebCore { static PassRefPtr<WebGLBuffer> create(WebGLRenderingContext*); bool associateBufferData(int size); + bool associateBufferData(ArrayBuffer* array); bool associateBufferData(ArrayBufferView* array); + bool associateBufferSubData(long offset, ArrayBuffer* array); bool associateBufferSubData(long offset, ArrayBufferView* array); unsigned byteLength() const; diff --git a/WebCore/html/canvas/WebGLRenderingContext.cpp b/WebCore/html/canvas/WebGLRenderingContext.cpp index a82a4ac4..f567ac8 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -115,12 +115,12 @@ WebGLRenderingContext::WebGLRenderingContext(HTMLCanvasElement* passedCanvas, Pa m_context->getIntegerv(GraphicsContext3D::IMPLEMENTATION_COLOR_READ_TYPE, &implementationColorReadType); m_implementationColorReadType = implementationColorReadType; - int maxTextureSize = 0; - m_context->getIntegerv(GraphicsContext3D::MAX_TEXTURE_SIZE, &maxTextureSize); - m_maxTextureSize = maxTextureSize; - int maxCubeMapTextureSize = 0; - m_context->getIntegerv(GraphicsContext3D::MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapTextureSize); - m_maxCubeMapTextureSize = maxCubeMapTextureSize; + 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); if (!isGLES2Compliant()) { createFallbackBlackTextures1x1(); @@ -141,17 +141,23 @@ void WebGLRenderingContext::markContextChanged() RenderBox* renderBox = canvas()->renderBox(); if (renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) renderBox->layer()->rendererContentChanged(); - else { #endif - if (!m_markedCanvasDirty) { - // Make sure the canvas's image buffer is allocated. - canvas()->buffer(); - canvas()->willDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); - m_markedCanvasDirty = true; - } -#if USE(ACCELERATED_COMPOSITING) + if (!m_markedCanvasDirty) { + // Make sure the canvas's image buffer is allocated. + canvas()->buffer(); + canvas()->willDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); + m_markedCanvasDirty = true; } -#endif +} + +bool WebGLRenderingContext::paintRenderingResultsToCanvas() +{ + if (m_markedCanvasDirty) { + m_markedCanvasDirty = false; + m_context->paintRenderingResultsToCanvas(this); + return true; + } + return false; } void WebGLRenderingContext::beginPaint() @@ -293,17 +299,20 @@ void WebGLRenderingContext::bindTexture(unsigned long target, WebGLTexture* text m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } - if (target == GraphicsContext3D::TEXTURE_2D) + int maxLevel = 0; + if (target == GraphicsContext3D::TEXTURE_2D) { m_textureUnits[m_activeTextureUnit].m_texture2DBinding = texture; - else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) + maxLevel = m_maxTextureLevel; + } else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) { m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding = texture; - else { + maxLevel = m_maxCubeMapTextureLevel; + } else { m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return; } m_context->bindTexture(target, texture); if (!isGLES2Compliant() && texture) - texture->setTarget(target); + texture->setTarget(target, maxLevel); cleanupAfterGraphicsCall(false); } @@ -349,22 +358,11 @@ void WebGLRenderingContext::blendFuncSeparate(unsigned long srcRGB, unsigned lon void WebGLRenderingContext::bufferData(unsigned long target, int size, unsigned long usage, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (!isGLES2Compliant()) { - if (!validateBufferDataUsage(usage)) - return; - } - if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER && m_boundElementArrayBuffer) { - if (!m_boundElementArrayBuffer->associateBufferData(size)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else if (target == GraphicsContext3D::ARRAY_BUFFER && m_boundArrayBuffer) { - if (!m_boundArrayBuffer->associateBufferData(size)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!buffer->associateBufferData(size)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } @@ -372,25 +370,29 @@ void WebGLRenderingContext::bufferData(unsigned long target, int size, unsigned cleanupAfterGraphicsCall(false); } -void WebGLRenderingContext::bufferData(unsigned long target, ArrayBufferView* data, unsigned long usage, ExceptionCode& ec) +void WebGLRenderingContext::bufferData(unsigned long target, ArrayBuffer* data, unsigned long usage, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (!isGLES2Compliant()) { - if (!validateBufferDataUsage(usage)) - return; + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!buffer->associateBufferData(data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; } - if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER && m_boundElementArrayBuffer) { - if (!m_boundElementArrayBuffer->associateBufferData(data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else if (target == GraphicsContext3D::ARRAY_BUFFER && m_boundArrayBuffer) { - if (!m_boundArrayBuffer->associateBufferData(data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + + m_context->bufferData(target, data, usage); + cleanupAfterGraphicsCall(false); +} + +void WebGLRenderingContext::bufferData(unsigned long target, ArrayBufferView* data, unsigned long usage, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + WebGLBuffer* buffer = validateBufferDataParameters(target, usage); + if (!buffer) + return; + if (!buffer->associateBufferData(data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } @@ -398,21 +400,29 @@ void WebGLRenderingContext::bufferData(unsigned long target, ArrayBufferView* da cleanupAfterGraphicsCall(false); } +void WebGLRenderingContext::bufferSubData(unsigned long target, long offset, ArrayBuffer* data, ExceptionCode& ec) +{ + UNUSED_PARAM(ec); + WebGLBuffer* buffer = validateBufferDataParameters(target, GraphicsContext3D::STATIC_DRAW); + if (!buffer) + return; + if (!buffer->associateBufferSubData(offset, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); + return; + } + + m_context->bufferSubData(target, offset, data); + cleanupAfterGraphicsCall(false); +} + void WebGLRenderingContext::bufferSubData(unsigned long target, long offset, ArrayBufferView* data, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (target == GraphicsContext3D::ELEMENT_ARRAY_BUFFER && m_boundElementArrayBuffer) { - if (!m_boundElementArrayBuffer->associateBufferSubData(offset, data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else if (target == GraphicsContext3D::ARRAY_BUFFER && m_boundArrayBuffer) { - if (!m_boundArrayBuffer->associateBufferSubData(offset, data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - } else { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + WebGLBuffer* buffer = validateBufferDataParameters(target, GraphicsContext3D::STATIC_DRAW); + if (!buffer) + return; + if (!buffer->associateBufferSubData(offset, data)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } @@ -507,10 +517,8 @@ void WebGLRenderingContext::copyTexImage2D(unsigned long target, long level, uns // FIXME: if the framebuffer is not complete, none of the below should be executed. WebGLTexture* tex = getTextureBinding(target); if (!isGLES2Compliant()) { - if (tex && !level) // only for level 0 - tex->setSize(target, width, height); if (tex) - tex->setInternalFormat(internalformat); + tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); } if (m_framebufferBinding && tex) m_framebufferBinding->onAttachedObjectChange(tex); @@ -1082,18 +1090,22 @@ void WebGLRenderingContext::frontFace(unsigned long mode) void WebGLRenderingContext::generateMipmap(unsigned long target) { + RefPtr<WebGLTexture> tex; if (!isGLES2Compliant()) { - RefPtr<WebGLTexture> tex = 0; if (target == GraphicsContext3D::TEXTURE_2D) tex = m_textureUnits[m_activeTextureUnit].m_texture2DBinding; else if (target == GraphicsContext3D::TEXTURE_CUBE_MAP) tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding; - if (tex && tex->isNPOT()) { + if (tex && !tex->canGenerateMipmaps()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } } m_context->generateMipmap(target); + if (!isGLES2Compliant()) { + if (tex) + tex->generateMipmapLevelInfo(); + } cleanupAfterGraphicsCall(false); } @@ -1337,7 +1349,8 @@ WebGLGetInfo WebGLRenderingContext::getParameter(unsigned long pname, ExceptionC case GraphicsContext3D::MAX_VIEWPORT_DIMS: return getWebGLIntArrayParameter(pname); case GraphicsContext3D::NUM_COMPRESSED_TEXTURE_FORMATS: - return getLongParameter(pname); + // WebGL 1.0 specifies that there are no compressed texture formats. + return WebGLGetInfo(static_cast<long>(0)); case GraphicsContext3D::NUM_SHADER_BINARY_FORMATS: // FIXME: should we always return 0 for this? return getLongParameter(pname); @@ -2082,10 +2095,8 @@ void WebGLRenderingContext::texImage2DBase(unsigned target, unsigned level, unsi border, format, type, pixels); WebGLTexture* tex = getTextureBinding(target); if (!isGLES2Compliant()) { - if (tex && !level) // only for level 0 - tex->setSize(target, width, height); if (tex) - tex->setInternalFormat(internalformat); + tex->setLevelInfo(target, level, internalformat, width, height, type); } if (m_framebufferBinding && tex) m_framebufferBinding->onAttachedObjectChange(tex); @@ -3369,9 +3380,6 @@ bool WebGLRenderingContext::validateTexFuncParameters(unsigned long target, long if (!validateTexFuncFormatAndType(format, type)) return false; - if (isGLES2Compliant()) - return true; - if (width < 0 || height < 0 || level < 0) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; @@ -3379,7 +3387,7 @@ bool WebGLRenderingContext::validateTexFuncParameters(unsigned long target, long switch (target) { case GraphicsContext3D::TEXTURE_2D: - if (width > m_maxTextureSize || height > m_maxTextureSize) { + if (width > m_maxTextureSize || height > m_maxTextureSize || level > m_maxTextureLevel) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; } @@ -3390,7 +3398,7 @@ bool WebGLRenderingContext::validateTexFuncParameters(unsigned long target, long case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: - if (width != height || width > m_maxCubeMapTextureSize) { + if (width != height || width > m_maxCubeMapTextureSize || level > m_maxCubeMapTextureLevel) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; } @@ -3592,16 +3600,32 @@ bool WebGLRenderingContext::validateUniformMatrixParameters(const WebGLUniformLo return true; } -bool WebGLRenderingContext::validateBufferDataUsage(unsigned long usage) +WebGLBuffer* WebGLRenderingContext::validateBufferDataParameters(unsigned long target, unsigned long usage) { + WebGLBuffer* buffer = 0; + switch (target) { + case GraphicsContext3D::ELEMENT_ARRAY_BUFFER: + buffer = m_boundElementArrayBuffer.get(); + break; + case GraphicsContext3D::ARRAY_BUFFER: + buffer = m_boundArrayBuffer.get(); + break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + if (!buffer) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return 0; + } switch (usage) { case GraphicsContext3D::STREAM_DRAW: case GraphicsContext3D::STATIC_DRAW: case GraphicsContext3D::DYNAMIC_DRAW: - return true; + return buffer; } m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); - return false; + return 0; } void WebGLRenderingContext::vertexAttribfImpl(unsigned long index, int expectedSize, float v0, float v1, float v2, float v3) diff --git a/WebCore/html/canvas/WebGLRenderingContext.h b/WebCore/html/canvas/WebGLRenderingContext.h index d03d972..fa9ffdb 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.h +++ b/WebCore/html/canvas/WebGLRenderingContext.h @@ -77,7 +77,9 @@ class WebKitCSSMatrix; void blendFuncSeparate(unsigned long srcRGB, unsigned long dstRGB, unsigned long srcAlpha, unsigned long dstAlpha); void bufferData(unsigned long target, int size, unsigned long usage, ExceptionCode&); + void bufferData(unsigned long target, ArrayBuffer* data, unsigned long usage, ExceptionCode&); void bufferData(unsigned long target, ArrayBufferView* data, unsigned long usage, ExceptionCode&); + void bufferSubData(unsigned long target, long offset, ArrayBuffer* data, ExceptionCode&); void bufferSubData(unsigned long target, long offset, ArrayBufferView* data, ExceptionCode&); unsigned long checkFramebufferStatus(unsigned long target); @@ -304,6 +306,10 @@ class WebKitCSSMatrix; void reshape(int width, int height); + // Return value true indicates canvas is updated during the call, + // false indicates no updates. + bool paintRenderingResultsToCanvas(); + // Helpers for notification about paint events. void beginPaint(); void endPaint(); @@ -410,8 +416,10 @@ class WebKitCSSMatrix; RefPtr<WebGLTexture> m_blackTexture2D; RefPtr<WebGLTexture> m_blackTextureCubeMap; - long m_maxTextureSize; - long m_maxCubeMapTextureSize; + int m_maxTextureSize; + int m_maxCubeMapTextureSize; + int m_maxTextureLevel; + int m_maxCubeMapTextureLevel; int m_packAlignment; int m_unpackAlignment; @@ -501,8 +509,9 @@ class WebKitCSSMatrix; bool validateUniformMatrixParameters(const WebGLUniformLocation* location, bool transpose, Float32Array* v, int mod); bool validateUniformMatrixParameters(const WebGLUniformLocation* location, bool transpose, void* v, int size, int mod); - // Helper function to validate usage for bufferData. - bool validateBufferDataUsage(unsigned long); + // Helper function to validate parameters for bufferData. + // Return the current bound buffer to target, or 0 if parameters are invalid. + WebGLBuffer* validateBufferDataParameters(unsigned long target, unsigned long usage); // Helper functions for vertexAttribNf{v}. void vertexAttribfImpl(unsigned long index, int expectedSize, float v0, float v1, float v2, float v3); diff --git a/WebCore/html/canvas/WebGLRenderingContext.idl b/WebCore/html/canvas/WebGLRenderingContext.idl index 1ea4c6d..654c7aa 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.idl +++ b/WebCore/html/canvas/WebGLRenderingContext.idl @@ -28,7 +28,8 @@ module html { interface [ Conditional=3D_CANVAS, InterfaceUUID=98fb48ae-7216-489c-862b-8e1217fc4443, - ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54 + ImplementationUUID=ab4f0781-152f-450e-9546-5b3987491a54, + DontCheckEnums ] WebGLRenderingContext : CanvasRenderingContext { /* ClearBufferMask */ @@ -477,9 +478,10 @@ module html { void blendEquationSeparate(in unsigned long modeRGB, in unsigned long modeAlpha); void blendFunc(in unsigned long sfactor, in unsigned long dfactor); void blendFuncSeparate(in unsigned long srcRGB, in unsigned long dstRGB, in unsigned long srcAlpha, in unsigned long dstAlpha); - + void bufferData(in unsigned long target, in ArrayBuffer data, in unsigned long usage) raises (DOMException); void bufferData(in unsigned long target, in ArrayBufferView data, in unsigned long usage) raises (DOMException); void bufferData(in unsigned long target, in long size, in unsigned long usage) raises (DOMException); + void bufferSubData(in unsigned long target, in long offset, in ArrayBuffer data) raises (DOMException); void bufferSubData(in unsigned long target, in long offset, in ArrayBufferView data) raises (DOMException); unsigned long checkFramebufferStatus(in unsigned long target); diff --git a/WebCore/html/canvas/WebGLTexture.cpp b/WebCore/html/canvas/WebGLTexture.cpp index 1cc7d5d..d832038 100644 --- a/WebCore/html/canvas/WebGLTexture.cpp +++ b/WebCore/html/canvas/WebGLTexture.cpp @@ -45,32 +45,39 @@ WebGLTexture::WebGLTexture(WebGLRenderingContext* ctx) , m_magFilter(GraphicsContext3D::LINEAR) , m_wrapS(GraphicsContext3D::REPEAT) , m_wrapT(GraphicsContext3D::REPEAT) - , m_internalFormat(0) , m_isNPOT(false) + , m_isComplete(false) , m_needToUseBlackTexture(false) { setObject(context()->graphicsContext3D()->createTexture()); - for (int ii = 0; ii < 6; ++ii) { - m_width[ii] = 0; - m_height[ii] = 0; - } } -void WebGLTexture::setTarget(unsigned long target) +void WebGLTexture::setTarget(unsigned long target, int maxLevel) { + if (!object()) + return; // Target is finalized the first time bindTexture() is called. if (m_target) return; switch (target) { case GraphicsContext3D::TEXTURE_2D: + m_target = target; + m_info.resize(1); + m_info[0].resize(maxLevel); + break; case GraphicsContext3D::TEXTURE_CUBE_MAP: m_target = target; + m_info.resize(6); + for (int ii = 0; ii < 6; ++ii) + m_info[ii].resize(maxLevel); break; } } void WebGLTexture::setParameteri(unsigned long pname, int param) { + if (!object() || !m_target) + return; switch (pname) { case GraphicsContext3D::TEXTURE_MIN_FILTER: switch (param) { @@ -113,88 +120,195 @@ void WebGLTexture::setParameteri(unsigned long pname, int param) default: return; } - updateNPOTStates(); + update(); } void WebGLTexture::setParameterf(unsigned long pname, float param) { + if (!object() || !m_target) + return; int iparam = static_cast<int>(param); setParameteri(pname, iparam); } -void WebGLTexture::setSize(unsigned long target, unsigned width, unsigned height) +void WebGLTexture::setLevelInfo(unsigned long target, int level, unsigned long internalFormat, int width, int height, unsigned long type) { - if (!width || !height) + if (!object() || !m_target) return; - int iTarget = -1; + // We assume level, internalFormat, width, height, and type have all been + // validated already. + int index = mapTargetToIndex(target); + if (index < 0) + return; + m_info[index][level].setInfo(internalFormat, width, height, type); + update(); +} + +void WebGLTexture::generateMipmapLevelInfo() +{ + if (!object() || !m_target) + return; + if (!canGenerateMipmaps()) + return; + if (m_isComplete) + return; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + const LevelInfo& info0 = m_info[ii][0]; + int width = info0.width; + int height = info0.height; + int levelCount = computeLevelCount(width, height); + for (int level = 1; level < levelCount; ++level) { + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + LevelInfo& info = m_info[ii][level]; + info.setInfo(info0.internalFormat, width, height, info0.type); + } + } + m_isComplete = true; +} + +unsigned long WebGLTexture::getInternalFormat() const +{ + if (!object() || !m_target) + return 0; + return m_info[0][0].internalFormat; +} + +bool WebGLTexture::isNPOT(unsigned width, unsigned height) +{ + if (!width || !height) + return false; + if ((width & (width - 1)) || (height & (height - 1))) + return true; + return false; +} + +bool WebGLTexture::isNPOT() const +{ + if (!object()) + return false; + return m_isNPOT; +} + +bool WebGLTexture::needToUseBlackTexture() const +{ + if (!object()) + return false; + return m_needToUseBlackTexture; +} + +void WebGLTexture::_deleteObject(Platform3DObject object) +{ + context()->graphicsContext3D()->deleteTexture(object); +} + +int WebGLTexture::mapTargetToIndex(unsigned long target) +{ if (m_target == GraphicsContext3D::TEXTURE_2D) { if (target == GraphicsContext3D::TEXTURE_2D) - iTarget = 0; - } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP && width == height) { + return 0; + } else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) { switch (target) { case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: - iTarget = 0; - break; + return 0; case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: - iTarget = 1; - break; + return 1; case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Y: - iTarget = 2; - break; + return 2; case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: - iTarget = 3; - break; + return 3; case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: - iTarget = 4; - break; + return 4; case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: - iTarget = 5; - break; + return 5; } } - if (iTarget < 0) - return; - m_width[iTarget] = width; - m_height[iTarget] = height; - updateNPOTStates(); + return -1; } -bool WebGLTexture::isNPOT(unsigned width, unsigned height) +bool WebGLTexture::canGenerateMipmaps() { - if (!width || !height) + if (isNPOT()) return false; - if ((width & (width - 1)) || (height & (height - 1))) - return true; - return false; + const LevelInfo& first = m_info[0][0]; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + const LevelInfo& info = m_info[ii][0]; + if (!info.valid + || info.width != first.width || info.height != first.height + || info.internalFormat != first.internalFormat || info.type != first.type) + return false; + } + return true; } -void WebGLTexture::_deleteObject(Platform3DObject object) +int WebGLTexture::computeLevelCount(int width, int height) { - context()->graphicsContext3D()->deleteTexture(object); + // return 1 + log2Floor(std::max(width, height)); + int n = std::max(width, height); + if (n <= 0) + return 0; + int log = 0; + int value = n; + for (int ii = 4; ii >= 0; --ii) { + int shift = (1 << ii); + int x = (value >> shift); + if (x) { + value = x; + log += shift; + } + } + ASSERT(value == 1); + return log + 1; } -void WebGLTexture::updateNPOTStates() +void WebGLTexture::update() { - int numTargets = 0; - if (m_target == GraphicsContext3D::TEXTURE_2D) - numTargets = 1; - else if (m_target == GraphicsContext3D::TEXTURE_CUBE_MAP) - numTargets = 6; m_isNPOT = false; - unsigned w0 = m_width[0], h0 = m_height[0]; - for (int ii = 0; ii < numTargets; ++ii) { - if (ii && (!m_width[ii] || !m_height[ii] || m_width[ii] != w0 || m_height[ii] != h0)) { - // We only set NPOT for complete cube map textures. - m_isNPOT = false; + for (size_t ii = 0; ii < m_info.size(); ++ii) { + if (isNPOT(m_info[ii][0].width, m_info[ii][0].height)) { + m_isNPOT = true; break; } - if (isNPOT(m_width[ii], m_height[ii])) - m_isNPOT = true; } + m_isComplete = true; + const LevelInfo& first = m_info[0][0]; + int levelCount = computeLevelCount(first.width, first.height); + if (levelCount < 1) + m_isComplete = false; + else { + for (size_t ii = 0; ii < m_info.size() && m_isComplete; ++ii) { + const LevelInfo& info0 = m_info[ii][0]; + if (!info0.valid + || info0.width != first.width || info0.height != first.height + || info0.internalFormat != first.internalFormat || info0.type != first.type) { + m_isComplete = false; + break; + } + int width = info0.width; + int height = info0.height; + for (int level = 1; level < levelCount; ++level) { + width = std::max(1, width >> 1); + height = std::max(1, height >> 1); + const LevelInfo& info = m_info[ii][level]; + if (!info.valid + || info.width != width || info.height != height + || info.internalFormat != info0.internalFormat || info.type != info0.type) { + m_isComplete = false; + break; + } + + } + } + } + m_needToUseBlackTexture = false; + // NPOT if (m_isNPOT && ((m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) || m_wrapS != GraphicsContext3D::CLAMP_TO_EDGE || m_wrapT != GraphicsContext3D::CLAMP_TO_EDGE)) m_needToUseBlackTexture = true; + // Completeness + if (!m_isComplete && m_minFilter != GraphicsContext3D::NEAREST && m_minFilter != GraphicsContext3D::LINEAR) + m_needToUseBlackTexture = true; } } diff --git a/WebCore/html/canvas/WebGLTexture.h b/WebCore/html/canvas/WebGLTexture.h index 4d16b59..d4a32f0 100644 --- a/WebCore/html/canvas/WebGLTexture.h +++ b/WebCore/html/canvas/WebGLTexture.h @@ -30,6 +30,7 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/Vector.h> namespace WebCore { @@ -47,19 +48,26 @@ namespace WebCore { cubeMapRWrapModeInitialized = initialized; } - void setTarget(unsigned long); + void setTarget(unsigned long target, int maxLevel); void setParameteri(unsigned long pname, int param); void setParameterf(unsigned long pname, float param); - void setSize(unsigned long target, unsigned width, unsigned height); - void setInternalFormat(unsigned long internalformat) { m_internalFormat = internalformat; } - unsigned long getInternalFormat() const { return m_internalFormat; } + void setLevelInfo(unsigned long target, int level, unsigned long internalFormat, int width, int height, unsigned long type); + bool canGenerateMipmaps(); + // Generate all level information. + void generateMipmapLevelInfo(); + + unsigned long getInternalFormat() const; + + // Whether width/height is NotPowerOfTwo. static bool isNPOT(unsigned, unsigned); - bool isNPOT() const { return m_isNPOT; } + bool isNPOT() const; // Determine if texture sampling should always return [0, 0, 0, 1] (OpenGL ES 2.0 Sec 3.8.2). - bool needToUseBlackTexture() const { return m_needToUseBlackTexture; } + bool needToUseBlackTexture() const; + + static int computeLevelCount(int width, int height); protected: WebGLTexture(WebGLRenderingContext*); @@ -69,7 +77,9 @@ namespace WebCore { private: virtual bool isTexture() const { return true; } - void updateNPOTStates(); + void update(); + + int mapTargetToIndex(unsigned long); bool cubeMapRWrapModeInitialized; @@ -80,12 +90,37 @@ namespace WebCore { int m_wrapS; int m_wrapT; - unsigned long m_internalFormat; - - unsigned m_width[6]; - unsigned m_height[6]; + class LevelInfo { + public: + LevelInfo() + : valid(false) + , internalFormat(0) + , width(0) + , height(0) + , type(0) + { + } + + void setInfo(unsigned long internalFmt, int w, int h, unsigned long tp) + { + valid = true; + internalFormat = internalFmt; + width = w; + height = h; + type = tp; + } + + bool valid; + unsigned long internalFormat; + int width; + int height; + unsigned long type; + }; + + Vector<Vector<LevelInfo> > m_info; bool m_isNPOT; + bool m_isComplete; bool m_needToUseBlackTexture; }; |