summaryrefslogtreecommitdiffstats
path: root/WebCore/html
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/html')
-rw-r--r--WebCore/html/Blob.cpp5
-rw-r--r--WebCore/html/Blob.h1
-rw-r--r--WebCore/html/BlobBuilder.cpp33
-rw-r--r--WebCore/html/File.cpp20
-rw-r--r--WebCore/html/File.h16
-rw-r--r--WebCore/html/File.idl3
-rw-r--r--WebCore/html/FileError.idl3
-rw-r--r--WebCore/html/FileReader.idl3
-rw-r--r--WebCore/html/FormDataList.cpp5
-rw-r--r--WebCore/html/HTMLAttributeNames.in1
-rw-r--r--WebCore/html/HTMLCanvasElement.cpp22
-rw-r--r--WebCore/html/HTMLCanvasElement.h8
-rw-r--r--WebCore/html/HTMLConstructionSite.cpp137
-rw-r--r--WebCore/html/HTMLConstructionSite.h23
-rw-r--r--WebCore/html/HTMLElementStack.cpp16
-rw-r--r--WebCore/html/HTMLElementStack.h2
-rw-r--r--WebCore/html/HTMLFormElement.cpp7
-rw-r--r--WebCore/html/HTMLFormElement.h4
-rw-r--r--WebCore/html/HTMLFormattingElementList.cpp45
-rw-r--r--WebCore/html/HTMLFormattingElementList.h25
-rw-r--r--WebCore/html/HTMLInputElement.cpp89
-rw-r--r--WebCore/html/HTMLInputElement.h5
-rw-r--r--WebCore/html/HTMLInputElement.idl7
-rw-r--r--WebCore/html/HTMLLinkElement.cpp4
-rw-r--r--WebCore/html/HTMLLinkElement.h1
-rw-r--r--WebCore/html/HTMLMediaElement.cpp103
-rw-r--r--WebCore/html/HTMLMediaElement.h3
-rw-r--r--WebCore/html/HTMLMediaElement.idl3
-rw-r--r--WebCore/html/HTMLToken.h23
-rw-r--r--WebCore/html/HTMLTreeBuilder.cpp334
-rw-r--r--WebCore/html/HTMLTreeBuilder.h18
-rw-r--r--WebCore/html/LegacyHTMLTreeBuilder.cpp46
-rw-r--r--WebCore/html/LegacyHTMLTreeBuilder.h1
-rw-r--r--WebCore/html/TimeRanges.cpp22
-rw-r--r--WebCore/html/TimeRanges.h2
-rw-r--r--WebCore/html/canvas/CanvasRenderingContext2D.cpp78
-rw-r--r--WebCore/html/canvas/CanvasRenderingContext2D.h2
-rw-r--r--WebCore/html/canvas/CanvasStyle.cpp24
-rw-r--r--WebCore/html/canvas/CanvasStyle.h2
-rw-r--r--WebCore/html/canvas/Float32Array.idl3
-rw-r--r--WebCore/html/canvas/Int16Array.idl3
-rw-r--r--WebCore/html/canvas/Int32Array.idl3
-rw-r--r--WebCore/html/canvas/Int8Array.idl3
-rw-r--r--WebCore/html/canvas/Uint16Array.idl3
-rw-r--r--WebCore/html/canvas/Uint32Array.idl3
-rw-r--r--WebCore/html/canvas/Uint8Array.idl3
-rw-r--r--WebCore/html/canvas/WebGLBuffer.cpp62
-rw-r--r--WebCore/html/canvas/WebGLBuffer.h2
-rw-r--r--WebCore/html/canvas/WebGLRenderingContext.cpp184
-rw-r--r--WebCore/html/canvas/WebGLRenderingContext.h17
-rw-r--r--WebCore/html/canvas/WebGLRenderingContext.idl6
-rw-r--r--WebCore/html/canvas/WebGLTexture.cpp212
-rw-r--r--WebCore/html/canvas/WebGLTexture.h57
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;
};