diff options
Diffstat (limited to 'WebCore/html')
76 files changed, 1923 insertions, 5010 deletions
diff --git a/WebCore/html/Blob.cpp b/WebCore/html/Blob.cpp index 977ffa6..3a62ab1 100644 --- a/WebCore/html/Blob.cpp +++ b/WebCore/html/Blob.cpp @@ -31,29 +31,93 @@ #include "config.h" #include "Blob.h" +#include "BlobData.h" #include "BlobItem.h" +#include "BlobURL.h" #include "FileSystem.h" +#include "ScriptExecutionContext.h" +#include "ThreadableBlobRegistry.h" namespace WebCore { -Blob::Blob(ScriptExecutionContext*, const String& type, const BlobItemList& items) - : m_type(type) +// FIXME: To be removed when we switch to using BlobData. +Blob::Blob(ScriptExecutionContext* scriptExecutionContext, const String& type, const BlobItemList& items) + : m_scriptExecutionContext(scriptExecutionContext) + , m_type(type) + , m_size(0) { + m_scriptExecutionContext->addBlob(this); for (size_t i = 0; i < items.size(); ++i) m_items.append(items[i]); } -Blob::Blob(ScriptExecutionContext*, const PassRefPtr<BlobItem>& item) +// FIXME: To be removed when we switch to using BlobData. +Blob::Blob(ScriptExecutionContext* scriptExecutionContext, const PassRefPtr<BlobItem>& item) + : m_scriptExecutionContext(scriptExecutionContext) + , m_size(0) { + m_scriptExecutionContext->addBlob(this); m_items.append(item); } -Blob::Blob(ScriptExecutionContext*, const String& path) +// FIXME: To be removed when we switch to using BlobData. +Blob::Blob(ScriptExecutionContext* scriptExecutionContext, const String& path) + : m_scriptExecutionContext(scriptExecutionContext) + , m_size(0) { + m_scriptExecutionContext->addBlob(this); // Note: this doesn't initialize the type unlike File(path). m_items.append(FileBlobItem::create(path)); } +Blob::Blob(ScriptExecutionContext* scriptExecutionContext, PassOwnPtr<BlobData> blobData, long long size) + : m_scriptExecutionContext(scriptExecutionContext) + , m_type(blobData->contentType()) + , m_size(size) +{ + ASSERT(blobData.get() && !blobData->items().isEmpty()); + + m_scriptExecutionContext->addBlob(this); + + // Create a new internal URL and register it with the provided blob data. + m_url = BlobURL::createURL(scriptExecutionContext); + ThreadableBlobRegistry::registerBlobURL(scriptExecutionContext, m_url, blobData); +} + +Blob::Blob(ScriptExecutionContext* scriptExecutionContext, const KURL& srcURL, const String& type, long long size) + : m_scriptExecutionContext(scriptExecutionContext) + , m_type(type) + , m_size(size) +{ + m_scriptExecutionContext->addBlob(this); + + // FIXME: To be removed when we switch to using BlobData. + if (srcURL.isEmpty()) + return; + + // Create a new internal URL and register it with the same blob data as the source URL. + m_url = BlobURL::createURL(scriptExecutionContext); + ThreadableBlobRegistry::registerBlobURL(scriptExecutionContext, m_url, srcURL); +} + +Blob::~Blob() +{ + // The internal URL is only used to refer to the Blob object. So we need to unregister the URL when the object is GC-ed. + if (m_scriptExecutionContext) { + m_scriptExecutionContext->removeBlob(this); + ThreadableBlobRegistry::unregisterBlobURL(m_scriptExecutionContext, m_url); + } +} + +void Blob::contextDestroyed() +{ + ASSERT(m_scriptExecutionContext); + + // Unregister the internal URL before the context is gone. + ThreadableBlobRegistry::unregisterBlobURL(m_scriptExecutionContext, m_url); + m_scriptExecutionContext = 0; +} + unsigned long long Blob::size() const { // FIXME: JavaScript cannot represent sizes as large as unsigned long long, we need to @@ -64,6 +128,7 @@ unsigned long long Blob::size() const return size; } +// FIXME: To be removed when we switch to using BlobData. const String& Blob::path() const { ASSERT(m_items.size() == 1 && m_items[0]->toFileBlobItem()); diff --git a/WebCore/html/Blob.h b/WebCore/html/Blob.h index 7afc7a0..374a401 100644 --- a/WebCore/html/Blob.h +++ b/WebCore/html/Blob.h @@ -32,39 +32,45 @@ #define Blob_h #include "BlobItem.h" +#include "KURL.h" #include "PlatformString.h" +#include <wtf/PassOwnPtr.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> namespace WebCore { +class BlobData; class ScriptExecutionContext; class Blob : public RefCounted<Blob> { public: + // FIXME: To be removed when we switch to using BlobData. static PassRefPtr<Blob> create(ScriptExecutionContext* scriptExecutionContext, const String& type, const BlobItemList& items) { return adoptRef(new Blob(scriptExecutionContext, type, items)); } - // FIXME: Deprecated method. This is called only from - // bindings/v8/SerializedScriptValue.cpp and the usage in it will become invalid once - // BlobBuilder is introduced. - static PassRefPtr<Blob> create(ScriptExecutionContext* scriptExecutionContext, const String& path) + // For deserialization. + static PassRefPtr<Blob> create(ScriptExecutionContext* scriptExecutionContext, const KURL& srcURL, const String& type, long long size) { - return adoptRef(new Blob(scriptExecutionContext, path)); + return adoptRef(new Blob(scriptExecutionContext, srcURL, type, size)); } - virtual ~Blob() { } + virtual ~Blob(); + void contextDestroyed(); + + const KURL& url() const { return m_url; } unsigned long long size() const; const String& type() const { return m_type; } virtual bool isFile() const { return false; } - // FIXME: Deprecated method. + // FIXME: To be removed when we switch to using BlobData. const String& path() const; + // FIXME: To be removed when we switch to using BlobData. const BlobItemList& items() const { return m_items; } #if ENABLE(BLOB) @@ -72,14 +78,26 @@ public: #endif protected: + // FIXME: To be removed when we switch to using BlobData. Blob(ScriptExecutionContext*, const String& type, const BlobItemList&); Blob(ScriptExecutionContext*, const PassRefPtr<BlobItem>&); - - // FIXME: Deprecated constructor. See also the comment for Blob::create(path). Blob(ScriptExecutionContext*, const String& path); + Blob(ScriptExecutionContext*, PassOwnPtr<BlobData>, long long size); + + // For deserialization. + Blob(ScriptExecutionContext*, const KURL& srcURL, const String& type, long long size); + + // FIXME: To be removed when we switch to using BlobData. BlobItemList m_items; + + // This is an internal URL referring to the blob data associated with this object. + // It is only used by FileReader to read the blob data via loading from the blob URL resource. + KURL m_url; + + ScriptExecutionContext* m_scriptExecutionContext; String m_type; + long long m_size; }; } // namespace WebCore diff --git a/WebCore/html/BlobBuilder.cpp b/WebCore/html/BlobBuilder.cpp index 0592ff0..29a7595 100644 --- a/WebCore/html/BlobBuilder.cpp +++ b/WebCore/html/BlobBuilder.cpp @@ -32,11 +32,11 @@ #include "BlobBuilder.h" -#include "AtomicString.h" #include "Blob.h" #include "ExceptionCode.h" #include "LineEnding.h" #include "TextEncoding.h" +#include <wtf/text/AtomicString.h> namespace WebCore { diff --git a/WebCore/html/BlobURL.cpp b/WebCore/html/BlobURL.cpp new file mode 100644 index 0000000..610aac4 --- /dev/null +++ b/WebCore/html/BlobURL.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "BlobURL.h" + +#include "KURL.h" +#include "PlatformString.h" +#include "ScriptExecutionContext.h" +#include "SecurityOrigin.h" +#include "UUID.h" + +namespace WebCore { + +KURL BlobURL::createURL(ScriptExecutionContext* scriptExecutionContext) +{ + // Create the blob URL in the following format: + // blob:%escaped_origin%/%UUID% + // The origin of the host page is encoded in the URL value to allow easy lookup of the origin when the security check needs + // to be performed. + String urlString = "blob:"; + urlString += encodeWithURLEscapeSequences(scriptExecutionContext->securityOrigin()->toString()); + urlString += "/"; + urlString += createCanonicalUUIDString(); + return KURL(ParsedURLString, urlString); +} + +KURL BlobURL::getOrigin(const KURL& url) +{ + ASSERT(url.protocolIs("blob")); + + unsigned startIndex = url.pathStart(); + unsigned afterEndIndex = url.pathAfterLastSlash(); + String origin = url.string().substring(startIndex, afterEndIndex - startIndex); + return KURL(ParsedURLString, decodeURLEscapeSequences(origin)); +} + +} // namespace WebCore diff --git a/WebCore/html/BlobURL.h b/WebCore/html/BlobURL.h new file mode 100644 index 0000000..2ce2c85 --- /dev/null +++ b/WebCore/html/BlobURL.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BlobURL_h +#define BlobURL_h + +#include "KURL.h" + +namespace WebCore { + +class ScriptExecutionContext; + +class BlobURL { +public: + static KURL createURL(ScriptExecutionContext*); + static KURL getOrigin(const KURL&); +}; + +} + +#endif // BlobURL_h diff --git a/WebCore/html/DataGridColumn.h b/WebCore/html/DataGridColumn.h index 3d480a9..555389c 100644 --- a/WebCore/html/DataGridColumn.h +++ b/WebCore/html/DataGridColumn.h @@ -28,10 +28,10 @@ #if ENABLE(DATAGRID) -#include "AtomicString.h" #include "RenderStyle.h" #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#include <wtf/text/AtomicString.h> namespace WebCore { diff --git a/WebCore/html/DataGridColumnList.cpp b/WebCore/html/DataGridColumnList.cpp index 9613402..15590b2 100644 --- a/WebCore/html/DataGridColumnList.cpp +++ b/WebCore/html/DataGridColumnList.cpp @@ -27,11 +27,11 @@ #if ENABLE(DATAGRID) -#include "AtomicString.h" #include "DataGridColumnList.h" #include "HTMLDataGridElement.h" #include "PlatformString.h" #include "RenderObject.h" +#include <wtf/text/AtomicString.h> namespace WebCore { diff --git a/WebCore/html/File.cpp b/WebCore/html/File.cpp index 109e0d3..253cb4d 100644 --- a/WebCore/html/File.cpp +++ b/WebCore/html/File.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "File.h" +#include "BlobData.h" #include "FileSystem.h" #include "MIMETypeRegistry.h" @@ -37,6 +38,13 @@ File::File(ScriptExecutionContext* scriptExecutionContext, const String& path) Init(); } +File::File(ScriptExecutionContext* scriptExecutionContext, const String& path, const KURL& url, const String& type) + : Blob(scriptExecutionContext, url, type, BlobDataItem::toEndOfFile) +{ + // FIXME: To be removed when we switch to using BlobData. + m_items.append(FileBlobItem::create(path)); +} + #if ENABLE(DIRECTORY_UPLOAD) File::File(ScriptExecutionContext* scriptExecutionContext, const String& relativePath, const String& filePath) : Blob(scriptExecutionContext, FileBlobItem::create(filePath, relativePath)) @@ -49,8 +57,8 @@ 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('.'); - if (index != -1) + size_t index = fileName.reverseFind('.'); + if (index != notFound) m_type = MIMETypeRegistry::getMIMETypeForExtension(fileName.substring(index + 1)); } diff --git a/WebCore/html/File.h b/WebCore/html/File.h index c0aecc8..06a73c5 100644 --- a/WebCore/html/File.h +++ b/WebCore/html/File.h @@ -39,6 +39,12 @@ public: return adoptRef(new File(scriptExecutionContext, path)); } + // For deserialization. + static PassRefPtr<File> create(ScriptExecutionContext* scriptExecutionContext, const String& path, const KURL& url, const String& type) + { + return adoptRef(new File(scriptExecutionContext, path, url, type)); + } + #if ENABLE(DIRECTORY_UPLOAD) static PassRefPtr<File> create(ScriptExecutionContext* scriptExecutionContext, const String& relativePath, const String& path) { @@ -60,11 +66,15 @@ public: private: File(ScriptExecutionContext*, const String& path); - void Init(); + + // For deserialization. + File(ScriptExecutionContext*, const String& path, const KURL&, const String& type); #if ENABLE(DIRECTORY_UPLOAD) File(ScriptExecutionContext*, const String& relativePath, const String& path); #endif + + void Init(); }; } // namespace WebCore diff --git a/WebCore/html/FileReader.cpp b/WebCore/html/FileReader.cpp index 88f218f..e99fdc4 100644 --- a/WebCore/html/FileReader.cpp +++ b/WebCore/html/FileReader.cpp @@ -160,33 +160,70 @@ void FileReader::terminate() void FileReader::didStart() { m_state = Opening; - m_streamProxy->openForRead(m_fileBlob.get()); + + ASSERT(m_fileBlob->items().size() == 1 && m_fileBlob->items().at(0)->toFileBlobItem()); + const FileRangeBlobItem* fileRangeItem = m_fileBlob->items().at(0)->toFileRangeBlobItem(); + double expectedModificationTime = fileRangeItem ? fileRangeItem->snapshotModificationTime() : 0; + + m_streamProxy->getSize(m_fileBlob->path(), expectedModificationTime); } void FileReader::didGetSize(long long size) { + // If the size is -1, it means the file has been moved or changed. Fail now. + if (size == -1) { + didFail(NOT_FOUND_ERR); + return; + } + m_state = Reading; fireEvent(eventNames().loadstartEvent); - m_totalBytes = size; - m_streamProxy->read(&m_buffer.at(0), m_buffer.size()); + ASSERT(m_fileBlob->items().size() == 1 && m_fileBlob->items().at(0)->toFileBlobItem()); + const FileRangeBlobItem* fileRangeItem = m_fileBlob->items().at(0)->toFileRangeBlobItem(); + long long start = fileRangeItem ? fileRangeItem->start() : 0; + + // The size passed back is the size of the whole file. If the underlying item is a sliced file, we need to use the slice length. + m_totalBytes = fileRangeItem ? fileRangeItem->size() : size; + + m_streamProxy->openForRead(m_fileBlob->path(), start, m_totalBytes); } -void FileReader::didRead(const char* data, int bytesRead) +void FileReader::didOpen(ExceptionCode ec) { - ASSERT(data && bytesRead); + if (ec) { + didFail(ec); + return; + } + + m_streamProxy->read(m_buffer.data(), m_buffer.size()); +} +void FileReader::didRead(int bytesRead) +{ // Bail out if we have aborted the reading. if (m_state == Completed) - return; + return; + + // If bytesRead is -1, it means an error happens. + if (bytesRead == -1) { + didFail(NOT_READABLE_ERR); + return; + } + + // If bytesRead is 0, it means the reading is done. + if (!bytesRead) { + didFinish(); + return; + } switch (m_readType) { case ReadFileAsBinaryString: - m_result += String(data, static_cast<unsigned>(bytesRead)); + m_result += String(m_buffer.data(), static_cast<unsigned>(bytesRead)); break; case ReadFileAsText: case ReadFileAsDataURL: - m_rawData.append(data, static_cast<unsigned>(bytesRead)); + m_rawData.append(m_buffer.data(), static_cast<unsigned>(bytesRead)); m_isRawDataConverted = false; break; default: @@ -205,7 +242,7 @@ void FileReader::didRead(const char* data, int bytesRead) } // Continue reading. - m_streamProxy->read(&m_buffer.at(0), m_buffer.size()); + m_streamProxy->read(m_buffer.data(), m_buffer.size()); } void FileReader::didFinish() @@ -240,15 +277,15 @@ FileReader::ReadyState FileReader::readyState() const switch (m_state) { case None: case Starting: - return Empty; + return EMPTY; case Opening: case Reading: - return Loading; + return LOADING; case Completed: - return Done; + return DONE; } ASSERT_NOT_REACHED(); - return Empty; + return EMPTY; } const ScriptString& FileReader::result() diff --git a/WebCore/html/FileReader.h b/WebCore/html/FileReader.h index a5339a9..2237af5 100644 --- a/WebCore/html/FileReader.h +++ b/WebCore/html/FileReader.h @@ -63,9 +63,9 @@ public: virtual ~FileReader(); enum ReadyState { - Empty = 0, - Loading = 1, - Done = 2 + EMPTY = 0, + LOADING = 1, + DONE = 2 }; void readAsBinaryString(Blob*); @@ -89,9 +89,8 @@ public: // FileStreamClient virtual void didStart(); virtual void didGetSize(long long); - virtual void didRead(const char*, int); - virtual void didFinish(); - virtual void didFail(ExceptionCode); + virtual void didOpen(ExceptionCode); + virtual void didRead(int); using RefCounted<FileReader>::ref; using RefCounted<FileReader>::deref; @@ -130,6 +129,8 @@ private: void fireEvent(const AtomicString& type); void convertToText(); void convertToDataURL(); + void didFinish(); + void didFail(ExceptionCode); InternalState m_state; EventTargetData m_eventTargetData; diff --git a/WebCore/html/FileReader.idl b/WebCore/html/FileReader.idl index fb3b979..b36e9d3 100644 --- a/WebCore/html/FileReader.idl +++ b/WebCore/html/FileReader.idl @@ -34,8 +34,7 @@ module html { CanBeConstructed, CallWith=ScriptExecutionContext, EventTarget, - NoStaticTables, - DontCheckEnums + NoStaticTables ] FileReader { // ready states const unsigned short EMPTY = 0; diff --git a/WebCore/html/FileStream.cpp b/WebCore/html/FileStream.cpp index abe7bad..a89c67a 100644 --- a/WebCore/html/FileStream.cpp +++ b/WebCore/html/FileStream.cpp @@ -39,9 +39,8 @@ namespace WebCore { -FileStream::FileStream(FileStreamClient* client) - : m_client(client) - , m_handle(invalidPlatformFileHandle) +FileStream::FileStream() + : m_handle(invalidPlatformFileHandle) , m_bytesProcessed(0) , m_totalBytesToRead(0) { @@ -52,130 +51,99 @@ FileStream::~FileStream() ASSERT(!isHandleValid(m_handle)); } +// FIXME: To be removed when we switch to using BlobData. void FileStream::start() { - ASSERT(!isMainThread()); - m_client->didStart(); } void FileStream::stop() { - ASSERT(!isMainThread()); close(); - m_client->didStop(); } -void FileStream::openForRead(Blob* blob) +long long FileStream::getSize(const String& path, double expectedModificationTime) { - ASSERT(!isMainThread()); + // Check the modification time for the possible file change. + time_t modificationTime; + if (!getFileModificationTime(path, modificationTime)) + return -1; + if (expectedModificationTime) { + if (static_cast<time_t>(expectedModificationTime) != modificationTime) + return -1; + } + + // Now get the file size. + long long length; + if (!getFileSize(path, length)) + return -1; + + return length; +} +ExceptionCode FileStream::openForRead(const String& path, long long offset, long long length) +{ if (isHandleValid(m_handle)) - return; - - // FIXME: Need to handle multiple items that may include non-file ones when BlobBuilder is introduced. - ASSERT(blob->items().size() >= 1); - const FileBlobItem* fileItem = blob->items().at(0)->toFileBlobItem(); - if (!fileItem) { - ASSERT(false); - m_client->didFail(NOT_READABLE_ERR); - return; - } + return 0; - // Check if the file exists by querying its modification time. We choose not to call fileExists() in order to save an - // extra file system call when the modification time is needed to check the validity of the sliced file blob. - // Per the spec, we need to return different error codes to differentiate between non-existent file and permission error. - // openFile() could not tell use the failure reason. - time_t currentModificationTime; - if (!getFileModificationTime(fileItem->path(), currentModificationTime)) { - m_client->didFail(NOT_FOUND_ERR); - return; - } + // Open the file. + m_handle = openFile(path, OpenForRead); + if (!isHandleValid(m_handle)) + return NOT_READABLE_ERR; - // Open the file blob. - m_handle = openFile(fileItem->path(), OpenForRead); - if (!isHandleValid(m_handle)) { - m_client->didFail(NOT_READABLE_ERR); - return; + // Jump to the beginning position if the file has been sliced. + if (offset > 0) { + if (seekFile(m_handle, offset, SeekFromBeginning) < 0) + return NOT_READABLE_ERR; } - const FileRangeBlobItem* fileRangeItem = fileItem->toFileRangeBlobItem(); - if (fileRangeItem) { - // Check the modificationt time for the possible file change. - if (static_cast<time_t>(fileRangeItem->snapshotModificationTime()) != currentModificationTime) { - m_client->didFail(NOT_READABLE_ERR); - return; - } - - // Jump to the beginning position if the file has been sliced. - if (fileRangeItem->start() > 0) { - if (seekFile(m_handle, fileRangeItem->start(), SeekFromBeginning) < 0) { - m_client->didFail(NOT_READABLE_ERR); - return; - } - } - } + m_totalBytesToRead = length; + m_bytesProcessed = 0; - // Get the size. - m_totalBytesToRead = blob->size(); - m_client->didGetSize(m_totalBytesToRead); + return 0; } -void FileStream::openForWrite(const String&) +ExceptionCode FileStream::openForWrite(const String&) { - ASSERT(!isMainThread()); // FIXME: to be implemented. + return NOT_SUPPORTED_ERR; } void FileStream::close() { - ASSERT(!isMainThread()); if (isHandleValid(m_handle)) { closeFile(m_handle); m_handle = invalidPlatformFileHandle; } } -void FileStream::read(char* buffer, int length) +int FileStream::read(char* buffer, int bufferSize) { - ASSERT(!isMainThread()); - - if (!isHandleValid(m_handle)) { - m_client->didFail(NOT_READABLE_ERR); - return; - } - - if (m_bytesProcessed >= m_totalBytesToRead) { - m_client->didFinish(); - return; - } + if (!isHandleValid(m_handle)) + return -1; long long remaining = m_totalBytesToRead - m_bytesProcessed; - int bytesToRead = (remaining < length) ? static_cast<int>(remaining) : length; - int bytesRead = readFromFile(m_handle, buffer, bytesToRead); - if (bytesRead < 0) { - m_client->didFail(NOT_READABLE_ERR); - return; - } - - if (!bytesRead) { - m_client->didFinish(); - return; - } - - m_bytesProcessed += bytesRead; - m_client->didRead(buffer, bytesRead); + int bytesToRead = (remaining < bufferSize) ? static_cast<int>(remaining) : bufferSize; + int bytesRead = 0; + if (bytesToRead > 0) + bytesRead = readFromFile(m_handle, buffer, bytesToRead); + if (bytesRead < 0) + return -1; + if (bytesRead > 0) + m_bytesProcessed += bytesRead; + + return bytesRead; } -void FileStream::write(Blob*, long long, int) +int FileStream::write(Blob*, long long, int) { - ASSERT(!isMainThread()); // FIXME: to be implemented. + return -1; } -void FileStream::truncate(long long) +ExceptionCode FileStream::truncate(long long) { - ASSERT(!isMainThread()); // FIXME: to be implemented. + return NOT_SUPPORTED_ERR; } } // namespace WebCore diff --git a/WebCore/html/FileStream.h b/WebCore/html/FileStream.h index b5eccd4..e299fe4 100644 --- a/WebCore/html/FileStream.h +++ b/WebCore/html/FileStream.h @@ -33,7 +33,7 @@ #if ENABLE(BLOB) || ENABLE(FILE_WRITER) -#include "FileStreamClient.h" +#include "ExceptionCode.h" #include "FileSystem.h" #include <wtf/Forward.h> #include <wtf/PassRefPtr.h> @@ -43,29 +43,52 @@ namespace WebCore { class Blob; -// All methods are synchronous and should be called on File or Worker thread. +// All methods are synchronous. class FileStream : public RefCounted<FileStream> { public: - static PassRefPtr<FileStream> create(FileStreamClient* client) + static PassRefPtr<FileStream> create() { - return adoptRef(new FileStream(client)); + return adoptRef(new FileStream()); } virtual ~FileStream(); + // FIXME: To be removed when we switch to using BlobData. void start(); + + // Aborts the operation. void stop(); - void openForRead(Blob*); - void openForWrite(const String& path); + // Gets the size of a file. Also validates if the file has been changed or not if the expected modification time is provided, i.e. non-zero. + // Returns total number of bytes if successful. -1 otherwise. + long long getSize(const String& path, double expectedModificationTime); + + // Opens a file for reading. The reading starts at the specified offset and lasts till the specified length. + // Returns 0 on success. Exception code otherwise. + ExceptionCode openForRead(const String& path, long long offset, long long length); + + // Opens a file for writing. + // Returns 0 on success. Exception code otherwise. + ExceptionCode openForWrite(const String& path); + + // Closes the file. void close(); - void read(char* buffer, int length); - void write(Blob* blob, long long position, int length); - void truncate(long long position); + + // Reads a file into the provided data buffer. + // Returns number of bytes being read on success. -1 otherwise. + // If 0 is returned, it means that the reading is completed. + int read(char* buffer, int length); + + // Writes a blob to the file. + // Returns number of bytes being written on success. -1 otherwise. + int write(Blob*, long long position, int length); + + // Truncates the file to the specified position. + // Returns 0 on success. Exception code otherwise. + ExceptionCode truncate(long long position); private: - FileStream(FileStreamClient*); + FileStream(); - FileStreamClient* m_client; PlatformFileHandle m_handle; long long m_bytesProcessed; long long m_totalBytesToRead; diff --git a/WebCore/html/FileStreamClient.h b/WebCore/html/FileStreamClient.h index 2e7091f..440d2fb 100644 --- a/WebCore/html/FileStreamClient.h +++ b/WebCore/html/FileStreamClient.h @@ -40,16 +40,18 @@ namespace WebCore { class FileStreamClient { public: // For reading. - virtual void didRead(const char*, int) { } + virtual void didRead(int) { } // For writing. virtual void didWrite(int) { } + virtual void didTruncate(ExceptionCode) { } - // For both reading and writing. + // FIXME: To be removed when we switch to using BlobData. virtual void didStart() { } + + // For both reading and writing. + virtual void didOpen(ExceptionCode) { } virtual void didStop() { } - virtual void didFinish() { } - virtual void didFail(ExceptionCode) { } virtual void didGetSize(long long) { } protected: diff --git a/WebCore/html/FileStreamProxy.cpp b/WebCore/html/FileStreamProxy.cpp index e3b9e79..f50b7e8 100644 --- a/WebCore/html/FileStreamProxy.cpp +++ b/WebCore/html/FileStreamProxy.cpp @@ -37,6 +37,7 @@ #include "Blob.h" #include "CrossThreadTask.h" #include "FileStream.h" +#include "FileStreamClient.h" #include "FileThread.h" #include "FileThreadTask.h" #include "PlatformString.h" @@ -47,7 +48,7 @@ namespace WebCore { inline FileStreamProxy::FileStreamProxy(ScriptExecutionContext* context, FileStreamClient* client) : m_context(context) , m_client(client) - , m_stream(FileStream::create(this)) + , m_stream(FileStream::create()) { } @@ -59,7 +60,7 @@ PassRefPtr<FileStreamProxy> FileStreamProxy::create(ScriptExecutionContext* cont // This is balanced by the deref in derefProxyOnContext below. proxy->ref(); - proxy->fileThread()->postTask(createFileThreadTask(proxy->m_stream.get(), &FileStream::start)); + proxy->fileThread()->postTask(createFileThreadTask(proxy.get(), &FileStreamProxy::startOnFileThread)); return proxy.release(); } @@ -68,133 +69,150 @@ FileStreamProxy::~FileStreamProxy() { } -void FileStreamProxy::openForRead(Blob* blob) +FileThread* FileStreamProxy::fileThread() { - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::openForRead, blob)); + ASSERT(m_context->isContextThread()); + ASSERT(m_context->fileThread()); + return m_context->fileThread(); } -void FileStreamProxy::openForWrite(const String& path) +static void didStart(ScriptExecutionContext*, FileStreamProxy* proxy) { - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::openForWrite, path)); + if (proxy->client()) + proxy->client()->didStart(); } -void FileStreamProxy::close() +void FileStreamProxy::startOnFileThread() { - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::close)); + m_stream->start(); + m_context->postTask(createCallbackTask(&didStart, this)); } -void FileStreamProxy::read(char* buffer, int length) +void FileStreamProxy::stop() { - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::read, buffer, length)); + // Clear the client so that we won't be calling callbacks on the client. + m_client = 0; + + fileThread()->unscheduleTasks(m_stream.get()); + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::stopOnFileThread)); } -void FileStreamProxy::write(Blob* blob, long long position, int length) +static void derefProxyOnContext(ScriptExecutionContext*, FileStreamProxy* proxy) { - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::write, blob, position, length)); + ASSERT(proxy->hasOneRef()); + proxy->deref(); } -void FileStreamProxy::truncate(long long position) +void FileStreamProxy::stopOnFileThread() { - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::truncate, position)); + m_stream->stop(); + m_context->postTask(createCallbackTask(&derefProxyOnContext, this)); } -FileThread* FileStreamProxy::fileThread() +static void didGetSize(ScriptExecutionContext*, FileStreamProxy* proxy, long long size) { - ASSERT(m_context->isContextThread()); - ASSERT(m_context->fileThread()); - return m_context->fileThread(); + if (proxy->client()) + proxy->client()->didGetSize(size); } -void FileStreamProxy::stop() +void FileStreamProxy::getSize(const String& path, double expectedModificationTime) { - // Clear the client so that we won't be calling callbacks on the client. - m_client = 0; + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::getSizeOnFileThread, path, expectedModificationTime)); +} - fileThread()->unscheduleTasks(m_stream.get()); - fileThread()->postTask(createFileThreadTask(m_stream.get(), &FileStream::stop)); +void FileStreamProxy::getSizeOnFileThread(const String& path, double expectedModificationTime) +{ + long long size = m_stream->getSize(path, expectedModificationTime); + m_context->postTask(createCallbackTask(&didGetSize, this, size)); } -static void notifyGetSizeOnContext(ScriptExecutionContext*, FileStreamProxy* proxy, long long size) +static void didOpen(ScriptExecutionContext*, FileStreamProxy* proxy, ExceptionCode ec) { if (proxy->client()) - proxy->client()->didGetSize(size); + proxy->client()->didOpen(ec); } -void FileStreamProxy::didGetSize(long long size) +void FileStreamProxy::openForRead(const String& path, long long offset, long long length) { - ASSERT(!m_context->isContextThread()); - m_context->postTask(createCallbackTask(¬ifyGetSizeOnContext, this, size)); + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::openForReadOnFileThread, path, offset, length)); } -static void notifyReadOnContext(ScriptExecutionContext*, FileStreamProxy* proxy, const char* data, int bytesRead) +void FileStreamProxy::openForReadOnFileThread(const String& path, long long offset, long long length) { - if (proxy->client()) - proxy->client()->didRead(data, bytesRead); + ExceptionCode ec = m_stream->openForRead(path, offset, length); + m_context->postTask(createCallbackTask(&didOpen, this, ec)); +} + +void FileStreamProxy::openForWrite(const String& path) +{ + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::openForWriteOnFileThread, path)); } -void FileStreamProxy::didRead(const char* data, int bytesRead) +void FileStreamProxy::openForWriteOnFileThread(const String& path) { - ASSERT(!m_context->isContextThread()); - m_context->postTask(createCallbackTask(¬ifyReadOnContext, this, data, bytesRead)); + ExceptionCode ec = m_stream->openForWrite(path); + m_context->postTask(createCallbackTask(&didOpen, this, ec)); } -static void notifyWriteOnContext(ScriptExecutionContext*, FileStreamProxy* proxy, int bytesWritten) +void FileStreamProxy::close() { - if (proxy->client()) - proxy->client()->didWrite(bytesWritten); + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::closeOnFileThread)); } -void FileStreamProxy::didWrite(int bytesWritten) +void FileStreamProxy::closeOnFileThread() { - ASSERT(!m_context->isContextThread()); - m_context->postTask(createCallbackTask(¬ifyWriteOnContext, this, bytesWritten)); + m_stream->close(); } -static void notifyStartOnContext(ScriptExecutionContext*, FileStreamProxy* proxy) +static void didRead(ScriptExecutionContext*, FileStreamProxy* proxy, int bytesRead) { if (proxy->client()) - proxy->client()->didStart(); + proxy->client()->didRead(bytesRead); } -void FileStreamProxy::didStart() +void FileStreamProxy::read(char* buffer, int length) +{ + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::readOnFileThread, buffer, length)); +} + +void FileStreamProxy::readOnFileThread(char* buffer, int length) { - ASSERT(!m_context->isContextThread()); - m_context->postTask(createCallbackTask(¬ifyStartOnContext, this)); + int bytesRead = m_stream->read(buffer, length); + m_context->postTask(createCallbackTask(&didRead, this, bytesRead)); } -static void notifyFinishOnContext(ScriptExecutionContext*, FileStreamProxy* proxy) +static void didWrite(ScriptExecutionContext*, FileStreamProxy* proxy, int bytesWritten) { if (proxy->client()) - proxy->client()->didFinish(); + proxy->client()->didWrite(bytesWritten); } -void FileStreamProxy::didFinish() +void FileStreamProxy::write(Blob* blob, long long position, int length) { - ASSERT(!m_context->isContextThread()); - m_context->postTask(createCallbackTask(¬ifyFinishOnContext, this)); + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::writeOnFileThread, blob, position, length)); } -static void notifyFailOnContext(ScriptExecutionContext*, FileStreamProxy* proxy, ExceptionCode ec) +void FileStreamProxy::writeOnFileThread(Blob* blob, long long position, int length) { - if (proxy->client()) - proxy->client()->didFail(ec); + int bytesWritten = m_stream->write(blob, position, length); + m_context->postTask(createCallbackTask(&didWrite, this, bytesWritten)); } -void FileStreamProxy::didFail(ExceptionCode ec) +static void didTruncate(ScriptExecutionContext*, FileStreamProxy* proxy, ExceptionCode ec) { - ASSERT(!m_context->isContextThread()); - m_context->postTask(createCallbackTask(¬ifyFailOnContext, this, ec)); + if (proxy->client()) + proxy->client()->didTruncate(ec); } -static void derefProxyOnContext(ScriptExecutionContext*, FileStreamProxy* proxy) +void FileStreamProxy::truncate(long long position) { - ASSERT(proxy->hasOneRef()); - proxy->deref(); + fileThread()->postTask(createFileThreadTask(this, &FileStreamProxy::truncateOnFileThread, position)); } -void FileStreamProxy::didStop() +void FileStreamProxy::truncateOnFileThread(long long position) { - m_context->postTask(createCallbackTask(&derefProxyOnContext, this)); + ExceptionCode ec = m_stream->truncate(position); + m_context->postTask(createCallbackTask(&didTruncate, this, ec)); } } // namespace WebCore diff --git a/WebCore/html/FileStreamProxy.h b/WebCore/html/FileStreamProxy.h index 8523d4a..1d03a58 100644 --- a/WebCore/html/FileStreamProxy.h +++ b/WebCore/html/FileStreamProxy.h @@ -34,7 +34,6 @@ #if ENABLE(BLOB) || ENABLE(FILE_WRITER) -#include "FileStreamClient.h" #include <wtf/Forward.h> #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> @@ -44,20 +43,22 @@ namespace WebCore { class Blob; class FileStream; +class FileStreamClient; class FileThread; class ScriptExecutionContext; -// A proxy module that calls corresponding FileStream methods on the file thread. Note: you must call stop() first and then release the reference to destruct the FileStreamProxy instance. -class FileStreamProxy : public RefCounted<FileStreamProxy>, public FileStreamClient { +// A proxy module that asynchronously calls corresponding FileStream methods on the file thread. Note: you must call stop() first and then release the reference to destruct the FileStreamProxy instance. +class FileStreamProxy : public RefCounted<FileStreamProxy> { public: static PassRefPtr<FileStreamProxy> create(ScriptExecutionContext*, FileStreamClient*); virtual ~FileStreamProxy(); - void openForRead(Blob* blob); + void getSize(const String& path, double expectedModificationTime); + void openForRead(const String& path, long long offset, long long length); void openForWrite(const String& path); void close(); void read(char* buffer, int length); - void write(Blob* blob, long long position, int length); + void write(Blob*, long long position, int length); void truncate(long long position); // Stops the proxy and scedules it to be destructed. All the pending tasks will be aborted and the file stream will be closed. @@ -69,17 +70,19 @@ public: private: FileStreamProxy(ScriptExecutionContext*, FileStreamClient*); - // FileStreamClient methods. - virtual void didGetSize(long long); - virtual void didRead(const char*, int); - virtual void didWrite(int); - virtual void didFinish(); - virtual void didFail(ExceptionCode); - virtual void didStart(); - virtual void didStop(); - FileThread* fileThread(); + // Called on File thread. + void startOnFileThread(); + void stopOnFileThread(); + void getSizeOnFileThread(const String& path, double expectedModificationTime); + void openForReadOnFileThread(const String& path, long long offset, long long length); + void openForWriteOnFileThread(const String& path); + void closeOnFileThread(); + void readOnFileThread(char* buffer, int length); + void writeOnFileThread(Blob*, long long position, int length); + void truncateOnFileThread(long long position); + RefPtr<ScriptExecutionContext> m_context; FileStreamClient* m_client; RefPtr<FileStream> m_stream; diff --git a/WebCore/html/FileThreadTask.h b/WebCore/html/FileThreadTask.h index 09b647f..3443457 100644 --- a/WebCore/html/FileThreadTask.h +++ b/WebCore/html/FileThreadTask.h @@ -39,11 +39,11 @@ namespace WebCore { -template<typename R, typename T> +template<typename T> class FileThreadTask0 : public FileThread::Task { public: - typedef R (T::*Method)(); - typedef FileThreadTask0<R, T> FileThreadTaskImpl; + typedef void (T::*Method)(); + typedef FileThreadTask0<T> FileThreadTaskImpl; static PassOwnPtr<FileThreadTaskImpl> create(T* instance, Method method) { @@ -66,11 +66,11 @@ private: Method m_method; }; -template<typename R, typename T, typename P1, typename MP1> +template<typename T, typename P1, typename MP1> class FileThreadTask1 : public FileThread::Task { public: - typedef R (T::*Method)(MP1); - typedef FileThreadTask1<R, T, P1, MP1> FileThreadTaskImpl; + typedef void (T::*Method)(MP1); + typedef FileThreadTask1<T, P1, MP1> FileThreadTaskImpl; typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; static PassOwnPtr<FileThreadTaskImpl> create(T* instance, Method method, Param1 parameter1) @@ -96,11 +96,11 @@ private: P1 m_parameter1; }; -template<typename R, typename T, typename P1, typename MP1, typename P2, typename MP2> +template<typename T, typename P1, typename MP1, typename P2, typename MP2> class FileThreadTask2 : public FileThread::Task { public: - typedef R (T::*Method)(MP1, MP2); - typedef FileThreadTask2<R, T, P1, MP1, P2, MP2> FileThreadTaskImpl; + typedef void (T::*Method)(MP1, MP2); + typedef FileThreadTask2<T, P1, MP1, P2, MP2> FileThreadTaskImpl; typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; @@ -129,11 +129,11 @@ private: P2 m_parameter2; }; -template<typename R, typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> +template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> class FileThreadTask3 : public FileThread::Task { public: typedef void (T::*Method)(MP1, MP2, MP3); - typedef FileThreadTask3<R, T, P1, MP1, P2, MP2, P3, MP3> FileThreadTaskImpl; + typedef FileThreadTask3<T, P1, MP1, P2, MP2, P3, MP3> FileThreadTaskImpl; typedef typename CrossThreadTaskTraits<P1>::ParamType Param1; typedef typename CrossThreadTaskTraits<P2>::ParamType Param2; typedef typename CrossThreadTaskTraits<P3>::ParamType Param3; @@ -165,56 +165,56 @@ private: P3 m_parameter3; }; -template<typename R, typename T> +template<typename T> PassOwnPtr<FileThread::Task> createFileThreadTask( T* const callee, - R (T::*method)()); + void (T::*method)()); -template<typename R, typename T> +template<typename T> PassOwnPtr<FileThread::Task> createFileThreadTask( T* const callee, - R (T::*method)()) + void (T::*method)()) { - return FileThreadTask0<R, T>::create( + return FileThreadTask0<T>::create( callee, method); } -template<typename R, typename T, typename P1, typename MP1> +template<typename T, typename P1, typename MP1> PassOwnPtr<FileThread::Task> createFileThreadTask( T* const callee, - R (T::*method)(MP1), + void (T::*method)(MP1), const P1& parameter1) { - return FileThreadTask1<R, T, typename CrossThreadCopier<P1>::Type, MP1>::create( + return FileThreadTask1<T, typename CrossThreadCopier<P1>::Type, MP1>::create( callee, method, CrossThreadCopier<P1>::copy(parameter1)); } -template<typename R, typename T, typename P1, typename MP1, typename P2, typename MP2> +template<typename T, typename P1, typename MP1, typename P2, typename MP2> PassOwnPtr<FileThread::Task> createFileThreadTask( T* const callee, - R (T::*method)(MP1, MP2), + void (T::*method)(MP1, MP2), const P1& parameter1, const P2& parameter2) { - return FileThreadTask2<R, T, typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2>::create( + return FileThreadTask2<T, typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2>::create( callee, method, CrossThreadCopier<P1>::copy(parameter1), CrossThreadCopier<P2>::copy(parameter2)); } -template<typename R, typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> +template<typename T, typename P1, typename MP1, typename P2, typename MP2, typename P3, typename MP3> PassOwnPtr<FileThread::Task> createFileThreadTask( T* const callee, - R (T::*method)(MP1, MP2, MP3), + void (T::*method)(MP1, MP2, MP3), const P1& parameter1, const P2& parameter2, const P3& parameter3) { - return FileThreadTask3<R, T, typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3>::create( + return FileThreadTask3<T, typename CrossThreadCopier<P1>::Type, MP1, typename CrossThreadCopier<P2>::Type, MP2, typename CrossThreadCopier<P3>::Type, MP3>::create( callee, method, CrossThreadCopier<P1>::copy(parameter1), diff --git a/WebCore/html/HTMLAnchorElement.cpp b/WebCore/html/HTMLAnchorElement.cpp index 0d2e92d..e1ee86a 100644 --- a/WebCore/html/HTMLAnchorElement.cpp +++ b/WebCore/html/HTMLAnchorElement.cpp @@ -381,11 +381,11 @@ void HTMLAnchorElement::setHost(const String& value) if (!url.canSetHostOrPort()) return; - int separator = value.find(':'); + size_t separator = value.find(':'); if (!separator) return; - if (separator == -1) + if (separator == notFound) url.setHostAndPort(value); else { unsigned portEnd; diff --git a/WebCore/html/HTMLCanvasElement.cpp b/WebCore/html/HTMLCanvasElement.cpp index ef5574a..84ab227 100644 --- a/WebCore/html/HTMLCanvasElement.cpp +++ b/WebCore/html/HTMLCanvasElement.cpp @@ -209,8 +209,7 @@ CanvasRenderingContext* HTMLCanvasElement::getContext(const String& type, Canvas void HTMLCanvasElement::willDraw(const FloatRect& rect) { - if (m_imageBuffer) - m_imageBuffer->clearImage(); + m_copiedImage.clear(); // Clear our image snapshot if we have one. if (RenderBox* ro = renderBox()) { FloatRect destRect = ro->contentBoxRect(); @@ -233,6 +232,7 @@ void HTMLCanvasElement::reset() return; bool ok; + bool hadImageBuffer = hasCreatedImageBuffer(); int w = getAttribute(widthAttr).toInt(&ok); if (!ok || w < 0) w = DefaultWidth; @@ -241,14 +241,13 @@ void HTMLCanvasElement::reset() h = DefaultHeight; IntSize oldSize = size(); - setSurfaceSize(IntSize(w, h)); + setSurfaceSize(IntSize(w, h)); // The image buffer gets cleared here. #if ENABLE(3D_CANVAS) - if (m_context && m_context->is3d()) + if (m_context && m_context->is3d() && oldSize != size()) static_cast<WebGLRenderingContext*>(m_context.get())->reshape(width(), height()); #endif - bool hadImageBuffer = hasCreatedImageBuffer(); if (m_context && m_context->is2d()) static_cast<CanvasRenderingContext2D*>(m_context.get())->reset(); @@ -277,23 +276,21 @@ void HTMLCanvasElement::paint(GraphicsContext* context, const IntRect& r) WebGLRenderingContext* context3D = 0; if (m_context && m_context->is3d()) { context3D = static_cast<WebGLRenderingContext*>(m_context.get()); - context3D->beginPaint(); + if (!context3D->paintsIntoCanvasBuffer()) + return; + context3D->paintRenderingResultsToCanvas(); } #endif if (hasCreatedImageBuffer()) { ImageBuffer* imageBuffer = buffer(); if (imageBuffer) { - Image* image = imageBuffer->imageForRendering(); - if (image) - context->drawImage(image, DeviceColorSpace, r); + if (imageBuffer->drawsUsingCopy()) + context->drawImage(copiedImage(), DeviceColorSpace, r); + else + context->drawImageBuffer(imageBuffer, DeviceColorSpace, r); } } - -#if ENABLE(3D_CANVAS) - if (context3D) - context3D->endPaint(); -#endif } #if ENABLE(3D_CANVAS) @@ -325,6 +322,7 @@ void HTMLCanvasElement::setSurfaceSize(const IntSize& size) m_size = size; m_hasCreatedImageBuffer = false; m_imageBuffer.clear(); + m_copiedImage.clear(); } String HTMLCanvasElement::toDataURL(const String& mimeType, const double* quality, ExceptionCode& ec) @@ -405,6 +403,7 @@ void HTMLCanvasElement::createImageBuffer() const return; m_imageBuffer->context()->scale(FloatSize(size.width() / unscaledSize.width(), size.height() / unscaledSize.height())); m_imageBuffer->context()->setShadowsIgnoreTransforms(true); + m_imageBuffer->context()->setImageInterpolationQuality(CanvasInterpolationQuality); } GraphicsContext* HTMLCanvasElement::drawingContext() const @@ -419,6 +418,18 @@ ImageBuffer* HTMLCanvasElement::buffer() const return m_imageBuffer.get(); } +Image* HTMLCanvasElement::copiedImage() const +{ + if (!m_copiedImage && buffer()) + m_copiedImage = buffer()->copyImage(); + return m_copiedImage.get(); +} + +void HTMLCanvasElement::clearCopiedImage() +{ + m_copiedImage.clear(); +} + AffineTransform HTMLCanvasElement::baseTransform() const { ASSERT(m_hasCreatedImageBuffer); diff --git a/WebCore/html/HTMLCanvasElement.h b/WebCore/html/HTMLCanvasElement.h index 2e3570d..3270667 100644 --- a/WebCore/html/HTMLCanvasElement.h +++ b/WebCore/html/HTMLCanvasElement.h @@ -38,6 +38,7 @@ class CanvasContextAttributes; class CanvasRenderingContext; class GraphicsContext; class HTMLCanvasElement; +class Image; class ImageBuffer; class IntSize; @@ -93,6 +94,8 @@ public: CanvasRenderingContext* renderingContext() const { return m_context.get(); } ImageBuffer* buffer() const; + Image* copiedImage() const; + void clearCopiedImage(); IntRect convertLogicalToDevice(const FloatRect&) const; IntSize convertLogicalToDevice(const FloatSize&) const; @@ -165,6 +168,8 @@ private: // m_createdImageBuffer means we tried to malloc the buffer. We didn't necessarily get it. mutable bool m_hasCreatedImageBuffer; mutable OwnPtr<ImageBuffer> m_imageBuffer; + + mutable RefPtr<Image> m_copiedImage; // FIXME: This is temporary for platforms that have to copy the image buffer to render (and for CSSCanvasValue). }; } //namespace diff --git a/WebCore/html/HTMLConstructionSite.cpp b/WebCore/html/HTMLConstructionSite.cpp index 1a9a373..a25c7d9 100644 --- a/WebCore/html/HTMLConstructionSite.cpp +++ b/WebCore/html/HTMLConstructionSite.cpp @@ -39,7 +39,6 @@ #include "HTMLScriptElement.h" #include "HTMLToken.h" #include "HTMLTokenizer.h" -#include "LegacyHTMLDocumentParser.h" #include "LegacyHTMLTreeBuilder.h" #include "LocalizedStrings.h" #if ENABLE(MATHML) @@ -93,11 +92,17 @@ PassRefPtr<ChildType> HTMLConstructionSite::attach(Node* parent, PassRefPtr<Chil // doesn't. It feels like we're missing a concept somehow. if (shouldFosterParent()) { fosterParent(child.get()); - ASSERT(child->attached()); + ASSERT(child->attached() || !child->parent()->attached()); return child.release(); } parent->parserAddChild(child); + + // An event handler (DOM Mutation, beforeload, et al.) could have removed + // the child, in which case we shouldn't try attaching it. + if (!child->parentNode()) + return child.release(); + // 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 // |parent| to hold a ref at this point. In the common case (at least diff --git a/WebCore/html/HTMLDocument.cpp b/WebCore/html/HTMLDocument.cpp index bc9de97..4e26c02 100644 --- a/WebCore/html/HTMLDocument.cpp +++ b/WebCore/html/HTMLDocument.cpp @@ -68,7 +68,6 @@ #include "HTMLBodyElement.h" #include "HTMLElementFactory.h" #include "HTMLNames.h" -#include "LegacyHTMLDocumentParser.h" #include "InspectorController.h" #include "KURL.h" #include "Page.h" @@ -290,11 +289,7 @@ DocumentParser* HTMLDocument::createParser() if (Page* page = this->page()) reportErrors = page->inspectorController()->windowVisible(); #endif - - if (settings() && settings()->html5ParserEnabled()) - return new HTMLDocumentParser(this, reportErrors); - - return new LegacyHTMLDocumentParser(this, reportErrors); + return new HTMLDocumentParser(this, reportErrors); } // -------------------------------------------------------------------------- @@ -328,9 +323,9 @@ void HTMLDocument::removeItemFromMap(HashCountedSet<AtomicStringImpl*>& map, con { if (name.isEmpty()) return; + map.remove(name.impl()); if (Frame* f = frame()) f->script()->namedItemRemoved(this, name); - map.remove(name.impl()); } void HTMLDocument::addNamedItem(const AtomicString& name) diff --git a/WebCore/html/HTMLDocument.h b/WebCore/html/HTMLDocument.h index 646e100..3b5fdfa 100644 --- a/WebCore/html/HTMLDocument.h +++ b/WebCore/html/HTMLDocument.h @@ -23,10 +23,10 @@ #ifndef HTMLDocument_h #define HTMLDocument_h -#include "AtomicStringHash.h" #include "CachedResourceClient.h" #include "Document.h" #include <wtf/HashCountedSet.h> +#include <wtf/text/AtomicStringHash.h> namespace WebCore { diff --git a/WebCore/html/HTMLDocumentParser.h b/WebCore/html/HTMLDocumentParser.h index d055861..d35cfaf 100644 --- a/WebCore/html/HTMLDocumentParser.h +++ b/WebCore/html/HTMLDocumentParser.h @@ -62,12 +62,14 @@ public: static void parseDocumentFragment(const String&, DocumentFragment*, Element* contextElement, FragmentScriptingPermission = FragmentScriptingAllowed); +protected: + virtual void insert(const SegmentedString&); + virtual void finish(); + private: // DocumentParser virtual bool hasInsertionPoint(); - virtual void insert(const SegmentedString&); virtual void append(const SegmentedString&); - virtual void finish(); virtual bool finishWasCalled(); virtual bool processingData() const; virtual void stopParsing(); diff --git a/WebCore/html/HTMLElement.cpp b/WebCore/html/HTMLElement.cpp index 6fc53a2..ff25e62 100644 --- a/WebCore/html/HTMLElement.cpp +++ b/WebCore/html/HTMLElement.cpp @@ -275,18 +275,23 @@ String HTMLElement::outerHTML() const return createMarkup(this); } -// FIXME: This method is unecessary with the new HTMLDocumentParser. -PassRefPtr<DocumentFragment> HTMLElement::createContextualFragment(const String& markup, FragmentScriptingPermission scriptingPermission) +static bool useLegacyTreeBuilder(Document* document) +{ + return !document || !document->settings() || !document->settings()->html5TreeBuilderEnabled(); +} + +// FIXME: This logic should move into Range::createContextualFragment +PassRefPtr<DocumentFragment> HTMLElement::deprecatedCreateContextualFragment(const String& markup, FragmentScriptingPermission scriptingPermission) { // The following is in accordance with the definition as used by IE. if (endTagRequirement() == TagStatusForbidden) return 0; - if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) || - hasLocalName(headTag) || hasLocalName(styleTag) || hasLocalName(titleTag)) + if (hasLocalName(colTag) || hasLocalName(colgroupTag) || hasLocalName(framesetTag) + || hasLocalName(headTag) || hasLocalName(styleTag) || hasLocalName(titleTag)) return 0; - return Element::createContextualFragment(markup, scriptingPermission); + return Element::deprecatedCreateContextualFragment(markup, scriptingPermission); } static inline bool hasOneChild(ContainerNode* node) @@ -339,23 +344,46 @@ static void replaceChildrenWithText(HTMLElement* element, const String& text, Ex element->appendChild(textNode.release(), ec); } +// We may want to move a version of this function into DocumentFragment.h/cpp +static PassRefPtr<DocumentFragment> createFragmentFromSource(const String& markup, Element* contextElement, ExceptionCode& ec) +{ + Document* document = contextElement->document(); + RefPtr<DocumentFragment> fragment; + + if (useLegacyTreeBuilder(document)) { + fragment = contextElement->deprecatedCreateContextualFragment(markup); + if (!fragment) + ec = NO_MODIFICATION_ALLOWED_ERR; + return fragment; + } + + fragment = DocumentFragment::create(document); + if (document->isHTMLDocument()) { + fragment->parseHTML(markup, contextElement); + return fragment; + } + + bool wasValid = fragment->parseXML(markup, contextElement); + if (!wasValid) { + ec = INVALID_STATE_ERR; + return 0; + } + return fragment; +} + void HTMLElement::setInnerHTML(const String& html, ExceptionCode& ec) { // FIXME: This code can be removed, it's handled by the HTMLDocumentParser correctly. - if (hasLocalName(scriptTag) || hasLocalName(styleTag)) { + if (useLegacyTreeBuilder(document()) && (hasLocalName(scriptTag) || hasLocalName(styleTag))) { // Script and CSS source shouldn't be parsed as HTML. removeChildren(); appendChild(document()->createTextNode(html), ec); return; } - RefPtr<DocumentFragment> fragment = createContextualFragment(html); - if (!fragment) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - - replaceChildrenWithFragment(this, fragment.release(), ec); + RefPtr<DocumentFragment> fragment = createFragmentFromSource(html, this, ec); + if (fragment) + replaceChildrenWithFragment(this, fragment.release(), ec); } void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec) @@ -365,17 +393,13 @@ void HTMLElement::setOuterHTML(const String& html, ExceptionCode& ec) ec = NO_MODIFICATION_ALLOWED_ERR; return; } - HTMLElement* parent = static_cast<HTMLElement*>(p); - RefPtr<DocumentFragment> fragment = parent->createContextualFragment(html); - if (!fragment) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does? - - parent->replaceChild(fragment.release(), this, ec); + RefPtr<DocumentFragment> fragment = createFragmentFromSource(html, parent, ec); + if (fragment) { + // FIXME: Why doesn't this have code to merge neighboring text nodes the way setOuterText does? + parent->replaceChild(fragment.release(), this, ec); + } } void HTMLElement::setInnerText(const String& text, ExceptionCode& ec) @@ -549,13 +573,35 @@ Element* HTMLElement::insertAdjacentElement(const String& where, Element* newChi return static_cast<Element*>(returnValue); } +// Step 3 of http://www.whatwg.org/specs/web-apps/current-work/multipage/apis-in-html-documents.html#insertadjacenthtml() +static Element* contextElementForInsertion(const String& where, Element* element, ExceptionCode& ec) +{ + if (equalIgnoringCase(where, "beforeBegin") || equalIgnoringCase(where, "afterEnd")) { + Node* parent = element->parentNode(); + if (parent && parent->isDocumentNode()) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return 0; + } + ASSERT(!parent || parent->isElementNode()); + return static_cast<Element*>(parent); + } + if (equalIgnoringCase(where, "afterBegin") || equalIgnoringCase(where, "beforeEnd")) + return element; + ec = SYNTAX_ERR; + return 0; +} + void HTMLElement::insertAdjacentHTML(const String& where, const String& markup, ExceptionCode& ec) { RefPtr<DocumentFragment> fragment = document()->createDocumentFragment(); + Element* contextElement = contextElementForInsertion(where, this, ec); + if (!contextElement) + return; + if (document()->isHTMLDocument()) - fragment->parseHTML(markup, this); + fragment->parseHTML(markup, contextElement); else { - if (!fragment->parseXML(markup, this)) + if (!fragment->parseXML(markup, contextElement)) // FIXME: We should propagate a syntax error exception out here. return; } diff --git a/WebCore/html/HTMLElement.h b/WebCore/html/HTMLElement.h index 03449c9..8f54d3e 100644 --- a/WebCore/html/HTMLElement.h +++ b/WebCore/html/HTMLElement.h @@ -46,7 +46,8 @@ public: String innerHTML() const; String outerHTML() const; - PassRefPtr<DocumentFragment> createContextualFragment(const String&, FragmentScriptingPermission = FragmentScriptingAllowed); + // deprecatedCreateContextualFragment logic should be moved into Range::createContextualFragment + PassRefPtr<DocumentFragment> deprecatedCreateContextualFragment(const String&, FragmentScriptingPermission = FragmentScriptingAllowed); void setInnerHTML(const String&, ExceptionCode&); void setOuterHTML(const String&, ExceptionCode&); void setInnerText(const String&, ExceptionCode&); diff --git a/WebCore/html/HTMLEmbedElement.cpp b/WebCore/html/HTMLEmbedElement.cpp index 3f700ef..afa56d4 100644 --- a/WebCore/html/HTMLEmbedElement.cpp +++ b/WebCore/html/HTMLEmbedElement.cpp @@ -89,8 +89,8 @@ void HTMLEmbedElement::parseMappedAttribute(Attribute* attr) if (attr->name() == typeAttr) { m_serviceType = value.string().lower(); - int pos = m_serviceType.find(";"); - if (pos != -1) + size_t pos = m_serviceType.find(";"); + if (pos != notFound) m_serviceType = m_serviceType.left(pos); if (!isImageType() && m_imageLoader) m_imageLoader.clear(); diff --git a/WebCore/html/HTMLEntityNames.gperf b/WebCore/html/HTMLEntityNames.gperf deleted file mode 100644 index c665efe..0000000 --- a/WebCore/html/HTMLEntityNames.gperf +++ /dev/null @@ -1,303 +0,0 @@ -%{ -/* - Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2002, 2003, 2004, 2005 Apple Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - - ---------------------------------------------------------------------------- - - HTMLEntityNames.gperf: input file to generate a hash table for entities - HTMLEntityNames.cpp: DO NOT EDIT! generated by WebCore/make-hash-tools.pl -*/ -%} -%struct-type -struct Entity { - const char *name; - int code; -}; -%language=ANSI-C -%readonly-tables -%global-table -%compare-strncmp -%define lookup-function-name findEntity -%define hash-function-name entity_hash_function -%includes -%enum -%% -AElig, 0x00c6 -AMP, 38 -Aacute, 0x00c1 -Acirc, 0x00c2 -Agrave, 0x00c0 -Alpha, 0x0391 -Aring, 0x00c5 -Atilde, 0x00c3 -Auml, 0x00c4 -Beta, 0x0392 -COPY, 0x00a9 -Ccedil, 0x00c7 -Chi, 0x03a7 -Dagger, 0x2021 -Delta, 0x0394 -ETH, 0x00d0 -Eacute, 0x00c9 -Ecirc, 0x00ca -Egrave, 0x00c8 -Epsilon, 0x0395 -Eta, 0x0397 -Euml, 0x00cb -GT, 62 -Gamma, 0x0393 -Iacute, 0x00cd -Icirc, 0x00ce -Igrave, 0x00cc -Iota, 0x0399 -Iuml, 0x00cf -Kappa, 0x039a -LT, 60 -Lambda, 0x039b -Mu, 0x039c -Ntilde, 0x00d1 -Nu, 0x039d -OElig, 0x0152 -Oacute, 0x00d3 -Ocirc, 0x00d4 -Ograve, 0x00d2 -Omega, 0x03a9 -Omicron, 0x039f -Oslash, 0x00d8 -Otilde, 0x00d5 -Ouml, 0x00d6 -Phi, 0x03a6 -Pi, 0x03a0 -Prime, 0x2033 -Psi, 0x03a8 -QUOT, 34 -REG, 0x00ae -Rho, 0x03a1 -Scaron, 0x0160 -Sigma, 0x03a3 -THORN, 0x00de -Tau, 0x03a4 -Theta, 0x0398 -Uacute, 0x00da -Ucirc, 0x00db -Ugrave, 0x00d9 -Upsilon, 0x03a5 -Uuml, 0x00dc -Xi, 0x039e -Yacute, 0x00dd -Yuml, 0x0178 -Zeta, 0x0396 -aacute, 0x00e1 -acirc, 0x00e2 -acute, 0x00b4 -aelig, 0x00e6 -agrave, 0x00e0 -alefsym, 0x2135 -alpha, 0x03b1 -amp, 38 -and, 0x2227 -ang, 0x2220 -apos, 0x0027 -aring, 0x00e5 -asymp, 0x2248 -atilde, 0x00e3 -auml, 0x00e4 -bdquo, 0x201e -beta, 0x03b2 -brvbar, 0x00a6 -bull, 0x2022 -cap, 0x2229 -ccedil, 0x00e7 -cedil, 0x00b8 -cent, 0x00a2 -chi, 0x03c7 -circ, 0x02c6 -clubs, 0x2663 -cong, 0x2245 -copy, 0x00a9 -crarr, 0x21b5 -cup, 0x222a -curren, 0x00a4 -dArr, 0x21d3 -dagger, 0x2020 -darr, 0x2193 -deg, 0x00b0 -delta, 0x03b4 -diams, 0x2666 -divide, 0x00f7 -eacute, 0x00e9 -ecirc, 0x00ea -egrave, 0x00e8 -empty, 0x2205 -emsp, 0x2003 -ensp, 0x2002 -epsilon, 0x03b5 -equiv, 0x2261 -eta, 0x03b7 -eth, 0x00f0 -euml, 0x00eb -euro, 0x20ac -exist, 0x2203 -fnof, 0x0192 -forall, 0x2200 -frac12, 0x00bd -frac14, 0x00bc -frac34, 0x00be -frasl, 0x2044 -gamma, 0x03b3 -ge, 0x2265 -gt, 62 -hArr, 0x21d4 -harr, 0x2194 -hearts, 0x2665 -hellip, 0x2026 -iacute, 0x00ed -icirc, 0x00ee -iexcl, 0x00a1 -igrave, 0x00ec -image, 0x2111 -infin, 0x221e -int, 0x222b -iota, 0x03b9 -iquest, 0x00bf -isin, 0x2208 -iuml, 0x00ef -kappa, 0x03ba -lArr, 0x21d0 -lambda, 0x03bb -lang, 0x3008 -laquo, 0x00ab -larr, 0x2190 -lceil, 0x2308 -ldquo, 0x201c -le, 0x2264 -lfloor, 0x230a -lowast, 0x2217 -loz, 0x25ca -lrm, 0x200e -lsaquo, 0x2039 -lsquo, 0x2018 -lt, 60 -macr, 0x00af -mdash, 0x2014 -micro, 0x00b5 -middot, 0x00b7 -minus, 0x2212 -mu, 0x03bc -nabla, 0x2207 -nbsp, 0x00a0 -ndash, 0x2013 -ne, 0x2260 -ni, 0x220b -not, 0x00ac -notin, 0x2209 -nsub, 0x2284 -nsup, 0x2285 -ntilde, 0x00f1 -nu, 0x03bd -oacute, 0x00f3 -ocirc, 0x00f4 -oelig, 0x0153 -ograve, 0x00f2 -oline, 0x203e -omega, 0x03c9 -omicron, 0x03bf -oplus, 0x2295 -or, 0x2228 -ordf, 0x00aa -ordm, 0x00ba -oslash, 0x00f8 -otilde, 0x00f5 -otimes, 0x2297 -ouml, 0x00f6 -para, 0x00b6 -part, 0x2202 -percnt, 0x0025 -permil, 0x2030 -perp, 0x22a5 -phi, 0x03c6 -pi, 0x03c0 -piv, 0x03d6 -plusmn, 0x00b1 -pound, 0x00a3 -prime, 0x2032 -prod, 0x220f -prop, 0x221d -psi, 0x03c8 -quot, 34 -rArr, 0x21d2 -radic, 0x221a -rang, 0x3009 -raquo, 0x00bb -rarr, 0x2192 -rceil, 0x2309 -rdquo, 0x201d -real, 0x211c -reg, 0x00ae -rfloor, 0x230b -rho, 0x03c1 -rlm, 0x200f -rsaquo, 0x203a -rsquo, 0x2019 -sbquo, 0x201a -scaron, 0x0161 -sdot, 0x22c5 -sect, 0x00a7 -shy, 0x00ad -sigma, 0x03c3 -sigmaf, 0x03c2 -sim, 0x223c -spades, 0x2660 -sub, 0x2282 -sube, 0x2286 -sum, 0x2211 -sup, 0x2283 -sup1, 0x00b9 -sup2, 0x00b2 -sup3, 0x00b3 -supe, 0x2287 -supl, 0x00b9 -szlig, 0x00df -tau, 0x03c4 -there4, 0x2234 -theta, 0x03b8 -thetasym, 0x03d1 -thinsp, 0x2009 -thorn, 0x00fe -tilde, 0x02dc -times, 0x00d7 -trade, 0x2122 -uArr, 0x21d1 -uacute, 0x00fa -uarr, 0x2191 -ucirc, 0x00fb -ugrave, 0x00f9 -uml, 0x00a8 -upsih, 0x03d2 -upsilon, 0x03c5 -uuml, 0x00fc -weierp, 0x2118 -xi, 0x03be -yacute, 0x00fd -yen, 0x00a5 -yuml, 0x00ff -zeta, 0x03b6 -zwj, 0x200d -zwnj, 0x200c -%% diff --git a/WebCore/html/HTMLEntityParser.cpp b/WebCore/html/HTMLEntityParser.cpp index 84b2006..f675844 100644 --- a/WebCore/html/HTMLEntityParser.cpp +++ b/WebCore/html/HTMLEntityParser.cpp @@ -28,21 +28,10 @@ #include "config.h" #include "HTMLEntityParser.h" +#include "HTMLEntitySearch.h" +#include "HTMLEntityTable.h" #include <wtf/Vector.h> -// Use __GNUC__ instead of PLATFORM(GCC) to stay consistent with the gperf generated c file -#ifdef __GNUC__ -// The main parser includes this too so we are getting two copies of the data. However, this way the code gets inlined. -#include "HTMLEntityNames.cpp" -#else -// Not inlined for non-GCC compilers -struct Entity { - const char* name; - int code; -}; -const struct Entity* findEntity(register const char* str, register unsigned int len); -#endif - using namespace WTF; namespace WebCore { @@ -113,7 +102,6 @@ unsigned consumeHTMLEntity(SegmentedString& source, bool& notEnoughCharacters, U EntityState entityState = Initial; unsigned result = 0; Vector<UChar, 10> consumedCharacters; - Vector<char, 10> entityName; while (!source.isEmpty()) { UChar cc = *source; @@ -177,7 +165,7 @@ unsigned consumeHTMLEntity(SegmentedString& source, bool& notEnoughCharacters, U else if (cc == ';') { source.advancePastNonNewline(); return legalEntityFor(result); - } else + } else return legalEntityFor(result); break; } @@ -192,48 +180,48 @@ unsigned consumeHTMLEntity(SegmentedString& source, bool& notEnoughCharacters, U break; } case Named: { - // FIXME: This code is wrong. We need to find the longest matching entity. - // The examples from the spec are: - // I'm ¬it; I tell you - // I'm ∉ I tell you - // In the first case, "¬" is the entity. In the second - // case, "∉" is the entity. - // FIXME: Our list of HTML entities is incomplete. - // FIXME: The number 8 below is bogus. - while (!source.isEmpty() && entityName.size() <= 8) { + HTMLEntitySearch entitySearch; + while (!source.isEmpty()) { cc = *source; - if (cc == ';') { - const Entity* entity = findEntity(entityName.data(), entityName.size()); - if (entity) { - source.advanceAndASSERT(';'); - return entity->code; - } - break; - } - if (!isAlphaNumeric(cc)) { - const Entity* entity = findEntity(entityName.data(), entityName.size()); - if (entity) { - // HTML5 tells us to ignore this entity, for historical reasons, - // if the lookhead character is '='. - if (additionalAllowedCharacter && cc == '=') - break; - // Some entities require a terminating semicolon, whereas other - // entities do not. The HTML5 spec has a giant list: - // - // http://www.whatwg.org/specs/web-apps/current-work/multipage/named-character-references.html#named-character-references - // - // However, the list seems to boil down to this branch: - if (entity->code > 255) - break; - return entity->code; - } + entitySearch.advance(cc); + if (!entitySearch.isEntityPrefix()) break; - } - entityName.append(cc); consumedCharacters.append(cc); source.advanceAndASSERT(cc); } notEnoughCharacters = source.isEmpty(); + if (notEnoughCharacters) { + // We can't an entity because there might be a longer entity + // that we could match if we had more data. + unconsumeCharacters(source, consumedCharacters); + return 0; + } + if (!entitySearch.mostRecentMatch()) { + ASSERT(!entitySearch.currentValue()); + unconsumeCharacters(source, consumedCharacters); + return 0; + } + if (entitySearch.mostRecentMatch()->length != entitySearch.currentLength()) { + // We've consumed too many characters. We need to walk the + // source back to the point at which we had consumed an + // actual entity. + unconsumeCharacters(source, consumedCharacters); + consumedCharacters.clear(); + const int length = entitySearch.mostRecentMatch()->length; + const UChar* reference = entitySearch.mostRecentMatch()->entity; + for (int i = 0; i < length; ++i) { + cc = *source; + ASSERT_UNUSED(reference, cc == *reference++); + consumedCharacters.append(cc); + source.advanceAndASSERT(cc); + ASSERT(!source.isEmpty()); + } + cc = *source; + } + if (entitySearch.mostRecentMatch()->lastCharacter() == ';') + return entitySearch.mostRecentMatch()->value; + if (!additionalAllowedCharacter || !(isAlphaNumeric(cc) || cc == '=')) + return entitySearch.mostRecentMatch()->value; unconsumeCharacters(source, consumedCharacters); return 0; } @@ -247,4 +235,23 @@ unsigned consumeHTMLEntity(SegmentedString& source, bool& notEnoughCharacters, U return 0; } +UChar decodeNamedEntity(const char* name) +{ + HTMLEntitySearch search; + while (*name) { + search.advance(*name++); + if (!search.isEntityPrefix()) + return 0; + } + search.advance(';'); + UChar32 entityValue = search.currentValue(); + if (U16_LENGTH(entityValue) != 1) { + // Callers need to move off this API if the entity table has values + // which do no fit in a 16 bit UChar! + ASSERT_NOT_REACHED(); + return 0; + } + return static_cast<UChar>(entityValue); +} + } // namespace WebCore diff --git a/WebCore/html/HTMLEntityParser.h b/WebCore/html/HTMLEntityParser.h index d37b0c3..1059b24 100644 --- a/WebCore/html/HTMLEntityParser.h +++ b/WebCore/html/HTMLEntityParser.h @@ -33,6 +33,9 @@ namespace WebCore { unsigned consumeHTMLEntity(SegmentedString&, bool& notEnoughCharacters, UChar additionalAllowedCharacter = '\0'); +// Used by the XML parser. Not suitable for use in HTML parsing. Use consumeHTMLEntity instead. +UChar decodeNamedEntity(const char*); + } #endif diff --git a/WebCore/html/HTMLEntitySearch.cpp b/WebCore/html/HTMLEntitySearch.cpp new file mode 100644 index 0000000..580609e --- /dev/null +++ b/WebCore/html/HTMLEntitySearch.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLEntitySearch.h" + +#include "HTMLEntityTable.h" + +namespace WebCore { + +namespace { + +const HTMLEntityTableEntry* halfway(const HTMLEntityTableEntry* left, const HTMLEntityTableEntry* right) +{ + return &left[(right - left) / 2]; +} + +} + +HTMLEntitySearch::HTMLEntitySearch() + : m_currentLength(0) + , m_currentValue(0) + , m_mostRecentMatch(0) + , m_first(HTMLEntityTable::firstEntry()) + , m_last(HTMLEntityTable::lastEntry()) +{ +} + +HTMLEntitySearch::CompareResult HTMLEntitySearch::compare(const HTMLEntityTableEntry* entry, UChar nextCharacter) const +{ + if (entry->length < m_currentLength + 1) + return Before; + UChar entryNextCharacter = entry->entity[m_currentLength]; + if (entryNextCharacter == nextCharacter) + return Prefix; + return entryNextCharacter < nextCharacter ? Before : After; +} + +const HTMLEntityTableEntry* HTMLEntitySearch::findFirst(UChar nextCharacter) const +{ + const HTMLEntityTableEntry* left = m_first; + const HTMLEntityTableEntry* right = m_last; + if (left == right) + return left; + CompareResult result = compare(left, nextCharacter); + if (result == Prefix) + return left; + if (result == After) + return right; + while (left + 1 < right) { + const HTMLEntityTableEntry* probe = halfway(left, right); + result = compare(probe, nextCharacter); + if (result == Before) + left = probe; + else { + ASSERT(result == After || result == Prefix); + right = probe; + } + } + ASSERT(left + 1 == right); + return right; +} + +const HTMLEntityTableEntry* HTMLEntitySearch::findLast(UChar nextCharacter) const +{ + const HTMLEntityTableEntry* left = m_first; + const HTMLEntityTableEntry* right = m_last; + if (left == right) + return right; + CompareResult result = compare(right, nextCharacter); + if (result == Prefix) + return right; + if (result == Before) + return left; + while (left + 1 < right) { + const HTMLEntityTableEntry* probe = halfway(left, right); + result = compare(probe, nextCharacter); + if (result == After) + right = probe; + else { + ASSERT(result == Before || result == Prefix); + left = probe; + } + } + ASSERT(left + 1 == right); + return left; +} + +void HTMLEntitySearch::advance(UChar nextCharacter) +{ + ASSERT(isEntityPrefix()); + if (!m_currentLength) { + m_first = HTMLEntityTable::firstEntryStartingWith(nextCharacter); + m_last = HTMLEntityTable::lastEntryStartingWith(nextCharacter); + } else { + m_first = findFirst(nextCharacter); + m_last = findLast(nextCharacter); + if (m_first == m_last && compare(m_first, nextCharacter) != Prefix) + return fail(); + } + ++m_currentLength; + if (m_first->length != m_currentLength) { + m_currentValue = 0; + return; + } + m_mostRecentMatch = m_first; + m_currentValue = m_mostRecentMatch->value; +} + +} diff --git a/WebCore/html/canvas/CanvasNumberArray.h b/WebCore/html/HTMLEntitySearch.h index 09714a0..11a23ae 100644 --- a/WebCore/html/canvas/CanvasNumberArray.h +++ b/WebCore/html/HTMLEntitySearch.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,32 +23,53 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef CanvasNumberArray_h -#define CanvasNumberArray_h +#ifndef HTMLEntitySearch_h +#define HTMLEntitySearch_h -#include <wtf/Forward.h> -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/Vector.h> -#include <limits> +#include "PlatformString.h" namespace WebCore { - class CanvasNumberArray : public RefCounted<CanvasNumberArray> { - public: - static PassRefPtr<CanvasNumberArray> create(unsigned length); - - Vector<float>& data() { return m_data; } - const Vector<float>& data() const { return m_data; } +struct HTMLEntityTableEntry; - unsigned length() const { return m_data.size(); } - float item(unsigned index) const { return (index >= m_data.size()) ? 0 : m_data[index]; } +class HTMLEntitySearch { +public: + HTMLEntitySearch(); - private: - CanvasNumberArray(unsigned length); - Vector<float> m_data; + void advance(UChar); + + bool isEntityPrefix() const { return !!m_first; } + int currentValue() const { return m_currentValue; } + int currentLength() const { return m_currentLength; } + + const HTMLEntityTableEntry* mostRecentMatch() const { return m_mostRecentMatch; } + +private: + enum CompareResult { + Before, + Prefix, + After, }; - -} // namespace WebCore -#endif // CanvasNumberArray_h + CompareResult compare(const HTMLEntityTableEntry*, UChar) const; + const HTMLEntityTableEntry* findFirst(UChar) const; + const HTMLEntityTableEntry* findLast(UChar) const; + + void fail() + { + m_currentValue = 0; + m_first = 0; + m_last = 0; + } + + int m_currentLength; + int m_currentValue; + + const HTMLEntityTableEntry* m_mostRecentMatch; + const HTMLEntityTableEntry* m_first; + const HTMLEntityTableEntry* m_last; +}; + +} + +#endif diff --git a/WebCore/html/canvas/CanvasNumberArray.cpp b/WebCore/html/HTMLEntityTable.h index 6cbdfff..3734c34 100644 --- a/WebCore/html/canvas/CanvasNumberArray.cpp +++ b/WebCore/html/HTMLEntityTable.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,10 +10,10 @@ * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR @@ -23,25 +23,30 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" +#ifndef HTMLEntityTable_h +#define HTMLEntityTable_h -#if ENABLE(3D_CANVAS) - -#include "CanvasNumberArray.h" #include "PlatformString.h" namespace WebCore { - -PassRefPtr<CanvasNumberArray> CanvasNumberArray::create(unsigned length) -{ - return adoptRef(new CanvasNumberArray(length)); -} -CanvasNumberArray::CanvasNumberArray(unsigned length) - : m_data(length) -{ -} +struct HTMLEntityTableEntry { + UChar lastCharacter() const { return entity[length - 1]; } + + const UChar* entity; + int length; + int value; +}; + +class HTMLEntityTable { +public: + static const HTMLEntityTableEntry* firstEntry(); + static const HTMLEntityTableEntry* lastEntry(); + + static const HTMLEntityTableEntry* firstEntryStartingWith(UChar); + static const HTMLEntityTableEntry* lastEntryStartingWith(UChar); +}; } -#endif // ENABLE(3D_CANVAS) +#endif diff --git a/WebCore/html/HTMLFormControlElement.cpp b/WebCore/html/HTMLFormControlElement.cpp index 5103bfa..ae1ac62 100644 --- a/WebCore/html/HTMLFormControlElement.cpp +++ b/WebCore/html/HTMLFormControlElement.cpp @@ -30,6 +30,7 @@ #include "Chrome.h" #include "ChromeClient.h" #include "Document.h" +#include "DocumentParser.h" #include "ElementRareData.h" #include "Event.h" #include "EventHandler.h" @@ -39,7 +40,6 @@ #include "HTMLInputElement.h" #include "HTMLNames.h" #include "LegacyHTMLTreeBuilder.h" -#include "LegacyHTMLDocumentParser.h" #include "LabelsNodeList.h" #include "Page.h" #include "RenderBox.h" @@ -526,7 +526,7 @@ static bool isNotLineBreak(UChar ch) { return ch != newlineCharacter && ch != ca bool HTMLTextFormControlElement::isPlaceholderEmpty() const { const AtomicString& attributeValue = getAttribute(placeholderAttr); - return attributeValue.string().find(isNotLineBreak) == -1; + return attributeValue.string().find(isNotLineBreak) == notFound; } bool HTMLTextFormControlElement::placeholderShouldBeVisible() const diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp index 8c759cb..50b6ed0 100644 --- a/WebCore/html/HTMLInputElement.cpp +++ b/WebCore/html/HTMLInputElement.cpp @@ -65,7 +65,6 @@ #include "RenderTheme.h" #include "ScriptEventListener.h" #include "StepRange.h" -#include "StringHash.h" #include "TextEvent.h" #ifdef ANDROID_ACCEPT_CHANGES_TO_FOCUSED_TEXTFIELDS #include "WebViewCore.h" @@ -74,6 +73,7 @@ #include <wtf/HashMap.h> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/StringHash.h> using namespace std; diff --git a/WebCore/html/HTMLInputElement.h b/WebCore/html/HTMLInputElement.h index 72ac589..18cbaa4 100644 --- a/WebCore/html/HTMLInputElement.h +++ b/WebCore/html/HTMLInputElement.h @@ -101,6 +101,7 @@ public: virtual bool isSearchField() const { return m_type == SEARCH; } virtual bool isInputTypeHidden() const { return m_type == HIDDEN; } virtual bool isPasswordField() const { return m_type == PASSWORD; } + virtual bool isCheckbox() const { return m_type == CHECKBOX; } bool isTelephoneField() const { return m_type == TELEPHONE; } bool isNumberField() const { return m_type == NUMBER; } bool isEmailField() const { return m_type == EMAIL; } diff --git a/WebCore/html/HTMLLinkElement.cpp b/WebCore/html/HTMLLinkElement.cpp index a638572..c778b6f 100644 --- a/WebCore/html/HTMLLinkElement.cpp +++ b/WebCore/html/HTMLLinkElement.cpp @@ -51,7 +51,6 @@ inline HTMLLinkElement::HTMLLinkElement(const QualifiedName& tagName, Document* , m_disabledState(Unset) , m_loading(false) , m_createdByParser(createdByParser) - , m_shouldProcessAfterAttach(false) { ASSERT(hasTagName(linkTag)); } @@ -260,28 +259,12 @@ void HTMLLinkElement::process() document()->updateStyleSelector(); } } - -void HTMLLinkElement::processCallback(Node* node) -{ - ASSERT_ARG(node, node && node->hasTagName(linkTag)); - static_cast<HTMLLinkElement*>(node)->process(); -} void HTMLLinkElement::insertedIntoDocument() { HTMLElement::insertedIntoDocument(); document()->addStyleSheetCandidateNode(this, m_createdByParser); - // Since processing a stylesheet link causes a beforeload event - // to fire, it is possible for JavaScript to remove the element in the midst - // of it being inserted into the DOM, which can lead to assertion failures - // and crashes. Avoid this by postponing the beforeload/load until after - // attach if there are beforeload listeners. - if (document()->hasListenerType(Document::BEFORELOAD_LISTENER)) { - m_shouldProcessAfterAttach = true; - return; - } - process(); } @@ -294,20 +277,8 @@ void HTMLLinkElement::removedFromDocument() // FIXME: It's terrible to do a synchronous update of the style selector just because a <style> or <link> element got removed. if (document()->renderer()) document()->updateStyleSelector(); - - m_shouldProcessAfterAttach = false; } -void HTMLLinkElement::attach() -{ - if (m_shouldProcessAfterAttach) { - m_shouldProcessAfterAttach = false; - queuePostAttachCallback(&HTMLLinkElement::processCallback, this); - } - - HTMLElement::attach(); -} - void HTMLLinkElement::finishParsingChildren() { m_createdByParser = false; diff --git a/WebCore/html/HTMLLinkElement.h b/WebCore/html/HTMLLinkElement.h index 057cccc..6d7643a 100644 --- a/WebCore/html/HTMLLinkElement.h +++ b/WebCore/html/HTMLLinkElement.h @@ -77,9 +77,6 @@ public: bool isDisabled() const { return m_disabledState == Disabled; } bool isEnabledViaScript() const { return m_disabledState == EnabledViaScript; } bool isIcon() const { return m_relAttribute.m_isIcon; } - - virtual void attach(); - virtual bool canLazyAttach() { return false; } private: virtual HTMLTagStatus endTagRequirement() const { return TagStatusForbidden; } @@ -137,7 +134,6 @@ private: RelAttribute m_relAttribute; bool m_loading; bool m_createdByParser; - bool m_shouldProcessAfterAttach; }; } //namespace diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp index 94c0dd2..13b7807 100644 --- a/WebCore/html/HTMLMediaElement.cpp +++ b/WebCore/html/HTMLMediaElement.cpp @@ -112,7 +112,6 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc) , m_processingMediaPlayerCallback(0) , m_playing(false) , m_isWaitingUntilMediaCanStart(false) - , m_processingLoad(false) , m_delayingTheLoadEvent(false) , m_haveFiredLoadedData(false) , m_inActiveDocument(true) @@ -512,7 +511,10 @@ void HTMLMediaElement::prepareForLoad() #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO) m_player = MediaPlayer::create(this); #else - createMediaPlayerProxy(); + if (m_player) + m_player->cancelLoad(); + else + createMediaPlayerProxy(); #endif // 4 - If the media element's networkState is not set to NETWORK_EMPTY, then run these substeps @@ -532,6 +534,16 @@ void HTMLMediaElement::prepareForLoad() m_error = 0; m_autoplaying = true; + // 7 - Invoke the media element's resource selection algorithm. + + // 8 - Note: Playback of any previously playing media resource for this element stops. + + // The resource selection algorithm + // 1 - Set the networkState to NETWORK_NO_SOURCE + m_networkState = NETWORK_NO_SOURCE; + + // 2 - Asynchronously await a stable state. + m_playedTimeRanges = TimeRanges::create(); m_lastSeekTime = 0; m_closedCaptionsVisible = false; @@ -549,11 +561,7 @@ void HTMLMediaElement::loadInternal() return; } - // Steps 1 - 6 were done in prepareForLoad - - // 7 - Invoke the media element's resource selection algorithm. selectMediaResource(); - m_processingLoad = false; } void HTMLMediaElement::selectMediaResource() @@ -561,11 +569,6 @@ void HTMLMediaElement::selectMediaResource() enum Mode { attribute, children }; Mode mode = attribute; - // 1 - Set the networkState to NETWORK_NO_SOURCE - m_networkState = NETWORK_NO_SOURCE; - - // 2 - Asynchronously await a stable state. - // 3 - ... the media element has neither a src attribute ... if (!hasAttribute(srcAttr)) { // ... nor a source element child: ... diff --git a/WebCore/html/HTMLMediaElement.h b/WebCore/html/HTMLMediaElement.h index aeec188..a73abba 100644 --- a/WebCore/html/HTMLMediaElement.h +++ b/WebCore/html/HTMLMediaElement.h @@ -340,7 +340,6 @@ private: bool m_playing : 1; bool m_isWaitingUntilMediaCanStart : 1; - bool m_processingLoad : 1; bool m_delayingTheLoadEvent : 1; bool m_haveFiredLoadedData : 1; bool m_inActiveDocument : 1; diff --git a/WebCore/html/HTMLObjectElement.cpp b/WebCore/html/HTMLObjectElement.cpp index d4bccc3..5989ec7 100644 --- a/WebCore/html/HTMLObjectElement.cpp +++ b/WebCore/html/HTMLObjectElement.cpp @@ -70,11 +70,11 @@ RenderWidget* HTMLObjectElement::renderWidgetForJSBindings() const void HTMLObjectElement::parseMappedAttribute(Attribute* attr) { String val = attr->value(); - int pos; + size_t pos; if (attr->name() == typeAttr) { m_serviceType = val.lower(); pos = m_serviceType.find(";"); - if (pos != -1) + if (pos != notFound) m_serviceType = m_serviceType.left(pos); if (renderer()) m_needWidgetUpdate = true; diff --git a/WebCore/html/HTMLScriptRunner.cpp b/WebCore/html/HTMLScriptRunner.cpp index 4a9058f..0d603ed 100644 --- a/WebCore/html/HTMLScriptRunner.cpp +++ b/WebCore/html/HTMLScriptRunner.cpp @@ -43,23 +43,22 @@ namespace WebCore { using namespace HTMLNames; -class NestScript : public Noncopyable { +// FIXME: Factor out to avoid duplication with HTMLDocumentParser. +class NestingLevelIncrementer : public Noncopyable { public: - NestScript(unsigned& nestingLevel, HTMLInputStream& inputStream) + explicit NestingLevelIncrementer(unsigned& nestingLevel) : m_nestingLevel(&nestingLevel) - , m_savedInsertionPoint(inputStream) { ++(*m_nestingLevel); } - ~NestScript() + ~NestingLevelIncrementer() { --(*m_nestingLevel); } private: unsigned* m_nestingLevel; - InsertionPointRecord m_savedInsertionPoint; }; HTMLScriptRunner::HTMLScriptRunner(Document* document, HTMLScriptRunnerHost* host) @@ -97,7 +96,7 @@ inline PassRefPtr<Event> createScriptErrorEvent() return Event::create(eventNames().errorEvent, true, false); } -ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) +ScriptSourceCode HTMLScriptRunner::sourceFromPendingScript(const PendingScript& script, bool& errorOccurred) const { if (script.cachedScript()) { errorOccurred = script.cachedScript()->errorOccurred(); @@ -118,22 +117,29 @@ bool HTMLScriptRunner::isPendingScriptReady(const PendingScript& script) return true; } -void HTMLScriptRunner::executePendingScript() +void HTMLScriptRunner::executeParsingBlockingScript() { ASSERT(!m_scriptNestingLevel); ASSERT(m_document->haveStylesheetsLoaded()); - bool errorOccurred = false; ASSERT(isPendingScriptReady(m_parsingBlockingScript)); - ScriptSourceCode sourceCode = sourceFromPendingScript(m_parsingBlockingScript, errorOccurred); // Stop watching loads before executeScript to prevent recursion if the script reloads itself. if (m_parsingBlockingScript.cachedScript() && m_parsingBlockingScript.watchingForLoad()) stopWatchingForLoad(m_parsingBlockingScript); + InsertionPointRecord insertionPointRecord(m_host->inputStream()); + executePendingScriptAndDispatchEvent(m_parsingBlockingScript); +} + +void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendingScript) +{ + bool errorOccurred = false; + ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred); + // Clear the pending script before possible rentrancy from executeScript() - RefPtr<Element> scriptElement = m_parsingBlockingScript.releaseElementAndClear(); + RefPtr<Element> scriptElement = pendingScript.releaseElementAndClear(); { - NestScript nestingLevel(m_scriptNestingLevel, m_host->inputStream()); + NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel); if (errorOccurred) scriptElement->dispatchEvent(createScriptErrorEvent()); else { @@ -144,12 +150,8 @@ void HTMLScriptRunner::executePendingScript() ASSERT(!m_scriptNestingLevel); } -void HTMLScriptRunner::executeScript(Element* element, const ScriptSourceCode& sourceCode) +void HTMLScriptRunner::executeScript(Element* element, const ScriptSourceCode& sourceCode) const { - // FIXME: We do not block inline <script> tags on stylesheets for now. - // When we do, || !element->hasAttribute(srcAttr) should be removed from - // the ASSERT below. See https://bugs.webkit.org/show_bug.cgi?id=40047 - ASSERT(m_document->haveStylesheetsLoaded() || !element->hasAttribute(srcAttr)); ScriptElement* scriptElement = toScriptElement(element); ASSERT(scriptElement); if (!scriptElement->shouldExecuteAsJavaScript()) @@ -204,7 +206,7 @@ bool HTMLScriptRunner::executeParsingBlockingScripts() // We only really need to check once. if (!isPendingScriptReady(m_parsingBlockingScript)) return false; - executePendingScript(); + executeParsingBlockingScript(); } return true; } @@ -228,31 +230,39 @@ bool HTMLScriptRunner::executeScriptsWaitingForStylesheets() return executeParsingBlockingScripts(); } -void HTMLScriptRunner::requestScript(Element* script) +void HTMLScriptRunner::requestParsingBlockingScript(Element* element) { - ASSERT(!m_parsingBlockingScript.element()); - AtomicString srcValue = script->getAttribute(srcAttr); + if (!requestPendingScript(m_parsingBlockingScript, element)) + return; + + ASSERT(m_parsingBlockingScript.cachedScript()); + + // We only care about a load callback if cachedScript is not already + // in the cache. Callers will attempt to run the m_parsingBlockingScript + // if possible before returning control to the parser. + if (!m_parsingBlockingScript.cachedScript()->isLoaded()) + watchForLoad(m_parsingBlockingScript); +} + +bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const +{ + ASSERT(!pendingScript.element()); + const AtomicString& srcValue = script->getAttribute(srcAttr); // Allow the host to disllow script loads (using the XSSAuditor, etc.) if (!m_host->shouldLoadExternalScriptFromSrc(srcValue)) - return; + return false; // FIXME: We need to resolve the url relative to the element. if (!script->dispatchBeforeLoadEvent(srcValue)) - return; - m_parsingBlockingScript.adoptElement(script); + return false; + pendingScript.adoptElement(script); // This should correctly return 0 for empty or invalid srcValues. CachedScript* cachedScript = m_document->docLoader()->requestScript(srcValue, toScriptElement(script)->scriptCharset()); if (!cachedScript) { notImplemented(); // Dispatch error event. - return; + return false; } - - m_parsingBlockingScript.setCachedScript(cachedScript); - - // We only care about a load callback if cachedScript is not already - // in the cache. Callers will attempt to run the m_parsingBlockingScript - // if possible before returning control to the parser. - if (!m_parsingBlockingScript.cachedScript()->isLoaded()) - watchForLoad(m_parsingBlockingScript); + pendingScript.setCachedScript(cachedScript); + return true; } // This method is meant to match the HTML5 definition of "running a script" @@ -261,17 +271,21 @@ void HTMLScriptRunner::runScript(Element* script, int startingLineNumber) { ASSERT(!haveParsingBlockingScript()); { - NestScript nestingLevel(m_scriptNestingLevel, m_host->inputStream()); + InsertionPointRecord insertionPointRecord(m_host->inputStream()); + NestingLevelIncrementer nestingLevelIncrementer(m_scriptNestingLevel); // Check script type and language, current code uses ScriptElement::shouldExecuteAsJavaScript(), but that may not be HTML5 compliant. notImplemented(); // event for support if (script->hasAttribute(srcAttr)) { // FIXME: Handle defer and async - requestScript(script); + requestParsingBlockingScript(script); } else { // FIXME: We do not block inline <script> tags on stylesheets to match the - // old parser for now. See https://bugs.webkit.org/show_bug.cgi?id=40047 + // old parser for now. When we do, the ASSERT below should be added. + // See https://bugs.webkit.org/show_bug.cgi?id=40047 + // ASSERT(document()->haveStylesheetsLoaded()); + ASSERT(isExecutingScript()); ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), startingLineNumber); executeScript(script, sourceCode); } diff --git a/WebCore/html/HTMLScriptRunner.h b/WebCore/html/HTMLScriptRunner.h index 85801b2..981d433 100644 --- a/WebCore/html/HTMLScriptRunner.h +++ b/WebCore/html/HTMLScriptRunner.h @@ -52,25 +52,27 @@ public: bool hasScriptsWaitingForStylesheets() const { return m_hasScriptsWaitingForStylesheets; } bool executeScriptsWaitingForStylesheets(); - bool isExecutingScript() { return !!m_scriptNestingLevel; } + bool isExecutingScript() const { return !!m_scriptNestingLevel; } private: Frame* frame() const; + void executeParsingBlockingScript(); + void executePendingScriptAndDispatchEvent(PendingScript&); + void executeScript(Element*, const ScriptSourceCode&) const; bool haveParsingBlockingScript() const; bool executeParsingBlockingScripts(); - void executePendingScript(); - void requestScript(Element*); + void requestParsingBlockingScript(Element*); + bool requestPendingScript(PendingScript&, Element*) const; + void runScript(Element*, int startingLineNumber); // Helpers for dealing with HTMLScriptRunnerHost void watchForLoad(PendingScript&); void stopWatchingForLoad(PendingScript&); - void executeScript(Element*, const ScriptSourceCode&); - bool isPendingScriptReady(const PendingScript&); - ScriptSourceCode sourceFromPendingScript(const PendingScript&, bool& errorOccurred); + ScriptSourceCode sourceFromPendingScript(const PendingScript&, bool& errorOccurred) const; Document* m_document; HTMLScriptRunnerHost* m_host; diff --git a/WebCore/html/HTMLSourceElement.cpp b/WebCore/html/HTMLSourceElement.cpp index 6c26610..96c9829 100644 --- a/WebCore/html/HTMLSourceElement.cpp +++ b/WebCore/html/HTMLSourceElement.cpp @@ -52,9 +52,9 @@ PassRefPtr<HTMLSourceElement> HTMLSourceElement::create(const QualifiedName& tag return adoptRef(new HTMLSourceElement(tagName, document)); } -void HTMLSourceElement::insertedIntoDocument() +void HTMLSourceElement::insertedIntoTree(bool deep) { - HTMLElement::insertedIntoDocument(); + HTMLElement::insertedIntoTree(deep); if (parentNode() && (parentNode()->hasTagName(audioTag) || parentNode()->hasTagName(videoTag))) { HTMLMediaElement* media = static_cast<HTMLMediaElement*>(parentNode()); if (media->networkState() == HTMLMediaElement::NETWORK_EMPTY) diff --git a/WebCore/html/HTMLSourceElement.h b/WebCore/html/HTMLSourceElement.h index 20049cc..7d49a3e 100644 --- a/WebCore/html/HTMLSourceElement.h +++ b/WebCore/html/HTMLSourceElement.h @@ -52,7 +52,7 @@ private: virtual HTMLTagStatus endTagRequirement() const { return TagStatusForbidden; } virtual int tagPriority() const { return 0; } - virtual void insertedIntoDocument(); + virtual void insertedIntoTree(bool); virtual bool isURLAttribute(Attribute*) const; void errorEventTimerFired(Timer<HTMLSourceElement>*); diff --git a/WebCore/html/HTMLToken.h b/WebCore/html/HTMLToken.h index 5f2869b..e42a829 100644 --- a/WebCore/html/HTMLToken.h +++ b/WebCore/html/HTMLToken.h @@ -45,8 +45,16 @@ public: EndOfFile, }; + class Range { + public: + int m_start; + int m_end; + }; + class Attribute { public: + Range m_nameRange; + Range m_valueRange; WTF::Vector<UChar, 32> m_name; WTF::Vector<UChar, 32> m_value; }; @@ -56,12 +64,22 @@ public: HTMLToken() { clear(); } - void clear() + void clear(int startIndex = 0) { m_type = Uninitialized; + m_range.m_start = startIndex; + m_range.m_end = startIndex; m_data.clear(); } + int startIndex() const { return m_range.m_start; } + int endIndex() const { return m_range.m_end; } + + void end(int endIndex) + { + m_range.m_end = endIndex; + } + void makeEndOfFile() { ASSERT(m_type == Uninitialized); @@ -146,12 +164,44 @@ public: ASSERT(m_type == StartTag || m_type == EndTag); m_attributes.grow(m_attributes.size() + 1); m_currentAttribute = &m_attributes.last(); +#ifndef NDEBUG + m_currentAttribute->m_nameRange.m_start = 0; + m_currentAttribute->m_nameRange.m_end = 0; + m_currentAttribute->m_valueRange.m_start = 0; + m_currentAttribute->m_valueRange.m_end = 0; +#endif + } + + void beginAttributeName(int index) + { + m_currentAttribute->m_nameRange.m_start = index; + } + + void endAttributeName(int index) + { + m_currentAttribute->m_nameRange.m_end = index; + m_currentAttribute->m_valueRange.m_start = index; + m_currentAttribute->m_valueRange.m_end = index; + } + + void beginAttributeValue(int index) + { + m_currentAttribute->m_valueRange.m_start = index; +#ifndef NDEBUG + m_currentAttribute->m_valueRange.m_end = 0; +#endif + } + + void endAttributeValue(int index) + { + m_currentAttribute->m_valueRange.m_end = index; } void appendToAttributeName(UChar character) { ASSERT(character); ASSERT(m_type == StartTag || m_type == EndTag); + ASSERT(m_currentAttribute->m_nameRange.m_start); m_currentAttribute->m_name.append(character); } @@ -159,6 +209,7 @@ public: { ASSERT(character); ASSERT(m_type == StartTag || m_type == EndTag); + ASSERT(m_currentAttribute->m_valueRange.m_start); m_currentAttribute->m_value.append(character); } @@ -280,6 +331,9 @@ private: Type m_type; + // Which characters from the input stream are represented by this token. + Range m_range; + // "name" for DOCTYPE, StartTag, and EndTag // "characters" for Character // "data" for Comment @@ -323,6 +377,10 @@ public: if (!iter->m_name.isEmpty()) { String name(iter->m_name.data(), iter->m_name.size()); String value(iter->m_value.data(), iter->m_value.size()); + ASSERT(iter->m_nameRange.m_start); + ASSERT(iter->m_nameRange.m_end); + ASSERT(iter->m_valueRange.m_start); + ASSERT(iter->m_valueRange.m_end); RefPtr<Attribute> mappedAttribute = Attribute::createMapped(name, value); if (!m_attributes) { m_attributes = NamedNodeMap::create(); diff --git a/WebCore/html/HTMLTokenizer.cpp b/WebCore/html/HTMLTokenizer.cpp index a52aba6..a18701a 100644 --- a/WebCore/html/HTMLTokenizer.cpp +++ b/WebCore/html/HTMLTokenizer.cpp @@ -28,7 +28,6 @@ #include "config.h" #include "HTMLTokenizer.h" -#include "AtomicString.h" #include "HTMLEntityParser.h" #include "HTMLToken.h" #include "HTMLNames.h" @@ -36,6 +35,7 @@ #include <wtf/ASCIICType.h> #include <wtf/CurrentTime.h> #include <wtf/UnusedParam.h> +#include <wtf/text/AtomicString.h> #include <wtf/text/CString.h> #include <wtf/unicode/Unicode.h> @@ -152,7 +152,7 @@ inline bool HTMLTokenizer::processEntity(SegmentedString& source) do { \ m_state = stateName; \ if (!m_inputStreamPreprocessor.advance(source, m_lineNumber)) \ - return shouldEmitBufferedCharacterToken(source); \ + return haveBufferedCharacterToken(); \ cc = m_inputStreamPreprocessor.nextInputCharacter(); \ goto stateName; \ } while (false) @@ -165,7 +165,7 @@ inline bool HTMLTokenizer::processEntity(SegmentedString& source) do { \ m_state = stateName; \ if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source, m_lineNumber)) \ - return shouldEmitBufferedCharacterToken(source); \ + return haveBufferedCharacterToken(); \ cc = m_inputStreamPreprocessor.nextInputCharacter(); \ goto stateName; \ } while (false) @@ -202,7 +202,7 @@ bool HTMLTokenizer::emitAndReconsumeIn(SegmentedString&, State state) // Check if we have buffered characters to emit first before emitting the EOF. bool HTMLTokenizer::emitEndOfFile(SegmentedString& source) { - if (shouldEmitBufferedCharacterToken(source)) + if (haveBufferedCharacterToken()) return true; m_state = DataState; source.advance(m_lineNumber); @@ -229,7 +229,7 @@ bool HTMLTokenizer::flushBufferedEndTag(SegmentedString& source) return true; \ if (source.isEmpty() \ || !m_inputStreamPreprocessor.peek(source, m_lineNumber)) \ - return shouldEmitBufferedCharacterToken(source); \ + return haveBufferedCharacterToken(); \ cc = m_inputStreamPreprocessor.nextInputCharacter(); \ goto stateName; \ } while (false) @@ -260,7 +260,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) } if (source.isEmpty() || !m_inputStreamPreprocessor.peek(source, m_lineNumber)) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); UChar cc = m_inputStreamPreprocessor.nextInputCharacter(); // http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#parsing-main-inbody @@ -308,7 +308,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) BEGIN_STATE(CharacterReferenceInDataState) { if (!processEntity(source)) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); SWITCH_TO(DataState); } END_STATE() @@ -329,7 +329,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) BEGIN_STATE(CharacterReferenceInRCDATAState) { if (!processEntity(source)) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); SWITCH_TO(RCDATAState); } END_STATE() @@ -864,6 +864,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) return emitAndResumeIn(source, DataState); else if (isASCIIUpper(cc)) { m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); m_token->appendToAttributeName(toLowerCase(cc)); ADVANCE_TO(AttributeNameState); } else if (cc == InputStreamPreprocessor::endOfFileMarker) { @@ -873,6 +874,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) if (cc == '"' || cc == '\'' || cc == '<' || cc == '=') parseError(); m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); m_token->appendToAttributeName(cc); ADVANCE_TO(AttributeNameState); } @@ -880,19 +882,24 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) END_STATE() BEGIN_STATE(AttributeNameState) { - if (isTokenizerWhitespace(cc)) + if (isTokenizerWhitespace(cc)) { + m_token->endAttributeName(source.numberOfCharactersConsumed()); ADVANCE_TO(AfterAttributeNameState); - else if (cc == '/') + } else if (cc == '/') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); ADVANCE_TO(SelfClosingStartTagState); - else if (cc == '=') + } else if (cc == '=') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); ADVANCE_TO(BeforeAttributeValueState); - else if (cc == '>') + } else if (cc == '>') { + m_token->endAttributeName(source.numberOfCharactersConsumed()); return emitAndResumeIn(source, DataState); - else if (isASCIIUpper(cc)) { + } else if (isASCIIUpper(cc)) { m_token->appendToAttributeName(toLowerCase(cc)); ADVANCE_TO(AttributeNameState); } else if (cc == InputStreamPreprocessor::endOfFileMarker) { parseError(); + m_token->endAttributeName(source.numberOfCharactersConsumed()); RECONSUME_IN(DataState); } else { if (cc == '"' || cc == '\'' || cc == '<' || cc == '=') @@ -914,6 +921,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) return emitAndResumeIn(source, DataState); else if (isASCIIUpper(cc)) { m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); m_token->appendToAttributeName(toLowerCase(cc)); ADVANCE_TO(AttributeNameState); } else if (cc == InputStreamPreprocessor::endOfFileMarker) { @@ -923,6 +931,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) if (cc == '"' || cc == '\'' || cc == '<') parseError(); m_token->addNewAttribute(); + m_token->beginAttributeName(source.numberOfCharactersConsumed()); m_token->appendToAttributeName(cc); ADVANCE_TO(AttributeNameState); } @@ -932,13 +941,16 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) BEGIN_STATE(BeforeAttributeValueState) { if (isTokenizerWhitespace(cc)) ADVANCE_TO(BeforeAttributeValueState); - else if (cc == '"') + else if (cc == '"') { + m_token->beginAttributeValue(source.numberOfCharactersConsumed() + 1); ADVANCE_TO(AttributeValueDoubleQuotedState); - else if (cc == '&') + } else if (cc == '&') { + m_token->beginAttributeValue(source.numberOfCharactersConsumed()); RECONSUME_IN(AttributeValueUnquotedState); - else if (cc == '\'') + } else if (cc == '\'') { + m_token->beginAttributeValue(source.numberOfCharactersConsumed() + 1); ADVANCE_TO(AttributeValueSingleQuotedState); - else if (cc == '>') { + } else if (cc == '>') { parseError(); return emitAndResumeIn(source, DataState); } else if (cc == InputStreamPreprocessor::endOfFileMarker) { @@ -947,6 +959,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) } else { if (cc == '<' || cc == '=' || cc == '`') parseError(); + m_token->beginAttributeValue(source.numberOfCharactersConsumed()); m_token->appendToAttributeValue(cc); ADVANCE_TO(AttributeValueUnquotedState); } @@ -954,13 +967,15 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) END_STATE() BEGIN_STATE(AttributeValueDoubleQuotedState) { - if (cc == '"') + if (cc == '"') { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); ADVANCE_TO(AfterAttributeValueQuotedState); - else if (cc == '&') { + } else if (cc == '&') { m_additionalAllowedCharacter = '"'; ADVANCE_TO(CharacterReferenceInAttributeValueState); } else if (cc == InputStreamPreprocessor::endOfFileMarker) { parseError(); + m_token->endAttributeValue(source.numberOfCharactersConsumed()); RECONSUME_IN(DataState); } else { m_token->appendToAttributeValue(cc); @@ -970,13 +985,15 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) END_STATE() BEGIN_STATE(AttributeValueSingleQuotedState) { - if (cc == '\'') + if (cc == '\'') { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); ADVANCE_TO(AfterAttributeValueQuotedState); - else if (cc == '&') { + } else if (cc == '&') { m_additionalAllowedCharacter = '\''; ADVANCE_TO(CharacterReferenceInAttributeValueState); } else if (cc == InputStreamPreprocessor::endOfFileMarker) { parseError(); + m_token->endAttributeValue(source.numberOfCharactersConsumed()); RECONSUME_IN(DataState); } else { m_token->appendToAttributeValue(cc); @@ -986,15 +1003,18 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) END_STATE() BEGIN_STATE(AttributeValueUnquotedState) { - if (isTokenizerWhitespace(cc)) + if (isTokenizerWhitespace(cc)) { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); ADVANCE_TO(BeforeAttributeNameState); - else if (cc == '&') { + } else if (cc == '&') { m_additionalAllowedCharacter = '>'; ADVANCE_TO(CharacterReferenceInAttributeValueState); - } else if (cc == '>') + } else if (cc == '>') { + m_token->endAttributeValue(source.numberOfCharactersConsumed()); return emitAndResumeIn(source, DataState); - else if (cc == InputStreamPreprocessor::endOfFileMarker) { + } else if (cc == InputStreamPreprocessor::endOfFileMarker) { parseError(); + m_token->endAttributeValue(source.numberOfCharactersConsumed()); RECONSUME_IN(DataState); } else { if (cc == '"' || cc == '\'' || cc == '<' || cc == '=' || cc == '`') @@ -1009,7 +1029,7 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) bool notEnoughCharacters = false; unsigned value = consumeHTMLEntity(source, notEnoughCharacters, m_additionalAllowedCharacter); if (notEnoughCharacters) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); if (!value) m_token->appendToAttributeValue('&'); else if (value < 0xFFFF) @@ -1093,14 +1113,14 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) m_token->beginComment(); SWITCH_TO(CommentStartState); } else if (result == SegmentedString::NotEnoughCharacters) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); } else if (cc == 'D' || cc == 'd') { SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(doctypeString); if (result == SegmentedString::DidMatch) { advanceStringAndASSERTIgnoringCase(source, "doctype"); SWITCH_TO(DOCTYPEState); } else if (result == SegmentedString::NotEnoughCharacters) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); } notImplemented(); // FIXME: We're still missing the bits about the insertion mode being in foreign content: @@ -1313,14 +1333,14 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) advanceStringAndASSERTIgnoringCase(source, "public"); SWITCH_TO(AfterDOCTYPEPublicKeywordState); } else if (result == SegmentedString::NotEnoughCharacters) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); } else if (cc == 'S' || cc == 's') { SegmentedString::LookAheadResult result = source.lookAheadIgnoringCase(systemString); if (result == SegmentedString::DidMatch) { advanceStringAndASSERTIgnoringCase(source, "system"); SWITCH_TO(AfterDOCTYPESystemKeywordState); } else if (result == SegmentedString::NotEnoughCharacters) - return shouldEmitBufferedCharacterToken(source); + return haveBufferedCharacterToken(); } parseError(); m_token->setForceQuirks(); @@ -1629,10 +1649,9 @@ inline void HTMLTokenizer::parseError() notImplemented(); } -inline bool HTMLTokenizer::shouldEmitBufferedCharacterToken(const SegmentedString& source) +inline bool HTMLTokenizer::haveBufferedCharacterToken() { - return source.isClosed() && m_token->type() == HTMLToken::Character; + return m_token->type() == HTMLToken::Character; } } - diff --git a/WebCore/html/HTMLTokenizer.h b/WebCore/html/HTMLTokenizer.h index 0e9ba3a..6fb3053 100644 --- a/WebCore/html/HTMLTokenizer.h +++ b/WebCore/html/HTMLTokenizer.h @@ -27,10 +27,10 @@ #ifndef HTMLTokenizer_h #define HTMLTokenizer_h -#include "AtomicString.h" #include "SegmentedString.h" #include <wtf/Noncopyable.h> #include <wtf/Vector.h> +#include <wtf/text/AtomicString.h> namespace WebCore { @@ -254,7 +254,7 @@ private: inline void saveEndTagNameIfNeeded(); inline bool isAppropriateEndTag(); - inline bool shouldEmitBufferedCharacterToken(const SegmentedString&); + inline bool haveBufferedCharacterToken(); State m_state; diff --git a/WebCore/html/HTMLTreeBuilder.cpp b/WebCore/html/HTMLTreeBuilder.cpp index 2c7d40d..fd0b62e 100644 --- a/WebCore/html/HTMLTreeBuilder.cpp +++ b/WebCore/html/HTMLTreeBuilder.cpp @@ -39,7 +39,6 @@ #include "HTMLScriptElement.h" #include "HTMLToken.h" #include "HTMLTokenizer.h" -#include "LegacyHTMLDocumentParser.h" #include "LegacyHTMLTreeBuilder.h" #include "LocalizedStrings.h" #include "MathMLNames.h" @@ -67,6 +66,11 @@ inline bool isTreeBuilderWhitepace(UChar c) return c == '\t' || c == '\x0A' || c == '\x0C' || c == '\x0D' || c == ' '; } +inline bool isNotTreeBuilderWhitepace(UChar c) +{ + return !isTreeBuilderWhitepace(c); +} + inline bool isTreeBuilderWhitepaceOrReplacementCharacter(UChar c) { return isTreeBuilderWhitepace(c) || c == 0xFFFD; @@ -303,21 +307,17 @@ public: void skipLeadingWhitespace() { - ASSERT(!isEmpty()); - while (isTreeBuilderWhitepace(*m_current)) { - if (++m_current == m_end) - return; - } + skipLeading<isTreeBuilderWhitepace>(); } String takeLeadingWhitespace() { - ASSERT(!isEmpty()); - const UChar* start = m_current; - skipLeadingWhitespace(); - if (start == m_current) - return String(); - return String(start, m_current - start); + return takeLeading<isTreeBuilderWhitepace>(); + } + + String takeLeadingNonWhitespace() + { + return takeLeading<isNotTreeBuilderWhitepace>(); } String takeRemaining() @@ -352,6 +352,27 @@ public: } private: + template<bool characterPredicate(UChar)> + void skipLeading() + { + ASSERT(!isEmpty()); + while (characterPredicate(*m_current)) { + if (++m_current == m_end) + return; + } + } + + template<bool characterPredicate(UChar)> + String takeLeading() + { + ASSERT(!isEmpty()); + const UChar* start = m_current; + skipLeading<characterPredicate>(); + if (start == m_current) + return String(); + return String(start, m_current - start); + } + const UChar* m_current; const UChar* m_end; }; @@ -391,13 +412,14 @@ HTMLTreeBuilder::HTMLTreeBuilder(HTMLTokenizer* tokenizer, DocumentFragment* fra { if (shouldUseLegacyTreeBuilder(fragment->document())) return; - // This is steps 2-6 of the HTML5 Fragment Case parsing algorithm: - // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case - if (contextElement) + if (contextElement) { + // Steps 4.2-4.6 of the HTML5 Fragment Case parsing algorithm: + // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#fragment-case m_document->setParseMode(contextElement->document()->parseMode()); - processFakeStartTag(htmlTag); - resetInsertionModeAppropriately(); - m_tree.setForm(closestFormAncestor(contextElement)); + processFakeStartTag(htmlTag); + resetInsertionModeAppropriately(); + m_tree.setForm(closestFormAncestor(contextElement)); + } } HTMLTreeBuilder::~HTMLTreeBuilder() @@ -1997,7 +2019,9 @@ void HTMLTreeBuilder::processEndTagForInCell(AtomicHTMLToken& token) m_tree.openElements()->popUntilPopped(token.name()); m_tree.activeFormattingElements()->clearToLastMarker(); setInsertionMode(InRowMode); - ASSERT(m_tree.currentElement()->hasTagName(trTag)); + // FIXME: The fragment case of this ASSERT is a spec bug: + // http://www.w3.org/Bugs/Public/show_bug.cgi?id=10338 + ASSERT(m_tree.currentElement()->hasTagName(trTag) || (isParsingFragment() && m_fragmentContext.contextElement()->hasTagName(trTag))); return; } if (token.name() == bodyTag @@ -2628,7 +2652,10 @@ ReprocessBuffer: return; if (!processColgroupEndTagForInColumnGroup()) { ASSERT(isParsingFragment()); - return; + // The spec tells us to drop these characters on the floor. + buffer.takeLeadingNonWhitespace(); + if (buffer.isEmpty()) + return; } goto ReprocessBuffer; } diff --git a/WebCore/html/HTMLViewSourceDocument.cpp b/WebCore/html/HTMLViewSourceDocument.cpp index e1b959b..e0e71ee 100644 --- a/WebCore/html/HTMLViewSourceDocument.cpp +++ b/WebCore/html/HTMLViewSourceDocument.cpp @@ -36,7 +36,9 @@ #include "HTMLTableElement.h" #include "HTMLTableRowElement.h" #include "HTMLTableSectionElement.h" -#include "LegacyHTMLDocumentParser.h" +#include "HTMLToken.h" +#include "HTMLViewSourceParser.h" +#include "SegmentedString.h" #include "Text.h" #include "TextDocument.h" @@ -53,15 +55,13 @@ HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, co DocumentParser* HTMLViewSourceDocument::createParser() { - // Use LegacyHTMLDocumentParser if applicable, otherwise use TextDocumentParser. + // Use HTMLDocumentParser if applicable, otherwise use TextDocumentParser. if (m_type == "text/html" || m_type == "application/xhtml+xml" || m_type == "image/svg+xml" || DOMImplementation::isXMLMIMEType(m_type) #if ENABLE(XHTMLMP) || m_type == "application/vnd.wap.xhtml+xml" #endif - ) { - // FIXME: Should respect Settings::html5ParserEnabled() - return new LegacyHTMLDocumentParser(this); - } + ) + return new HTMLViewSourceParser(this); return createTextDocumentParser(this); } @@ -100,107 +100,91 @@ void HTMLViewSourceDocument::addViewSourceText(const String& text) addText(text, ""); } -void HTMLViewSourceDocument::addViewSourceToken(Token* token) +void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token) { if (!m_current) createContainingTable(); - if (token->tagName == textAtom) - addText(token->text.get(), ""); - else if (token->tagName == commentAtom) { - if (token->beginTag) { - m_current = addSpanWithClassName("webkit-html-comment"); - addText(String("<!--") + token->text.get() + "-->", "webkit-html-comment"); - } - } else { - // Handle the tag. - String classNameStr = "webkit-html-tag"; - m_current = addSpanWithClassName(classNameStr); - - String text = "<"; - if (!token->beginTag) - text += "/"; - text += token->tagName; - Vector<UChar>* guide = token->m_sourceInfo.get(); - if (!guide || !guide->size()) - text += ">"; - - addText(text, classNameStr); - - // Walk our guide string that tells us where attribute names/values should go. - if (guide && guide->size()) { - unsigned size = guide->size(); - unsigned begin = 0; - unsigned currAttr = 0; - RefPtr<Attribute> attr = 0; - for (unsigned i = 0; i < size; i++) { - if (guide->at(i) == 'a' || guide->at(i) == 'x' || guide->at(i) == 'v') { - // Add in the string. - addText(String(static_cast<UChar*>(guide->data()) + begin, i - begin), classNameStr); - - begin = i + 1; - - if (guide->at(i) == 'a') { - if (token->attrs && currAttr < token->attrs->length()) - attr = token->attrs->attributeItem(currAttr++); - else - attr = 0; - } - if (attr) { - if (guide->at(i) == 'a') { - String name = attr->name().toString(); - - m_current = addSpanWithClassName("webkit-html-attribute-name"); - addText(name, "webkit-html-attribute-name"); - if (m_current != m_tbody) - m_current = static_cast<Element*>(m_current->parent()); - } else { - const String& value = attr->value().string(); - - // Compare ignoring case since LegacyHTMLDocumentParser doesn't - // lower names when passing in tokens to - // HTMLViewSourceDocument. - if (equalIgnoringCase(token->tagName, "base") && equalIgnoringCase(attr->name().localName(), "href")) { - // Catch the href attribute in the base element. - // It will be used for rendering anchors created - // by addLink() below. - setBaseElementURL(KURL(url(), value)); - } - - // FIXME: XML could use namespace prefixes and confuse us. - if (equalIgnoringCase(attr->name().localName(), "src") || equalIgnoringCase(attr->name().localName(), "href")) - m_current = addLink(value, equalIgnoringCase(token->tagName, "a")); - else - m_current = addSpanWithClassName("webkit-html-attribute-value"); - addText(value, "webkit-html-attribute-value"); - if (m_current != m_tbody) - m_current = static_cast<Element*>(m_current->parent()); - } - } - } - } - - // Add in any string that might be left. - if (begin < size) - addText(String(static_cast<UChar*>(guide->data()) + begin, size - begin), classNameStr); - - // Add in the end tag. - addText(">", classNameStr); - } - - m_current = m_td; + switch (token.type()) { + case HTMLToken::Uninitialized: + ASSERT_NOT_REACHED(); + break; + case HTMLToken::DOCTYPE: + processDoctypeToken(source, token); + break; + case HTMLToken::EndOfFile: + break; + case HTMLToken::StartTag: + case HTMLToken::EndTag: + processTagToken(source, token); + break; + case HTMLToken::Comment: + processCommentToken(source, token); + break; + case HTMLToken::Character: + processCharacterToken(source, token); + break; } } -void HTMLViewSourceDocument::addViewSourceDoctypeToken(DoctypeToken* doctypeToken) +void HTMLViewSourceDocument::processDoctypeToken(const String& source, HTMLToken&) { if (!m_current) createContainingTable(); m_current = addSpanWithClassName("webkit-html-doctype"); - String text = "<"; - text += String::adopt(doctypeToken->m_source); - text += ">"; - addText(text, "webkit-html-doctype"); + addText(source, "webkit-html-doctype"); + m_current = m_td; +} + +void HTMLViewSourceDocument::processTagToken(const String& source, HTMLToken& token) +{ + String classNameStr = "webkit-html-tag"; + m_current = addSpanWithClassName(classNameStr); + + AtomicString tagName(token.name().data(), token.name().size()); + + unsigned index = 0; + HTMLToken::AttributeList::const_iterator iter = token.attributes().begin(); + while (index < source.length()) { + if (iter == token.attributes().end()) { + // We want to show the remaining characters in the token. + index = addRange(source, index, source.length(), ""); + ASSERT(index == source.length()); + break; + } + + AtomicString name(iter->m_name.data(), iter->m_name.size()); + String value(iter->m_value.data(), iter->m_value.size()); + + index = addRange(source, index, iter->m_nameRange.m_start - token.startIndex(), ""); + index = addRange(source, index, iter->m_nameRange.m_end - token.startIndex(), "webkit-html-attribute-name"); + + if (tagName == baseTag && name == hrefAttr) { + // Catch the href attribute in the base element. It will be used + // for rendering anchors created by addLink() below. + setBaseElementURL(KURL(url(), value)); + } + + index = addRange(source, index, iter->m_valueRange.m_start - token.startIndex(), ""); + + bool isLink = name == srcAttr || name == hrefAttr; + index = addRange(source, index, iter->m_valueRange.m_end - token.startIndex(), "webkit-html-attribute-value", isLink, tagName == aTag); + + ++iter; + } + m_current = m_td; +} + +void HTMLViewSourceDocument::processCommentToken(const String& source, HTMLToken&) +{ + m_current = addSpanWithClassName("webkit-html-comment"); + addText(source, "webkit-html-comment"); + m_current = m_td; +} + +void HTMLViewSourceDocument::processCharacterToken(const String& source, HTMLToken&) +{ + addText(source, ""); } PassRefPtr<Element> HTMLViewSourceDocument::addSpanWithClassName(const String& className) @@ -287,6 +271,25 @@ void HTMLViewSourceDocument::addText(const String& text, const String& className m_current = m_tbody; } +int HTMLViewSourceDocument::addRange(const String& source, int start, int end, const String& className, bool isLink, bool isAnchor) +{ + ASSERT(start <= end); + if (start == end) + return start; + + String text = source.substring(start, end - start); + if (!className.isEmpty()) { + if (isLink) + m_current = addLink(text, isAnchor); + else + m_current = addSpanWithClassName(className); + } + addText(text, className); + if (!className.isEmpty() && m_current != m_tbody) + m_current = static_cast<Element*>(m_current->parent()); + return end; +} + PassRefPtr<Element> HTMLViewSourceDocument::addLink(const String& url, bool isAnchor) { if (m_current == m_tbody) diff --git a/WebCore/html/HTMLViewSourceDocument.h b/WebCore/html/HTMLViewSourceDocument.h index cf6cfc1..8805848 100644 --- a/WebCore/html/HTMLViewSourceDocument.h +++ b/WebCore/html/HTMLViewSourceDocument.h @@ -29,11 +29,9 @@ namespace WebCore { -class DoctypeToken; class HTMLTableCellElement; class HTMLTableSectionElement; - -struct Token; +class HTMLToken; class HTMLViewSourceDocument : public HTMLDocument { public: @@ -42,20 +40,27 @@ public: return adoptRef(new HTMLViewSourceDocument(frame, url, mimeType)); } - void addViewSourceToken(Token*); // Used by the LegacyHTMLDocumentParser. + void addSource(const String&, HTMLToken&); + + void addViewSourceToken(HTMLToken&); // Used by the HTMLDocumentParser. void addViewSourceText(const String&); // Used by the TextDocumentParser. - void addViewSourceDoctypeToken(DoctypeToken*); private: HTMLViewSourceDocument(Frame*, const KURL&, const String& mimeType); - // Returns LegacyHTMLDocumentParser or TextDocumentParser based on m_type. + // Returns HTMLViewSourceParser or TextDocumentParser based on m_type. virtual DocumentParser* createParser(); + void processDoctypeToken(const String& source, HTMLToken&); + void processTagToken(const String& source, HTMLToken&); + void processCommentToken(const String& source, HTMLToken&); + void processCharacterToken(const String& source, HTMLToken&); + void createContainingTable(); PassRefPtr<Element> addSpanWithClassName(const String&); void addLine(const String& className); void addText(const String& text, const String& className); + int addRange(const String& source, int start, int end, const String& className, bool isLink = false, bool isAnchor = false); PassRefPtr<Element> addLink(const String& url, bool isAnchor); String m_type; diff --git a/WebCore/html/HTMLViewSourceParser.cpp b/WebCore/html/HTMLViewSourceParser.cpp new file mode 100644 index 0000000..3da4c23 --- /dev/null +++ b/WebCore/html/HTMLViewSourceParser.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "HTMLViewSourceParser.h" + +#include "HTMLNames.h" +#include "HTMLTreeBuilder.h" +#include "HTMLViewSourceDocument.h" + +namespace WebCore { + +HTMLViewSourceParser::~HTMLViewSourceParser() +{ +} + +void HTMLViewSourceParser::insert(const SegmentedString&) +{ + ASSERT_NOT_REACHED(); +} + +void HTMLViewSourceParser::pumpTokenizer() +{ + while (m_tokenizer.nextToken(m_input.current(), m_token)) { + m_token.end(m_input.current().numberOfCharactersConsumed()); + document()->addSource(sourceForToken(), m_token); + updateTokenizerState(); + m_token.clear(m_input.current().numberOfCharactersConsumed()); + } +} + +void HTMLViewSourceParser::append(const SegmentedString& input) +{ + m_input.appendToEnd(input); + m_source.append(input); + pumpTokenizer(); +} + +String HTMLViewSourceParser::sourceForToken() +{ + if (m_token.type() == HTMLToken::EndOfFile) + return String(); + + ASSERT(m_source.numberOfCharactersConsumed() == m_token.startIndex()); + UChar* data = 0; + int length = m_token.endIndex() - m_token.startIndex(); + String source = String::createUninitialized(length, data); + for (int i = 0; i < length; ++i) { + data[i] = *m_source; + m_source.advance(); + } + return source; +} + +void HTMLViewSourceParser::updateTokenizerState() +{ + // FIXME: The tokenizer should do this work for us. + if (m_token.type() != HTMLToken::StartTag) + return; + + AtomicString tagName(m_token.name().data(), m_token.name().size()); + m_tokenizer.setState(HTMLTreeBuilder::adjustedLexerState(m_tokenizer.state(), tagName, m_document->frame())); + if (tagName == HTMLNames::scriptTag) { + // The tree builder handles scriptTag separately from the other tokenizer + // state adjustments, so we need to handle it separately too. + ASSERT(m_tokenizer.state() == HTMLTokenizer::DataState); + m_tokenizer.setState(HTMLTokenizer::ScriptDataState); + } +} + +void HTMLViewSourceParser::finish() +{ + if (!m_input.haveSeenEndOfFile()) + m_input.markEndOfFile(); + pumpTokenizer(); + document()->finishedParsing(); +} + +bool HTMLViewSourceParser::finishWasCalled() +{ + return m_input.haveSeenEndOfFile(); +} + +} diff --git a/WebCore/html/HTMLViewSourceParser.h b/WebCore/html/HTMLViewSourceParser.h new file mode 100644 index 0000000..2571301 --- /dev/null +++ b/WebCore/html/HTMLViewSourceParser.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HTMLViewSourceParser_h +#define HTMLViewSourceParser_h + +#include "DecodedDataDocumentParser.h" +#include "HTMLInputStream.h" +#include "HTMLToken.h" +#include "HTMLTokenizer.h" +#include "HTMLViewSourceDocument.h" +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class HTMLTokenizer; +class HTMLScriptRunner; +class HTMLTreeBuilder; +class HTMLPreloadScanner; +class LegacyHTMLTreeBuilder; +class ScriptController; +class ScriptSourceCode; + +class HTMLViewSourceParser : public DecodedDataDocumentParser { +public: + // FIXME: Make private with a create method. + HTMLViewSourceParser(HTMLViewSourceDocument* document) + : DecodedDataDocumentParser(document) + { + } + + virtual ~HTMLViewSourceParser(); + +private: + // DocumentParser + virtual void insert(const SegmentedString&); + virtual void append(const SegmentedString&); + virtual void finish(); + virtual bool finishWasCalled(); + + HTMLViewSourceDocument* document() const { return static_cast<HTMLViewSourceDocument*>(m_document); } + + void pumpTokenizer(); + String sourceForToken(); + void updateTokenizerState(); + + HTMLInputStream m_input; + SegmentedString m_source; + HTMLToken m_token; + HTMLTokenizer m_tokenizer; +}; + +} + +#endif diff --git a/WebCore/html/LegacyHTMLDocumentParser.cpp b/WebCore/html/LegacyHTMLDocumentParser.cpp deleted file mode 100644 index 980d6ed..0000000 --- a/WebCore/html/LegacyHTMLDocumentParser.cpp +++ /dev/null @@ -1,2126 +0,0 @@ -/* - Copyright (C) 1997 Martin Jones (mjones@kde.org) - (C) 1997 Torben Weis (weis@kde.org) - (C) 1998 Waldo Bastian (bastian@kde.org) - (C) 1999 Lars Knoll (knoll@kde.org) - (C) 1999 Antti Koivisto (koivisto@kde.org) - (C) 2001 Dirk Mueller (mueller@kde.org) - Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - Copyright (C) 2005, 2006 Alexey Proskuryakov (ap@nypop.com) - Copyright (C) 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#include "config.h" -#include "LegacyHTMLDocumentParser.h" - -#include "Attribute.h" -#include "CSSHelper.h" -#include "Cache.h" -#include "CachedScript.h" -#include "DocLoader.h" -#include "DocumentFragment.h" -#include "Event.h" -#include "EventNames.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameView.h" -#include "HTMLElement.h" -#include "HTMLNames.h" -#include "LegacyHTMLTreeBuilder.h" -#include "HTMLScriptElement.h" -#include "HTMLViewSourceDocument.h" -#include "ImageLoader.h" -#include "InspectorTimelineAgent.h" -#include "Page.h" -#include "LegacyPreloadScanner.h" -#include "ScriptSourceCode.h" -#include "ScriptValue.h" -#include "XSSAuditor.h" -#include <wtf/ASCIICType.h> -#include <wtf/CurrentTime.h> - -#include "HTMLEntityNames.cpp" - -#define PRELOAD_SCANNER_ENABLED 1 - -using namespace WTF; -using namespace std; - -namespace WebCore { - -using namespace HTMLNames; - -// This value is used to define how many loops (approximately tokens) -// the parser will make before checking if it should yield. -// To increase responsiveness reduce both ChunkSize and TimeDelay contants. -static const int defaultTokenizerChunkSize = 4096; - -// FIXME: We would like this constant to be 200ms. -// Yielding more aggressively results in increased responsiveness and better incremental rendering. -// It slows down overall page-load on slower machines, though, so for now we set a value of 500. -// TimeDelay controls the maximum time the parser will run before yielding. -// Inline script execution can cause the parser to excede this limit. -static const double defaultTokenizerTimeDelay = 0.500; - -static const char commentStart [] = "<!--"; -static const char doctypeStart [] = "<!doctype"; -static const char publicStart [] = "public"; -static const char systemStart [] = "system"; -static const char scriptEnd [] = "</script"; -static const char xmpEnd [] = "</xmp"; -static const char styleEnd [] = "</style"; -static const char textareaEnd [] = "</textarea"; -static const char titleEnd [] = "</title"; -static const char iframeEnd [] = "</iframe"; - -// Full support for MS Windows extensions to Latin-1. -// Technically these extensions should only be activated for pages -// marked "windows-1252" or "cp1252", but -// in the standard Microsoft way, these extensions infect hundreds of thousands -// of web pages. Note that people with non-latin-1 Microsoft extensions -// are SOL. -// -// See: http://www.microsoft.com/globaldev/reference/WinCP.asp -// http://www.bbsinc.com/iso8859.html -// http://www.obviously.com/ -// -// There may be better equivalents - -// We only need this for entities. For non-entity text, we handle this in the text encoding. - -static const UChar windowsLatin1ExtensionArray[32] = { - 0x20AC, 0x0081, 0x201A, 0x0192, 0x201E, 0x2026, 0x2020, 0x2021, // 80-87 - 0x02C6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008D, 0x017D, 0x008F, // 88-8F - 0x0090, 0x2018, 0x2019, 0x201C, 0x201D, 0x2022, 0x2013, 0x2014, // 90-97 - 0x02DC, 0x2122, 0x0161, 0x203A, 0x0153, 0x009D, 0x017E, 0x0178 // 98-9F -}; - -static inline UChar fixUpChar(UChar c) -{ - if ((c & ~0x1F) != 0x0080) - return c; - return windowsLatin1ExtensionArray[c - 0x80]; -} - -static inline bool tagMatch(const char* s1, const UChar* s2, unsigned length) -{ - for (unsigned i = 0; i != length; ++i) { - unsigned char c1 = s1[i]; - unsigned char uc1 = toASCIIUpper(static_cast<char>(c1)); - UChar c2 = s2[i]; - if (c1 != c2 && uc1 != c2) - return false; - } - return true; -} - -inline void Token::addAttribute(AtomicString& attrName, const AtomicString& attributeValue, bool viewSourceMode) -{ - if (!attrName.isEmpty()) { - ASSERT(!attrName.contains('/')); - RefPtr<Attribute> a = Attribute::createMapped(attrName, attributeValue); - if (!attrs) { - attrs = NamedNodeMap::create(); - attrs->reserveInitialCapacity(10); - } - attrs->insertAttribute(a.release(), viewSourceMode); - } - - attrName = emptyAtom; -} - -// ---------------------------------------------------------------------------- - -LegacyHTMLDocumentParser::LegacyHTMLDocumentParser(HTMLDocument* document, bool reportErrors) - : ScriptableDocumentParser(document) - , m_buffer(0) - , m_scriptCode(0) - , m_scriptCodeSize(0) - , m_scriptCodeCapacity(0) - , m_scriptCodeResync(0) - , m_executingScript(0) - , m_requestingScript(false) - , m_hasScriptsWaitingForStylesheets(false) - , m_timer(this, &LegacyHTMLDocumentParser::timerFired) - , m_externalScriptsTimer(this, &LegacyHTMLDocumentParser::executeExternalScriptsTimerFired) - , m_treeBuilder(new LegacyHTMLTreeBuilder(document, reportErrors)) - , m_inWrite(false) - , m_fragment(false) - , m_scriptingPermission(FragmentScriptingAllowed) -{ - begin(); -} - -LegacyHTMLDocumentParser::LegacyHTMLDocumentParser(HTMLViewSourceDocument* document) - : ScriptableDocumentParser(document, true) - , m_buffer(0) - , m_scriptCode(0) - , m_scriptCodeSize(0) - , m_scriptCodeCapacity(0) - , m_scriptCodeResync(0) - , m_executingScript(0) - , m_requestingScript(false) - , m_hasScriptsWaitingForStylesheets(false) - , m_timer(this, &LegacyHTMLDocumentParser::timerFired) - , m_externalScriptsTimer(this, &LegacyHTMLDocumentParser::executeExternalScriptsTimerFired) - , m_inWrite(false) - , m_fragment(false) - , m_scriptingPermission(FragmentScriptingAllowed) -{ - begin(); -} - -LegacyHTMLDocumentParser::LegacyHTMLDocumentParser(DocumentFragment* frag, FragmentScriptingPermission scriptingPermission) - : ScriptableDocumentParser(frag->document()) - , m_buffer(0) - , m_scriptCode(0) - , m_scriptCodeSize(0) - , m_scriptCodeCapacity(0) - , m_scriptCodeResync(0) - , m_executingScript(0) - , m_requestingScript(false) - , m_hasScriptsWaitingForStylesheets(false) - , m_timer(this, &LegacyHTMLDocumentParser::timerFired) - , m_externalScriptsTimer(this, &LegacyHTMLDocumentParser::executeExternalScriptsTimerFired) - , m_treeBuilder(new LegacyHTMLTreeBuilder(frag, scriptingPermission)) - , m_inWrite(false) - , m_fragment(true) - , m_scriptingPermission(scriptingPermission) -{ - begin(); -} - -void LegacyHTMLDocumentParser::reset() -{ - ASSERT(m_executingScript == 0); - - while (!m_pendingScripts.isEmpty()) { - CachedResourceHandle<CachedScript> cs = m_pendingScripts.takeFirst(); - ASSERT(cache()->disabled() || cs->accessCount() > 0); - cs->removeClient(this); - } - - fastFree(m_buffer); - m_buffer = m_dest = 0; - m_bufferSize = 0; - - fastFree(m_scriptCode); - m_scriptCode = 0; - m_scriptCodeSize = m_scriptCodeCapacity = m_scriptCodeResync = 0; - - m_timer.stop(); - m_externalScriptsTimer.stop(); - - m_state.setAllowYield(false); - m_state.setForceSynchronous(false); - - m_currentToken.reset(); - m_doctypeToken.reset(); - m_doctypeSearchCount = 0; - m_doctypeSecondarySearchCount = 0; - m_hasScriptsWaitingForStylesheets = false; -} - -void LegacyHTMLDocumentParser::begin() -{ - m_executingScript = 0; - m_requestingScript = false; - m_hasScriptsWaitingForStylesheets = false; - m_state.setLoadingExtScript(false); - reset(); - m_bufferSize = 254; - m_buffer = static_cast<UChar*>(fastMalloc(sizeof(UChar) * 254)); - m_dest = m_buffer; - tquote = NoQuote; - searchCount = 0; - m_state.setEntityState(NoEntity); - m_scriptTagSrcAttrValue = String(); - m_pendingSrc.clear(); - m_currentPrependingSrc = 0; - m_noMoreData = false; - m_brokenComments = false; - m_brokenServer = false; - m_lineNumber = 0; - m_currentScriptTagStartLineNumber = 0; - m_currentTagStartLineNumber = 0; - m_state.setForceSynchronous(false); - - Page* page = document()->page(); - if (page && page->hasCustomHTMLTokenizerTimeDelay()) - m_tokenizerTimeDelay = page->customHTMLTokenizerTimeDelay(); - else - m_tokenizerTimeDelay = defaultTokenizerTimeDelay; - - if (page && page->hasCustomHTMLTokenizerChunkSize()) - m_tokenizerChunkSize = page->customHTMLTokenizerChunkSize(); - else - m_tokenizerChunkSize = defaultTokenizerChunkSize; -} - -void LegacyHTMLDocumentParser::setForceSynchronous(bool force) -{ - m_state.setForceSynchronous(force); -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::processListing(SegmentedString list, State state) -{ - // This function adds the listing 'list' as - // preformatted text-tokens to the token-collection - while (!list.isEmpty()) { - if (state.skipLF()) { - state.setSkipLF(false); - if (*list == '\n') { - list.advance(); - continue; - } - } - - checkBuffer(); - - if (*list == '\n' || *list == '\r') { - if (state.discardLF()) - // Ignore this LF - state.setDiscardLF(false); // We have discarded 1 LF - else - *m_dest++ = '\n'; - - /* Check for MS-DOS CRLF sequence */ - if (*list == '\r') - state.setSkipLF(true); - - list.advance(); - } else { - state.setDiscardLF(false); - *m_dest++ = *list; - list.advance(); - } - } - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseNonHTMLText(SegmentedString& src, State state) -{ - ASSERT(state.inTextArea() || state.inTitle() || state.inIFrame() || !state.hasEntityState()); - ASSERT(!state.hasTagState()); - ASSERT(state.inXmp() + state.inTextArea() + state.inTitle() + state.inStyle() + state.inScript() + state.inIFrame() == 1); - if (state.inScript() && !m_currentScriptTagStartLineNumber) - m_currentScriptTagStartLineNumber = m_lineNumber; - - if (state.inComment()) - state = parseComment(src, state); - - int lastDecodedEntityPosition = -1; - while (!src.isEmpty()) { - checkScriptBuffer(); - UChar ch = *src; - - if (!m_scriptCodeResync && !m_brokenComments && - !state.inXmp() && ch == '-' && m_scriptCodeSize >= 3 && !src.escaped() && - m_scriptCode[m_scriptCodeSize - 3] == '<' && m_scriptCode[m_scriptCodeSize - 2] == '!' && m_scriptCode[m_scriptCodeSize - 1] == '-' && - (lastDecodedEntityPosition < m_scriptCodeSize - 3)) { - state.setInComment(true); - state = parseComment(src, state); - continue; - } - if (m_scriptCodeResync && !tquote && ch == '>') { - src.advancePastNonNewline(); - m_scriptCodeSize = m_scriptCodeResync - 1; - m_scriptCodeResync = 0; - m_scriptCode[m_scriptCodeSize] = m_scriptCode[m_scriptCodeSize + 1] = 0; - if (state.inScript()) - state = scriptHandler(state); - else { - state = processListing(SegmentedString(String(m_scriptCode, m_scriptCodeSize)), state); - processToken(); - if (state.inStyle()) { - m_currentToken.tagName = styleTag.localName(); - m_currentToken.beginTag = false; - } else if (state.inTextArea()) { - m_currentToken.tagName = textareaTag.localName(); - m_currentToken.beginTag = false; - } else if (state.inTitle()) { - m_currentToken.tagName = titleTag.localName(); - m_currentToken.beginTag = false; - } else if (state.inXmp()) { - m_currentToken.tagName = xmpTag.localName(); - m_currentToken.beginTag = false; - } else if (state.inIFrame()) { - m_currentToken.tagName = iframeTag.localName(); - m_currentToken.beginTag = false; - } - processToken(); - state.setInStyle(false); - state.setInScript(false); - state.setInTextArea(false); - state.setInTitle(false); - state.setInXmp(false); - state.setInIFrame(false); - tquote = NoQuote; - m_scriptCodeSize = m_scriptCodeResync = 0; - } - return state; - } - // possible end of tagname, lets check. - if (!m_scriptCodeResync && !state.escaped() && !src.escaped() && (ch == '>' || ch == '/' || isASCIISpace(ch)) && - m_scriptCodeSize >= m_searchStopperLength && - tagMatch(m_searchStopper, m_scriptCode + m_scriptCodeSize - m_searchStopperLength, m_searchStopperLength) && - (lastDecodedEntityPosition < m_scriptCodeSize - m_searchStopperLength)) { - m_scriptCodeResync = m_scriptCodeSize-m_searchStopperLength+1; - tquote = NoQuote; - continue; - } - if (m_scriptCodeResync && !state.escaped()) { - if (ch == '\"') - tquote = (tquote == NoQuote) ? DoubleQuote : ((tquote == SingleQuote) ? SingleQuote : NoQuote); - else if (ch == '\'') - tquote = (tquote == NoQuote) ? SingleQuote : (tquote == DoubleQuote) ? DoubleQuote : NoQuote; - else if (tquote != NoQuote && (ch == '\r' || ch == '\n')) - tquote = NoQuote; - } - state.setEscaped(!state.escaped() && ch == '\\'); - if (!m_scriptCodeResync && (state.inTextArea() || state.inTitle() || state.inIFrame()) && !src.escaped() && ch == '&') { - UChar* scriptCodeDest = m_scriptCode + m_scriptCodeSize; - src.advancePastNonNewline(); - state = parseEntity(src, scriptCodeDest, state, m_cBufferPos, true, false); - if (scriptCodeDest == m_scriptCode + m_scriptCodeSize) - lastDecodedEntityPosition = m_scriptCodeSize; - else - m_scriptCodeSize = scriptCodeDest - m_scriptCode; - } else { - m_scriptCode[m_scriptCodeSize++] = ch; - src.advance(m_lineNumber); - } - } - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::scriptHandler(State state) -{ - // We are inside a <script> - bool doScriptExec = false; - int startLine = m_currentScriptTagStartLineNumber + 1; // Script line numbers are 1 based, HTMLTokenzier line numbers are 0 based - - // Reset m_currentScriptTagStartLineNumber to indicate that we've finished parsing the current script element - m_currentScriptTagStartLineNumber = 0; - - // (Bugzilla 3837) Scripts following a frameset element should not execute or, - // in the case of extern scripts, even load. - bool followingFrameset = (document()->body() && document()->body()->hasTagName(framesetTag)); - - CachedScript* cs = 0; - // don't load external scripts for standalone documents (for now) - if (!inViewSourceMode()) { - if (!m_scriptTagSrcAttrValue.isEmpty() && document()->frame()) { - // forget what we just got; load from src url instead - if (!m_treeBuilder->skipMode() && !followingFrameset) { - // The parser might have been stopped by for example a window.close call in an earlier script. - // If so, we don't want to load scripts. - if (!m_parserStopped && m_scriptNode->dispatchBeforeLoadEvent(m_scriptTagSrcAttrValue) && - (cs = document()->docLoader()->requestScript(m_scriptTagSrcAttrValue, m_scriptTagCharsetAttrValue))) - m_pendingScripts.append(cs); - else - m_scriptNode = 0; - } else - m_scriptNode = 0; - m_scriptTagSrcAttrValue = String(); - } else { - // Parse m_scriptCode containing <script> info - doScriptExec = m_scriptNode->shouldExecuteAsJavaScript(); -#if ENABLE(XHTMLMP) - if (!doScriptExec) - document()->setShouldProcessNoscriptElement(true); -#endif - m_scriptNode = 0; - } - } - - state = processListing(SegmentedString(String(m_scriptCode, m_scriptCodeSize)), state); - RefPtr<Node> node = processToken(); - - if (node && m_scriptingPermission == FragmentScriptingNotAllowed) { - ExceptionCode ec; - node->remove(ec); - node = 0; - } - - String scriptString = node ? node->textContent() : ""; - m_currentToken.tagName = scriptTag.localName(); - m_currentToken.beginTag = false; - processToken(); - - state.setInScript(false); - m_scriptCodeSize = m_scriptCodeResync = 0; - - // FIXME: The script should be syntax highlighted. - if (inViewSourceMode()) - return state; - - SegmentedString* savedPrependingSrc = m_currentPrependingSrc; - SegmentedString prependingSrc; - m_currentPrependingSrc = &prependingSrc; - - if (!m_treeBuilder->skipMode() && !followingFrameset) { - if (cs) { - if (savedPrependingSrc) - savedPrependingSrc->append(m_src); - else - m_pendingSrc.prepend(m_src); - setSrc(SegmentedString()); - - // the ref() call below may call notifyFinished if the script is already in cache, - // and that mucks with the state directly, so we must write it back to the object. - m_state = state; - bool savedRequestingScript = m_requestingScript; - m_requestingScript = true; - cs->addClient(this); - m_requestingScript = savedRequestingScript; - state = m_state; - // will be 0 if script was already loaded and ref() executed it - if (!m_pendingScripts.isEmpty()) - state.setLoadingExtScript(true); - } else if (!m_fragment && doScriptExec) { - if (!m_executingScript) - m_pendingSrc.prepend(m_src); - else - prependingSrc = m_src; - setSrc(SegmentedString()); - state = scriptExecution(ScriptSourceCode(scriptString, document()->frame() ? document()->frame()->document()->url() : KURL(), startLine), state); - } - } - - if (!m_executingScript && !state.loadingExtScript()) { - m_src.append(m_pendingSrc); - m_pendingSrc.clear(); - } else if (!prependingSrc.isEmpty()) { - // restore first so that the write appends in the right place - // (does not hurt to do it again below) - m_currentPrependingSrc = savedPrependingSrc; - - // we need to do this slightly modified bit of one of the write() cases - // because we want to prepend to m_pendingSrc rather than appending - // if there's no previous prependingSrc - if (!m_pendingScripts.isEmpty()) { - if (m_currentPrependingSrc) - m_currentPrependingSrc->append(prependingSrc); - else - m_pendingSrc.prepend(prependingSrc); - } else { - m_state = state; - write(prependingSrc, false); - state = m_state; - } - } - -#if PRELOAD_SCANNER_ENABLED - if (!m_pendingScripts.isEmpty() && !m_executingScript) { - if (!m_preloadScanner) - m_preloadScanner.set(new LegacyPreloadScanner(document())); - if (!m_preloadScanner->inProgress()) { - m_preloadScanner->begin(); - m_preloadScanner->write(m_pendingSrc); - } - } -#endif - m_currentPrependingSrc = savedPrependingSrc; - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::scriptExecution(const ScriptSourceCode& sourceCode, State state) -{ - if (m_fragment || !document()->frame()) - return state; - m_executingScript++; - - SegmentedString* savedPrependingSrc = m_currentPrependingSrc; - SegmentedString prependingSrc; - m_currentPrependingSrc = &prependingSrc; - - m_state = state; - document()->frame()->script()->executeScript(sourceCode); - state = m_state; - - state.setAllowYield(true); - - m_executingScript--; - - if (!m_executingScript && !state.loadingExtScript()) { - m_pendingSrc.prepend(prependingSrc); - m_src.append(m_pendingSrc); - m_pendingSrc.clear(); - } else if (!prependingSrc.isEmpty()) { - // restore first so that the write appends in the right place - // (does not hurt to do it again below) - m_currentPrependingSrc = savedPrependingSrc; - - // we need to do this slightly modified bit of one of the write() cases - // because we want to prepend to m_pendingSrc rather than appending - // if there's no previous prependingSrc - if (!m_pendingScripts.isEmpty()) { - if (m_currentPrependingSrc) - m_currentPrependingSrc->append(prependingSrc); - else - m_pendingSrc.prepend(prependingSrc); - -#if PRELOAD_SCANNER_ENABLED - // We are stuck waiting for another script. Lets check the source that - // was just document.write()n for anything to load. - LegacyPreloadScanner documentWritePreloadScanner(document()); - documentWritePreloadScanner.begin(); - documentWritePreloadScanner.write(prependingSrc); - documentWritePreloadScanner.end(); -#endif - } else { - m_state = state; - write(prependingSrc, false); - state = m_state; - } - } - - m_currentPrependingSrc = savedPrependingSrc; - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseComment(SegmentedString& src, State state) -{ - // FIXME: Why does this code even run for comments inside <script> and <style>? This seems bogus. - checkScriptBuffer(src.length()); - while (!src.isEmpty()) { - UChar ch = *src; - m_scriptCode[m_scriptCodeSize++] = ch; - if (ch == '>') { - bool handleBrokenComments = m_brokenComments && !(state.inScript() || state.inStyle()); - int endCharsCount = 1; // start off with one for the '>' character - if (m_scriptCodeSize > 2 && m_scriptCode[m_scriptCodeSize-3] == '-' && m_scriptCode[m_scriptCodeSize-2] == '-') { - endCharsCount = 3; - } else if (m_scriptCodeSize > 3 && m_scriptCode[m_scriptCodeSize-4] == '-' && m_scriptCode[m_scriptCodeSize-3] == '-' && - m_scriptCode[m_scriptCodeSize-2] == '!') { - // Other browsers will accept --!> as a close comment, even though it's - // not technically valid. - endCharsCount = 4; - } - if (handleBrokenComments || endCharsCount > 1) { - src.advancePastNonNewline(); - if (!(state.inTitle() || state.inScript() || state.inXmp() || state.inTextArea() || state.inStyle() || state.inIFrame())) { - checkScriptBuffer(); - m_scriptCode[m_scriptCodeSize] = 0; - m_scriptCode[m_scriptCodeSize + 1] = 0; - m_currentToken.tagName = commentAtom; - m_currentToken.beginTag = true; - state = processListing(SegmentedString(String(m_scriptCode, m_scriptCodeSize - endCharsCount)), state); - processToken(); - m_currentToken.tagName = commentAtom; - m_currentToken.beginTag = false; - processToken(); - m_scriptCodeSize = 0; - } - state.setInComment(false); - return state; // Finished parsing comment - } - } - src.advance(m_lineNumber); - } - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseServer(SegmentedString& src, State state) -{ - checkScriptBuffer(src.length()); - while (!src.isEmpty()) { - UChar ch = *src; - m_scriptCode[m_scriptCodeSize++] = ch; - if (ch == '>' && m_scriptCodeSize > 1 && m_scriptCode[m_scriptCodeSize - 2] == '%') { - src.advancePastNonNewline(); - state.setInServer(false); - m_scriptCodeSize = 0; - return state; // Finished parsing server include - } - src.advance(m_lineNumber); - } - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseProcessingInstruction(SegmentedString& src, State state) -{ - UChar oldchar = 0; - while (!src.isEmpty()) { - UChar chbegin = *src; - if (chbegin == '\'') - tquote = tquote == SingleQuote ? NoQuote : SingleQuote; - else if (chbegin == '\"') - tquote = tquote == DoubleQuote ? NoQuote : DoubleQuote; - // Look for '?>' - // Some crappy sites omit the "?" before it, so - // we look for an unquoted '>' instead. (IE compatible) - else if (chbegin == '>' && (!tquote || oldchar == '?')) { - // We got a '?>' sequence - state.setInProcessingInstruction(false); - src.advancePastNonNewline(); - state.setDiscardLF(true); - return state; // Finished parsing comment! - } - src.advance(m_lineNumber); - oldchar = chbegin; - } - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseText(SegmentedString& src, State state) -{ - while (!src.isEmpty()) { - UChar cc = *src; - - if (state.skipLF()) { - state.setSkipLF(false); - if (cc == '\n') { - src.advancePastNewline(m_lineNumber); - continue; - } - } - - // do we need to enlarge the buffer? - checkBuffer(); - - if (cc == '\r') { - state.setSkipLF(true); - *m_dest++ = '\n'; - } else - *m_dest++ = cc; - src.advance(m_lineNumber); - } - - return state; -} - - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseEntity(SegmentedString& src, UChar*& dest, State state, unsigned& cBufferPos, bool start, bool parsingTag) -{ - if (start) { - cBufferPos = 0; - state.setEntityState(SearchEntity); - EntityUnicodeValue = 0; - } - - while (!src.isEmpty()) { - UChar cc = *src; - switch (state.entityState()) { - case NoEntity: - ASSERT(state.entityState() != NoEntity); - return state; - - case SearchEntity: - if (cc == '#') { - m_cBuffer[cBufferPos++] = cc; - src.advancePastNonNewline(); - state.setEntityState(NumericSearch); - } else - state.setEntityState(EntityName); - break; - - case NumericSearch: - if (cc == 'x' || cc == 'X') { - m_cBuffer[cBufferPos++] = cc; - src.advancePastNonNewline(); - state.setEntityState(Hexadecimal); - } else if (cc >= '0' && cc <= '9') - state.setEntityState(Decimal); - else - state.setEntityState(SearchSemicolon); - break; - - case Hexadecimal: { - int ll = min(src.length(), 10 - cBufferPos); - while (ll--) { - cc = *src; - if (!((cc >= '0' && cc <= '9') || (cc >= 'a' && cc <= 'f') || (cc >= 'A' && cc <= 'F'))) { - state.setEntityState(SearchSemicolon); - break; - } - int digit; - if (cc < 'A') - digit = cc - '0'; - else - digit = (cc - 'A' + 10) & 0xF; // handle both upper and lower case without a branch - EntityUnicodeValue = EntityUnicodeValue * 16 + digit; - m_cBuffer[cBufferPos++] = cc; - src.advancePastNonNewline(); - } - if (cBufferPos == 10) - state.setEntityState(SearchSemicolon); - break; - } - case Decimal: - { - int ll = min(src.length(), 9-cBufferPos); - while (ll--) { - cc = *src; - - if (!(cc >= '0' && cc <= '9')) { - state.setEntityState(SearchSemicolon); - break; - } - - EntityUnicodeValue = EntityUnicodeValue * 10 + (cc - '0'); - m_cBuffer[cBufferPos++] = cc; - src.advancePastNonNewline(); - } - if (cBufferPos == 9) - state.setEntityState(SearchSemicolon); - break; - } - case EntityName: - { - int ll = min(src.length(), 9-cBufferPos); - while (ll--) { - cc = *src; - - if (!((cc >= 'a' && cc <= 'z') || (cc >= '0' && cc <= '9') || (cc >= 'A' && cc <= 'Z'))) { - state.setEntityState(SearchSemicolon); - break; - } - - m_cBuffer[cBufferPos++] = cc; - src.advancePastNonNewline(); - } - if (cBufferPos == 9) - state.setEntityState(SearchSemicolon); - if (state.entityState() == SearchSemicolon) { - if (cBufferPos > 1) { - // Since the maximum length of entity name is 9, - // so a single char array which is allocated on - // the stack, its length is 10, should be OK. - // Also if we have an illegal character, we treat it - // as illegal entity name. - unsigned testedEntityNameLen = 0; - char tmpEntityNameBuffer[10]; - - ASSERT(cBufferPos < 10); - for (; testedEntityNameLen < cBufferPos; ++testedEntityNameLen) { - if (m_cBuffer[testedEntityNameLen] > 0x7e) - break; - tmpEntityNameBuffer[testedEntityNameLen] = m_cBuffer[testedEntityNameLen]; - } - - const Entity *e; - - if (testedEntityNameLen == cBufferPos) - e = findEntity(tmpEntityNameBuffer, cBufferPos); - else - e = 0; - - if (e) - EntityUnicodeValue = e->code; - - // be IE compatible - if (parsingTag && EntityUnicodeValue > 255 && *src != ';') - EntityUnicodeValue = 0; - } - } - else - break; - } - case SearchSemicolon: - // Don't allow values that are more than 21 bits. - if (EntityUnicodeValue > 0 && EntityUnicodeValue <= 0x10FFFF) { - if (!inViewSourceMode()) { - if (*src == ';') - src.advancePastNonNewline(); - if (EntityUnicodeValue <= 0xFFFF) { - checkBuffer(); - src.push(fixUpChar(EntityUnicodeValue)); - } else { - // Convert to UTF-16, using surrogate code points. - checkBuffer(2); - src.push(U16_LEAD(EntityUnicodeValue)); - src.push(U16_TRAIL(EntityUnicodeValue)); - } - } else { - // FIXME: We should eventually colorize entities by sending them as a special token. - // 12 bytes required: up to 10 bytes in m_cBuffer plus the - // leading '&' and trailing ';' - checkBuffer(12); - *dest++ = '&'; - for (unsigned i = 0; i < cBufferPos; i++) - dest[i] = m_cBuffer[i]; - dest += cBufferPos; - if (*src == ';') { - *dest++ = ';'; - src.advancePastNonNewline(); - } - } - } else { - // 11 bytes required: up to 10 bytes in m_cBuffer plus the - // leading '&' - checkBuffer(11); - // ignore the sequence, add it to the buffer as plaintext - *dest++ = '&'; - for (unsigned i = 0; i < cBufferPos; i++) - dest[i] = m_cBuffer[i]; - dest += cBufferPos; - } - - state.setEntityState(NoEntity); - return state; - } - } - - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseDoctype(SegmentedString& src, State state) -{ - ASSERT(state.inDoctype()); - while (!src.isEmpty() && state.inDoctype()) { - UChar c = *src; - bool isWhitespace = c == '\r' || c == '\n' || c == '\t' || c == ' '; - switch (m_doctypeToken.state()) { - case DoctypeBegin: { - m_doctypeToken.setState(DoctypeBeforeName); - if (isWhitespace) { - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } - break; - } - case DoctypeBeforeName: { - if (c == '>') { - // Malformed. Just exit. - src.advancePastNonNewline(); - state.setInDoctype(false); - if (inViewSourceMode()) - processDoctypeToken(); - } else if (isWhitespace) { - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else - m_doctypeToken.setState(DoctypeName); - break; - } - case DoctypeName: { - if (c == '>') { - // Valid doctype. Emit it. - src.advancePastNonNewline(); - state.setInDoctype(false); - processDoctypeToken(); - } else if (isWhitespace) { - m_doctypeSearchCount = 0; // Used now to scan for PUBLIC - m_doctypeSecondarySearchCount = 0; // Used now to scan for SYSTEM - m_doctypeToken.setState(DoctypeAfterName); - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else { - src.advancePastNonNewline(); - m_doctypeToken.m_name.append(c); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } - break; - } - case DoctypeAfterName: { - if (c == '>') { - // Valid doctype. Emit it. - src.advancePastNonNewline(); - state.setInDoctype(false); - processDoctypeToken(); - } else if (!isWhitespace) { - src.advancePastNonNewline(); - if (toASCIILower(c) == publicStart[m_doctypeSearchCount]) { - m_doctypeSearchCount++; - if (m_doctypeSearchCount == 6) - // Found 'PUBLIC' sequence - m_doctypeToken.setState(DoctypeBeforePublicID); - } else if (m_doctypeSearchCount > 0) { - m_doctypeSearchCount = 0; - m_doctypeToken.setState(DoctypeBogus); - } else if (toASCIILower(c) == systemStart[m_doctypeSecondarySearchCount]) { - m_doctypeSecondarySearchCount++; - if (m_doctypeSecondarySearchCount == 6) - // Found 'SYSTEM' sequence - m_doctypeToken.setState(DoctypeBeforeSystemID); - } else { - m_doctypeSecondarySearchCount = 0; - m_doctypeToken.setState(DoctypeBogus); - } - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else { - src.advance(m_lineNumber); // Whitespace keeps us in the after name state. - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } - break; - } - case DoctypeBeforePublicID: { - if (c == '\"' || c == '\'') { - tquote = c == '\"' ? DoubleQuote : SingleQuote; - m_doctypeToken.setState(DoctypePublicID); - src.advancePastNonNewline(); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else if (c == '>') { - // Considered bogus. Don't process the doctype. - src.advancePastNonNewline(); - state.setInDoctype(false); - if (inViewSourceMode()) - processDoctypeToken(); - } else if (isWhitespace) { - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else - m_doctypeToken.setState(DoctypeBogus); - break; - } - case DoctypePublicID: { - if ((c == '\"' && tquote == DoubleQuote) || (c == '\'' && tquote == SingleQuote)) { - src.advancePastNonNewline(); - m_doctypeToken.setState(DoctypeAfterPublicID); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else if (c == '>') { - // Considered bogus. Don't process the doctype. - src.advancePastNonNewline(); - state.setInDoctype(false); - if (inViewSourceMode()) - processDoctypeToken(); - } else { - m_doctypeToken.m_publicID.append(c); - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } - break; - } - case DoctypeAfterPublicID: - if (c == '\"' || c == '\'') { - tquote = c == '\"' ? DoubleQuote : SingleQuote; - m_doctypeToken.setState(DoctypeSystemID); - src.advancePastNonNewline(); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else if (c == '>') { - // Valid doctype. Emit it now. - src.advancePastNonNewline(); - state.setInDoctype(false); - processDoctypeToken(); - } else if (isWhitespace) { - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else - m_doctypeToken.setState(DoctypeBogus); - break; - case DoctypeBeforeSystemID: - if (c == '\"' || c == '\'') { - tquote = c == '\"' ? DoubleQuote : SingleQuote; - m_doctypeToken.setState(DoctypeSystemID); - src.advancePastNonNewline(); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else if (c == '>') { - // Considered bogus. Don't process the doctype. - src.advancePastNonNewline(); - state.setInDoctype(false); - } else if (isWhitespace) { - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else - m_doctypeToken.setState(DoctypeBogus); - break; - case DoctypeSystemID: - if ((c == '\"' && tquote == DoubleQuote) || (c == '\'' && tquote == SingleQuote)) { - src.advancePastNonNewline(); - m_doctypeToken.setState(DoctypeAfterSystemID); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else if (c == '>') { - // Considered bogus. Don't process the doctype. - src.advancePastNonNewline(); - state.setInDoctype(false); - if (inViewSourceMode()) - processDoctypeToken(); - } else { - m_doctypeToken.m_systemID.append(c); - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } - break; - case DoctypeAfterSystemID: - if (c == '>') { - // Valid doctype. Emit it now. - src.advancePastNonNewline(); - state.setInDoctype(false); - processDoctypeToken(); - } else if (isWhitespace) { - src.advance(m_lineNumber); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } else - m_doctypeToken.setState(DoctypeBogus); - break; - case DoctypeBogus: - if (c == '>') { - // Done with the bogus doctype. - src.advancePastNonNewline(); - state.setInDoctype(false); - if (inViewSourceMode()) - processDoctypeToken(); - } else { - src.advance(m_lineNumber); // Just keep scanning for '>' - if (inViewSourceMode()) - m_doctypeToken.m_source.append(c); - } - break; - default: - break; - } - } - return state; -} - -LegacyHTMLDocumentParser::State LegacyHTMLDocumentParser::parseTag(SegmentedString& src, State state) -{ - ASSERT(!state.hasEntityState()); - - unsigned cBufferPos = m_cBufferPos; - - bool lastIsSlash = false; - - while (!src.isEmpty()) { - checkBuffer(); - switch (state.tagState()) { - case NoTag: - { - m_cBufferPos = cBufferPos; - return state; - } - case TagName: - { - if (searchCount > 0) { - if (*src == commentStart[searchCount]) { - searchCount++; - if (searchCount == 2) - m_doctypeSearchCount++; // A '!' is also part of a doctype, so we are moving through that still as well. - else - m_doctypeSearchCount = 0; - if (searchCount == 4) { - // Found '<!--' sequence - src.advancePastNonNewline(); - m_dest = m_buffer; // ignore the previous part of this tag - state.setInComment(true); - state.setTagState(NoTag); - - // Fix bug 34302 at kde.bugs.org. Go ahead and treat - // <!--> as a valid comment, since both mozilla and IE on windows - // can handle this case. Only do this in quirks mode. -dwh - if (!src.isEmpty() && *src == '>' && document()->inCompatMode()) { - state.setInComment(false); - src.advancePastNonNewline(); - if (!src.isEmpty()) - m_cBuffer[cBufferPos++] = *src; - } else - state = parseComment(src, state); - - m_cBufferPos = cBufferPos; - return state; // Finished parsing tag! - } - m_cBuffer[cBufferPos++] = *src; - src.advancePastNonNewline(); - break; - } else - searchCount = 0; // Stop looking for '<!--' sequence - } - - if (m_doctypeSearchCount > 0) { - if (toASCIILower(*src) == doctypeStart[m_doctypeSearchCount]) { - m_doctypeSearchCount++; - m_cBuffer[cBufferPos++] = *src; - src.advancePastNonNewline(); - if (m_doctypeSearchCount == 9) { - // Found '<!DOCTYPE' sequence - state.setInDoctype(true); - state.setTagState(NoTag); - m_doctypeToken.reset(); - if (inViewSourceMode()) - m_doctypeToken.m_source.append(m_cBuffer, cBufferPos); - state = parseDoctype(src, state); - m_cBufferPos = cBufferPos; - return state; - } - break; - } else - m_doctypeSearchCount = 0; // Stop looking for '<!DOCTYPE' sequence - } - - bool finish = false; - unsigned int ll = min(src.length(), CBUFLEN - cBufferPos); - while (ll--) { - UChar curchar = *src; - if (isASCIISpace(curchar) || curchar == '>' || curchar == '<') { - finish = true; - break; - } - - // tolower() shows up on profiles. This is faster! - if (curchar >= 'A' && curchar <= 'Z' && !inViewSourceMode()) - m_cBuffer[cBufferPos++] = curchar + ('a' - 'A'); - else - m_cBuffer[cBufferPos++] = curchar; - src.advancePastNonNewline(); - } - - // Disadvantage: we add the possible rest of the tag - // as attribute names. ### judge if this causes problems - if (finish || CBUFLEN == cBufferPos) { - bool beginTag; - UChar* ptr = m_cBuffer; - unsigned int len = cBufferPos; - m_cBuffer[cBufferPos] = '\0'; - if ((cBufferPos > 0) && (*ptr == '/')) { - // End Tag - beginTag = false; - ptr++; - len--; - } - else - // Start Tag - beginTag = true; - - // Ignore the / in fake xml tags like <br/>. We trim off the "/" so that we'll get "br" as the tag name and not "br/". - if (len > 1 && ptr[len-1] == '/' && !inViewSourceMode()) - ptr[--len] = '\0'; - - // Now that we've shaved off any invalid / that might have followed the name), make the tag. - // FIXME: FireFox and WinIE turn !foo nodes into comments, we ignore comments. (fast/parser/tag-with-exclamation-point.html) - if (ptr[0] != '!' || inViewSourceMode()) { - m_currentToken.tagName = AtomicString(ptr); - m_currentToken.beginTag = beginTag; - } - m_dest = m_buffer; - state.setTagState(SearchAttribute); - cBufferPos = 0; - } - break; - } - case SearchAttribute: - while (!src.isEmpty()) { - UChar curchar = *src; - // In this mode just ignore any quotes we encounter and treat them like spaces. - if (!isASCIISpace(curchar) && curchar != '\'' && curchar != '"') { - if (curchar == '<' || curchar == '>') - state.setTagState(SearchEnd); - else - state.setTagState(AttributeName); - - cBufferPos = 0; - break; - } - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(curchar); - src.advance(m_lineNumber); - } - break; - case AttributeName: - { - m_rawAttributeBeforeValue.clear(); - int ll = min(src.length(), CBUFLEN - cBufferPos); - while (ll--) { - UChar curchar = *src; - // If we encounter a "/" when scanning an attribute name, treat it as a delimiter. This allows the - // cases like <input type=checkbox checked/> to work (and accommodates XML-style syntax as per HTML5). - if (curchar <= '>' && (curchar >= '<' || isASCIISpace(curchar) || curchar == '/')) { - m_cBuffer[cBufferPos] = '\0'; - m_attrName = AtomicString(m_cBuffer); - m_dest = m_buffer; - *m_dest++ = 0; - state.setTagState(SearchEqual); - if (inViewSourceMode()) - m_currentToken.addViewSourceChar('a'); - break; - } - - // tolower() shows up on profiles. This is faster! - if (curchar >= 'A' && curchar <= 'Z' && !inViewSourceMode()) - m_cBuffer[cBufferPos++] = curchar + ('a' - 'A'); - else - m_cBuffer[cBufferPos++] = curchar; - - m_rawAttributeBeforeValue.append(curchar); - src.advance(m_lineNumber); - } - if (cBufferPos == CBUFLEN) { - m_cBuffer[cBufferPos] = '\0'; - m_attrName = AtomicString(m_cBuffer); - m_dest = m_buffer; - *m_dest++ = 0; - state.setTagState(SearchEqual); - if (inViewSourceMode()) - m_currentToken.addViewSourceChar('a'); - } - break; - } - case SearchEqual: - while (!src.isEmpty()) { - UChar curchar = *src; - - if (lastIsSlash && curchar == '>') { - // This is a quirk (with a long sad history). We have to do this - // since widgets do <script src="foo.js"/> and expect the tag to close. - if (m_currentToken.tagName == scriptTag) - m_currentToken.selfClosingTag = true; - m_currentToken.brokenXMLStyle = true; - } - - // In this mode just ignore any quotes or slashes we encounter and treat them like spaces. - if (!isASCIISpace(curchar) && curchar != '\'' && curchar != '"' && curchar != '/') { - if (curchar == '=') { - state.setTagState(SearchValue); - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(curchar); - m_rawAttributeBeforeValue.append(curchar); - src.advancePastNonNewline(); - } else { - m_currentToken.addAttribute(m_attrName, emptyAtom, inViewSourceMode()); - m_dest = m_buffer; - state.setTagState(SearchAttribute); - lastIsSlash = false; - } - break; - } - - lastIsSlash = curchar == '/'; - - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(curchar); - m_rawAttributeBeforeValue.append(curchar); - src.advance(m_lineNumber); - } - break; - case SearchValue: - while (!src.isEmpty()) { - UChar curchar = *src; - if (!isASCIISpace(curchar)) { - if (curchar == '\'' || curchar == '\"') { - tquote = curchar == '\"' ? DoubleQuote : SingleQuote; - state.setTagState(QuotedValue); - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(curchar); - m_rawAttributeBeforeValue.append(curchar); - src.advancePastNonNewline(); - } else - state.setTagState(Value); - - break; - } - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(curchar); - m_rawAttributeBeforeValue.append(curchar); - src.advance(m_lineNumber); - } - break; - case QuotedValue: - while (!src.isEmpty()) { - checkBuffer(); - - UChar curchar = *src; - if (curchar <= '>' && !src.escaped()) { - if (curchar == '>' && m_attrName.isEmpty()) { - // Handle a case like <img '>. Just go ahead and be willing - // to close the whole tag. Don't consume the character and - // just go back into SearchEnd while ignoring the whole - // value. - // FIXME: Note that this is actually not a very good solution. - // It doesn't handle the general case of - // unmatched quotes among attributes that have names. -dwh - while (m_dest > m_buffer + 1 && (m_dest[-1] == '\n' || m_dest[-1] == '\r')) - m_dest--; // remove trailing newlines - AtomicString attributeValue(m_buffer + 1, m_dest - m_buffer - 1); - if (!attributeValue.contains('/')) - m_attrName = attributeValue; // Just make the name/value match. (FIXME: Is this some WinIE quirk?) - m_currentToken.addAttribute(m_attrName, attributeValue, inViewSourceMode()); - if (inViewSourceMode()) - m_currentToken.addViewSourceChar('x'); - state.setTagState(SearchAttribute); - m_dest = m_buffer; - tquote = NoQuote; - break; - } - - if (curchar == '&') { - src.advancePastNonNewline(); - state = parseEntity(src, m_dest, state, cBufferPos, true, true); - break; - } - - if ((tquote == SingleQuote && curchar == '\'') || (tquote == DoubleQuote && curchar == '\"')) { - // some <input type=hidden> rely on trailing spaces. argh - while (m_dest > m_buffer + 1 && (m_dest[-1] == '\n' || m_dest[-1] == '\r')) - m_dest--; // remove trailing newlines - AtomicString attributeValue(m_buffer + 1, m_dest - m_buffer - 1); - if (m_attrName.isEmpty() && !attributeValue.contains('/')) { - m_attrName = attributeValue; // Make the name match the value. (FIXME: Is this a WinIE quirk?) - if (inViewSourceMode()) - m_currentToken.addViewSourceChar('x'); - } else if (inViewSourceMode()) - m_currentToken.addViewSourceChar('v'); - - if (m_currentToken.beginTag && m_currentToken.tagName == scriptTag && !inViewSourceMode() && !m_treeBuilder->skipMode() && m_attrName == srcAttr) { - String context(m_rawAttributeBeforeValue.data(), m_rawAttributeBeforeValue.size()); - if (xssAuditor() && !xssAuditor()->canLoadExternalScriptFromSrc(attributeValue)) - attributeValue = blankURL().string(); - } - - m_currentToken.addAttribute(m_attrName, attributeValue, inViewSourceMode()); - m_dest = m_buffer; - state.setTagState(SearchAttribute); - tquote = NoQuote; - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(curchar); - src.advancePastNonNewline(); - break; - } - } - - *m_dest++ = curchar; - src.advance(m_lineNumber); - } - break; - case Value: - while (!src.isEmpty()) { - checkBuffer(); - UChar curchar = *src; - if (curchar <= '>' && !src.escaped()) { - // parse Entities - if (curchar == '&') { - src.advancePastNonNewline(); - state = parseEntity(src, m_dest, state, cBufferPos, true, true); - break; - } - // no quotes. Every space means end of value - // '/' does not delimit in IE! - if (isASCIISpace(curchar) || curchar == '>') { - AtomicString attributeValue(m_buffer + 1, m_dest - m_buffer - 1); - - if (m_currentToken.beginTag && m_currentToken.tagName == scriptTag && !inViewSourceMode() && !m_treeBuilder->skipMode() && m_attrName == srcAttr) { - String context(m_rawAttributeBeforeValue.data(), m_rawAttributeBeforeValue.size()); - if (xssAuditor() && !xssAuditor()->canLoadExternalScriptFromSrc(attributeValue)) - attributeValue = blankURL().string(); - } - - m_currentToken.addAttribute(m_attrName, attributeValue, inViewSourceMode()); - if (inViewSourceMode()) - m_currentToken.addViewSourceChar('v'); - m_dest = m_buffer; - state.setTagState(SearchAttribute); - break; - } - } - - *m_dest++ = curchar; - src.advance(m_lineNumber); - } - break; - case SearchEnd: - { - while (!src.isEmpty()) { - UChar ch = *src; - if (ch == '>' || ch == '<') - break; - if (ch == '/') - m_currentToken.selfClosingTag = true; - if (inViewSourceMode()) - m_currentToken.addViewSourceChar(ch); - src.advance(m_lineNumber); - } - if (src.isEmpty()) - break; - - searchCount = 0; // Stop looking for '<!--' sequence - state.setTagState(NoTag); - tquote = NoQuote; - - if (*src != '<') - src.advance(m_lineNumber); - - if (m_currentToken.tagName == nullAtom) { //stop if tag is unknown - m_cBufferPos = cBufferPos; - return state; - } - - AtomicString tagName = m_currentToken.tagName; - - // Handle <script src="foo"/> like Mozilla/Opera. We have to do this now for Dashboard - // compatibility. - bool isSelfClosingScript = m_currentToken.selfClosingTag && m_currentToken.beginTag && m_currentToken.tagName == scriptTag; - bool beginTag = !m_currentToken.selfClosingTag && m_currentToken.beginTag; - if (m_currentToken.beginTag && m_currentToken.tagName == scriptTag && !inViewSourceMode() && !m_treeBuilder->skipMode()) { - Attribute* a = 0; - m_scriptTagSrcAttrValue = String(); - m_scriptTagCharsetAttrValue = String(); - if (m_currentToken.attrs && !m_fragment) { - if (document()->frame() && document()->frame()->script()->canExecuteScripts(NotAboutToExecuteScript)) { - if ((a = m_currentToken.attrs->getAttributeItem(srcAttr))) - m_scriptTagSrcAttrValue = document()->completeURL(deprecatedParseURL(a->value())).string(); - } - } - } - - RefPtr<Node> n = processToken(); - m_cBufferPos = cBufferPos; - if (n || inViewSourceMode()) { - State savedState = state; - SegmentedString savedSrc = src; - long savedLineno = m_lineNumber; - if ((tagName == preTag || tagName == listingTag) && !inViewSourceMode()) { - if (beginTag) - state.setDiscardLF(true); // Discard the first LF after we open a pre. - } else if (tagName == scriptTag) { - ASSERT(!m_scriptNode); - m_scriptNode = static_pointer_cast<HTMLScriptElement>(n); - if (m_scriptNode) - m_scriptTagCharsetAttrValue = m_scriptNode->scriptCharset(); - if (beginTag) { - m_searchStopper = scriptEnd; - m_searchStopperLength = 8; - state.setInScript(true); - state = parseNonHTMLText(src, state); - } else if (isSelfClosingScript) { // Handle <script src="foo"/> - state.setInScript(true); - state = scriptHandler(state); - } - } else if (tagName == styleTag) { - if (beginTag) { - m_searchStopper = styleEnd; - m_searchStopperLength = 7; - state.setInStyle(true); - state = parseNonHTMLText(src, state); - } - } else if (tagName == textareaTag) { - if (beginTag) { - m_searchStopper = textareaEnd; - m_searchStopperLength = 10; - state.setInTextArea(true); - state = parseNonHTMLText(src, state); - } - } else if (tagName == titleTag) { - if (beginTag) { - m_searchStopper = titleEnd; - m_searchStopperLength = 7; - state.setInTitle(true); - state = parseNonHTMLText(src, state); - } - } else if (tagName == xmpTag) { - if (beginTag) { - m_searchStopper = xmpEnd; - m_searchStopperLength = 5; - state.setInXmp(true); - state = parseNonHTMLText(src, state); - } - } else if (tagName == iframeTag) { - if (beginTag) { - m_searchStopper = iframeEnd; - m_searchStopperLength = 8; - state.setInIFrame(true); - state = parseNonHTMLText(src, state); - } - } - if (src.isEmpty() && (state.inTitle() || inViewSourceMode()) && !state.inComment() && !(state.inScript() && m_currentScriptTagStartLineNumber)) { - // We just ate the rest of the document as the #text node under the special tag! - // Reset the state then retokenize without special handling. - // Let the parser clean up the missing close tag. - // FIXME: This is incorrect, because src.isEmpty() doesn't mean we're - // at the end of the document unless m_noMoreData is also true. We need - // to detect this case elsewhere, and save the state somewhere other - // than a local variable. - state = savedState; - src = savedSrc; - m_lineNumber = savedLineno; - m_scriptCodeSize = 0; - } - } - if (tagName == plaintextTag) - state.setInPlainText(beginTag); - return state; // Finished parsing tag! - } - } // end switch - } - m_cBufferPos = cBufferPos; - return state; -} - -inline bool LegacyHTMLDocumentParser::continueProcessing(int& processedCount, double startTime, State &state) -{ - // We don't want to be checking elapsed time with every character, so we only check after we've - // processed a certain number of characters. - bool allowedYield = state.allowYield(); - state.setAllowYield(false); - if (!state.loadingExtScript() && !state.forceSynchronous() && !m_executingScript && (processedCount > m_tokenizerChunkSize || allowedYield)) { - processedCount = 0; - if (currentTime() - startTime > m_tokenizerTimeDelay) { - /* FIXME: We'd like to yield aggressively to give stylesheets the opportunity to - load, but this hurts overall performance on slower machines. For now turn this - off. - || (!document()->haveStylesheetsLoaded() && - (document()->documentElement()->id() != ID_HTML || document()->body()))) {*/ - // Schedule the timer to keep processing as soon as possible. - m_timer.startOneShot(0); - return false; - } - } - - processedCount++; - return true; -} - -// Turns the statemachine one crank using the passed in State object. -// This does not modify m_state directly in order to be reentrant. -ALWAYS_INLINE void LegacyHTMLDocumentParser::advance(State& state) -{ - // do we need to enlarge the buffer? - checkBuffer(); - - UChar cc = *m_src; - - bool wasSkipLF = state.skipLF(); - if (wasSkipLF) - state.setSkipLF(false); - - if (wasSkipLF && (cc == '\n')) - m_src.advance(); - else if (state.needsSpecialWriteHandling()) { - // it's important to keep needsSpecialWriteHandling with the flags this block tests - if (state.hasEntityState()) - state = parseEntity(m_src, m_dest, state, m_cBufferPos, false, state.hasTagState()); - else if (state.inPlainText()) - state = parseText(m_src, state); - else if (state.inAnyNonHTMLText()) - state = parseNonHTMLText(m_src, state); - else if (state.inComment()) - state = parseComment(m_src, state); - else if (state.inDoctype()) - state = parseDoctype(m_src, state); - else if (state.inServer()) - state = parseServer(m_src, state); - else if (state.inProcessingInstruction()) - state = parseProcessingInstruction(m_src, state); - else if (state.hasTagState()) - state = parseTag(m_src, state); - else if (state.startTag()) { - state.setStartTag(false); - - switch (cc) { - case '/': - break; - case '!': { - // <!-- comment --> or <!DOCTYPE ...> - searchCount = 1; // Look for '<!--' sequence to start comment or '<!DOCTYPE' sequence to start doctype - m_doctypeSearchCount = 1; - break; - } - case '?': { - // xml processing instruction - state.setInProcessingInstruction(true); - tquote = NoQuote; - state = parseProcessingInstruction(m_src, state); - return; - } - case '%': - if (!m_brokenServer) { - // <% server stuff, handle as comment %> - state.setInServer(true); - tquote = NoQuote; - state = parseServer(m_src, state); - return; - } - // else fall through - default: { - if (((cc >= 'a') && (cc <= 'z')) || ((cc >= 'A') && (cc <= 'Z'))) { - // Start of a Start-Tag - } else { - // Invalid tag - // Add as is - *m_dest = '<'; - m_dest++; - return; - } - } - }; // end case - - processToken(); - - m_cBufferPos = 0; - state.setTagState(TagName); - state = parseTag(m_src, state); - } - } else if (cc == '&' && !m_src.escaped()) { - m_src.advancePastNonNewline(); - state = parseEntity(m_src, m_dest, state, m_cBufferPos, true, state.hasTagState()); - } else if (cc == '<' && !m_src.escaped()) { - m_currentTagStartLineNumber = m_lineNumber; - m_src.advancePastNonNewline(); - state.setStartTag(true); - state.setDiscardLF(false); - } else if (cc == '\n' || cc == '\r') { - if (state.discardLF()) - // Ignore this LF - state.setDiscardLF(false); // We have discarded 1 LF - else { - // Process this LF - *m_dest++ = '\n'; - if (cc == '\r' && !m_src.excludeLineNumbers()) - m_lineNumber++; - } - - /* Check for MS-DOS CRLF sequence */ - if (cc == '\r') - state.setSkipLF(true); - m_src.advance(m_lineNumber); - } else { - state.setDiscardLF(false); - *m_dest++ = cc; - m_src.advancePastNonNewline(); - } -} - -void LegacyHTMLDocumentParser::willWriteHTML(const SegmentedString& source) -{ - #if ENABLE(INSPECTOR) - if (InspectorTimelineAgent* timelineAgent = document()->inspectorTimelineAgent()) - timelineAgent->willWriteHTML(source.length(), m_lineNumber); - #endif -} - -void LegacyHTMLDocumentParser::didWriteHTML() -{ - #if ENABLE(INSPECTOR) - if (InspectorTimelineAgent* timelineAgent = document()->inspectorTimelineAgent()) - timelineAgent->didWriteHTML(m_lineNumber); - #endif -} - -void LegacyHTMLDocumentParser::write(const SegmentedString& str, bool appendData) -{ - if (!m_buffer) - return; - - if (m_parserStopped) - return; - - SegmentedString source(str); - if (m_executingScript) - source.setExcludeLineNumbers(); - - if ((m_executingScript && appendData) || !m_pendingScripts.isEmpty()) { - // don't parse; we will do this later - if (m_currentPrependingSrc) - m_currentPrependingSrc->append(source); - else { - m_pendingSrc.append(source); -#if PRELOAD_SCANNER_ENABLED - if (m_preloadScanner && m_preloadScanner->inProgress() && appendData) - m_preloadScanner->write(source); -#endif - } - return; - } - -#if PRELOAD_SCANNER_ENABLED - if (m_preloadScanner && m_preloadScanner->inProgress() && appendData) - m_preloadScanner->end(); -#endif - - if (!m_src.isEmpty()) - m_src.append(source); - else - setSrc(source); - - // Once a timer is set, it has control of when the parser continues. - if (m_timer.isActive()) - return; - - bool wasInWrite = m_inWrite; - m_inWrite = true; - - willWriteHTML(source); - - Frame* frame = document()->frame(); - State state = m_state; - int processedCount = 0; - double startTime = currentTime(); - - while (!m_src.isEmpty() && (!frame || !frame->redirectScheduler()->locationChangePending())) { - if (!continueProcessing(processedCount, startTime, state)) - break; - advance(state); - } - - didWriteHTML(); - - m_inWrite = wasInWrite; - m_state = state; - - if (m_noMoreData && !m_inWrite && !state.loadingExtScript() && !m_executingScript && !m_timer.isActive()) - end(); // this actually causes us to be deleted - - // After parsing, go ahead and dispatch image beforeload events, but only if we're doing - // document parsing. For document fragments we wait, since they'll likely end up in the document by the time - // the beforeload events fire. - if (!m_fragment) - ImageLoader::dispatchPendingBeforeLoadEvents(); -} - -void LegacyHTMLDocumentParser::insert(const SegmentedString& source) -{ - // FIXME: forceSynchronous should always be the same as the bool passed to - // write(). However LegacyHTMLDocumentParser uses write("", false) to pump - // the parser (after running external scripts, etc.) thus necessitating a - // separate state for forceSynchronous. - bool wasForcedSynchronous = forceSynchronous(); - setForceSynchronous(true); - write(source, false); - setForceSynchronous(wasForcedSynchronous); -} - -void LegacyHTMLDocumentParser::append(const SegmentedString& source) -{ - write(source, true); -} - -void LegacyHTMLDocumentParser::stopParsing() -{ - DocumentParser::stopParsing(); - m_timer.stop(); - - // FIXME: Why is LegacyHTMLDocumentParser the only DocumentParser which calls checkCompleted? - // The FrameLoader needs to know that the parser has finished with its data, - // regardless of whether it happened naturally or due to manual intervention. - if (!m_fragment && document()->frame()) - document()->frame()->loader()->checkCompleted(); -} - -bool LegacyHTMLDocumentParser::processingData() const -{ - return m_timer.isActive() || m_inWrite; -} - -void LegacyHTMLDocumentParser::timerFired(Timer<LegacyHTMLDocumentParser>*) -{ - if (document()->view() && document()->view()->layoutPending() && !document()->minimumLayoutDelay()) { - // Restart the timer and let layout win. This is basically a way of ensuring that the layout - // timer has higher priority than our timer. - m_timer.startOneShot(0); - return; - } - - // Invoke write() as though more data came in. This might cause us to get deleted. - write(SegmentedString(), true); -} - -void LegacyHTMLDocumentParser::end() -{ - ASSERT(!m_timer.isActive()); - m_timer.stop(); // Only helps if assertion above fires, but do it anyway. - - if (m_buffer) { - // parseTag is using the buffer for different matters - if (!m_state.hasTagState()) - processToken(); - - fastFree(m_scriptCode); - m_scriptCode = 0; - m_scriptCodeSize = m_scriptCodeCapacity = m_scriptCodeResync = 0; - - fastFree(m_buffer); - m_buffer = 0; - } - - if (!inViewSourceMode()) - m_treeBuilder->finished(); - else - document()->finishedParsing(); -} - -void LegacyHTMLDocumentParser::finish() -{ - // do this as long as we don't find matching comment ends - while ((m_state.inComment() || m_state.inServer()) && m_scriptCode && m_scriptCodeSize) { - // we've found an unmatched comment start - if (m_state.inComment()) - m_brokenComments = true; - else - m_brokenServer = true; - checkScriptBuffer(); - m_scriptCode[m_scriptCodeSize] = 0; - m_scriptCode[m_scriptCodeSize + 1] = 0; - int pos; - String food; - if (m_state.inScript() || m_state.inStyle() || m_state.inTextArea()) - food = String(m_scriptCode, m_scriptCodeSize); - else if (m_state.inServer()) { - food = "<"; - food.append(m_scriptCode, m_scriptCodeSize); - } else { - pos = find(m_scriptCode, m_scriptCodeSize, '>'); - food = String(m_scriptCode + pos + 1, m_scriptCodeSize - pos - 1); - } - fastFree(m_scriptCode); - m_scriptCode = 0; - m_scriptCodeSize = m_scriptCodeCapacity = m_scriptCodeResync = 0; - m_state.setInComment(false); - m_state.setInServer(false); - if (!food.isEmpty()) - write(food, true); - } - // this indicates we will not receive any more data... but if we are waiting on - // an external script to load, we can't finish parsing until that is done - m_noMoreData = true; - if (!m_inWrite && !m_state.loadingExtScript() && !m_executingScript && !m_timer.isActive()) - end(); // this actually causes us to be deleted -} - -bool LegacyHTMLDocumentParser::finishWasCalled() -{ - return m_noMoreData; -} - -PassRefPtr<Node> LegacyHTMLDocumentParser::processToken() -{ - if (m_dest > m_buffer) { - m_currentToken.text = StringImpl::createStrippingNullCharacters(m_buffer, m_dest - m_buffer); - if (m_currentToken.tagName != commentAtom) - m_currentToken.tagName = textAtom; - } else if (m_currentToken.tagName == nullAtom) { - m_currentToken.reset(); - return 0; - } - - m_dest = m_buffer; - - RefPtr<Node> n; - - if (!m_parserStopped) { - if (NamedNodeMap* map = m_currentToken.attrs.get()) - map->shrinkToLength(); - if (inViewSourceMode()) - static_cast<HTMLViewSourceDocument*>(document())->addViewSourceToken(&m_currentToken); - else - // pass the token over to the parser, the parser DOES NOT delete the token - n = m_treeBuilder->parseToken(&m_currentToken); - } - m_currentToken.reset(); - - return n.release(); -} - -void LegacyHTMLDocumentParser::processDoctypeToken() -{ - if (inViewSourceMode()) - static_cast<HTMLViewSourceDocument*>(document())->addViewSourceDoctypeToken(&m_doctypeToken); - else - m_treeBuilder->parseDoctypeToken(&m_doctypeToken); -} - -LegacyHTMLDocumentParser::~LegacyHTMLDocumentParser() -{ - ASSERT(!m_inWrite); - reset(); -} - - -void LegacyHTMLDocumentParser::enlargeBuffer(int len) -{ - // Resize policy: Always at least double the size of the buffer each time. - int delta = max(len, m_bufferSize); - - // Check for overflow. - // For now, handle overflow the same way we handle fastRealloc failure, with CRASH. - static const int maxSize = INT_MAX / sizeof(UChar); - if (delta > maxSize - m_bufferSize) - CRASH(); - - int newSize = m_bufferSize + delta; - int oldOffset = m_dest - m_buffer; - m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); - m_dest = m_buffer + oldOffset; - m_bufferSize = newSize; -} - -void LegacyHTMLDocumentParser::enlargeScriptBuffer(int len) -{ - // Resize policy: Always at least double the size of the buffer each time. - int delta = max(len, m_scriptCodeCapacity); - - // Check for overflow. - // For now, handle overflow the same way we handle fastRealloc failure, with CRASH. - static const int maxSize = INT_MAX / sizeof(UChar); - if (delta > maxSize - m_scriptCodeCapacity) - CRASH(); - - int newSize = m_scriptCodeCapacity + delta; - // If we allow fastRealloc(ptr, 0), it will call CRASH(). We run into this - // case if the HTML being parsed begins with "<!--" and there's more data - // coming. - if (!newSize) { - ASSERT(!m_scriptCode); - return; - } - - m_scriptCode = static_cast<UChar*>(fastRealloc(m_scriptCode, newSize * sizeof(UChar))); - m_scriptCodeCapacity = newSize; -} - -void LegacyHTMLDocumentParser::executeScriptsWaitingForStylesheets() -{ - ASSERT(document()->haveStylesheetsLoaded()); - - if (m_hasScriptsWaitingForStylesheets) - notifyFinished(0); -} - -void LegacyHTMLDocumentParser::notifyFinished(CachedResource*) -{ - executeExternalScriptsIfReady(); -} - -void LegacyHTMLDocumentParser::executeExternalScriptsIfReady() -{ - ASSERT(!m_pendingScripts.isEmpty()); - - // Make external scripts wait for external stylesheets. - // FIXME: This needs to be done for inline scripts too. - m_hasScriptsWaitingForStylesheets = !document()->haveStylesheetsLoaded(); - if (m_hasScriptsWaitingForStylesheets) - return; - - bool finished = false; - - double startTime = currentTime(); - while (!finished && m_pendingScripts.first()->isLoaded()) { - if (!continueExecutingExternalScripts(startTime)) - break; - - CachedResourceHandle<CachedScript> cs = m_pendingScripts.takeFirst(); - ASSERT(cache()->disabled() || cs->accessCount() > 0); - - setSrc(SegmentedString()); - - // make sure we forget about the script before we execute the new one - // infinite recursion might happen otherwise - ScriptSourceCode sourceCode(cs.get()); - bool errorOccurred = cs->errorOccurred(); - cs->removeClient(this); - - RefPtr<Node> n = m_scriptNode.release(); - - if (errorOccurred) - n->dispatchEvent(Event::create(eventNames().errorEvent, true, false)); - else { - if (static_cast<HTMLScriptElement*>(n.get())->shouldExecuteAsJavaScript()) - m_state = scriptExecution(sourceCode, m_state); -#if ENABLE(XHTMLMP) - else - document()->setShouldProcessNoscriptElement(true); -#endif - n->dispatchEvent(Event::create(eventNames().loadEvent, false, false)); - } - - // The state of m_pendingScripts.isEmpty() can change inside the scriptExecution() - // call above, so test afterwards. - finished = m_pendingScripts.isEmpty(); - if (finished) { - ASSERT(!m_hasScriptsWaitingForStylesheets); - m_state.setLoadingExtScript(false); - } else if (m_hasScriptsWaitingForStylesheets) { - // m_hasScriptsWaitingForStylesheets flag might have changed during the script execution. - // If it did we are now blocked waiting for stylesheets and should not execute more scripts until they arrive. - finished = true; - } - - // 'm_requestingScript' is true when we are called synchronously from - // scriptHandler(). In that case scriptHandler() will take care - // of m_pendingSrc. - if (!m_requestingScript) { - SegmentedString rest = m_pendingSrc; - m_pendingSrc.clear(); - write(rest, false); - // we might be deleted at this point, do not access any members. - } - } -} - -void LegacyHTMLDocumentParser::executeExternalScriptsTimerFired(Timer<LegacyHTMLDocumentParser>*) -{ - if (document()->view() && document()->view()->layoutPending() && !document()->minimumLayoutDelay()) { - // Restart the timer and do layout first. - m_externalScriptsTimer.startOneShot(0); - return; - } - - // Continue executing external scripts. - executeExternalScriptsIfReady(); -} - -bool LegacyHTMLDocumentParser::continueExecutingExternalScripts(double startTime) -{ - if (m_externalScriptsTimer.isActive()) - return false; - - if (currentTime() - startTime > m_tokenizerTimeDelay) { - // Schedule the timer to keep processing as soon as possible. - m_externalScriptsTimer.startOneShot(0); - return false; - } - return true; -} - -bool LegacyHTMLDocumentParser::isWaitingForScripts() const -{ - return m_state.loadingExtScript(); -} - -void LegacyHTMLDocumentParser::setSrc(const SegmentedString& source) -{ - m_src = source; -} - -void LegacyHTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, FragmentScriptingPermission scriptingPermission) -{ - LegacyHTMLDocumentParser parser(fragment, scriptingPermission); - parser.setForceSynchronous(true); - parser.write(source, true); - parser.finish(); - ASSERT(!parser.processingData()); // make sure we're done (see 3963151) -} - -UChar decodeNamedEntity(const char* name) -{ - const Entity* e = findEntity(name, strlen(name)); - return e ? e->code : 0; -} - -} diff --git a/WebCore/html/LegacyHTMLDocumentParser.h b/WebCore/html/LegacyHTMLDocumentParser.h deleted file mode 100644 index 49e6976..0000000 --- a/WebCore/html/LegacyHTMLDocumentParser.h +++ /dev/null @@ -1,452 +0,0 @@ -/* - Copyright (C) 1997 Martin Jones (mjones@kde.org) - (C) 1997 Torben Weis (weis@kde.org) - (C) 1998 Waldo Bastian (bastian@kde.org) - (C) 2001 Dirk Mueller (mueller@kde.org) - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - -#ifndef LegacyHTMLDocumentParser_h -#define LegacyHTMLDocumentParser_h - -#include "CachedResourceClient.h" -#include "CachedResourceHandle.h" -#include "FragmentScriptingPermission.h" -#include "NamedNodeMap.h" -#include "ScriptableDocumentParser.h" -#include "SegmentedString.h" -#include "Timer.h" -#include <wtf/Deque.h> -#include <wtf/OwnPtr.h> -#include <wtf/Vector.h> - -namespace WebCore { - -class CachedScript; -class DocumentFragment; -class Document; -class HTMLDocument; -class HTMLScriptElement; -class HTMLViewSourceDocument; -class FrameView; -class LegacyHTMLTreeBuilder; -class Node; -class LegacyPreloadScanner; -class ScriptSourceCode; - -/** - * @internal - * represents one HTML tag. Consists of a numerical id, and the list - * of attributes. Can also represent text. In this case the id = 0 and - * text contains the text. - */ -struct Token { - Token() - : beginTag(true) - , selfClosingTag(false) - , brokenXMLStyle(false) - , m_sourceInfo(0) - { } - ~Token() { } - - void addAttribute(AtomicString& attrName, const AtomicString& v, bool viewSourceMode); - - bool isOpenTag(const QualifiedName& fullName) const { return beginTag && fullName.localName() == tagName; } - bool isCloseTag(const QualifiedName& fullName) const { return !beginTag && fullName.localName() == tagName; } - - void reset() - { - attrs = 0; - text = 0; - tagName = nullAtom; - beginTag = true; - selfClosingTag = false; - brokenXMLStyle = false; - if (m_sourceInfo) - m_sourceInfo->clear(); - } - - void addViewSourceChar(UChar c) { if (!m_sourceInfo.get()) m_sourceInfo.set(new Vector<UChar>); m_sourceInfo->append(c); } - - RefPtr<NamedNodeMap> attrs; - RefPtr<StringImpl> text; - AtomicString tagName; - bool beginTag; - bool selfClosingTag; - bool brokenXMLStyle; - OwnPtr<Vector<UChar> > m_sourceInfo; -}; - -enum DoctypeState { - DoctypeBegin, - DoctypeBeforeName, - DoctypeName, - DoctypeAfterName, - DoctypeBeforePublicID, - DoctypePublicID, - DoctypeAfterPublicID, - DoctypeBeforeSystemID, - DoctypeSystemID, - DoctypeAfterSystemID, - DoctypeBogus -}; - -class DoctypeToken { -public: - DoctypeToken() {} - - void reset() - { - m_name.clear(); - m_publicID.clear(); - m_systemID.clear(); - m_state = DoctypeBegin; - m_source.clear(); - m_forceQuirks = false; - } - - DoctypeState state() { return m_state; } - void setState(DoctypeState s) { m_state = s; } - - Vector<UChar> m_name; - Vector<UChar> m_publicID; - Vector<UChar> m_systemID; - DoctypeState m_state; - - Vector<UChar> m_source; - - bool m_forceQuirks; // Used by the HTML5 parser. -}; - -//----------------------------------------------------------------------------- - -// FIXME: This class does too much. Right now it is both an HTML tokenizer as well -// as handling all of the non-tokenizer-specific junk related to tokenizing HTML -// (like dealing with <script> tags). The HTML tokenizer bits should be pushed -// down into a separate HTML tokenizer class. - -class LegacyHTMLDocumentParser : public ScriptableDocumentParser, public CachedResourceClient { -public: - LegacyHTMLDocumentParser(HTMLDocument*, bool reportErrors); - LegacyHTMLDocumentParser(HTMLViewSourceDocument*); - LegacyHTMLDocumentParser(DocumentFragment*, FragmentScriptingPermission = FragmentScriptingAllowed); - virtual ~LegacyHTMLDocumentParser(); - - bool forceSynchronous() const { return m_state.forceSynchronous(); } - void setForceSynchronous(bool force); - - static void parseDocumentFragment(const String&, DocumentFragment*, FragmentScriptingPermission = FragmentScriptingAllowed); - -protected: - // Exposed for FTPDirectoryDocumentParser - virtual void insert(const SegmentedString&); - virtual void finish(); - -private: - // ScriptableDocumentParser - virtual void append(const SegmentedString&); - virtual bool finishWasCalled(); - virtual bool isWaitingForScripts() const; - virtual void stopParsing(); - virtual bool processingData() const; - virtual bool isExecutingScript() const { return !!m_executingScript; } - - virtual int lineNumber() const { return m_lineNumber; } - virtual int columnNumber() const { return 1; } - - virtual bool processingContentWrittenByScript() const { return m_src.excludeLineNumbers(); } - - virtual void executeScriptsWaitingForStylesheets(); - - virtual LegacyHTMLTreeBuilder* htmlTreeBuilder() const { return m_treeBuilder.get(); } - - class State; - - void begin(); - void end(); - void reset(); - - void willWriteHTML(const SegmentedString&); - void write(const SegmentedString&, bool appendData); - ALWAYS_INLINE void advance(State&); - void didWriteHTML(); - - PassRefPtr<Node> processToken(); - void processDoctypeToken(); - - State processListing(SegmentedString, State); - State parseComment(SegmentedString&, State); - State parseDoctype(SegmentedString&, State); - State parseServer(SegmentedString&, State); - State parseText(SegmentedString&, State); - State parseNonHTMLText(SegmentedString&, State); - State parseTag(SegmentedString&, State); - State parseEntity(SegmentedString&, UChar*& dest, State, unsigned& cBufferPos, bool start, bool parsingTag); - State parseProcessingInstruction(SegmentedString&, State); - State scriptHandler(State); - State scriptExecution(const ScriptSourceCode&, State); - void setSrc(const SegmentedString&); - - // check if we have enough space in the buffer. - // if not enlarge it - inline void checkBuffer(int len = 10) - { - if ((m_dest - m_buffer) > m_bufferSize - len) - enlargeBuffer(len); - } - - inline void checkScriptBuffer(int len = 10) - { - if (m_scriptCodeSize + len >= m_scriptCodeCapacity) - enlargeScriptBuffer(len); - } - - void enlargeBuffer(int len); - void enlargeScriptBuffer(int len); - - bool continueProcessing(int& processedCount, double startTime, State&); - void timerFired(Timer<LegacyHTMLDocumentParser>*); - void allDataProcessed(); - - // from CachedResourceClient - void notifyFinished(CachedResource*); - - void executeExternalScriptsIfReady(); - void executeExternalScriptsTimerFired(Timer<LegacyHTMLDocumentParser>*); - bool continueExecutingExternalScripts(double startTime); - - // Internal buffers - /////////////////// - UChar* m_buffer; - int m_bufferSize; - UChar* m_dest; - - Token m_currentToken; - - // This buffer holds the raw characters we've seen between the beginning of - // the attribute name and the first character of the attribute value. - Vector<UChar, 32> m_rawAttributeBeforeValue; - - // DocumentParser flags - ////////////////// - // are we in quotes within a html tag - enum { NoQuote, SingleQuote, DoubleQuote } tquote; - - // Are we in a &... character entity description? - enum EntityState { - NoEntity = 0, - SearchEntity = 1, - NumericSearch = 2, - Hexadecimal = 3, - Decimal = 4, - EntityName = 5, - SearchSemicolon = 6 - }; - unsigned EntityUnicodeValue; - - enum TagState { - NoTag = 0, - TagName = 1, - SearchAttribute = 2, - AttributeName = 3, - SearchEqual = 4, - SearchValue = 5, - QuotedValue = 6, - Value = 7, - SearchEnd = 8 - }; - - class State { - public: - State() : m_bits(0) { } - - TagState tagState() const { return static_cast<TagState>(m_bits & TagMask); } - void setTagState(TagState t) { m_bits = (m_bits & ~TagMask) | t; } - EntityState entityState() const { return static_cast<EntityState>((m_bits & EntityMask) >> EntityShift); } - void setEntityState(EntityState e) { m_bits = (m_bits & ~EntityMask) | (e << EntityShift); } - - bool inScript() const { return testBit(InScript); } - void setInScript(bool v) { setBit(InScript, v); } - bool inStyle() const { return testBit(InStyle); } - void setInStyle(bool v) { setBit(InStyle, v); } - bool inXmp() const { return testBit(InXmp); } - void setInXmp(bool v) { setBit(InXmp, v); } - bool inTitle() const { return testBit(InTitle); } - void setInTitle(bool v) { setBit(InTitle, v); } - bool inIFrame() const { return testBit(InIFrame); } - void setInIFrame(bool v) { setBit(InIFrame, v); } - bool inPlainText() const { return testBit(InPlainText); } - void setInPlainText(bool v) { setBit(InPlainText, v); } - bool inProcessingInstruction() const { return testBit(InProcessingInstruction); } - void setInProcessingInstruction(bool v) { return setBit(InProcessingInstruction, v); } - bool inComment() const { return testBit(InComment); } - void setInComment(bool v) { setBit(InComment, v); } - bool inDoctype() const { return testBit(InDoctype); } - void setInDoctype(bool v) { setBit(InDoctype, v); } - bool inTextArea() const { return testBit(InTextArea); } - void setInTextArea(bool v) { setBit(InTextArea, v); } - bool escaped() const { return testBit(Escaped); } - void setEscaped(bool v) { setBit(Escaped, v); } - bool inServer() const { return testBit(InServer); } - void setInServer(bool v) { setBit(InServer, v); } - bool skipLF() const { return testBit(SkipLF); } - void setSkipLF(bool v) { setBit(SkipLF, v); } - bool startTag() const { return testBit(StartTag); } - void setStartTag(bool v) { setBit(StartTag, v); } - bool discardLF() const { return testBit(DiscardLF); } - void setDiscardLF(bool v) { setBit(DiscardLF, v); } - bool allowYield() const { return testBit(AllowYield); } - void setAllowYield(bool v) { setBit(AllowYield, v); } - bool loadingExtScript() const { return testBit(LoadingExtScript); } - void setLoadingExtScript(bool v) { setBit(LoadingExtScript, v); } - bool forceSynchronous() const { return testBit(ForceSynchronous); } - void setForceSynchronous(bool v) { setBit(ForceSynchronous, v); } - - bool inAnyNonHTMLText() const { return m_bits & (InScript | InStyle | InXmp | InTextArea | InTitle | InIFrame); } - bool hasTagState() const { return m_bits & TagMask; } - bool hasEntityState() const { return m_bits & EntityMask; } - - bool needsSpecialWriteHandling() const { return m_bits & (InScript | InStyle | InXmp | InTextArea | InTitle | InIFrame | TagMask | EntityMask | InPlainText | InComment | InDoctype | InServer | InProcessingInstruction | StartTag); } - - private: - static const int EntityShift = 4; - enum StateBits { - TagMask = (1 << 4) - 1, - EntityMask = (1 << 7) - (1 << 4), - InScript = 1 << 7, - InStyle = 1 << 8, - // Bit 9 unused - InXmp = 1 << 10, - InTitle = 1 << 11, - InPlainText = 1 << 12, - InProcessingInstruction = 1 << 13, - InComment = 1 << 14, - InTextArea = 1 << 15, - Escaped = 1 << 16, - InServer = 1 << 17, - SkipLF = 1 << 18, - StartTag = 1 << 19, - DiscardLF = 1 << 20, // FIXME: should clarify difference between skip and discard - AllowYield = 1 << 21, - LoadingExtScript = 1 << 22, - ForceSynchronous = 1 << 23, - InIFrame = 1 << 24, - InDoctype = 1 << 25 - }; - - void setBit(StateBits bit, bool value) - { - if (value) - m_bits |= bit; - else - m_bits &= ~bit; - } - bool testBit(StateBits bit) const { return m_bits & bit; } - - unsigned m_bits; - }; - - State m_state; - - DoctypeToken m_doctypeToken; - int m_doctypeSearchCount; - int m_doctypeSecondarySearchCount; - - bool m_brokenServer; - - // Name of an attribute that we just scanned. - AtomicString m_attrName; - - // Used to store the code of a scripting sequence - UChar* m_scriptCode; - // Size of the script sequenze stored in @ref #scriptCode - int m_scriptCodeSize; - // Maximal size that can be stored in @ref #scriptCode - int m_scriptCodeCapacity; - // resync point of script code size - int m_scriptCodeResync; - - // Stores characters if we are scanning for a string like "</script>" - UChar searchBuffer[10]; - - // Counts where we are in the string we are scanning for - int searchCount; - // the stopper string - const char* m_searchStopper; - int m_searchStopperLength; - - // if no more data is coming, just parse what we have (including ext scripts that - // may be still downloading) and finish - bool m_noMoreData; - // URL to get source code of script from - String m_scriptTagSrcAttrValue; - String m_scriptTagCharsetAttrValue; - // the HTML code we will parse after the external script we are waiting for has loaded - SegmentedString m_pendingSrc; - - // the HTML code we will parse after this particular script has - // loaded, but before all pending HTML - SegmentedString* m_currentPrependingSrc; - - // true if we are executing a script while parsing a document. This causes the parsing of - // the output of the script to be postponed until after the script has finished executing - int m_executingScript; - Deque<CachedResourceHandle<CachedScript> > m_pendingScripts; - RefPtr<HTMLScriptElement> m_scriptNode; - - bool m_requestingScript; - bool m_hasScriptsWaitingForStylesheets; - - // if we found one broken comment, there are most likely others as well - // store a flag to get rid of the O(n^2) behaviour in such a case. - bool m_brokenComments; - // current line number - int m_lineNumber; - int m_currentScriptTagStartLineNumber; - int m_currentTagStartLineNumber; - - double m_tokenizerTimeDelay; - int m_tokenizerChunkSize; - - // The timer for continued processing. - Timer<LegacyHTMLDocumentParser> m_timer; - - // The timer for continued executing external scripts. - Timer<LegacyHTMLDocumentParser> m_externalScriptsTimer; - -// This buffer can hold arbitrarily long user-defined attribute names, such as in EMBED tags. -// So any fixed number might be too small, but rather than rewriting all usage of this buffer -// we'll just make it large enough to handle all imaginable cases. -#define CBUFLEN 1024 - UChar m_cBuffer[CBUFLEN + 2]; - unsigned int m_cBufferPos; - - SegmentedString m_src; - OwnPtr<LegacyHTMLTreeBuilder> m_treeBuilder; - bool m_inWrite; - bool m_fragment; - FragmentScriptingPermission m_scriptingPermission; - - OwnPtr<LegacyPreloadScanner> m_preloadScanner; -}; - -UChar decodeNamedEntity(const char*); - -} // namespace WebCore - -#endif // LegacyHTMLDocumentParser_h diff --git a/WebCore/html/LegacyHTMLTreeBuilder.cpp b/WebCore/html/LegacyHTMLTreeBuilder.cpp index ee0bcfc..f39579c 100644 --- a/WebCore/html/LegacyHTMLTreeBuilder.cpp +++ b/WebCore/html/LegacyHTMLTreeBuilder.cpp @@ -52,9 +52,9 @@ #include "HTMLTableCellElement.h" #include "HTMLTableRowElement.h" #include "HTMLTableSectionElement.h" -#include "LegacyHTMLDocumentParser.h" #include "LocalizedStrings.h" #include "Page.h" +#include "ScriptableDocumentParser.h" #include "Settings.h" #include "Text.h" #include "TreeDepthLimit.h" diff --git a/WebCore/html/LegacyHTMLTreeBuilder.h b/WebCore/html/LegacyHTMLTreeBuilder.h index 4ac8413..ed2b857 100644 --- a/WebCore/html/LegacyHTMLTreeBuilder.h +++ b/WebCore/html/LegacyHTMLTreeBuilder.h @@ -26,6 +26,7 @@ #include "FragmentScriptingPermission.h" #include "HTMLParserErrorCodes.h" +#include "NamedNodeMap.h" #include "QualifiedName.h" #include <wtf/Forward.h> #include <wtf/OwnPtr.h> @@ -44,7 +45,97 @@ class HTMLParserQuirks; class Node; struct HTMLStackElem; -struct Token; + +/** + * @internal + * represents one HTML tag. Consists of a numerical id, and the list + * of attributes. Can also represent text. In this case the id = 0 and + * text contains the text. + */ +struct Token { + Token() + : beginTag(true) + , selfClosingTag(false) + , brokenXMLStyle(false) + , m_sourceInfo(0) + { } + ~Token() { } + + void addAttribute(AtomicString& attrName, const AtomicString& v, bool viewSourceMode); + + bool isOpenTag(const QualifiedName& fullName) const { return beginTag && fullName.localName() == tagName; } + bool isCloseTag(const QualifiedName& fullName) const { return !beginTag && fullName.localName() == tagName; } + + void reset() + { + attrs = 0; + text = 0; + tagName = nullAtom; + beginTag = true; + selfClosingTag = false; + brokenXMLStyle = false; + if (m_sourceInfo) + m_sourceInfo->clear(); + } + + void addViewSourceChar(UChar c) + { + if (!m_sourceInfo.get()) + m_sourceInfo.set(new Vector<UChar>); + m_sourceInfo->append(c); + } + + RefPtr<NamedNodeMap> attrs; + RefPtr<StringImpl> text; + AtomicString tagName; + bool beginTag; + bool selfClosingTag; + bool brokenXMLStyle; + OwnPtr<Vector<UChar> > m_sourceInfo; +}; + +enum DoctypeState { + DoctypeBegin, + DoctypeBeforeName, + DoctypeName, + DoctypeAfterName, + DoctypeBeforePublicID, + DoctypePublicID, + DoctypeAfterPublicID, + DoctypeBeforeSystemID, + DoctypeSystemID, + DoctypeAfterSystemID, + DoctypeBogus +}; + +class DoctypeToken { +public: + DoctypeToken() {} + + void reset() + { + m_name.clear(); + m_publicID.clear(); + m_systemID.clear(); + m_state = DoctypeBegin; + m_source.clear(); + m_forceQuirks = false; + } + + DoctypeState state() { return m_state; } + void setState(DoctypeState s) { m_state = s; } + + Vector<UChar> m_name; + Vector<UChar> m_publicID; + Vector<UChar> m_systemID; + DoctypeState m_state; + + Vector<UChar> m_source; + + bool m_forceQuirks; // Used by the HTML5 parser. +}; + +//----------------------------------------------------------------------------- /** * The parser for HTML. It receives a stream of tokens from the LegacyHTMLDocumentParser, and diff --git a/WebCore/html/LegacyPreloadScanner.cpp b/WebCore/html/LegacyPreloadScanner.cpp deleted file mode 100644 index c9fda4f..0000000 --- a/WebCore/html/LegacyPreloadScanner.cpp +++ /dev/null @@ -1,856 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * Copyright (C) 2009 Torch Mobile, Inc. http://www.torchmobile.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "LegacyPreloadScanner.h" - -#include "AtomicString.h" -#include "CachedCSSStyleSheet.h" -#include "CachedImage.h" -#include "CachedResource.h" -#include "CachedResourceClient.h" -#include "CachedScript.h" -#include "CSSHelper.h" -#include "DocLoader.h" -#include "Document.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "HTMLLinkElement.h" -#include "HTMLNames.h" -#include <wtf/text/CString.h> -#include <wtf/CurrentTime.h> -#include <wtf/unicode/Unicode.h> - -// Use __GNUC__ instead of PLATFORM(GCC) to stay consistent with the gperf generated c file -#ifdef __GNUC__ -// The main tokenizer includes this too so we are getting two copies of the data. However, this way the code gets inlined. -#include "HTMLEntityNames.cpp" -#else -// Not inlined for non-GCC compilers -struct Entity { - const char* name; - int code; -}; -const struct Entity* findEntity(register const char* str, register unsigned int len); -#endif - -#define PRELOAD_DEBUG 0 - -using namespace WTF; - -namespace WebCore { - -using namespace HTMLNames; - -LegacyPreloadScanner::LegacyPreloadScanner(Document* doc) - : m_inProgress(false) - , m_timeUsed(0) - , m_bodySeen(false) - , m_document(doc) -{ -#if PRELOAD_DEBUG - printf("CREATING PRELOAD SCANNER FOR %s\n", m_document->url().string().latin1().data()); -#endif -} - -LegacyPreloadScanner::~LegacyPreloadScanner() -{ -#if PRELOAD_DEBUG - printf("DELETING PRELOAD SCANNER FOR %s\n", m_document->url().string().latin1().data()); - printf("TOTAL TIME USED %.4fs\n", m_timeUsed); -#endif -} - -void LegacyPreloadScanner::begin() -{ - ASSERT(!m_inProgress); - reset(); - m_inProgress = true; -} - -void LegacyPreloadScanner::end() -{ - ASSERT(m_inProgress); - m_inProgress = false; -} - -void LegacyPreloadScanner::reset() -{ - m_source.clear(); - - m_state = Data; - m_escape = false; - m_contentModel = PCDATA; - m_commentPos = 0; - - m_closeTag = false; - m_tagName.clear(); - m_attributeName.clear(); - m_attributeValue.clear(); - m_lastStartTag = AtomicString(); - - m_urlToLoad = String(); - m_charset = String(); - m_linkIsStyleSheet = false; - m_lastCharacterIndex = 0; - clearLastCharacters(); - - m_cssState = CSSInitial; - m_cssRule.clear(); - m_cssRuleValue.clear(); -} - -bool LegacyPreloadScanner::scanningBody() const -{ - return m_document->body() || m_bodySeen; -} - -void LegacyPreloadScanner::write(const SegmentedString& source) -{ -#if PRELOAD_DEBUG - double startTime = currentTime(); -#endif - tokenize(source); -#if PRELOAD_DEBUG - m_timeUsed += currentTime() - startTime; -#endif -} - -static inline bool isWhitespace(UChar c) -{ - return c == ' ' || c == '\n' || c == '\r' || c == '\t'; -} - -inline void LegacyPreloadScanner::clearLastCharacters() -{ - memset(m_lastCharacters, 0, lastCharactersBufferSize * sizeof(UChar)); -} - -inline void LegacyPreloadScanner::rememberCharacter(UChar c) -{ - m_lastCharacterIndex = (m_lastCharacterIndex + 1) % lastCharactersBufferSize; - m_lastCharacters[m_lastCharacterIndex] = c; -} - -inline bool LegacyPreloadScanner::lastCharactersMatch(const char* chars, unsigned count) const -{ - unsigned pos = m_lastCharacterIndex; - while (count) { - if (chars[count - 1] != m_lastCharacters[pos]) - return false; - --count; - if (!pos) - pos = lastCharactersBufferSize; - --pos; - } - return true; -} - -static inline unsigned legalEntityFor(unsigned value) -{ - // FIXME There is a table for more exceptions in the HTML5 specification. - if (value == 0 || value > 0x10FFFF || (value >= 0xD800 && value <= 0xDFFF)) - return 0xFFFD; - return value; -} - -unsigned LegacyPreloadScanner::consumeEntity(SegmentedString& source, bool& notEnoughCharacters) -{ - enum EntityState { - Initial, - NumberType, - MaybeHex, - Hex, - Decimal, - Named - }; - EntityState entityState = Initial; - unsigned result = 0; - Vector<UChar, 10> seenChars; - Vector<char, 10> entityName; - - while (!source.isEmpty()) { - UChar cc = *source; - seenChars.append(cc); - switch (entityState) { - case Initial: - if (isWhitespace(cc) || cc == '<' || cc == '&') - return 0; - else if (cc == '#') - entityState = NumberType; - else if ((cc >= 'a' && cc <= 'z') || (cc >= 'A' && cc <= 'Z')) { - entityName.append(cc); - entityState = Named; - } else - return 0; - break; - case NumberType: - if (cc == 'x' || cc == 'X') - entityState = MaybeHex; - else if (cc >= '0' && cc <= '9') { - entityState = Decimal; - result = cc - '0'; - } else { - source.push('#'); - return 0; - } - break; - case MaybeHex: - if (cc >= '0' && cc <= '9') - result = cc - '0'; - else if (cc >= 'a' && cc <= 'f') - result = 10 + cc - 'a'; - else if (cc >= 'A' && cc <= 'F') - result = 10 + cc - 'A'; - else { - source.push('#'); - source.push(seenChars[1]); - return 0; - } - entityState = Hex; - break; - case Hex: - if (cc >= '0' && cc <= '9') - result = result * 16 + cc - '0'; - else if (cc >= 'a' && cc <= 'f') - result = result * 16 + 10 + cc - 'a'; - else if (cc >= 'A' && cc <= 'F') - result = result * 16 + 10 + cc - 'A'; - else if (cc == ';') { - source.advance(); - return legalEntityFor(result); - } else - return legalEntityFor(result); - break; - case Decimal: - if (cc >= '0' && cc <= '9') - result = result * 10 + cc - '0'; - else if (cc == ';') { - source.advance(); - return legalEntityFor(result); - } else - return legalEntityFor(result); - break; - case Named: - // This is the attribute only version, generic version matches somewhat differently - while (entityName.size() <= 8) { - if (cc == ';') { - const Entity* entity = findEntity(entityName.data(), entityName.size()); - if (entity) { - source.advance(); - return entity->code; - } - break; - } - if (!(cc >= 'a' && cc <= 'z') && !(cc >= 'A' && cc <= 'Z') && !(cc >= '0' && cc <= '9')) { - const Entity* entity = findEntity(entityName.data(), entityName.size()); - if (entity) - return entity->code; - break; - } - entityName.append(cc); - source.advance(); - if (source.isEmpty()) - goto outOfCharacters; - cc = *source; - seenChars.append(cc); - } - if (seenChars.size() == 2) - source.push(seenChars[0]); - else if (seenChars.size() == 3) { - source.push(seenChars[0]); - source.push(seenChars[1]); - } else - source.prepend(SegmentedString(String(seenChars.data(), seenChars.size() - 1))); - return 0; - } - source.advance(); - } -outOfCharacters: - notEnoughCharacters = true; - source.prepend(SegmentedString(String(seenChars.data(), seenChars.size()))); - return 0; -} - -void LegacyPreloadScanner::tokenize(const SegmentedString& source) -{ - ASSERT(m_inProgress); - - m_source.append(source); - - // This is a simplified HTML5 Tokenizer - // http://www.whatwg.org/specs/web-apps/current-work/#tokenisation0 - while (!m_source.isEmpty()) { - UChar cc = *m_source; - switch (m_state) { - case Data: - while (1) { - rememberCharacter(cc); - if (cc == '&') { - if (m_contentModel == PCDATA || m_contentModel == RCDATA) { - m_state = EntityData; - break; - } - } else if (cc == '-') { - if ((m_contentModel == RCDATA || m_contentModel == CDATA) && !m_escape) { - if (lastCharactersMatch("<!--", 4)) - m_escape = true; - } - } else if (cc == '<') { - if (m_contentModel == PCDATA || ((m_contentModel == RCDATA || m_contentModel == CDATA) && !m_escape)) { - m_state = TagOpen; - break; - } - } else if (cc == '>') { - if ((m_contentModel == RCDATA || m_contentModel == CDATA) && m_escape) { - if (lastCharactersMatch("-->", 3)) - m_escape = false; - } - } - emitCharacter(cc); - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case EntityData: - // should try to consume the entity but we only care about entities in attributes - m_state = Data; - break; - case TagOpen: - if (m_contentModel == RCDATA || m_contentModel == CDATA) { - if (cc == '/') - m_state = CloseTagOpen; - else { - m_state = Data; - continue; - } - } else if (m_contentModel == PCDATA) { - if (cc == '!') - m_state = MarkupDeclarationOpen; - else if (cc == '/') - m_state = CloseTagOpen; - else if (cc >= 'A' && cc <= 'Z') { - m_tagName.clear(); - m_charset = String(); - m_tagName.append(cc + 0x20); - m_closeTag = false; - m_state = TagName; - } else if (cc >= 'a' && cc <= 'z') { - m_tagName.clear(); - m_charset = String(); - m_tagName.append(cc); - m_closeTag = false; - m_state = TagName; - } else if (cc == '>') { - m_state = Data; - } else if (cc == '?') { - m_state = BogusComment; - } else { - m_state = Data; - continue; - } - } - break; - case CloseTagOpen: - if (m_contentModel == RCDATA || m_contentModel == CDATA) { - if (!m_lastStartTag.length()) { - m_state = Data; - continue; - } - if (m_source.length() < m_lastStartTag.length() + 1) - return; - Vector<UChar> tmpString; - UChar tmpChar = 0; - bool match = true; - for (unsigned n = 0; n < m_lastStartTag.length() + 1; n++) { - tmpChar = Unicode::toLower(*m_source); - if (n < m_lastStartTag.length() && tmpChar != m_lastStartTag[n]) - match = false; - tmpString.append(tmpChar); - m_source.advance(); - } - m_source.prepend(SegmentedString(String(tmpString.data(), tmpString.size()))); - if (!match || (!isWhitespace(tmpChar) && tmpChar != '>' && tmpChar != '/')) { - m_state = Data; - continue; - } - } - if (cc >= 'A' && cc <= 'Z') { - m_tagName.clear(); - m_charset = String(); - m_tagName.append(cc + 0x20); - m_closeTag = true; - m_state = TagName; - } else if (cc >= 'a' && cc <= 'z') { - m_tagName.clear(); - m_charset = String(); - m_tagName.append(cc); - m_closeTag = true; - m_state = TagName; - } else if (cc == '>') { - m_state = Data; - } else - m_state = BogusComment; - break; - case TagName: - while (1) { - if (isWhitespace(cc)) { - m_state = BeforeAttributeName; - break; - } - if (cc == '>') { - emitTag(); - m_state = Data; - break; - } - if (cc == '/') { - m_state = BeforeAttributeName; - break; - } - if (cc >= 'A' && cc <= 'Z') - m_tagName.append(cc + 0x20); - else - m_tagName.append(cc); - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case BeforeAttributeName: - if (isWhitespace(cc)) - ; - else if (cc == '>') { - emitTag(); - m_state = Data; - } else if (cc >= 'A' && cc <= 'Z') { - m_attributeName.clear(); - m_attributeValue.clear(); - m_attributeName.append(cc + 0x20); - m_state = AttributeName; - } else if (cc == '/') - ; - else { - m_attributeName.clear(); - m_attributeValue.clear(); - m_attributeName.append(cc); - m_state = AttributeName; - } - break; - case AttributeName: - while (1) { - if (isWhitespace(cc)) { - m_state = AfterAttributeName; - break; - } - if (cc == '=') { - m_state = BeforeAttributeValue; - break; - } - if (cc == '>') { - emitTag(); - m_state = Data; - break; - } - if (cc == '/') { - m_state = BeforeAttributeName; - break; - } - if (cc >= 'A' && cc <= 'Z') - m_attributeName.append(cc + 0x20); - else - m_attributeName.append(cc); - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case AfterAttributeName: - if (isWhitespace(cc)) - ; - else if (cc == '=') - m_state = BeforeAttributeValue; - else if (cc == '>') { - emitTag(); - m_state = Data; - } else if (cc >= 'A' && cc <= 'Z') { - m_attributeName.clear(); - m_attributeValue.clear(); - m_attributeName.append(cc + 0x20); - m_state = AttributeName; - } else if (cc == '/') - m_state = BeforeAttributeName; - else { - m_attributeName.clear(); - m_attributeValue.clear(); - m_attributeName.append(cc); - m_state = AttributeName; - } - break; - case BeforeAttributeValue: - if (isWhitespace(cc)) - ; - else if (cc == '"') - m_state = AttributeValueDoubleQuoted; - else if (cc == '&') { - m_state = AttributeValueUnquoted; - continue; - } else if (cc == '\'') - m_state = AttributeValueSingleQuoted; - else if (cc == '>') { - emitTag(); - m_state = Data; - } else { - m_attributeValue.append(cc); - m_state = AttributeValueUnquoted; - } - break; - case AttributeValueDoubleQuoted: - while (1) { - if (cc == '"') { - processAttribute(); - m_state = BeforeAttributeName; - break; - } - if (cc == '&') { - m_stateBeforeEntityInAttributeValue = m_state; - m_state = EntityInAttributeValue; - break; - } - m_attributeValue.append(cc); - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case AttributeValueSingleQuoted: - while (1) { - if (cc == '\'') { - processAttribute(); - m_state = BeforeAttributeName; - break; - } - if (cc == '&') { - m_stateBeforeEntityInAttributeValue = m_state; - m_state = EntityInAttributeValue; - break; - } - m_attributeValue.append(cc); - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case AttributeValueUnquoted: - while (1) { - if (isWhitespace(cc)) { - processAttribute(); - m_state = BeforeAttributeName; - break; - } - if (cc == '&') { - m_stateBeforeEntityInAttributeValue = m_state; - m_state = EntityInAttributeValue; - break; - } - if (cc == '>') { - processAttribute(); - emitTag(); - m_state = Data; - break; - } - m_attributeValue.append(cc); - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case EntityInAttributeValue: - { - bool notEnoughCharacters = false; - unsigned entity = consumeEntity(m_source, notEnoughCharacters); - if (notEnoughCharacters) - return; - if (entity > 0xFFFF) { - m_attributeValue.append(U16_LEAD(entity)); - m_attributeValue.append(U16_TRAIL(entity)); - } else if (entity) - m_attributeValue.append(entity); - else - m_attributeValue.append('&'); - } - m_state = m_stateBeforeEntityInAttributeValue; - continue; - case BogusComment: - while (1) { - if (cc == '>') { - m_state = Data; - break; - } - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case MarkupDeclarationOpen: { - if (cc == '-') { - if (m_source.length() < 2) - return; - m_source.advance(); - cc = *m_source; - if (cc == '-') - m_state = CommentStart; - else { - m_state = BogusComment; - continue; - } - // If we cared about the DOCTYPE we would test to enter those states here - } else { - m_state = BogusComment; - continue; - } - break; - } - case CommentStart: - if (cc == '-') - m_state = CommentStartDash; - else if (cc == '>') - m_state = Data; - else - m_state = Comment; - break; - case CommentStartDash: - if (cc == '-') - m_state = CommentEnd; - else if (cc == '>') - m_state = Data; - else - m_state = Comment; - break; - case Comment: - while (1) { - if (cc == '-') { - m_state = CommentEndDash; - break; - } - m_source.advance(); - if (m_source.isEmpty()) - return; - cc = *m_source; - } - break; - case CommentEndDash: - if (cc == '-') - m_state = CommentEnd; - else - m_state = Comment; - break; - case CommentEnd: - if (cc == '>') - m_state = Data; - else if (cc == '-') - ; - else - m_state = Comment; - break; - } - m_source.advance(); - } -} - -void LegacyPreloadScanner::processAttribute() -{ - AtomicString tag = AtomicString(m_tagName.data(), m_tagName.size()); - AtomicString attribute = AtomicString(m_attributeName.data(), m_attributeName.size()); - - String value(m_attributeValue.data(), m_attributeValue.size()); - if (tag == scriptTag || tag == imgTag) { - if (attribute == srcAttr && m_urlToLoad.isEmpty()) - m_urlToLoad = deprecatedParseURL(value); - else if (attribute == charsetAttr) - m_charset = value; - } else if (tag == linkTag) { - if (attribute == hrefAttr && m_urlToLoad.isEmpty()) - m_urlToLoad = deprecatedParseURL(value); - else if (attribute == relAttr) { - HTMLLinkElement::RelAttribute rel; - HTMLLinkElement::tokenizeRelAttribute(value, rel); - m_linkIsStyleSheet = rel.m_isStyleSheet && !rel.m_isAlternate && !rel.m_isIcon && !rel.m_isDNSPrefetch; - } else if (attribute == charsetAttr) - m_charset = value; - } -} - -inline void LegacyPreloadScanner::emitCharacter(UChar c) -{ - if (m_contentModel == CDATA && m_lastStartTag == styleTag) - tokenizeCSS(c); -} - -inline void LegacyPreloadScanner::tokenizeCSS(UChar c) -{ - // We are just interested in @import rules, no need for real tokenization here - // Searching for other types of resources is probably low payoff - switch (m_cssState) { - case CSSInitial: - if (c == '@') - m_cssState = CSSRuleStart; - else if (c == '/') - m_cssState = CSSMaybeComment; - break; - case CSSMaybeComment: - if (c == '*') - m_cssState = CSSComment; - else - m_cssState = CSSInitial; - break; - case CSSComment: - if (c == '*') - m_cssState = CSSMaybeCommentEnd; - break; - case CSSMaybeCommentEnd: - if (c == '/') - m_cssState = CSSInitial; - else if (c == '*') - ; - else - m_cssState = CSSComment; - break; - case CSSRuleStart: - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) { - m_cssRule.clear(); - m_cssRuleValue.clear(); - m_cssRule.append(c); - m_cssState = CSSRule; - } else - m_cssState = CSSInitial; - break; - case CSSRule: - if (isWhitespace(c)) - m_cssState = CSSAfterRule; - else if (c == ';') - m_cssState = CSSInitial; - else - m_cssRule.append(c); - break; - case CSSAfterRule: - if (isWhitespace(c)) - ; - else if (c == ';') - m_cssState = CSSInitial; - else { - m_cssState = CSSRuleValue; - m_cssRuleValue.append(c); - } - break; - case CSSRuleValue: - if (isWhitespace(c)) - m_cssState = CSSAfterRuleValue; - else if (c == ';') { - emitCSSRule(); - m_cssState = CSSInitial; - } else - m_cssRuleValue.append(c); - break; - case CSSAfterRuleValue: - if (isWhitespace(c)) - ; - else if (c == ';') { - emitCSSRule(); - m_cssState = CSSInitial; - } else { - // FIXME media rules - m_cssState = CSSInitial; - } - break; - } -} - -void LegacyPreloadScanner::emitTag() -{ - if (m_closeTag) { - m_contentModel = PCDATA; - m_cssState = CSSInitial; - clearLastCharacters(); - return; - } - - AtomicString tag(m_tagName.data(), m_tagName.size()); - m_lastStartTag = tag; - - if (tag == textareaTag || tag == titleTag) - m_contentModel = RCDATA; - else if (tag == styleTag || tag == xmpTag || tag == scriptTag || tag == iframeTag || tag == noembedTag || tag == noframesTag) - m_contentModel = CDATA; - else if (tag == noscriptTag) - // we wouldn't be here if scripts were disabled - m_contentModel = CDATA; - else if (tag == plaintextTag) - m_contentModel = PLAINTEXT; - else - m_contentModel = PCDATA; - - if (tag == bodyTag) - m_bodySeen = true; - - if (m_urlToLoad.isEmpty()) { - m_linkIsStyleSheet = false; - return; - } - - if (tag == scriptTag) - m_document->docLoader()->preload(CachedResource::Script, m_urlToLoad, m_charset, scanningBody()); - else if (tag == imgTag) - m_document->docLoader()->preload(CachedResource::ImageResource, m_urlToLoad, String(), scanningBody()); - else if (tag == linkTag && m_linkIsStyleSheet) - m_document->docLoader()->preload(CachedResource::CSSStyleSheet, m_urlToLoad, m_charset, scanningBody()); - - m_urlToLoad = String(); - m_charset = String(); - m_linkIsStyleSheet = false; -} - -void LegacyPreloadScanner::emitCSSRule() -{ - String rule(m_cssRule.data(), m_cssRule.size()); - if (equalIgnoringCase(rule, "import") && !m_cssRuleValue.isEmpty()) { - String value(m_cssRuleValue.data(), m_cssRuleValue.size()); - String url = deprecatedParseURL(value); - if (!url.isEmpty()) - m_document->docLoader()->preload(CachedResource::CSSStyleSheet, url, String(), scanningBody()); - } - m_cssRule.clear(); - m_cssRuleValue.clear(); -} - -} diff --git a/WebCore/html/LegacyPreloadScanner.h b/WebCore/html/LegacyPreloadScanner.h deleted file mode 100644 index 95710ab..0000000 --- a/WebCore/html/LegacyPreloadScanner.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All Rights Reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef LegacyPreloadScanner_h -#define LegacyPreloadScanner_h - -#include "AtomicString.h" -#include "SegmentedString.h" -#include <wtf/Noncopyable.h> -#include <wtf/Vector.h> - -namespace WebCore { - - class CachedResource; - class CachedResourceClient; - class Document; - - class LegacyPreloadScanner : public Noncopyable { - public: - LegacyPreloadScanner(Document*); - ~LegacyPreloadScanner(); - void begin(); - void write(const SegmentedString&); - void end(); - bool inProgress() const { return m_inProgress; } - - bool scanningBody() const; - - static unsigned consumeEntity(SegmentedString&, bool& notEnoughCharacters); - - private: - void tokenize(const SegmentedString&); - void reset(); - - void emitTag(); - void emitCharacter(UChar); - - void tokenizeCSS(UChar); - void emitCSSRule(); - - void processAttribute(); - - - void clearLastCharacters(); - void rememberCharacter(UChar); - bool lastCharactersMatch(const char*, unsigned count) const; - - bool m_inProgress; - SegmentedString m_source; - - enum State { - Data, - EntityData, - TagOpen, - CloseTagOpen, - TagName, - BeforeAttributeName, - AttributeName, - AfterAttributeName, - BeforeAttributeValue, - AttributeValueDoubleQuoted, - AttributeValueSingleQuoted, - AttributeValueUnquoted, - EntityInAttributeValue, - BogusComment, - MarkupDeclarationOpen, - CommentStart, - CommentStartDash, - Comment, - CommentEndDash, - CommentEnd - }; - State m_state; - bool m_escape; - enum ContentModel { - PCDATA, - RCDATA, - CDATA, - PLAINTEXT - }; - ContentModel m_contentModel; - unsigned m_commentPos; - State m_stateBeforeEntityInAttributeValue; - - static const unsigned lastCharactersBufferSize = 8; - UChar m_lastCharacters[lastCharactersBufferSize]; - unsigned m_lastCharacterIndex; - - bool m_closeTag; - Vector<UChar, 32> m_tagName; - Vector<UChar, 32> m_attributeName; - Vector<UChar> m_attributeValue; - AtomicString m_lastStartTag; - - String m_urlToLoad; - String m_charset; - bool m_linkIsStyleSheet; - - enum CSSState { - CSSInitial, - CSSMaybeComment, - CSSComment, - CSSMaybeCommentEnd, - CSSRuleStart, - CSSRule, - CSSAfterRule, - CSSRuleValue, - CSSAfterRuleValue - }; - CSSState m_cssState; - Vector<UChar, 16> m_cssRule; - Vector<UChar> m_cssRuleValue; - - double m_timeUsed; - - bool m_bodySeen; - Document* m_document; - }; - -} - -#endif diff --git a/WebCore/html/ThreadableBlobRegistry.cpp b/WebCore/html/ThreadableBlobRegistry.cpp new file mode 100644 index 0000000..1df290d --- /dev/null +++ b/WebCore/html/ThreadableBlobRegistry.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "ThreadableBlobRegistry.h" + +#include "BlobData.h" +#include "BlobRegistry.h" +#include "CrossThreadTask.h" +#include "NotImplemented.h" +#include "ScriptExecutionContext.h" +#include "WorkerContext.h" +#include "WorkerLoaderProxy.h" +#include "WorkerThread.h" + +namespace WebCore { + +static void postTaskToMainThread(ScriptExecutionContext* scriptExecutionContext, PassOwnPtr<ScriptExecutionContext::Task> task) +{ +#if ENABLE(WORKERS) + ASSERT(scriptExecutionContext->isWorkerContext()); + WorkerLoaderProxy& proxy = static_cast<WorkerContext*>(scriptExecutionContext)->thread()->workerLoaderProxy(); + proxy.postTaskToLoader(task); +#else + notImplemented(); +#endif +} + +static void registerBlobURLTask(ScriptExecutionContext*, const KURL& url, PassOwnPtr<BlobData> blobData) +{ + BlobRegistry::instance().registerBlobURL(url, blobData); +} + +void ThreadableBlobRegistry::registerBlobURL(ScriptExecutionContext* scriptExecutionContext, const KURL& url, PassOwnPtr<BlobData> blobData) +{ + if (scriptExecutionContext->isWorkerContext()) + postTaskToMainThread(scriptExecutionContext, createCallbackTask(®isterBlobURLTask, url, blobData)); + else + registerBlobURLTask(scriptExecutionContext, url, blobData); +} + +static void registerBlobURLFromTask(ScriptExecutionContext*, const KURL& url, const KURL& srcURL) +{ + BlobRegistry::instance().registerBlobURL(url, srcURL); +} + +void ThreadableBlobRegistry::registerBlobURL(ScriptExecutionContext* scriptExecutionContext, const KURL& url, const KURL& srcURL) +{ + if (scriptExecutionContext->isWorkerContext()) + postTaskToMainThread(scriptExecutionContext, createCallbackTask(®isterBlobURLFromTask, url, srcURL)); + else + registerBlobURLFromTask(scriptExecutionContext, url, srcURL); +} + +static void unregisterBlobURLTask(ScriptExecutionContext*, const KURL& url) +{ + BlobRegistry::instance().unregisterBlobURL(url); +} + +void ThreadableBlobRegistry::unregisterBlobURL(ScriptExecutionContext* scriptExecutionContext, const KURL& url) +{ + if (scriptExecutionContext->isWorkerContext()) + postTaskToMainThread(scriptExecutionContext, createCallbackTask(&unregisterBlobURLTask, url)); + else + unregisterBlobURLTask(scriptExecutionContext, url); +} + +} // namespace WebCore diff --git a/WebCore/html/ThreadableBlobRegistry.h b/WebCore/html/ThreadableBlobRegistry.h new file mode 100644 index 0000000..7dce6bb --- /dev/null +++ b/WebCore/html/ThreadableBlobRegistry.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ThreadableBlobRegistry_h +#define ThreadableBlobRegistry_h + +#include <wtf/PassOwnPtr.h> + +namespace WebCore { + +class BlobData; +class KURL; +class ScriptExecutionContext; + +class ThreadableBlobRegistry { +public: + static void registerBlobURL(ScriptExecutionContext*, const KURL&, PassOwnPtr<BlobData>); + static void registerBlobURL(ScriptExecutionContext*, const KURL&, const KURL& srcURL); + static void unregisterBlobURL(ScriptExecutionContext*, const KURL&); +}; + +} // namespace WebCore + +#endif // ThreadableBlobRegistry_h diff --git a/WebCore/html/canvas/CanvasNumberArray.idl b/WebCore/html/canvas/CanvasNumberArray.idl deleted file mode 100644 index 036d4ee..0000000 --- a/WebCore/html/canvas/CanvasNumberArray.idl +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2009 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -module html { - interface [ - Conditional=3D_CANVAS, - HasCustomIndexGetter - ] CanvasNumberArray { - readonly attribute unsigned long length; - }; -} diff --git a/WebCore/html/canvas/CanvasPattern.cpp b/WebCore/html/canvas/CanvasPattern.cpp index 62a4620..818d7d3 100644 --- a/WebCore/html/canvas/CanvasPattern.cpp +++ b/WebCore/html/canvas/CanvasPattern.cpp @@ -57,7 +57,7 @@ void CanvasPattern::parseRepetitionType(const String& type, bool& repeatX, bool& ec = SYNTAX_ERR; } -CanvasPattern::CanvasPattern(Image* image, bool repeatX, bool repeatY, bool originClean) +CanvasPattern::CanvasPattern(PassRefPtr<Image> image, bool repeatX, bool repeatY, bool originClean) : m_pattern(Pattern::create(image, repeatX, repeatY)) , m_originClean(originClean) { diff --git a/WebCore/html/canvas/CanvasPattern.h b/WebCore/html/canvas/CanvasPattern.h index 91e0794..58848a9 100644 --- a/WebCore/html/canvas/CanvasPattern.h +++ b/WebCore/html/canvas/CanvasPattern.h @@ -41,7 +41,7 @@ namespace WebCore { public: static void parseRepetitionType(const String&, bool& repeatX, bool& repeatY, ExceptionCode&); - static PassRefPtr<CanvasPattern> create(Image* image, bool repeatX, bool repeatY, bool originClean) + static PassRefPtr<CanvasPattern> create(PassRefPtr<Image> image, bool repeatX, bool repeatY, bool originClean) { return adoptRef(new CanvasPattern(image, repeatX, repeatY, originClean)); } @@ -51,7 +51,7 @@ namespace WebCore { bool originClean() const { return m_originClean; } private: - CanvasPattern(Image*, bool repeatX, bool repeatY, bool originClean); + CanvasPattern(PassRefPtr<Image>, bool repeatX, bool repeatY, bool originClean); RefPtr<Pattern> m_pattern; bool m_originClean; diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/WebCore/html/canvas/CanvasRenderingContext2D.cpp index 559ddda..58ec1d0 100644 --- a/WebCore/html/canvas/CanvasRenderingContext2D.cpp +++ b/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -126,7 +126,7 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bo return; if (FrameView* view = canvas->document()->view()) { if (ScrollView* rootView = view->root()) { - if (HostWindow* hostWindow = view->root()->hostWindow()) { + if (HostWindow* hostWindow = rootView->hostWindow()) { // Set up our context GraphicsContext3D::Attributes attr; attr.stencil = true; @@ -167,8 +167,8 @@ void CanvasRenderingContext2D::reset() } CanvasRenderingContext2D::State::State() - : m_strokeStyle(CanvasStyle::create(Color::black)) - , m_fillStyle(CanvasStyle::create(Color::black)) + : m_strokeStyle(CanvasStyle::createFromRGBA(Color::black)) + , m_fillStyle(CanvasStyle::createFromRGBA(Color::black)) , m_lineWidth(1) , m_lineCap(ButtCap) , m_lineJoin(MiterJoin) @@ -550,82 +550,82 @@ void CanvasRenderingContext2D::setStrokeColor(const String& color) { if (color == state().m_unparsedStrokeColor) return; - setStrokeStyle(CanvasStyle::create(color)); + setStrokeStyle(CanvasStyle::createFromString(color)); state().m_unparsedStrokeColor = color; } void CanvasRenderingContext2D::setStrokeColor(float grayLevel) { - if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(grayLevel, grayLevel, grayLevel, 1.0f)) + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) return; - setStrokeStyle(CanvasStyle::create(grayLevel, 1)); + setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); } void CanvasRenderingContext2D::setStrokeColor(const String& color, float alpha) { - setStrokeStyle(CanvasStyle::create(color, alpha)); + setStrokeStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); } void CanvasRenderingContext2D::setStrokeColor(float grayLevel, float alpha) { - if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(grayLevel, grayLevel, grayLevel, alpha)) + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) return; - setStrokeStyle(CanvasStyle::create(grayLevel, alpha)); + setStrokeStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); } void CanvasRenderingContext2D::setStrokeColor(float r, float g, float b, float a) { - if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(r, g, b, a)) + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentRGBA(r, g, b, a)) return; - setStrokeStyle(CanvasStyle::create(r, g, b, a)); + setStrokeStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); } void CanvasRenderingContext2D::setStrokeColor(float c, float m, float y, float k, float a) { - if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentColor(c, m, y, k, a)) + if (state().m_strokeStyle && state().m_strokeStyle->isEquivalentCMYKA(c, m, y, k, a)) return; - setStrokeStyle(CanvasStyle::create(c, m, y, k, a)); + setStrokeStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); } void CanvasRenderingContext2D::setFillColor(const String& color) { if (color == state().m_unparsedFillColor) return; - setFillStyle(CanvasStyle::create(color)); + setFillStyle(CanvasStyle::createFromString(color)); state().m_unparsedFillColor = color; } void CanvasRenderingContext2D::setFillColor(float grayLevel) { - if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(grayLevel, grayLevel, grayLevel, 1.0f)) + if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, 1.0f)) return; - setFillStyle(CanvasStyle::create(grayLevel, 1)); + setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, 1.0f)); } void CanvasRenderingContext2D::setFillColor(const String& color, float alpha) { - setFillStyle(CanvasStyle::create(color, alpha)); + setFillStyle(CanvasStyle::createFromStringWithOverrideAlpha(color, alpha)); } void CanvasRenderingContext2D::setFillColor(float grayLevel, float alpha) { - if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(grayLevel, grayLevel, grayLevel, alpha)) + if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(grayLevel, grayLevel, grayLevel, alpha)) return; - setFillStyle(CanvasStyle::create(grayLevel, alpha)); + setFillStyle(CanvasStyle::createFromGrayLevelWithAlpha(grayLevel, alpha)); } void CanvasRenderingContext2D::setFillColor(float r, float g, float b, float a) { - if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(r, g, b, a)) + if (state().m_fillStyle && state().m_fillStyle->isEquivalentRGBA(r, g, b, a)) return; - setFillStyle(CanvasStyle::create(r, g, b, a)); + setFillStyle(CanvasStyle::createFromRGBAChannels(r, g, b, a)); } void CanvasRenderingContext2D::setFillColor(float c, float m, float y, float k, float a) { - if (state().m_fillStyle && state().m_fillStyle->isEquivalentColor(c, m, y, k, a)) + if (state().m_fillStyle && state().m_fillStyle->isEquivalentCMYKA(c, m, y, k, a)) return; - setFillStyle(CanvasStyle::create(c, m, y, k, a)); + setFillStyle(CanvasStyle::createFromCMYKAChannels(c, m, y, k, a)); } void CanvasRenderingContext2D::beginPath() @@ -825,8 +825,16 @@ void CanvasRenderingContext2D::stroke() c->beginPath(); c->addPath(m_path); +#if PLATFORM(QT) + // Fast approximation of the stroke's bounding rect. + // This yields a slightly oversized rect but is very fast + // compared to Path::strokeBoundingRect(). + FloatRect boundingRect = m_path.platformPath().controlPointRect(); + boundingRect.inflate(state().m_miterLimit + state().m_lineWidth); +#else CanvasStrokeStyleApplier strokeApplier(this); FloatRect boundingRect = m_path.strokeBoundingRect(&strokeApplier); +#endif willDraw(boundingRect); c->strokePath(); @@ -1264,7 +1272,7 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const sourceCanvas->makeRenderingResultsAvailable(); - c->drawImage(buffer->image(), DeviceColorSpace, destRect, sourceRect, state().m_globalComposite); + c->drawImageBuffer(buffer, 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. } @@ -1464,7 +1472,7 @@ PassRefPtr<CanvasPattern> CanvasRenderingContext2D::createPattern(HTMLCanvasElem CanvasPattern::parseRepetitionType(repetitionType, repeatX, repeatY, ec); if (ec) return 0; - return CanvasPattern::create(canvas->buffer()->image(), repeatX, repeatY, canvas->originClean()); + return CanvasPattern::create(canvas->copiedImage(), repeatX, repeatY, canvas->originClean()); } void CanvasRenderingContext2D::willDraw(const FloatRect& r, unsigned options) @@ -1820,7 +1828,7 @@ void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo maskImageContext->drawBidiText(font, textRun, location); c->save(); - c->clipToImageBuffer(maskRect, maskImage.get()); + c->clipToImageBuffer(maskImage.get(), maskRect); drawStyle->applyFillColor(c); c->fillRect(maskRect); c->restore(); diff --git a/WebCore/html/canvas/CanvasStyle.cpp b/WebCore/html/canvas/CanvasStyle.cpp index 52b31c8..fd3c6e5 100644 --- a/WebCore/html/canvas/CanvasStyle.cpp +++ b/WebCore/html/canvas/CanvasStyle.cpp @@ -55,12 +55,6 @@ CanvasStyle::CanvasStyle(RGBA32 rgba) { } -CanvasStyle::CanvasStyle(float grayLevel) - : m_type(RGBA) - , m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, 1.0f)) -{ -} - CanvasStyle::CanvasStyle(float grayLevel, float alpha) : m_type(RGBA) , m_rgba(makeRGBA32FromFloats(grayLevel, grayLevel, grayLevel, alpha)) @@ -92,7 +86,7 @@ CanvasStyle::CanvasStyle(PassRefPtr<CanvasPattern> pattern) { } -PassRefPtr<CanvasStyle> CanvasStyle::create(const String& color) +PassRefPtr<CanvasStyle> CanvasStyle::createFromString(const String& color) { RGBA32 rgba; if (!CSSParser::parseColor(rgba, color)) @@ -100,7 +94,7 @@ PassRefPtr<CanvasStyle> CanvasStyle::create(const String& color) return adoptRef(new CanvasStyle(rgba)); } -PassRefPtr<CanvasStyle> CanvasStyle::create(const String& color, float alpha) +PassRefPtr<CanvasStyle> CanvasStyle::createFromStringWithOverrideAlpha(const String& color, float alpha) { RGBA32 rgba; if (!CSSParser::parseColor(rgba, color)) @@ -108,13 +102,13 @@ PassRefPtr<CanvasStyle> CanvasStyle::create(const String& color, float alpha) return adoptRef(new CanvasStyle(colorWithOverrideAlpha(rgba, alpha))); } -PassRefPtr<CanvasStyle> CanvasStyle::create(PassRefPtr<CanvasGradient> gradient) +PassRefPtr<CanvasStyle> CanvasStyle::createFromGradient(PassRefPtr<CanvasGradient> gradient) { if (!gradient) return 0; return adoptRef(new CanvasStyle(gradient)); } -PassRefPtr<CanvasStyle> CanvasStyle::create(PassRefPtr<CanvasPattern> pattern) +PassRefPtr<CanvasStyle> CanvasStyle::createFromPattern(PassRefPtr<CanvasPattern> pattern) { if (!pattern) return 0; @@ -144,7 +138,7 @@ bool CanvasStyle::isEquivalentColor(const CanvasStyle& other) const return false; } -bool CanvasStyle::isEquivalentColor(float r, float g, float b, float a) const +bool CanvasStyle::isEquivalentRGBA(float r, float g, float b, float a) const { if (m_type != RGBA) return false; @@ -152,7 +146,7 @@ bool CanvasStyle::isEquivalentColor(float r, float g, float b, float a) const return m_rgba == makeRGBA32FromFloats(r, g, b, a); } -bool CanvasStyle::isEquivalentColor(float c, float m, float y, float k, float a) const +bool CanvasStyle::isEquivalentCMYKA(float c, float m, float y, float k, float a) const { if (m_type != CMYKA) return false; diff --git a/WebCore/html/canvas/CanvasStyle.h b/WebCore/html/canvas/CanvasStyle.h index 8e47e63..3ca760a 100644 --- a/WebCore/html/canvas/CanvasStyle.h +++ b/WebCore/html/canvas/CanvasStyle.h @@ -38,15 +38,14 @@ namespace WebCore { class CanvasStyle : public RefCounted<CanvasStyle> { public: - static PassRefPtr<CanvasStyle> create(RGBA32 rgba) { return adoptRef(new CanvasStyle(rgba)); } - static PassRefPtr<CanvasStyle> create(const String& color); - static PassRefPtr<CanvasStyle> create(const String& color, float alpha); - static PassRefPtr<CanvasStyle> create(float grayLevel) { return adoptRef(new CanvasStyle(grayLevel)); } - static PassRefPtr<CanvasStyle> create(float grayLevel, float alpha) { return adoptRef(new CanvasStyle(grayLevel, alpha)); } - static PassRefPtr<CanvasStyle> create(float r, float g, float b, float a) { return adoptRef(new CanvasStyle(r, g, b, a)); } - static PassRefPtr<CanvasStyle> create(float c, float m, float y, float k, float a) { return adoptRef(new CanvasStyle(c, m, y, k, a)); } - static PassRefPtr<CanvasStyle> create(PassRefPtr<CanvasGradient> gradient); - static PassRefPtr<CanvasStyle> create(PassRefPtr<CanvasPattern> pattern); + static PassRefPtr<CanvasStyle> createFromRGBA(RGBA32 rgba) { return adoptRef(new CanvasStyle(rgba)); } + static PassRefPtr<CanvasStyle> createFromString(const String& color); + static PassRefPtr<CanvasStyle> createFromStringWithOverrideAlpha(const String& color, float alpha); + static PassRefPtr<CanvasStyle> createFromGrayLevelWithAlpha(float grayLevel, float alpha) { return adoptRef(new CanvasStyle(grayLevel, alpha)); } + static PassRefPtr<CanvasStyle> createFromRGBAChannels(float r, float g, float b, float a) { return adoptRef(new CanvasStyle(r, g, b, a)); } + static PassRefPtr<CanvasStyle> createFromCMYKAChannels(float c, float m, float y, float k, float a) { return adoptRef(new CanvasStyle(c, m, y, k, a)); } + static PassRefPtr<CanvasStyle> createFromGradient(PassRefPtr<CanvasGradient>); + static PassRefPtr<CanvasStyle> createFromPattern(PassRefPtr<CanvasPattern>); String color() const { return Color(m_rgba).serialized(); } CanvasGradient* canvasGradient() const { return m_gradient.get(); } @@ -56,12 +55,11 @@ namespace WebCore { void applyStrokeColor(GraphicsContext*); bool isEquivalentColor(const CanvasStyle&) const; - bool isEquivalentColor(float r, float g, float b, float a) const; - bool isEquivalentColor(float c, float m, float y, float k, float a) const; + bool isEquivalentRGBA(float r, float g, float b, float a) const; + bool isEquivalentCMYKA(float c, float m, float y, float k, float a) const; private: CanvasStyle(RGBA32 rgba); - CanvasStyle(float grayLevel); CanvasStyle(float grayLevel, float alpha); CanvasStyle(float r, float g, float b, float a); CanvasStyle(float c, float m, float y, float k, float a); diff --git a/WebCore/html/canvas/WebGLObject.cpp b/WebCore/html/canvas/WebGLObject.cpp index 6a34269..5fd5534 100644 --- a/WebCore/html/canvas/WebGLObject.cpp +++ b/WebCore/html/canvas/WebGLObject.cpp @@ -35,8 +35,9 @@ namespace WebCore { WebGLObject::WebGLObject(WebGLRenderingContext* context) : m_object(0) - , m_shouldDeleteObject(true) , m_context(context) + , m_attachmentCount(0) + , m_deleted(false) { } @@ -46,27 +47,26 @@ WebGLObject::~WebGLObject() m_context->removeObject(this); } -void WebGLObject::setObject(Platform3DObject object, bool shouldDeleteObject) +void WebGLObject::setObject(Platform3DObject object) { if (object == m_object) return; deleteObject(); m_object = object; - m_shouldDeleteObject = shouldDeleteObject; } void WebGLObject::deleteObject() { if (m_object) { - if (m_shouldDeleteObject) - if (m_context) { - m_context->graphicsContext3D()->makeContextCurrent(); - deleteObjectImpl(m_object); - } - m_object = 0; + if (m_context) { + m_context->graphicsContext3D()->makeContextCurrent(); + deleteObjectImpl(m_object); + } + if (!m_attachmentCount) + m_object = 0; + m_deleted = true; } - m_shouldDeleteObject = true; } } diff --git a/WebCore/html/canvas/WebGLObject.h b/WebCore/html/canvas/WebGLObject.h index b66311f..18d4cf9 100644 --- a/WebCore/html/canvas/WebGLObject.h +++ b/WebCore/html/canvas/WebGLObject.h @@ -40,7 +40,7 @@ public: virtual ~WebGLObject(); Platform3DObject object() const { return m_object; } - void setObject(Platform3DObject, bool shouldDeleteObject = true); + void setObject(Platform3DObject); void deleteObject(); void detachContext() @@ -58,20 +58,25 @@ public: virtual bool isShader() const { return false; } virtual bool isTexture() const { return false; } + void onAttached() { ++m_attachmentCount; } + void onDetached() + { + if (m_attachmentCount) + --m_attachmentCount; + if (!m_attachmentCount && m_deleted) + m_object = 0; + } + unsigned getAttachmentCount() { return m_attachmentCount; } + protected: WebGLObject(WebGLRenderingContext*); virtual void deleteObjectImpl(Platform3DObject) = 0; private: Platform3DObject m_object; - // The shouldDeleteObject flag indicates whether this wrapper - // owns the underlying resource and should delete it when the - // wrapper is unreferenced for the last time and deleted. It - // is only set to false for certain objects returned from get - // queries. FIXME: should consider canonicalizing all of these - // objects in the future. - bool m_shouldDeleteObject; WebGLRenderingContext* m_context; + unsigned m_attachmentCount; + bool m_deleted; }; } // namespace WebCore diff --git a/WebCore/html/canvas/WebGLProgram.cpp b/WebCore/html/canvas/WebGLProgram.cpp index 846b171..8cf3c42 100644 --- a/WebCore/html/canvas/WebGLProgram.cpp +++ b/WebCore/html/canvas/WebGLProgram.cpp @@ -45,9 +45,15 @@ WebGLProgram::WebGLProgram(WebGLRenderingContext* ctx) setObject(context()->graphicsContext3D()->createProgram()); } -void WebGLProgram::deleteObjectImpl(Platform3DObject object) +void WebGLProgram::deleteObjectImpl(Platform3DObject obj) { - context()->graphicsContext3D()->deleteProgram(object); + context()->graphicsContext3D()->deleteProgram(obj); + if (!object()) { + if (m_vertexShader) + m_vertexShader->onDetached(); + if (m_fragmentShader) + m_fragmentShader->onDetached(); + } } bool WebGLProgram::cacheActiveAttribLocations() @@ -94,6 +100,58 @@ bool WebGLProgram::isUsingVertexAttrib0() const return false; } +WebGLShader* WebGLProgram::getAttachedShader(GraphicsContext3D::WebGLEnumType type) +{ + switch (type) { + case GraphicsContext3D::VERTEX_SHADER: + return m_vertexShader.get(); + case GraphicsContext3D::FRAGMENT_SHADER: + return m_fragmentShader.get(); + default: + return 0; + } +} + +bool WebGLProgram::attachShader(WebGLShader* shader) +{ + if (!shader || !shader->object()) + return false; + switch (shader->getType()) { + case GraphicsContext3D::VERTEX_SHADER: + if (m_vertexShader) + return false; + m_vertexShader = shader; + return true; + case GraphicsContext3D::FRAGMENT_SHADER: + if (m_fragmentShader) + return false; + m_fragmentShader = shader; + return true; + default: + return false; + } +} + +bool WebGLProgram::detachShader(WebGLShader* shader) +{ + if (!shader || !shader->object()) + return false; + switch (shader->getType()) { + case GraphicsContext3D::VERTEX_SHADER: + if (m_vertexShader != shader) + return false; + m_vertexShader = 0; + return true; + case GraphicsContext3D::FRAGMENT_SHADER: + if (m_fragmentShader != shader) + return false; + m_fragmentShader = 0; + return true; + default: + return false; + } +} + } #endif // ENABLE(3D_CANVAS) diff --git a/WebCore/html/canvas/WebGLProgram.h b/WebCore/html/canvas/WebGLProgram.h index 0156938..e5548eb 100644 --- a/WebCore/html/canvas/WebGLProgram.h +++ b/WebCore/html/canvas/WebGLProgram.h @@ -28,6 +28,8 @@ #include "WebGLObject.h" +#include "WebGLShader.h" + #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> @@ -54,6 +56,10 @@ public: bool isLinkFailureFlagSet() const { return m_linkFailure; } void setLinkFailureFlag(bool failed) { m_linkFailure = failed; } + WebGLShader* getAttachedShader(GraphicsContext3D::WebGLEnumType); + bool attachShader(WebGLShader*); + bool detachShader(WebGLShader*); + protected: WebGLProgram(WebGLRenderingContext*); @@ -65,6 +71,9 @@ private: Vector<int> m_activeAttribLocations; bool m_linkFailure; + + RefPtr<WebGLShader> m_vertexShader; + RefPtr<WebGLShader> m_fragmentShader; }; } // namespace WebCore diff --git a/WebCore/html/canvas/WebGLRenderingContext.cpp b/WebCore/html/canvas/WebGLRenderingContext.cpp index 0284ec6..4465833 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -158,27 +158,11 @@ void WebGLRenderingContext::markContextChanged() void WebGLRenderingContext::paintRenderingResultsToCanvas() { - if (m_markedCanvasDirty) { - // FIXME: It should not be necessary to clear the image before doing a readback. - // Investigate why this is needed and remove if possible. - canvas()->buffer()->clearImage(); - m_markedCanvasDirty = false; - m_context->paintRenderingResultsToCanvas(this); - } -} - -void WebGLRenderingContext::beginPaint() -{ - if (m_markedCanvasDirty) - m_context->beginPaint(this); -} - -void WebGLRenderingContext::endPaint() -{ - if (m_markedCanvasDirty) { - m_markedCanvasDirty = false; - m_context->endPaint(); - } + if (!m_markedCanvasDirty) + return; + canvas()->clearCopiedImage(); + m_markedCanvasDirty = false; + m_context->paintRenderingResultsToCanvas(this); } void WebGLRenderingContext::reshape(int width, int height) @@ -191,7 +175,9 @@ void WebGLRenderingContext::reshape(int width, int height) #endif m_needsUpdate = false; } - + + // We don't have to mark the canvas as dirty, since the newly created image buffer will also start off + // clear (and this matches what reshape will do). m_context->reshape(width, height); } @@ -217,7 +203,12 @@ void WebGLRenderingContext::attachShader(WebGLProgram* program, WebGLShader* sha UNUSED_PARAM(ec); if (!validateWebGLObject(program) || !validateWebGLObject(shader)) return; + if (!program->attachShader(shader)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } m_context->attachShader(objectOrZero(program), objectOrZero(shader)); + shader->onAttached(); cleanupAfterGraphicsCall(false); } @@ -521,6 +512,9 @@ void WebGLRenderingContext::copyTexImage2D(unsigned long target, long level, uns { if (!validateTexFuncParameters(target, level, internalformat, width, height, border, internalformat, GraphicsContext3D::UNSIGNED_BYTE)) return; + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; if (!isGLES2Compliant()) { if (m_framebufferBinding && m_framebufferBinding->object() && !isTexInternalFormatColorBufferCombinationValid(internalformat, @@ -535,21 +529,20 @@ void WebGLRenderingContext::copyTexImage2D(unsigned long target, long level, uns } m_context->copyTexImage2D(target, level, internalformat, x, y, width, height, border); // FIXME: if the framebuffer is not complete, none of the below should be executed. - WebGLTexture* tex = getTextureBinding(target); - if (!isGLES2Compliant()) { - if (tex) - tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); - } - if (m_framebufferBinding && tex) + if (!isGLES2Compliant()) + tex->setLevelInfo(target, level, internalformat, width, height, GraphicsContext3D::UNSIGNED_BYTE); + if (m_framebufferBinding) m_framebufferBinding->onAttachedObjectChange(tex); cleanupAfterGraphicsCall(false); } void WebGLRenderingContext::copyTexSubImage2D(unsigned long target, long level, long xoffset, long yoffset, long x, long y, unsigned long width, unsigned long height) { + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; if (!isGLES2Compliant()) { - WebGLTexture* tex = getTextureBinding(target); - if (m_framebufferBinding && m_framebufferBinding->object() && tex + if (m_framebufferBinding && m_framebufferBinding->object() && !isTexInternalFormatColorBufferCombinationValid(tex->getInternalFormat(), m_framebufferBinding->getColorBufferFormat())) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); @@ -648,7 +641,12 @@ void WebGLRenderingContext::deleteProgram(WebGLProgram* program) { if (!program) return; - + if (program->context() != this) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } + if (!program->object()) + return; program->deleteObject(); } @@ -703,7 +701,12 @@ void WebGLRenderingContext::detachShader(WebGLProgram* program, WebGLShader* sha UNUSED_PARAM(ec); if (!validateWebGLObject(program) || !validateWebGLObject(shader)) return; + if (!program->detachShader(shader)) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return; + } m_context->detachShader(objectOrZero(program), objectOrZero(shader)); + shader->onDetached(); cleanupAfterGraphicsCall(false); } @@ -890,7 +893,7 @@ bool WebGLRenderingContext::validateRenderingState(long numElementsRequired) bool WebGLRenderingContext::validateWebGLObject(WebGLObject* object) { - if (!object) { + if (!object || !object->object()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return false; } @@ -1110,22 +1113,18 @@ void WebGLRenderingContext::frontFace(unsigned long mode) void WebGLRenderingContext::generateMipmap(unsigned long target) { - RefPtr<WebGLTexture> tex; + WebGLTexture* tex = validateTextureBinding(target, false); + if (!tex) + return; if (!isGLES2Compliant()) { - 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->canGenerateMipmaps()) { + if (!tex->canGenerateMipmaps()) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } } m_context->generateMipmap(target); - if (!isGLES2Compliant()) { - if (tex) - tex->generateMipmapLevelInfo(); - } + if (!isGLES2Compliant()) + tex->generateMipmapLevelInfo(); cleanupAfterGraphicsCall(false); } @@ -1574,11 +1573,9 @@ String WebGLRenderingContext::getString(unsigned long name) WebGLGetInfo WebGLRenderingContext::getTexParameter(unsigned long target, unsigned long pname, ExceptionCode& ec) { UNUSED_PARAM(ec); - if (target != GraphicsContext3D::TEXTURE_2D - && target != GraphicsContext3D::TEXTURE_CUBE_MAP) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + WebGLTexture* tex = validateTextureBinding(target, false); + if (!tex) return WebGLGetInfo(); - } WebGLStateRestorer(this, false); int value = 0; switch (pname) { @@ -1886,21 +1883,7 @@ void WebGLRenderingContext::linkProgram(WebGLProgram* program, ExceptionCode& ec if (!validateWebGLObject(program)) return; if (!isGLES2Compliant()) { - Vector<WebGLShader*> shaders; - bool succeed = getAttachedShaders(program, shaders, ec); - if (succeed) { - bool vShader = false; - bool fShader = false; - for (size_t ii = 0; ii < shaders.size() && (!vShader || !fShader); ++ii) { - if (shaders[ii]->getType() == GraphicsContext3D::VERTEX_SHADER) - vShader = true; - else if (shaders[ii]->getType() == GraphicsContext3D::FRAGMENT_SHADER) - fShader = true; - } - if (!vShader || !fShader) - succeed = false; - } - if (!succeed) { + if (!program->getAttachedShader(GraphicsContext3D::VERTEX_SHADER) || !program->getAttachedShader(GraphicsContext3D::FRAGMENT_SHADER)) { program->setLinkFailureFlag(true); return; } @@ -2100,6 +2083,9 @@ void WebGLRenderingContext::texImage2DBase(unsigned target, unsigned level, unsi ec = 0; if (!validateTexFuncParameters(target, level, internalformat, width, height, border, format, type)) return; + WebGLTexture* tex = validateTextureBinding(target, true); + if (!tex) + return; if (!isGLES2Compliant()) { if (level && WebGLTexture::isNPOT(width, height)) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); @@ -2108,12 +2094,9 @@ void WebGLRenderingContext::texImage2DBase(unsigned target, unsigned level, unsi } m_context->texImage2D(target, level, internalformat, width, height, border, format, type, pixels); - WebGLTexture* tex = getTextureBinding(target); - if (!isGLES2Compliant()) { - if (tex) - tex->setLevelInfo(target, level, internalformat, width, height, type); - } - if (m_framebufferBinding && tex) + if (!isGLES2Compliant()) + tex->setLevelInfo(target, level, internalformat, width, height, type); + if (m_framebufferBinding) m_framebufferBinding->onAttachedObjectChange(tex); cleanupAfterGraphicsCall(false); } @@ -2200,7 +2183,8 @@ void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, unsigned m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } - texImage2DImpl(target, level, internalformat, format, type, canvas->buffer()->image(), + + texImage2DImpl(target, level, internalformat, format, type, canvas->copiedImage(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); } @@ -2219,130 +2203,12 @@ void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, unsigned cleanupAfterGraphicsCall(false); } -// Obsolete texImage2D entry points -- to be removed shortly. (FIXME) - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, ImageData* pixels, - ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, ImageData pixels)"); - texImage2D(target, level, pixels, 0, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, ImageData* pixels, - bool flipY, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, ImageData pixels, GLboolean flipY)"); - texImage2D(target, level, pixels, flipY, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, ImageData* pixels, - bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, ImageData pixels, GLboolean flipY, GLboolean premultiplyAlpha)"); - ec = 0; - Vector<uint8_t> data; - if (!m_context->extractImageData(pixels, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, flipY, premultiplyAlpha, data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - texImage2DBase(target, level, GraphicsContext3D::RGBA, pixels->width(), pixels->height(), 0, - GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, data.data(), ec); -} - - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLImageElement* image, - ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, HTMLImageElement image)"); - texImage2D(target, level, image, 0, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLImageElement* image, - bool flipY, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, HTMLImageElement image, GLboolean flipY)"); - texImage2D(target, level, image, flipY, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLImageElement* image, - bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, HTMLImageElement image, GLboolean flipY, GLboolean premultiplyAlpha)"); - ec = 0; - if (!image || !image->cachedImage()) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - texImage2DImpl(target, level, GraphicsContext3D::RGBA, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, image->cachedImage()->image(), flipY, premultiplyAlpha, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, - ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, HTMLCanvasElement canvas)"); - texImage2D(target, level, canvas, 0, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, - bool flipY, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, HTMLCanvasElement canvas, GLboolean flipY)"); - texImage2D(target, level, canvas, flipY, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, - bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texImage2D(GLenum target, GLint level, HTMLCanvasElement canvas, GLboolean flipY, GLboolean premultiplyAlpha)"); - ec = 0; - if (!canvas || !canvas->buffer()) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - texImage2DImpl(target, level, GraphicsContext3D::RGBA, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, canvas->buffer()->image(), flipY, premultiplyAlpha, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, - ExceptionCode& ec) -{ - texImage2D(target, level, video, 0, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, - bool flipY, ExceptionCode& ec) -{ - texImage2D(target, level, video, flipY, 0, ec); -} - -void WebGLRenderingContext::texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, - bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - // FIXME: Need implement this call - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(video); - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - - ec = 0; - cleanupAfterGraphicsCall(false); -} - void WebGLRenderingContext::texParameter(unsigned long target, unsigned long pname, float paramf, int parami, bool isFloat) { + WebGLTexture* tex = validateTextureBinding(target, false); + if (!tex) + return; if (!isGLES2Compliant()) { - RefPtr<WebGLTexture> tex = 0; - switch (target) { - case GraphicsContext3D::TEXTURE_2D: - tex = m_textureUnits[m_activeTextureUnit].m_texture2DBinding; - break; - case GraphicsContext3D::TEXTURE_CUBE_MAP: - tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding; - break; - default: - m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); - return; - } switch (pname) { case GraphicsContext3D::TEXTURE_MIN_FILTER: case GraphicsContext3D::TEXTURE_MAG_FILTER: @@ -2359,12 +2225,10 @@ void WebGLRenderingContext::texParameter(unsigned long target, unsigned long pna m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); return; } - if (tex) { - if (isFloat) - tex->setParameterf(pname, paramf); - else - tex->setParameteri(pname, parami); - } + if (isFloat) + tex->setParameterf(pname, paramf); + else + tex->setParameteri(pname, parami); } if (isFloat) m_context->texParameterf(target, pname, paramf); @@ -2391,7 +2255,8 @@ void WebGLRenderingContext::texSubImage2DBase(unsigned target, unsigned level, u ec = 0; if (!validateTexFuncFormatAndType(format, type)) return; - + if (!validateTextureBinding(target, true)) + return; m_context->texSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); cleanupAfterGraphicsCall(false); } @@ -2469,7 +2334,8 @@ void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsig m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); return; } - texSubImage2DImpl(target, level, xoffset, yoffset, format, type, canvas->buffer()->image(), + + texSubImage2DImpl(target, level, xoffset, yoffset, format, type, canvas->copiedImage(), m_unpackFlipY, m_unpackPremultiplyAlpha, ec); } @@ -2488,117 +2354,6 @@ void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsig cleanupAfterGraphicsCall(false); } -// Obsolete texSubImage2D entry points -- to be removed shortly. (FIXME) - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - ImageData* pixels, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, ImageData pixels)"); - texSubImage2D(target, level, xoffset, yoffset, pixels, 0, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - ImageData* pixels, bool flipY, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, ImageData pixels, GLboolean flipY)"); - texSubImage2D(target, level, xoffset, yoffset, pixels, flipY, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - ImageData* pixels, bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, ImageData pixels, GLboolean flipY, GLboolean premultiplyAlpha)"); - ec = 0; - Vector<uint8_t> data; - if (!m_context->extractImageData(pixels, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, flipY, premultiplyAlpha, data)) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - texSubImage2DBase(target, level, xoffset, yoffset, pixels->width(), pixels->height(), - GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, data.data(), ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLImageElement* image, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, HTMLImageElement image)"); - texSubImage2D(target, level, xoffset, yoffset, image, 0, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLImageElement* image, bool flipY, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, HTMLImageElement image, GLboolean flipY)"); - texSubImage2D(target, level, xoffset, yoffset, image, flipY, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLImageElement* image, bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, HTMLImageElement image, GLboolean flipY, GLboolean premultiplyAlpha)"); - ec = 0; - if (!image || !image->cachedImage()) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - texSubImage2DImpl(target, level, xoffset, yoffset, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, image->cachedImage()->image(), - flipY, premultiplyAlpha, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLCanvasElement* canvas, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, HTMLCanvasElement canvas)"); - texSubImage2D(target, level, xoffset, yoffset, canvas, 0, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLCanvasElement* canvas, bool flipY, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, HTMLCanvasElement canvas, GLboolean flipY)"); - texSubImage2D(target, level, xoffset, yoffset, canvas, flipY, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLCanvasElement* canvas, bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - printWarningToConsole("Calling obsolete texSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, HTMLCanvasElement canvas, GLboolean flipY, GLboolean premultiplyAlpha)"); - ec = 0; - if (!canvas || !canvas->buffer()) { - m_context->synthesizeGLError(GraphicsContext3D::INVALID_VALUE); - return; - } - texSubImage2DImpl(target, level, xoffset, yoffset, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, canvas->buffer()->image(), - flipY, premultiplyAlpha, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLVideoElement* video, ExceptionCode& ec) -{ - texSubImage2D(target, level, xoffset, yoffset, video, 0, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLVideoElement* video, bool flipY, ExceptionCode& ec) -{ - texSubImage2D(target, level, xoffset, yoffset, video, flipY, 0, ec); -} - -void WebGLRenderingContext::texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, - HTMLVideoElement* video, bool flipY, bool premultiplyAlpha, ExceptionCode& ec) -{ - // FIXME: Need to implement this call - UNUSED_PARAM(target); - UNUSED_PARAM(level); - UNUSED_PARAM(xoffset); - UNUSED_PARAM(yoffset); - UNUSED_PARAM(video); - UNUSED_PARAM(flipY); - UNUSED_PARAM(premultiplyAlpha); - ec = 0; - cleanupAfterGraphicsCall(false); -} - void WebGLRenderingContext::uniform1f(const WebGLUniformLocation* location, float x, ExceptionCode& ec) { UNUSED_PARAM(ec); @@ -2935,13 +2690,23 @@ void WebGLRenderingContext::uniformMatrix4fv(const WebGLUniformLocation* locatio void WebGLRenderingContext::useProgram(WebGLProgram* program, ExceptionCode& ec) { - UNUSED_PARAM(ec); if (program && program->context() != this) { m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); return; } - m_currentProgram = program; - m_context->useProgram(objectOrZero(program)); + if (program && program->object() && !getProgramParameter(program, GraphicsContext3D::LINK_STATUS, ec).getBool()) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + cleanupAfterGraphicsCall(false); + return; + } + if (m_currentProgram != program) { + if (m_currentProgram) + m_currentProgram->onDetached(); + m_currentProgram = program; + m_context->useProgram(objectOrZero(program)); + if (program) + program->onAttached(); + } cleanupAfterGraphicsCall(false); } @@ -3185,7 +2950,8 @@ WebGLGetInfo WebGLRenderingContext::getUnsignedLongParameter(unsigned long pname { int value; m_context->getIntegerv(pname, &value); - return WebGLGetInfo(static_cast<unsigned long>(value)); + unsigned int uValue = static_cast<unsigned int>(value); + return WebGLGetInfo(static_cast<unsigned long>(uValue)); } WebGLGetInfo WebGLRenderingContext::getWebGLFloatArrayParameter(unsigned long pname) @@ -3309,12 +3075,12 @@ bool WebGLRenderingContext::isTexInternalFormatColorBufferCombinationValid(unsig return false; } -WebGLTexture* WebGLRenderingContext::getTextureBinding(unsigned long target) +WebGLTexture* WebGLRenderingContext::validateTextureBinding(unsigned long target, bool useSixEnumsForCubeMap) { - RefPtr<WebGLTexture> tex = 0; + WebGLTexture* tex = 0; switch (target) { case GraphicsContext3D::TEXTURE_2D: - tex = m_textureUnits[m_activeTextureUnit].m_texture2DBinding; + tex = m_textureUnits[m_activeTextureUnit].m_texture2DBinding.get(); break; case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_X: case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_X: @@ -3322,12 +3088,26 @@ WebGLTexture* WebGLRenderingContext::getTextureBinding(unsigned long target) case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Y: case GraphicsContext3D::TEXTURE_CUBE_MAP_POSITIVE_Z: case GraphicsContext3D::TEXTURE_CUBE_MAP_NEGATIVE_Z: - tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding; + if (!useSixEnumsForCubeMap) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding.get(); + break; + case GraphicsContext3D::TEXTURE_CUBE_MAP: + if (useSixEnumsForCubeMap) { + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; + } + tex = m_textureUnits[m_activeTextureUnit].m_textureCubeMapBinding.get(); break; + default: + m_context->synthesizeGLError(GraphicsContext3D::INVALID_ENUM); + return 0; } - if (tex && tex->object()) - return tex.get(); - return 0; + if (!tex) + m_context->synthesizeGLError(GraphicsContext3D::INVALID_OPERATION); + return tex; } bool WebGLRenderingContext::validateTexFuncFormatAndType(unsigned long format, unsigned long type) diff --git a/WebCore/html/canvas/WebGLRenderingContext.h b/WebCore/html/canvas/WebGLRenderingContext.h index 608797f..48fa7c8 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.h +++ b/WebCore/html/canvas/WebGLRenderingContext.h @@ -206,19 +206,6 @@ public: unsigned format, unsigned type, HTMLCanvasElement* canvas, ExceptionCode&); void texImage2D(unsigned target, unsigned level, unsigned internalformat, unsigned format, unsigned type, HTMLVideoElement* video, ExceptionCode&); - // Obsolete entry points -- to be removed shortly. (FIXME) - void texImage2D(unsigned target, unsigned level, ImageData* pixels, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, ImageData* pixels, bool flipY, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, ImageData* pixels, bool flipY, bool premultiplyAlpha, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLImageElement* image, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLImageElement* image, bool flipY, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLImageElement* image, bool flipY, bool premultiplyAlpha, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, bool flipY, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLCanvasElement* canvas, bool flipY, bool premultiplyAlpha, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, bool flipY, ExceptionCode&); - void texImage2D(unsigned target, unsigned level, HTMLVideoElement* video, bool flipY, bool premultiplyAlpha, ExceptionCode&); void texParameterf(unsigned target, unsigned pname, float param); void texParameteri(unsigned target, unsigned pname, int param); @@ -234,19 +221,6 @@ public: unsigned format, unsigned type, HTMLCanvasElement* canvas, ExceptionCode&); void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, unsigned format, unsigned type, HTMLVideoElement* video, ExceptionCode&); - // Obsolete entry points -- to be removed shortly. (FIXME) - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, ImageData* pixels, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, ImageData* pixels, bool flipY, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, ImageData* pixels, bool flipY, bool premultiplyAlpha, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLImageElement* image, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLImageElement* image, bool flipY, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLImageElement* image, bool flipY, bool premultiplyAlpha, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLCanvasElement* canvas, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLCanvasElement* canvas, bool flipY, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLCanvasElement* canvas, bool flipY, bool premultiplyAlpha, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLVideoElement* video, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLVideoElement* video, bool flipY, ExceptionCode&); - void texSubImage2D(unsigned target, unsigned level, unsigned xoffset, unsigned yoffset, HTMLVideoElement* video, bool flipY, bool premultiplyAlpha, ExceptionCode&); void uniform1f(const WebGLUniformLocation* location, float x, ExceptionCode&); void uniform1fv(const WebGLUniformLocation* location, Float32Array* v, ExceptionCode&); @@ -305,12 +279,10 @@ public: virtual void paintRenderingResultsToCanvas(); - // Helpers for notification about paint events. - void beginPaint(); - void endPaint(); - void removeObject(WebGLObject*); + bool paintsIntoCanvasBuffer() const { return m_context->paintsIntoCanvasBuffer(); } + private: friend class WebGLObject; @@ -460,8 +432,10 @@ public: bool isTexInternalFormatColorBufferCombinationValid(unsigned long texInternalFormat, unsigned long colorBufferFormat); - // Helper function to get the current bound texture. - WebGLTexture* getTextureBinding(unsigned long target); + // Helper function to check target and texture bound to the target. + // Generate GL errors and return 0 if target is invalid or texture bound is + // null. Otherwise, return the texture bound to the target. + WebGLTexture* validateTextureBinding(unsigned long target, bool useSixEnumsForCubeMap); // Helper function to check input format/type for functions {copy}Tex{Sub}Image. // Generates GL error and returns false if parameters are invalid. diff --git a/WebCore/html/canvas/WebGLRenderingContext.idl b/WebCore/html/canvas/WebGLRenderingContext.idl index 711aa42..960dd0b 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.idl +++ b/WebCore/html/canvas/WebGLRenderingContext.idl @@ -622,15 +622,6 @@ module html { in unsigned long format, in unsigned long type, in HTMLCanvasElement canvas) raises (DOMException); void texImage2D(in unsigned long target, in long level, in unsigned long internalformat, in unsigned long format, in unsigned long type, in HTMLVideoElement video) raises (DOMException); - // Obsolete entry points -- to be removed shortly. (FIXME) - void texImage2D(in unsigned long target, in long level, in ImageData pixels, - in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); - void texImage2D(in unsigned long target, in long level, in HTMLImageElement image, - in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); - void texImage2D(in unsigned long target, in long level, in HTMLCanvasElement canvas, - in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); - void texImage2D(in unsigned long target, in long level, in HTMLVideoElement video, - in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, in long width, in long height, @@ -643,15 +634,6 @@ module html { in unsigned long format, in unsigned long type, in HTMLCanvasElement canvas) raises (DOMException); void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, in unsigned long format, in unsigned long type, in HTMLVideoElement video) raises (DOMException); - // Obsolete entry points -- to be removed shortly. (FIXME) - void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, - in ImageData pixels, in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); - void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, - in HTMLImageElement image, in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); - void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, - in HTMLCanvasElement canvas, in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); - void texSubImage2D(in unsigned long target, in long level, in long xoffset, in long yoffset, - in HTMLVideoElement video, in [Optional] boolean flipY, in [Optional] boolean premultiplyAlpha) raises (DOMException); void uniform1f(in WebGLUniformLocation location, in float x) raises(DOMException); [Custom] void uniform1fv(in WebGLUniformLocation location, in Float32Array v) raises(DOMException); diff --git a/WebCore/html/canvas/WebGLTexture.cpp b/WebCore/html/canvas/WebGLTexture.cpp index 2c50bf8..e6dfd0a 100644 --- a/WebCore/html/canvas/WebGLTexture.cpp +++ b/WebCore/html/canvas/WebGLTexture.cpp @@ -151,21 +151,22 @@ void WebGLTexture::generateMipmapLevelInfo() 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); + if (!m_isComplete) { + 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; } - m_isComplete = true; + m_needToUseBlackTexture = false; } unsigned long WebGLTexture::getInternalFormat() const |