diff options
Diffstat (limited to 'WebCore/html')
51 files changed, 2654 insertions, 169 deletions
diff --git a/WebCore/html/FTPDirectoryDocument.cpp b/WebCore/html/FTPDirectoryDocument.cpp new file mode 100644 index 0000000..6475ea9 --- /dev/null +++ b/WebCore/html/FTPDirectoryDocument.cpp @@ -0,0 +1,451 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#if ENABLE(FTPDIR) +#include "FTPDirectoryDocument.h" + +#include "CharacterNames.h" +#include "HTMLDocumentParser.h" +#include "HTMLNames.h" +#include "HTMLTableElement.h" +#include "LocalizedStrings.h" +#include "Logging.h" +#include "FTPDirectoryParser.h" +#include "SegmentedString.h" +#include "Settings.h" +#include "SharedBuffer.h" +#include "Text.h" + +#include <wtf/text/CString.h> +#include <wtf/CurrentTime.h> +#include <wtf/StdLibExtras.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +class FTPDirectoryDocumentParser : public HTMLDocumentParser { +public: + static PassRefPtr<FTPDirectoryDocumentParser> create(HTMLDocument* document) + { + return adoptRef(new FTPDirectoryDocumentParser(document)); + } + + virtual void append(const SegmentedString&); + virtual void finish(); + + virtual bool isWaitingForScripts() const { return false; } + + inline void checkBuffer(int len = 10) + { + if ((m_dest - m_buffer) > m_size - len) { + // Enlarge buffer + int newSize = max(m_size * 2, m_size + len); + int oldOffset = m_dest - m_buffer; + m_buffer = static_cast<UChar*>(fastRealloc(m_buffer, newSize * sizeof(UChar))); + m_dest = m_buffer + oldOffset; + m_size = newSize; + } + } + +private: + FTPDirectoryDocumentParser(HTMLDocument*); + + // The parser will attempt to load the document template specified via the preference + // Failing that, it will fall back and create the basic document which will have a minimal + // table for presenting the FTP directory in a useful manner + bool loadDocumentTemplate(); + void createBasicDocument(); + + void parseAndAppendOneLine(const String&); + void appendEntry(const String& name, const String& size, const String& date, bool isDirectory); + PassRefPtr<Element> createTDForFilename(const String&); + + RefPtr<HTMLTableElement> m_tableElement; + + bool m_skipLF; + bool m_parsedTemplate; + + int m_size; + UChar* m_buffer; + UChar* m_dest; + String m_carryOver; + + ListState m_listState; +}; + +FTPDirectoryDocumentParser::FTPDirectoryDocumentParser(HTMLDocument* document) + : HTMLDocumentParser(document, false) + , m_skipLF(false) + , m_parsedTemplate(false) + , m_size(254) + , m_buffer(static_cast<UChar*>(fastMalloc(sizeof(UChar) * m_size))) + , m_dest(m_buffer) +{ +} + +void FTPDirectoryDocumentParser::appendEntry(const String& filename, const String& size, const String& date, bool isDirectory) +{ + ExceptionCode ec; + + RefPtr<Element> rowElement = m_tableElement->insertRow(-1, ec); + rowElement->setAttribute("class", "ftpDirectoryEntryRow", ec); + + RefPtr<Element> element = document()->createElement(tdTag, false); + element->appendChild(Text::create(document(), String(&noBreakSpace, 1)), ec); + if (isDirectory) + element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeDirectory", ec); + else + element->setAttribute("class", "ftpDirectoryIcon ftpDirectoryTypeFile", ec); + rowElement->appendChild(element, ec); + + element = createTDForFilename(filename); + element->setAttribute("class", "ftpDirectoryFileName", ec); + rowElement->appendChild(element, ec); + + element = document()->createElement(tdTag, false); + element->appendChild(Text::create(document(), date), ec); + element->setAttribute("class", "ftpDirectoryFileDate", ec); + rowElement->appendChild(element, ec); + + element = document()->createElement(tdTag, false); + element->appendChild(Text::create(document(), size), ec); + element->setAttribute("class", "ftpDirectoryFileSize", ec); + rowElement->appendChild(element, ec); +} + +PassRefPtr<Element> FTPDirectoryDocumentParser::createTDForFilename(const String& filename) +{ + ExceptionCode ec; + + String fullURL = document()->baseURL().string(); + if (fullURL[fullURL.length() - 1] == '/') + fullURL.append(filename); + else + fullURL.append("/" + filename); + + RefPtr<Element> anchorElement = document()->createElement(aTag, false); + anchorElement->setAttribute("href", fullURL, ec); + anchorElement->appendChild(Text::create(document(), filename), ec); + + RefPtr<Element> tdElement = document()->createElement(tdTag, false); + tdElement->appendChild(anchorElement, ec); + + return tdElement.release(); +} + +static String processFilesizeString(const String& size, bool isDirectory) +{ + if (isDirectory) + return "--"; + + bool valid; + int64_t bytes = size.toUInt64(&valid); + if (!valid) + return unknownFileSizeText(); + + if (bytes < 1000000) + return String::format("%.2f KB", static_cast<float>(bytes)/1000); + + if (bytes < 1000000000) + return String::format("%.2f MB", static_cast<float>(bytes)/1000000); + + return String::format("%.2f GB", static_cast<float>(bytes)/1000000000); +} + +static bool wasLastDayOfMonth(int year, int month, int day) +{ + static int lastDays[] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + if (month < 0 || month > 11) + return false; + + if (month == 2) { + if (year % 4 == 0 && (year % 100 || year % 400 == 0)) { + if (day == 29) + return true; + return false; + } + + if (day == 28) + return true; + return false; + } + + return lastDays[month] == day; +} + +static String processFileDateString(const FTPTime& fileTime) +{ + // FIXME: Need to localize this string? + + String timeOfDay; + + if (!(fileTime.tm_hour == 0 && fileTime.tm_min == 0 && fileTime.tm_sec == 0)) { + int hour = fileTime.tm_hour; + ASSERT(hour >= 0 && hour < 24); + + if (hour < 12) { + if (hour == 0) + hour = 12; + timeOfDay = String::format(", %i:%02i AM", hour, fileTime.tm_min); + } else { + hour = hour - 12; + if (hour == 0) + hour = 12; + timeOfDay = String::format(", %i:%02i PM", hour, fileTime.tm_min); + } + } + + // If it was today or yesterday, lets just do that - but we have to compare to the current time + struct tm now; + time_t now_t = time(NULL); + getLocalTime(&now_t, &now); + + // localtime does "year = current year - 1900", compensate for that for readability and comparison purposes + now.tm_year += 1900; + + if (fileTime.tm_year == now.tm_year) { + if (fileTime.tm_mon == now.tm_mon) { + if (fileTime.tm_mday == now.tm_mday) + return "Today" + timeOfDay; + if (fileTime.tm_mday == now.tm_mday - 1) + return "Yesterday" + timeOfDay; + } + + if (now.tm_mday == 1 && (now.tm_mon == fileTime.tm_mon + 1 || (now.tm_mon == 0 && fileTime.tm_mon == 11)) && + wasLastDayOfMonth(fileTime.tm_year, fileTime.tm_mon, fileTime.tm_mday)) + return "Yesterday" + timeOfDay; + } + + if (fileTime.tm_year == now.tm_year - 1 && fileTime.tm_mon == 12 && fileTime.tm_mday == 31 && now.tm_mon == 1 && now.tm_mday == 1) + return "Yesterday" + timeOfDay; + + static const char* months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "???" }; + + int month = fileTime.tm_mon; + if (month < 0 || month > 11) + month = 12; + + String dateString; + + if (fileTime.tm_year > -1) + dateString = String::format("%s %i, %i", months[month], fileTime.tm_mday, fileTime.tm_year); + else + dateString = String::format("%s %i, %i", months[month], fileTime.tm_mday, now.tm_year); + + return dateString + timeOfDay; +} + +void FTPDirectoryDocumentParser::parseAndAppendOneLine(const String& inputLine) +{ + ListResult result; + CString latin1Input = inputLine.latin1(); + + FTPEntryType typeResult = parseOneFTPLine(latin1Input.data(), m_listState, result); + + // FTPMiscEntry is a comment or usage statistic which we don't care about, and junk is invalid data - bail in these 2 cases + if (typeResult == FTPMiscEntry || typeResult == FTPJunkEntry) + return; + + String filename(result.filename, result.filenameLength); + if (result.type == FTPDirectoryEntry) { + filename.append("/"); + + // We have no interest in linking to "current directory" + if (filename == "./") + return; + } + + LOG(FTP, "Appending entry - %s, %s", filename.ascii().data(), result.fileSize.ascii().data()); + + appendEntry(filename, processFilesizeString(result.fileSize, result.type == FTPDirectoryEntry), processFileDateString(result.modifiedTime), result.type == FTPDirectoryEntry); +} + +static inline PassRefPtr<SharedBuffer> createTemplateDocumentData(Settings* settings) +{ + RefPtr<SharedBuffer> buffer = 0; + if (settings) + buffer = SharedBuffer::createWithContentsOfFile(settings->ftpDirectoryTemplatePath()); + if (buffer) + LOG(FTP, "Loaded FTPDirectoryTemplate of length %i\n", buffer->size()); + return buffer.release(); +} + +bool FTPDirectoryDocumentParser::loadDocumentTemplate() +{ + DEFINE_STATIC_LOCAL(RefPtr<SharedBuffer>, templateDocumentData, (createTemplateDocumentData(document()->settings()))); + // FIXME: Instead of storing the data, we'd rather actually parse the template data into the template Document once, + // store that document, then "copy" it whenever we get an FTP directory listing. There are complexities with this + // approach that make it worth putting this off. + + if (!templateDocumentData) { + LOG_ERROR("Could not load templateData"); + return false; + } + + HTMLDocumentParser::insert(String(templateDocumentData->data(), templateDocumentData->size())); + + RefPtr<Element> tableElement = document()->getElementById("ftpDirectoryTable"); + if (!tableElement) + LOG_ERROR("Unable to find element by id \"ftpDirectoryTable\" in the template document."); + else if (!tableElement->hasTagName(tableTag)) + LOG_ERROR("Element of id \"ftpDirectoryTable\" is not a table element"); + else + m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + + // Bail if we found the table element + if (m_tableElement) + return true; + + // Otherwise create one manually + tableElement = document()->createElement(tableTag, false); + m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + ExceptionCode ec; + m_tableElement->setAttribute("id", "ftpDirectoryTable", ec); + + // If we didn't find the table element, lets try to append our own to the body + // If that fails for some reason, cram it on the end of the document as a last + // ditch effort + if (Element* body = document()->body()) + body->appendChild(m_tableElement, ec); + else + document()->appendChild(m_tableElement, ec); + + return true; +} + +void FTPDirectoryDocumentParser::createBasicDocument() +{ + LOG(FTP, "Creating a basic FTP document structure as no template was loaded"); + + // FIXME: Make this "basic document" more acceptable + + RefPtr<Element> bodyElement = document()->createElement(bodyTag, false); + + ExceptionCode ec; + document()->appendChild(bodyElement, ec); + + RefPtr<Element> tableElement = document()->createElement(tableTag, false); + m_tableElement = static_cast<HTMLTableElement*>(tableElement.get()); + m_tableElement->setAttribute("id", "ftpDirectoryTable", ec); + + bodyElement->appendChild(m_tableElement, ec); +} + +void FTPDirectoryDocumentParser::append(const SegmentedString& source) +{ + // Make sure we have the table element to append to by loading the template set in the pref, or + // creating a very basic document with the appropriate table + if (!m_tableElement) { + if (!loadDocumentTemplate()) + createBasicDocument(); + ASSERT(m_tableElement); + } + + bool foundNewLine = false; + + m_dest = m_buffer; + SegmentedString str = source; + while (!str.isEmpty()) { + UChar c = *str; + + if (c == '\r') { + *m_dest++ = '\n'; + foundNewLine = true; + // possibly skip an LF in the case of an CRLF sequence + m_skipLF = true; + } else if (c == '\n') { + if (!m_skipLF) + *m_dest++ = c; + else + m_skipLF = false; + } else { + *m_dest++ = c; + m_skipLF = false; + } + + str.advance(); + + // Maybe enlarge the buffer + checkBuffer(); + } + + if (!foundNewLine) { + m_dest = m_buffer; + return; + } + + UChar* start = m_buffer; + UChar* cursor = start; + + while (cursor < m_dest) { + if (*cursor == '\n') { + m_carryOver.append(String(start, cursor - start)); + LOG(FTP, "%s", m_carryOver.ascii().data()); + parseAndAppendOneLine(m_carryOver); + m_carryOver = String(); + + start = ++cursor; + } else + cursor++; + } + + // Copy the partial line we have left to the carryover buffer + if (cursor - start > 1) + m_carryOver.append(String(start, cursor - start - 1)); +} + +void FTPDirectoryDocumentParser::finish() +{ + // Possible the last line in the listing had no newline, so try to parse it now + if (!m_carryOver.isEmpty()) { + parseAndAppendOneLine(m_carryOver); + m_carryOver = String(); + } + + m_tableElement = 0; + fastFree(m_buffer); + + HTMLDocumentParser::finish(); +} + +FTPDirectoryDocument::FTPDirectoryDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) +{ +#ifndef NDEBUG + LogFTP.state = WTFLogChannelOn; +#endif +} + +PassRefPtr<DocumentParser> FTPDirectoryDocument::createParser() +{ + return FTPDirectoryDocumentParser::create(this); +} + +} + +#endif // ENABLE(FTPDIR) diff --git a/WebCore/html/FTPDirectoryDocument.h b/WebCore/html/FTPDirectoryDocument.h new file mode 100644 index 0000000..e7e52f7 --- /dev/null +++ b/WebCore/html/FTPDirectoryDocument.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef FTPDirectoryDocument_h +#define FTPDirectoryDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class DOMImplementation; + +class FTPDirectoryDocument : public HTMLDocument { +public: + static PassRefPtr<FTPDirectoryDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new FTPDirectoryDocument(frame, url)); + } + +private: + FTPDirectoryDocument(Frame*, const KURL&); + virtual PassRefPtr<DocumentParser> createParser(); +}; + +} // namespace WebCore + +#endif // FTPDirectoryDocument_h diff --git a/WebCore/html/HTMLEmbedElement.cpp b/WebCore/html/HTMLEmbedElement.cpp index eeb28e7..e88ee81 100644 --- a/WebCore/html/HTMLEmbedElement.cpp +++ b/WebCore/html/HTMLEmbedElement.cpp @@ -120,6 +120,52 @@ void HTMLEmbedElement::parseMappedAttribute(Attribute* attr) HTMLPlugInImageElement::parseMappedAttribute(attr); } +void HTMLEmbedElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues) +{ + NamedNodeMap* attributes = this->attributes(true); + if (!attributes) + return; + + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + paramNames.append(it->localName().string()); + paramValues.append(it->value().string()); + } +} + +// FIXME: This should be unified with HTMLObjectElement::updateWidget and +// moved down into HTMLPluginImageElement.cpp +void HTMLEmbedElement::updateWidget(bool onlyCreateNonNetscapePlugins) +{ + ASSERT(!renderEmbeddedObject()->pluginCrashedOrWasMissing()); + // FIXME: We should ASSERT(needsWidgetUpdate()), but currently + // FrameView::updateWidget() calls updateWidget(false) without checking if + // the widget actually needs updating! + setNeedsWidgetUpdate(false); + + if (m_url.isEmpty() && m_serviceType.isEmpty()) + return; + + // Note these pass m_url and m_serviceType to allow better code sharing with + // <object> which modifies url and serviceType before calling these. + if (!allowedToLoadFrameURL(m_url)) + return; + if (onlyCreateNonNetscapePlugins && wouldLoadAsNetscapePlugin(m_url, m_serviceType)) + return; + + // FIXME: These should be joined into a PluginParameters class. + Vector<String> paramNames; + Vector<String> paramValues; + parametersForPlugin(paramNames, paramValues); + + if (!dispatchBeforeLoadEvent(m_url)) + return; + + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + // FIXME: beforeLoad could have detached the renderer! Just like in the <object> case above. + loader->requestObject(this, m_url, getAttribute(nameAttr), m_serviceType, paramNames, paramValues); +} + bool HTMLEmbedElement::rendererIsNeeded(RenderStyle* style) { if (isImageType()) diff --git a/WebCore/html/HTMLEmbedElement.h b/WebCore/html/HTMLEmbedElement.h index e27b717..70eb0dc 100644 --- a/WebCore/html/HTMLEmbedElement.h +++ b/WebCore/html/HTMLEmbedElement.h @@ -41,13 +41,17 @@ private: virtual void insertedIntoDocument(); virtual void removedFromDocument(); virtual void attributeChanged(Attribute*, bool preserveDecls = false); - + virtual bool isURLAttribute(Attribute*) const; virtual const QualifiedName& imageSourceAttributeName() const; virtual RenderWidget* renderWidgetForJSBindings() const; + virtual void updateWidget(bool onlyCreateNonNetscapePlugins); + virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + + void parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues); }; } diff --git a/WebCore/html/HTMLImageElement.cpp b/WebCore/html/HTMLImageElement.cpp index b7ece78..d223b1e 100644 --- a/WebCore/html/HTMLImageElement.cpp +++ b/WebCore/html/HTMLImageElement.cpp @@ -267,10 +267,8 @@ int HTMLImageElement::width(bool ignorePendingStylesheets) const return width; // if the image is available, use its width - if (m_imageLoader.image()) { - float zoomFactor = document()->view() ? document()->view()->pageZoomFactor() : 1.0f; - return m_imageLoader.image()->imageSize(zoomFactor).width(); - } + if (m_imageLoader.image()) + return m_imageLoader.image()->imageSize(1.0f).width(); } if (ignorePendingStylesheets) @@ -278,7 +276,8 @@ int HTMLImageElement::width(bool ignorePendingStylesheets) const else document()->updateLayout(); - return renderBox() ? renderBox()->contentWidth() : 0; + RenderBox* box = renderBox(); + return box ? adjustForAbsoluteZoom(box->contentWidth(), box) : 0; } int HTMLImageElement::height(bool ignorePendingStylesheets) const @@ -291,10 +290,8 @@ int HTMLImageElement::height(bool ignorePendingStylesheets) const return height; // if the image is available, use its height - if (m_imageLoader.image()) { - float zoomFactor = document()->view() ? document()->view()->pageZoomFactor() : 1.0f; - return m_imageLoader.image()->imageSize(zoomFactor).height(); - } + if (m_imageLoader.image()) + return m_imageLoader.image()->imageSize(1.0f).height(); } if (ignorePendingStylesheets) @@ -302,7 +299,8 @@ int HTMLImageElement::height(bool ignorePendingStylesheets) const else document()->updateLayout(); - return renderBox() ? renderBox()->contentHeight() : 0; + RenderBox* box = renderBox(); + return box ? adjustForAbsoluteZoom(box->contentHeight(), box) : 0; } int HTMLImageElement::naturalWidth() const diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp index c2e5416..5ff979b 100644 --- a/WebCore/html/HTMLInputElement.cpp +++ b/WebCore/html/HTMLInputElement.cpp @@ -263,7 +263,8 @@ bool HTMLInputElement::typeMismatch(const String& value) const case COLOR: return !isValidColorString(value); case NUMBER: - return !parseToDoubleForNumberType(value, 0); + ASSERT(parseToDoubleForNumberType(value, 0)); + return false; case URL: return !KURL(KURL(), value).isValid(); case EMAIL: { @@ -830,6 +831,14 @@ void HTMLInputElement::handleFocusEvent() void HTMLInputElement::handleBlurEvent() { + if (inputType() == NUMBER) { + // Reset the renderer value, which might be unmatched with the element value. + setFormControlValueMatchesRenderer(false); + // We need to reset the renderer value explicitly because an unacceptable + // renderer value should be purged before style calculation. + if (renderer()) + renderer()->updateFromElement(); + } InputElement::dispatchBlurEvent(this, this); } @@ -2273,7 +2282,7 @@ void HTMLInputElement::defaultEventHandler(Event* evt) && evt->isKeyboardEvent() && focused() && document()->frame() - && document()->frame()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) { + && document()->frame()->editor()->doTextFieldCommandFromEvent(this, static_cast<KeyboardEvent*>(evt))) { evt->setDefaultHandled(); return; } @@ -2676,8 +2685,18 @@ FileList* HTMLInputElement::files() return m_fileList.get(); } +bool HTMLInputElement::isAcceptableValue(const String& proposedValue) const +{ + if (inputType() != NUMBER) + return true; + return proposedValue.isEmpty() || parseToDoubleForNumberType(proposedValue, 0); +} + String HTMLInputElement::sanitizeValue(const String& proposedValue) const { + if (inputType() == NUMBER) + return parseToDoubleForNumberType(proposedValue, 0) ? proposedValue : String(); + if (isTextField()) return InputElement::sanitizeValueForTextField(this, proposedValue); @@ -2690,6 +2709,11 @@ String HTMLInputElement::sanitizeValue(const String& proposedValue) const return proposedValue; } +bool HTMLInputElement::hasUnacceptableValue() const +{ + return inputType() == NUMBER && renderer() && !isAcceptableValue(toRenderTextControl(renderer())->text()); +} + bool HTMLInputElement::needsActivationCallback() { return inputType() == PASSWORD || m_autocomplete == Off; diff --git a/WebCore/html/HTMLInputElement.h b/WebCore/html/HTMLInputElement.h index 657b468..e023796 100644 --- a/WebCore/html/HTMLInputElement.h +++ b/WebCore/html/HTMLInputElement.h @@ -281,7 +281,9 @@ private: virtual void cacheSelection(int start, int end); + virtual bool isAcceptableValue(const String&) const; virtual String sanitizeValue(const String&) const; + virtual bool hasUnacceptableValue() const; virtual void documentDidBecomeActive(); diff --git a/WebCore/html/HTMLLinkElement.cpp b/WebCore/html/HTMLLinkElement.cpp index bc7b9a6..939b375 100644 --- a/WebCore/html/HTMLLinkElement.cpp +++ b/WebCore/html/HTMLLinkElement.cpp @@ -27,7 +27,7 @@ #include "Attribute.h" #include "CSSHelper.h" #include "CachedCSSStyleSheet.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" #include "Frame.h" #include "FrameLoader.h" @@ -212,7 +212,7 @@ void HTMLLinkElement::process() #if ENABLE(LINK_PREFETCH) if (m_relAttribute.m_isLinkPrefetch && m_url.isValid() && document()->frame()) - document()->docLoader()->requestLinkPrefetch(m_url); + document()->cachedResourceLoader()->requestLinkPrefetch(m_url); #endif bool acceptIfTypeContainsTextCSS = document()->page() && document()->page()->settings() && document()->page()->settings()->treatsAnyTextCSSLinkAsStylesheet(); @@ -243,7 +243,7 @@ void HTMLLinkElement::process() if (!isAlternate()) document()->addPendingSheet(); - m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(m_url, charset); + m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(m_url, charset); if (m_cachedSheet) m_cachedSheet->addClient(this); diff --git a/WebCore/html/HTMLMediaElement.cpp b/WebCore/html/HTMLMediaElement.cpp index bc7960c..827158e 100644 --- a/WebCore/html/HTMLMediaElement.cpp +++ b/WebCore/html/HTMLMediaElement.cpp @@ -29,14 +29,14 @@ #include "HTMLMediaElement.h" #include "Attribute.h" -#include "CSSHelper.h" -#include "CSSPropertyNames.h" -#include "CSSValueKeywords.h" #include "Chrome.h" #include "ChromeClient.h" #include "ClientRect.h" #include "ClientRectList.h" #include "ContentType.h" +#include "CSSHelper.h" +#include "CSSPropertyNames.h" +#include "CSSValueKeywords.h" #include "Event.h" #include "EventNames.h" #include "ExceptionCode.h" @@ -48,12 +48,13 @@ #include "HTMLNames.h" #include "HTMLSourceElement.h" #include "HTMLVideoElement.h" -#include "MIMETypeRegistry.h" +#include "Logging.h" #include "MediaDocument.h" #include "MediaError.h" #include "MediaList.h" #include "MediaPlayer.h" #include "MediaQueryEvaluator.h" +#include "MIMETypeRegistry.h" #include "Page.h" #include "RenderVideo.h" #include "RenderView.h" @@ -78,10 +79,33 @@ using namespace std; namespace WebCore { +#if !LOG_DISABLED +static String urlForLogging(const String& url) +{ + static unsigned maximumURLLengthForLogging = 128; + + if (url.length() < maximumURLLengthForLogging) + return url; + return url.substring(0, maximumURLLengthForLogging) + "..."; +} + +static const char *boolString(bool val) +{ + return val ? "true" : "false"; +} +#endif + +#ifndef LOG_MEDIA_EVENTS +// Default to not logging events because so many are generated they can overwhelm the rest of +// the logging. +#define LOG_MEDIA_EVENTS 0 +#endif + using namespace HTMLNames; -HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc) - : HTMLElement(tagName, doc) +HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* document) + : HTMLElement(tagName, document) + , ActiveDOMObject(document, this) , m_loadTimer(this, &HTMLMediaElement::loadTimerFired) , m_asyncEventTimer(this, &HTMLMediaElement::asyncEventTimerFired) , m_progressEventTimer(this, &HTMLMediaElement::progressEventTimerFired) @@ -132,8 +156,8 @@ HTMLMediaElement::HTMLMediaElement(const QualifiedName& tagName, Document* doc) , m_loadInitiatedByUserGesture(false) , m_completelyLoaded(false) { - document()->registerForDocumentActivationCallbacks(this); - document()->registerForMediaVolumeCallbacks(this); + document->registerForDocumentActivationCallbacks(this); + document->registerForMediaVolumeCallbacks(this); } HTMLMediaElement::~HTMLMediaElement() @@ -365,6 +389,9 @@ void HTMLMediaElement::scheduleNextSourceChild() void HTMLMediaElement::scheduleEvent(const AtomicString& eventName) { +#if LOG_MEDIA_EVENTS + LOG(Media, "HTMLMediaElement::scheduleEvent - scheduling '%s'", eventName.string().ascii().data()); +#endif m_pendingEvents.append(Event::create(eventName, false, true)); if (!m_asyncEventTimer.isActive()) m_asyncEventTimer.startOneShot(0); @@ -387,6 +414,9 @@ void HTMLMediaElement::asyncEventTimerFired(Timer<HTMLMediaElement>*) m_pendingEvents.swap(pendingEvents); unsigned count = pendingEvents.size(); for (unsigned ndx = 0; ndx < count; ++ndx) { +#if LOG_MEDIA_EVENTS + LOG(Media, "HTMLMediaElement::asyncEventTimerFired - dispatching '%s'", pendingEvents[ndx]->type().string().ascii().data()); +#endif if (pendingEvents[ndx]->type() == eventNames().canplayEvent) { m_dispatchingCanPlayEvent = true; dispatchEvent(pendingEvents[ndx].release(), ec); @@ -479,11 +509,15 @@ String HTMLMediaElement::canPlayType(const String& mimeType) const break; } + LOG(Media, "HTMLMediaElement::canPlayType(%s) -> %s", mimeType.utf8().data(), canPlay.utf8().data()); + return canPlay; } void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec) { + LOG(Media, "HTMLMediaElement::load(isUserGesture : %s)", boolString(isUserGesture)); + if (m_restrictions & RequireUserGestureForLoadRestriction && !isUserGesture) ec = INVALID_STATE_ERR; else { @@ -495,6 +529,8 @@ void HTMLMediaElement::load(bool isUserGesture, ExceptionCode& ec) void HTMLMediaElement::prepareForLoad() { + LOG(Media, "HTMLMediaElement::prepareForLoad"); + // Perform the cleanup required for the resource load algorithm to run. stopPeriodicTimers(); m_loadTimer.stop(); @@ -578,6 +614,8 @@ void HTMLMediaElement::loadInternal() void HTMLMediaElement::selectMediaResource() { + LOG(Media, "HTMLMediaElement::selectMediaResource"); + enum Mode { attribute, children }; Mode mode = attribute; @@ -596,6 +634,8 @@ void HTMLMediaElement::selectMediaResource() // ... set the networkState to NETWORK_EMPTY, and abort these steps m_networkState = NETWORK_EMPTY; + + LOG(Media, "HTMLMediaElement::selectMediaResource, nothing to load"); return; } @@ -616,6 +656,7 @@ void HTMLMediaElement::selectMediaResource() KURL mediaURL = getNonEmptyURLAttribute(srcAttr); if (mediaURL.isEmpty()) { noneSupported(); + LOG(Media, "HTMLMediaElement::selectMediaResource, empty 'src'"); return; } @@ -626,6 +667,7 @@ void HTMLMediaElement::selectMediaResource() } else noneSupported(); + LOG(Media, "HTMLMediaElement::selectMediaResource, 'src' not used"); return; } @@ -656,6 +698,8 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content { ASSERT(isSafeToLoadURL(initialURL, Complain)); + LOG(Media, "HTMLMediaElement::loadResource(%s, %s)", urlForLogging(initialURL.string()).utf8().data(), contentType.raw().utf8().data()); + Frame* frame = document()->frame(); if (!frame) return; @@ -672,6 +716,8 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content m_currentSrc = url; + LOG(Media, "HTMLMediaElement::loadResource - m_currentSrc -> %s", urlForLogging(m_currentSrc).utf8().data()); + if (m_sendProgressEvents) startProgressEventTimer(); @@ -698,16 +744,19 @@ void HTMLMediaElement::loadResource(const KURL& initialURL, ContentType& content bool HTMLMediaElement::isSafeToLoadURL(const KURL& url, InvalidSourceAction actionIfInvalid) { - if (!url.isValid()) + if (!url.isValid()) { + LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE because url is invalid", urlForLogging(url.string()).utf8().data()); return false; + } Frame* frame = document()->frame(); FrameLoader* loader = frame ? frame->loader() : 0; // don't allow remote to local urls, and check with the frame loader client. - if (!loader || !SecurityOrigin::canLoad(url, String(), document())) { + if (!loader || !SecurityOrigin::canDisplay(url, String(), document())) { if (actionIfInvalid == Complain) FrameLoader::reportLocalLoadFailed(frame, url.string()); + LOG(Media, "HTMLMediaElement::isSafeToLoadURL(%s) -> FALSE rejected by SecurityOrigin", urlForLogging(url.string()).utf8().data()); return false; } @@ -727,6 +776,8 @@ void HTMLMediaElement::startProgressEventTimer() void HTMLMediaElement::waitForSourceChange() { + LOG(Media, "HTMLMediaElement::waitForSourceChange"); + stopPeriodicTimers(); m_loadState = WaitingForSource; @@ -739,6 +790,8 @@ void HTMLMediaElement::waitForSourceChange() void HTMLMediaElement::noneSupported() { + LOG(Media, "HTMLMediaElement::noneSupported"); + stopPeriodicTimers(); m_loadState = WaitingForSource; m_currentSourceNode = 0; @@ -769,6 +822,8 @@ void HTMLMediaElement::noneSupported() void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) { + LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast<int>(err->code())); + // 1 - The user agent should cancel the fetching process. stopPeriodicTimers(); m_loadState = WaitingForSource; @@ -794,6 +849,8 @@ void HTMLMediaElement::mediaEngineError(PassRefPtr<MediaError> err) void HTMLMediaElement::cancelPendingEventsAndCallbacks() { + LOG(Media, "HTMLMediaElement::cancelPendingEventsAndCallbacks"); + m_pendingEvents.clear(); for (Node* node = firstChild(); node; node = node->nextSibling()) { @@ -821,6 +878,8 @@ void HTMLMediaElement::mediaPlayerNetworkStateChanged(MediaPlayer*) void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) { + LOG(Media, "HTMLMediaElement::setNetworkState(%d) - current state is %d", static_cast<int>(state), static_cast<int>(m_networkState)); + if (state == MediaPlayer::Empty) { // just update the cached state and leave, we can't do anything m_networkState = NETWORK_EMPTY; @@ -834,10 +893,13 @@ void HTMLMediaElement::setNetworkState(MediaPlayer::NetworkState state) // <source> children, schedule the next one if (m_readyState < HAVE_METADATA && m_loadState == LoadingFromSourceElement) { m_currentSourceNode->scheduleErrorEvent(); - if (havePotentialSourceChild()) + if (havePotentialSourceChild()) { + LOG(Media, "HTMLMediaElement::setNetworkState scheduling next <source>"); scheduleNextSourceChild(); - else + } else { + LOG(Media, "HTMLMediaElement::setNetworkState no more <source> elements, waiting"); waitForSourceChange(); + } return; } @@ -891,6 +953,8 @@ void HTMLMediaElement::mediaPlayerReadyStateChanged(MediaPlayer*) void HTMLMediaElement::setReadyState(MediaPlayer::ReadyState state) { + LOG(Media, "HTMLMediaElement::setReadyState(%d) - current state is %d,", static_cast<int>(state), static_cast<int>(m_readyState)); + // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it bool wasPotentiallyPlaying = potentiallyPlaying(); @@ -1004,18 +1068,22 @@ void HTMLMediaElement::progressEventTimerFired(Timer<HTMLMediaElement>*) void HTMLMediaElement::rewind(float timeDelta) { + LOG(Media, "HTMLMediaElement::rewind(%f)", timeDelta); + ExceptionCode e; setCurrentTime(max(currentTime() - timeDelta, minTimeSeekable()), e); } void HTMLMediaElement::returnToRealtime() { + LOG(Media, "HTMLMediaElement::returnToRealtime"); ExceptionCode e; setCurrentTime(maxTimeSeekable(), e); } void HTMLMediaElement::addPlayedRange(float start, float end) { + LOG(Media, "HTMLMediaElement::addPlayedRange(%f, %f)", start, end); if (!m_playedTimeRanges) m_playedTimeRanges = TimeRanges::create(); m_playedTimeRanges->add(start, end); @@ -1028,6 +1096,8 @@ bool HTMLMediaElement::supportsSave() const void HTMLMediaElement::seek(float time, ExceptionCode& ec) { + LOG(Media, "HTMLMediaElement::seek(%f)", time); + // 4.8.9.9 Seeking // 1 - If the media element's readyState is HAVE_NOTHING, then raise an INVALID_STATE_ERR exception. @@ -1080,6 +1150,8 @@ void HTMLMediaElement::seek(float time, ExceptionCode& ec) void HTMLMediaElement::finishSeek() { + LOG(Media, "HTMLMediaElement::finishSeek"); + // 4.8.10.10 Seeking step 12 m_seeking = false; @@ -1164,6 +1236,8 @@ float HTMLMediaElement::playbackRate() const void HTMLMediaElement::setPlaybackRate(float rate) { + LOG(Media, "HTMLMediaElement::setPlaybackRate(%f)", rate); + if (m_playbackRate != rate) { m_playbackRate = rate; scheduleEvent(eventNames().ratechangeEvent); @@ -1179,6 +1253,8 @@ bool HTMLMediaElement::webkitPreservesPitch() const void HTMLMediaElement::setWebkitPreservesPitch(bool preservesPitch) { + LOG(Media, "HTMLMediaElement::setWebkitPreservesPitch(%s)", boolString(preservesPitch)); + m_webkitPreservesPitch = preservesPitch; if (!m_player) @@ -1202,6 +1278,7 @@ bool HTMLMediaElement::autoplay() const void HTMLMediaElement::setAutoplay(bool b) { + LOG(Media, "HTMLMediaElement::setAutoplay(%s)", boolString(b)); setBooleanAttribute(autoplayAttr, b); } @@ -1225,11 +1302,14 @@ String HTMLMediaElement::preload() const void HTMLMediaElement::setPreload(const String& preload) { + LOG(Media, "HTMLMediaElement::setPreload(%s)", preload.utf8().data()); setAttribute(preloadAttr, preload); } void HTMLMediaElement::play(bool isUserGesture) { + LOG(Media, "HTMLMediaElement::play(isUserGesture : %s)", boolString(isUserGesture)); + if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) return; @@ -1249,6 +1329,8 @@ void HTMLMediaElement::play(bool isUserGesture) void HTMLMediaElement::playInternal() { + LOG(Media, "HTMLMediaElement::playInternal"); + // 4.8.10.9. Playing the media resource if (!m_player || m_networkState == NETWORK_EMPTY) scheduleLoad(); @@ -1276,6 +1358,8 @@ void HTMLMediaElement::playInternal() void HTMLMediaElement::pause(bool isUserGesture) { + LOG(Media, "HTMLMediaElement::pause(isUserGesture : %s)", boolString(isUserGesture)); + if (m_restrictions & RequireUserGestureForRateChangeRestriction && !isUserGesture) return; @@ -1285,6 +1369,8 @@ void HTMLMediaElement::pause(bool isUserGesture) void HTMLMediaElement::pauseInternal() { + LOG(Media, "HTMLMediaElement::pauseInternal"); + // 4.8.10.9. Playing the media resource if (!m_player || m_networkState == NETWORK_EMPTY) scheduleLoad(); @@ -1307,6 +1393,7 @@ bool HTMLMediaElement::loop() const void HTMLMediaElement::setLoop(bool b) { + LOG(Media, "HTMLMediaElement::setLoop(%s)", boolString(b)); setBooleanAttribute(loopAttr, b); } @@ -1323,6 +1410,7 @@ bool HTMLMediaElement::controls() const void HTMLMediaElement::setControls(bool b) { + LOG(Media, "HTMLMediaElement::setControls(%s)", boolString(b)); setBooleanAttribute(controlsAttr, b); } @@ -1333,6 +1421,8 @@ float HTMLMediaElement::volume() const void HTMLMediaElement::setVolume(float vol, ExceptionCode& ec) { + LOG(Media, "HTMLMediaElement::setControls(%f)", vol); + if (vol < 0.0f || vol > 1.0f) { ec = INDEX_SIZE_ERR; return; @@ -1352,6 +1442,8 @@ bool HTMLMediaElement::muted() const void HTMLMediaElement::setMuted(bool muted) { + LOG(Media, "HTMLMediaElement::setMuted(%s)", boolString(muted)); + if (m_muted != muted) { m_muted = muted; // Avoid recursion when the player reports volume changes. @@ -1369,6 +1461,8 @@ void HTMLMediaElement::setMuted(bool muted) void HTMLMediaElement::togglePlayState() { + LOG(Media, "HTMLMediaElement::togglePlayState - canPlay() is %s", boolString(canPlay())); + // We can safely call the internal play/pause methods, which don't check restrictions, because // this method is only called from the built-in media controller if (canPlay()) @@ -1379,6 +1473,8 @@ void HTMLMediaElement::togglePlayState() void HTMLMediaElement::beginScrubbing() { + LOG(Media, "HTMLMediaElement::beginScrubbing - paused() is %s", boolString(paused())); + if (!paused()) { if (ended()) { // Because a media element stays in non-paused state when it reaches end, playback resumes @@ -1396,6 +1492,8 @@ void HTMLMediaElement::beginScrubbing() void HTMLMediaElement::endScrubbing() { + LOG(Media, "HTMLMediaElement::beginScrubbing - m_pausedInternal is %s", boolString(m_pausedInternal)); + if (m_pausedInternal) setPausedInternal(false); } @@ -1482,6 +1580,13 @@ bool HTMLMediaElement::havePotentialSourceChild() KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSourceAction actionIfInvalid) { +#if !LOG_DISABLED + // Don't log if this was just called to find out if there are any valid <source> elements. + bool shouldLog = actionIfInvalid != DoNothing; + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild(contentType : \"%s\")", contentType ? contentType->raw().utf8().data() : ""); +#endif + KURL mediaURL; Node* node; bool lookingForPreviousNode = m_currentSourceNode; @@ -1501,17 +1606,29 @@ KURL HTMLMediaElement::selectNextSourceChild(ContentType *contentType, InvalidSo // If candidate does not have a src attribute, or if its src attribute's value is the empty string ... jump down to the failed step below mediaURL = source->getNonEmptyURLAttribute(srcAttr); +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'src' is %s", urlForLogging(mediaURL).utf8().data()); +#endif if (mediaURL.isEmpty()) goto check_again; if (source->hasAttribute(mediaAttr)) { MediaQueryEvaluator screenEval("screen", document()->frame(), renderer() ? renderer()->style() : 0); RefPtr<MediaList> media = MediaList::createAllowingDescriptionSyntax(source->media()); +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'media' is %s", source->media().utf8().data()); +#endif if (!screenEval.eval(media.get())) goto check_again; } if (source->hasAttribute(typeAttr)) { +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild - 'type' is %s", source->type().utf8().data()); +#endif if (!MediaPlayer::supportsType(ContentType(source->type()))) goto check_again; } @@ -1533,11 +1650,17 @@ check_again: if (!canUse) m_currentSourceNode = 0; +#if !LOG_DISABLED + if (shouldLog) + LOG(Media, "HTMLMediaElement::selectNextSourceChild -> %s", canUse ? urlForLogging(mediaURL.string()).utf8().data() : ""); +#endif return canUse ? mediaURL : KURL(); } void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerTimeChanged"); + beginProcessingMediaPlayerCallback(); // Always call scheduleTimeupdateEvent when the media engine reports a time discontinuity, @@ -1572,6 +1695,8 @@ void HTMLMediaElement::mediaPlayerTimeChanged(MediaPlayer*) void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerVolumeChanged"); + beginProcessingMediaPlayerCallback(); if (m_player) m_volume = m_player->volume(); @@ -1581,6 +1706,8 @@ void HTMLMediaElement::mediaPlayerVolumeChanged(MediaPlayer*) void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerMuteChanged"); + beginProcessingMediaPlayerCallback(); if (m_player) setMuted(m_player->muted()); @@ -1589,6 +1716,8 @@ void HTMLMediaElement::mediaPlayerMuteChanged(MediaPlayer*) void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerDurationChanged"); + beginProcessingMediaPlayerCallback(); scheduleEvent(eventNames().durationchangeEvent); if (renderer()) @@ -1598,6 +1727,8 @@ void HTMLMediaElement::mediaPlayerDurationChanged(MediaPlayer*) void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerRateChanged"); + beginProcessingMediaPlayerCallback(); // Stash the rate in case the one we tried to set isn't what the engine is // using (eg. it can't handle the rate we set) @@ -1605,8 +1736,25 @@ void HTMLMediaElement::mediaPlayerRateChanged(MediaPlayer*) endProcessingMediaPlayerCallback(); } +void HTMLMediaElement::mediaPlayerPlaybackStateChanged(MediaPlayer*) +{ + LOG(Media, "HTMLMediaElement::mediaPlayerPlaybackStateChanged"); + + if (!m_player) + return; + + beginProcessingMediaPlayerCallback(); + if (m_player->paused()) + pauseInternal(); + else + playInternal(); + endProcessingMediaPlayerCallback(); +} + void HTMLMediaElement::mediaPlayerSawUnsupportedTracks(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerSawUnsupportedTracks"); + // The MediaPlayer came across content it cannot completely handle. // This is normally acceptable except when we are in a standalone // MediaDocument. If so, tell the document what has happened. @@ -1628,6 +1776,8 @@ void HTMLMediaElement::mediaPlayerRepaint(MediaPlayer*) void HTMLMediaElement::mediaPlayerSizeChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerSizeChanged"); + beginProcessingMediaPlayerCallback(); if (renderer()) renderer()->updateFromElement(); @@ -1646,6 +1796,8 @@ bool HTMLMediaElement::mediaPlayerRenderingCanBeAccelerated(MediaPlayer*) void HTMLMediaElement::mediaPlayerRenderingModeChanged(MediaPlayer*) { + LOG(Media, "HTMLMediaElement::mediaPlayerRenderingModeChanged"); + // Kick off a fake recalcStyle that will update the compositing tree. setNeedsStyleRecalc(SyntheticStyleChange); } @@ -1780,24 +1932,36 @@ void HTMLMediaElement::updatePlayState() bool shouldBePlaying = potentiallyPlaying(); bool playerPaused = m_player->paused(); - if (shouldBePlaying && playerPaused) { + + LOG(Media, "HTMLMediaElement::updatePlayState - shouldBePlaying = %s, playerPaused = %s", + boolString(shouldBePlaying), boolString(playerPaused)); + + if (shouldBePlaying) { setDisplayMode(Video); - // Set rate before calling play in case the rate was set before the media engine wasn't setup. - // The media engine should just stash the rate since it isn't already playing. - m_player->setRate(m_playbackRate); - m_player->play(); + if (playerPaused) { + // Set rate before calling play in case the rate was set before the media engine was setup. + // The media engine should just stash the rate since it isn't already playing. + m_player->setRate(m_playbackRate); + m_player->play(); + } + startPlaybackProgressTimer(); m_playing = true; - } else if (!shouldBePlaying && !playerPaused) { - m_player->pause(); + + } else { // Should not be playing right now + if (!playerPaused) + m_player->pause(); + m_playbackProgressTimer.stop(); m_playing = false; float time = currentTime(); if (time > m_lastSeekTime) addPlayedRange(m_lastSeekTime, time); - } else if (couldPlayIfEnoughData() && playerPaused) - m_player->prepareToPlay(); + + if (couldPlayIfEnoughData()) + m_player->prepareToPlay(); + } if (renderer()) renderer()->updateFromElement(); @@ -1817,6 +1981,8 @@ void HTMLMediaElement::stopPeriodicTimers() void HTMLMediaElement::userCancelledLoad() { + LOG(Media, "HTMLMediaElement::userCancelledLoad"); + if (m_networkState == NETWORK_EMPTY || m_completelyLoaded) return; @@ -1855,8 +2021,21 @@ void HTMLMediaElement::userCancelledLoad() m_readyState = HAVE_NOTHING; } -void HTMLMediaElement::documentWillBecomeInactive() +bool HTMLMediaElement::canSuspend() const +{ + return true; +} + +void HTMLMediaElement::stop() +{ + LOG(Media, "HTMLMediaElement::stop"); + suspend(); +} + +void HTMLMediaElement::suspend() { + LOG(Media, "HTMLMediaElement::suspend"); + if (m_isFullscreen) exitFullscreen(); @@ -1873,8 +2052,10 @@ void HTMLMediaElement::documentWillBecomeInactive() cancelPendingEventsAndCallbacks(); } -void HTMLMediaElement::documentDidBecomeActive() +void HTMLMediaElement::resume() { + LOG(Media, "HTMLMediaElement::resume"); + m_inActiveDocument = true; setPausedInternal(false); @@ -1891,8 +2072,18 @@ void HTMLMediaElement::documentDidBecomeActive() renderer()->updateFromElement(); } +bool HTMLMediaElement::hasPendingActivity() const +{ + // Return true when we have pending events so we can't fire events after the JS + // object gets collected. + bool pending = m_pendingEvents.size(); + LOG(Media, "HTMLMediaElement::hasPendingActivity -> %s", boolString(pending)); + return pending; +} + void HTMLMediaElement::mediaVolumeDidChange() { + LOG(Media, "HTMLMediaElement::mediaVolumeDidChange"); updateVolume(); } @@ -2004,6 +2195,8 @@ void HTMLMediaElement::createMediaPlayerProxy() if (!loader) return; + LOG(Media, "HTMLMediaElement::createMediaPlayerProxy"); + KURL url; Vector<String> paramNames; Vector<String> paramValues; @@ -2016,10 +2209,26 @@ void HTMLMediaElement::createMediaPlayerProxy() if (m_proxyWidget) m_needWidgetUpdate = false; } + +void HTMLMediaElement::updateWidget(bool) +{ + mediaElement->setNeedWidgetUpdate(false); + + Vector<String> paramNames; + Vector<String> paramValues; + KURL kurl; + + mediaElement->getPluginProxyParams(kurl, paramNames, paramValues); + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + loader->loadMediaPlayerProxyPlugin(mediaElement, kurl, paramNames, paramValues); +} + #endif // ENABLE(PLUGIN_PROXY_FOR_VIDEO) void HTMLMediaElement::enterFullscreen() { + LOG(Media, "HTMLMediaElement::enterFullscreen"); + ASSERT(!m_isFullscreen); m_isFullscreen = true; if (document() && document()->page()) { @@ -2030,6 +2239,8 @@ void HTMLMediaElement::enterFullscreen() void HTMLMediaElement::exitFullscreen() { + LOG(Media, "HTMLMediaElement::exitFullscreen"); + ASSERT(m_isFullscreen); m_isFullscreen = false; if (document() && document()->page()) { @@ -2062,6 +2273,8 @@ bool HTMLMediaElement::closedCaptionsVisible() const void HTMLMediaElement::setClosedCaptionsVisible(bool closedCaptionVisible) { + LOG(Media, "HTMLMediaElement::setClosedCaptionsVisible(%s)", boolString(closedCaptionVisible)); + if (!m_player ||!hasClosedCaptions()) return; @@ -2089,6 +2302,8 @@ bool HTMLMediaElement::webkitHasClosedCaptions() const void HTMLMediaElement::mediaCanStart() { + LOG(Media, "HTMLMediaElement::mediaCanStart"); + ASSERT(m_isWaitingUntilMediaCanStart); m_isWaitingUntilMediaCanStart = false; loadInternal(); @@ -2118,6 +2333,8 @@ void HTMLMediaElement::setShouldDelayLoadEvent(bool shouldDelay) return; } + LOG(Media, "HTMLMediaElement::setShouldDelayLoadEvent(%s)", boolString(shouldDelay)); + m_shouldDelayLoadEvent = shouldDelay; m_isWaitingToDecrementLoadEventDelayCount = false; if (shouldDelay) diff --git a/WebCore/html/HTMLMediaElement.h b/WebCore/html/HTMLMediaElement.h index 3895fe3..adea0fd 100644 --- a/WebCore/html/HTMLMediaElement.h +++ b/WebCore/html/HTMLMediaElement.h @@ -29,6 +29,7 @@ #if ENABLE(VIDEO) #include "HTMLElement.h" +#include "ActiveDOMObject.h" #include "MediaCanStartListener.h" #include "MediaPlayer.h" @@ -51,7 +52,7 @@ class Widget; // But it can't be until the Chromium WebMediaPlayerClientImpl class is fixed so it // no longer depends on typecasting a MediaPlayerClient to an HTMLMediaElement. -class HTMLMediaElement : public HTMLElement, public MediaPlayerClient, private MediaCanStartListener { +class HTMLMediaElement : public HTMLElement, public MediaPlayerClient, private MediaCanStartListener, private ActiveDOMObject { public: MediaPlayer* player() const { return m_player.get(); } @@ -153,6 +154,7 @@ public: void getPluginProxyParams(KURL& url, Vector<String>& names, Vector<String>& values); virtual void finishParsingChildren(); void createMediaPlayerProxy(); + void updateWidget(bool onlyCreateNonNetscapePlugins); #endif bool hasSingleSecurityOrigin() const { return !m_player || m_player->hasSingleSecurityOrigin(); } @@ -195,8 +197,13 @@ private: float getTimeOffsetAttribute(const QualifiedName&, float valueOnError) const; void setTimeOffsetAttribute(const QualifiedName&, float value); - virtual void documentWillBecomeInactive(); - virtual void documentDidBecomeActive(); + // ActiveDOMObject functions. + virtual bool canSuspend() const; + virtual void suspend(); + virtual void resume(); + virtual void stop(); + virtual bool hasPendingActivity() const; + virtual void mediaVolumeDidChange(); virtual void updateDisplayState() { } @@ -212,6 +219,7 @@ private: virtual void mediaPlayerMuteChanged(MediaPlayer*); virtual void mediaPlayerDurationChanged(MediaPlayer*); virtual void mediaPlayerRateChanged(MediaPlayer*); + virtual void mediaPlayerPlaybackStateChanged(MediaPlayer*); virtual void mediaPlayerSawUnsupportedTracks(MediaPlayer*); virtual void mediaPlayerRepaint(MediaPlayer*); virtual void mediaPlayerSizeChanged(MediaPlayer*); diff --git a/WebCore/html/HTMLObjectElement.cpp b/WebCore/html/HTMLObjectElement.cpp index e8884ef..56a6095 100644 --- a/WebCore/html/HTMLObjectElement.cpp +++ b/WebCore/html/HTMLObjectElement.cpp @@ -33,6 +33,7 @@ #include "HTMLFormElement.h" #include "HTMLImageLoader.h" #include "HTMLNames.h" +#include "HTMLParamElement.h" #include "MIMETypeRegistry.h" #include "RenderEmbeddedObject.h" #include "RenderImage.h" @@ -115,6 +116,173 @@ void HTMLObjectElement::parseMappedAttribute(Attribute* attr) HTMLPlugInImageElement::parseMappedAttribute(attr); } +typedef HashMap<String, String, CaseFoldingHash> ClassIdToTypeMap; + +static ClassIdToTypeMap* createClassIdToTypeMap() +{ + ClassIdToTypeMap* map = new ClassIdToTypeMap; + map->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); + map->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); + map->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); + map->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); + map->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); + map->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); + return map; +} + +static String serviceTypeForClassId(const String& classId) +{ + // Return early if classId is empty (since we won't do anything below). + // Furthermore, if classId is null, calling get() below will crash. + if (classId.isEmpty()) + return String(); + + static ClassIdToTypeMap* map = createClassIdToTypeMap(); + return map->get(classId); +} + +static void mapDataParamToSrc(Vector<String>* paramNames, Vector<String>* paramValues) +{ + // Some plugins don't understand the "data" attribute of the OBJECT tag (i.e. Real and WMP + // require "src" attribute). + int srcIndex = -1, dataIndex = -1; + for (unsigned int i = 0; i < paramNames->size(); ++i) { + if (equalIgnoringCase((*paramNames)[i], "src")) + srcIndex = i; + else if (equalIgnoringCase((*paramNames)[i], "data")) + dataIndex = i; + } + + if (srcIndex == -1 && dataIndex != -1) { + paramNames->append("src"); + paramValues->append((*paramValues)[dataIndex]); + } +} + +// FIXME: This function should not deal with url or serviceType! +void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType) +{ + HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; + + // Scan the PARAM children and store their name/value pairs. + // Get the URL and type from the params if we don't already have them. + for (Node* child = firstChild(); child; child = child->nextSibling()) { + if (!child->hasTagName(paramTag)) + continue; + + HTMLParamElement* p = static_cast<HTMLParamElement*>(child); + String name = p->name(); + if (name.isEmpty()) + continue; + + uniqueParamNames.add(name.impl()); + paramNames.append(p->name()); + paramValues.append(p->value()); + + // FIXME: url adjustment does not belong in this function. + if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) + url = deprecatedParseURL(p->value()); + // FIXME: serviceType calculation does not belong in this function. + if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { + serviceType = p->value(); + size_t pos = serviceType.find(";"); + if (pos != notFound) + serviceType = serviceType.left(pos); + } + } + + // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag + // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is + // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means + // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, + // else our Java plugin will misinterpret it. [4004531] + String codebase; + if (MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { + codebase = "codebase"; + uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already + } + + // Turn the attributes of the <object> element into arrays, but don't override <param> values. + NamedNodeMap* attributes = this->attributes(true); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + const AtomicString& name = it->name().localName(); + if (!uniqueParamNames.contains(name.impl())) { + paramNames.append(name.string()); + paramValues.append(it->value().string()); + } + } + } + + mapDataParamToSrc(¶mNames, ¶mValues); + + // If we still don't have a type, try to map from a specific CLASSID to a type. + if (serviceType.isEmpty()) + serviceType = serviceTypeForClassId(classId()); +} + + +bool HTMLObjectElement::hasFallbackContent() const +{ + for (Node* child = firstChild(); child; child = child->nextSibling()) { + // Ignore whitespace-only text, and <param> tags, any other content is fallback content. + if (child->isTextNode()) { + if (!static_cast<Text*>(child)->containsOnlyWhitespace()) + return true; + } else if (!child->hasTagName(paramTag)) + return true; + } + return false; +} + +// FIXME: This should be unified with HTMLEmbedElement::updateWidget and +// moved down into HTMLPluginImageElement.cpp +void HTMLObjectElement::updateWidget(bool onlyCreateNonNetscapePlugins) +{ + ASSERT(!renderEmbeddedObject()->pluginCrashedOrWasMissing()); + // FIXME: We should ASSERT(needsWidgetUpdate()), but currently + // FrameView::updateWidget() calls updateWidget(false) without checking if + // the widget actually needs updating! + setNeedsWidgetUpdate(false); + // FIXME: This should ASSERT isFinishedParsingChildren() instead. + if (!isFinishedParsingChildren()) + return; + + String url = this->url(); + String serviceType = this->serviceType(); + + // FIXME: These should be joined into a PluginParameters class. + Vector<String> paramNames; + Vector<String> paramValues; + parametersForPlugin(paramNames, paramValues, url, serviceType); + + // Note: url is modified above by parametersForPlugin. + if (!allowedToLoadFrameURL(url)) + return; + + bool fallbackContent = hasFallbackContent(); + renderEmbeddedObject()->setHasFallbackContent(fallbackContent); + + if (onlyCreateNonNetscapePlugins && wouldLoadAsNetscapePlugin(url, serviceType)) + return; + + bool beforeLoadAllowedLoad = dispatchBeforeLoadEvent(url); + + // beforeload events can modify the DOM, potentially causing + // RenderWidget::destroy() to be called. Ensure we haven't been + // destroyed before continuing. + // FIXME: Should this render fallback content? + if (!renderer()) + return; + + SubframeLoader* loader = document()->frame()->loader()->subframeLoader(); + bool success = beforeLoadAllowedLoad && loader->requestObject(this, url, getAttribute(nameAttr), serviceType, paramNames, paramValues); + + if (!success && fallbackContent) + renderFallbackContent(); +} + bool HTMLObjectElement::rendererIsNeeded(RenderStyle* style) { // FIXME: This check should not be needed, detached documents never render! diff --git a/WebCore/html/HTMLObjectElement.h b/WebCore/html/HTMLObjectElement.h index 9fafae9..2d416c3 100644 --- a/WebCore/html/HTMLObjectElement.h +++ b/WebCore/html/HTMLObjectElement.h @@ -31,8 +31,6 @@ class HTMLObjectElement : public HTMLPlugInImageElement { public: static PassRefPtr<HTMLObjectElement> create(const QualifiedName&, Document*, bool createdByParser); - void renderFallbackContent(); - bool isDocNamedItem() const { return m_docNamedItem; } const String& classId() const { return m_classId; } @@ -40,6 +38,7 @@ public: bool containsJavaApplet() const; virtual bool useFallbackContent() const { return m_useFallbackContent; } + void renderFallbackContent(); private: HTMLObjectElement(const QualifiedName&, Document*, bool createdByParser); @@ -59,8 +58,15 @@ private: virtual void addSubresourceAttributeURLs(ListHashSet<KURL>&) const; + virtual void updateWidget(bool onlyCreateNonNetscapePlugins); void updateDocNamedItem(); + bool hasFallbackContent() const; + + // FIXME: This function should not deal with url or serviceType + // so that we can better share code between <object> and <embed>. + void parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues, String& url, String& serviceType); + AtomicString m_id; String m_classId; bool m_docNamedItem : 1; diff --git a/WebCore/html/HTMLPlugInImageElement.cpp b/WebCore/html/HTMLPlugInImageElement.cpp index 75407dd..9ac5ad8 100644 --- a/WebCore/html/HTMLPlugInImageElement.cpp +++ b/WebCore/html/HTMLPlugInImageElement.cpp @@ -26,6 +26,7 @@ #include "FrameLoaderClient.h" #include "HTMLImageLoader.h" #include "Image.h" +#include "Page.h" #include "RenderEmbeddedObject.h" #include "RenderImage.h" @@ -63,6 +64,46 @@ bool HTMLPlugInImageElement::isImageType() return Image::supportsType(m_serviceType); } +// We don't use m_url, as it may not be the final URL that the object loads, +// depending on <param> values. +bool HTMLPlugInImageElement::allowedToLoadFrameURL(const String& url) +{ + ASSERT(document()); + ASSERT(document()->frame()); + if (document()->frame()->page()->frameCount() >= Page::maxNumberOfFrames) + return false; + + // We allow one level of self-reference because some sites depend on that. + // But we don't allow more than one. + KURL completeURL = document()->completeURL(url); + bool foundSelfReference = false; + for (Frame* frame = document()->frame(); frame; frame = frame->tree()->parent()) { + if (equalIgnoringFragmentIdentifier(frame->loader()->url(), completeURL)) { + if (foundSelfReference) + return false; + foundSelfReference = true; + } + } + return true; +} + +// We don't use m_url, or m_serviceType as they may not be the final values +// that <object> uses depending on <param> values. +bool HTMLPlugInImageElement::wouldLoadAsNetscapePlugin(const String& url, const String& serviceType) +{ + ASSERT(document()); + ASSERT(document()->frame()); + FrameLoader* frameLoader = document()->frame()->loader(); + ASSERT(frameLoader); + KURL completedURL; + if (!url.isEmpty()) + completedURL = frameLoader->completeURL(url); + + if (frameLoader->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return true; + return false; +} + RenderObject* HTMLPlugInImageElement::createRenderer(RenderArena* arena, RenderStyle* style) { // Fallback content breaks the DOM->Renderer class relationship of this @@ -106,7 +147,7 @@ void HTMLPlugInImageElement::attach() void HTMLPlugInImageElement::detach() { - // FIXME: Because of the insanity that is HTMLObjectElement::recalcStyle, + // FIXME: Because of the insanity that is HTMLPlugInImageElement::recalcStyle, // we can end up detaching during an attach() call, before we even have a // renderer. In that case, don't mark the widget for update. if (attached() && renderer() && !useFallbackContent()) @@ -115,11 +156,18 @@ void HTMLPlugInImageElement::detach() HTMLPlugInElement::detach(); } -void HTMLPlugInImageElement::updateWidget() +void HTMLPlugInImageElement::updateWidgetIfNecessary() { document()->updateStyleIfNeeded(); - if (needsWidgetUpdate() && renderEmbeddedObject() && !useFallbackContent() && !isImageType()) - renderEmbeddedObject()->updateWidget(true); + + if (!needsWidgetUpdate() || useFallbackContent() || isImageType()) + return; + + if (!renderEmbeddedObject() || renderEmbeddedObject()->pluginCrashedOrWasMissing()) + return; + + // True indicates that this code path should only create non-netscape plugins (no clue why). + updateWidget(true); } void HTMLPlugInImageElement::finishParsingChildren() @@ -142,7 +190,7 @@ void HTMLPlugInImageElement::willMoveToNewOwnerDocument() void HTMLPlugInImageElement::updateWidgetCallback(Node* n) { - static_cast<HTMLPlugInImageElement*>(n)->updateWidget(); + static_cast<HTMLPlugInImageElement*>(n)->updateWidgetIfNecessary(); } } // namespace WebCore diff --git a/WebCore/html/HTMLPlugInImageElement.h b/WebCore/html/HTMLPlugInImageElement.h index 65c5f37..60ad0e6 100644 --- a/WebCore/html/HTMLPlugInImageElement.h +++ b/WebCore/html/HTMLPlugInImageElement.h @@ -27,23 +27,23 @@ namespace WebCore { class HTMLImageLoader; +class FrameLoader; // Base class for HTMLObjectElement and HTMLEmbedElement class HTMLPlugInImageElement : public HTMLPlugInElement { public: - const String& serviceType() const { return m_serviceType; } - const String& url() const { return m_url; } - - bool needsWidgetUpdate() const { return m_needsWidgetUpdate; } - void setNeedsWidgetUpdate(bool needsWidgetUpdate) { m_needsWidgetUpdate = needsWidgetUpdate; } - RenderEmbeddedObject* renderEmbeddedObject() const; + virtual void updateWidget(bool onlyCreateNonNetscapePlugins) = 0; + protected: HTMLPlugInImageElement(const QualifiedName& tagName, Document*, bool createdByParser); bool isImageType(); + const String& serviceType() const { return m_serviceType; } + const String& url() const { return m_url; } + OwnPtr<HTMLImageLoader> m_imageLoader; String m_serviceType; String m_url; @@ -52,6 +52,12 @@ protected: virtual void attach(); virtual void detach(); + bool needsWidgetUpdate() const { return m_needsWidgetUpdate; } + void setNeedsWidgetUpdate(bool needsWidgetUpdate) { m_needsWidgetUpdate = needsWidgetUpdate; } + + bool allowedToLoadFrameURL(const String& url); + bool wouldLoadAsNetscapePlugin(const String& url, const String& serviceType); + private: virtual bool canLazyAttach() { return false; } virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); @@ -60,7 +66,7 @@ private: virtual void finishParsingChildren(); virtual void willMoveToNewOwnerDocument(); - void updateWidget(); + void updateWidgetIfNecessary(); virtual bool useFallbackContent() const { return false; } bool m_needsWidgetUpdate; diff --git a/WebCore/html/HTMLSelectElement.cpp b/WebCore/html/HTMLSelectElement.cpp index b1b6d23..c680e92 100644 --- a/WebCore/html/HTMLSelectElement.cpp +++ b/WebCore/html/HTMLSelectElement.cpp @@ -85,8 +85,17 @@ void HTMLSelectElement::setSelectedIndex(int optionIndex, bool deselect) SelectElement::setSelectedIndex(m_data, this, optionIndex, deselect, false, false); } -void HTMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow) +void HTMLSelectElement::setSelectedIndexByUser(int optionIndex, bool deselect, bool fireOnChangeNow, bool allowMultipleSelection) { + // List box selects can fire onchange events through user interaction, such as + // mousedown events. This allows that same behavior programmatically. + if (!m_data.usesMenuList()) { + updateSelectedState(m_data, this, optionIndex, allowMultipleSelection, false); + if (fireOnChangeNow) + listBoxOnChange(); + return; + } + // Bail out if this index is already the selected one, to avoid running unnecessary JavaScript that can mess up // autofill, when there is no actual change (see https://bugs.webkit.org/show_bug.cgi?id=35256 and rdar://7467917 ). // Perhaps this logic could be moved into SelectElement, but some callers of SelectElement::setSelectedIndex() diff --git a/WebCore/html/HTMLSelectElement.h b/WebCore/html/HTMLSelectElement.h index 79b0789..a3b4460 100644 --- a/WebCore/html/HTMLSelectElement.h +++ b/WebCore/html/HTMLSelectElement.h @@ -41,7 +41,7 @@ public: virtual int selectedIndex() const; virtual void setSelectedIndex(int index, bool deselect = true); - virtual void setSelectedIndexByUser(int index, bool deselect = true, bool fireOnChangeNow = false); + virtual void setSelectedIndexByUser(int index, bool deselect = true, bool fireOnChangeNow = false, bool allowMultipleSelection = false); unsigned length() const; diff --git a/WebCore/html/HTMLViewSourceDocument.cpp b/WebCore/html/HTMLViewSourceDocument.cpp index 3299b27..f7217eb 100644 --- a/WebCore/html/HTMLViewSourceDocument.cpp +++ b/WebCore/html/HTMLViewSourceDocument.cpp @@ -41,7 +41,7 @@ #include "HTMLViewSourceParser.h" #include "SegmentedString.h" #include "Text.h" -#include "TextDocument.h" +#include "TextViewSourceParser.h" namespace WebCore { @@ -58,7 +58,6 @@ HTMLViewSourceDocument::HTMLViewSourceDocument(Frame* frame, const KURL& url, co PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser() { - // 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" @@ -66,7 +65,7 @@ PassRefPtr<DocumentParser> HTMLViewSourceDocument::createParser() ) return HTMLViewSourceParser::create(this); - return createTextDocumentParser(this); + return TextViewSourceParser::create(this); } void HTMLViewSourceDocument::createContainingTable() @@ -96,13 +95,6 @@ void HTMLViewSourceDocument::createContainingTable() m_current = m_tbody; } -void HTMLViewSourceDocument::addViewSourceText(const String& text) -{ - if (!m_current) - createContainingTable(); - addText(text, ""); -} - void HTMLViewSourceDocument::addSource(const String& source, HTMLToken& token) { if (!m_current) diff --git a/WebCore/html/HTMLViewSourceDocument.h b/WebCore/html/HTMLViewSourceDocument.h index 445c95b..30e4df3 100644 --- a/WebCore/html/HTMLViewSourceDocument.h +++ b/WebCore/html/HTMLViewSourceDocument.h @@ -42,9 +42,6 @@ public: void addSource(const String&, HTMLToken&); - void addViewSourceToken(HTMLToken&); // Used by the HTMLDocumentParser. - void addViewSourceText(const String&); // Used by the TextDocumentParser. - private: HTMLViewSourceDocument(Frame*, const KURL&, const String& mimeType); diff --git a/WebCore/html/ImageDocument.cpp b/WebCore/html/ImageDocument.cpp new file mode 100644 index 0000000..6951b7b --- /dev/null +++ b/WebCore/html/ImageDocument.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ImageDocument.h" + +#include "CachedImage.h" +#include "DocumentLoader.h" +#include "EventListener.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "FrameView.h" +#include "HTMLImageElement.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "NotImplemented.h" +#include "Page.h" +#include "RawDataDocumentParser.h" +#include "Settings.h" + +using std::min; + +namespace WebCore { + +using namespace HTMLNames; + +class ImageEventListener : public EventListener { +public: + static PassRefPtr<ImageEventListener> create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); } + static const ImageEventListener* cast(const EventListener* listener) + { + return listener->type() == ImageEventListenerType + ? static_cast<const ImageEventListener*>(listener) + : 0; + } + + virtual bool operator==(const EventListener& other); + +private: + ImageEventListener(ImageDocument* document) + : EventListener(ImageEventListenerType) + , m_doc(document) + { + } + + virtual void handleEvent(ScriptExecutionContext*, Event*); + + ImageDocument* m_doc; +}; + +class ImageDocumentParser : public RawDataDocumentParser { +public: + static PassRefPtr<ImageDocumentParser> create(ImageDocument* document) + { + return adoptRef(new ImageDocumentParser(document)); + } + + ImageDocument* document() const + { + return static_cast<ImageDocument*>(RawDataDocumentParser::document()); + } + +private: + ImageDocumentParser(ImageDocument* document) + : RawDataDocumentParser(document) + { + } + + virtual void appendBytes(DocumentWriter*, const char*, int, bool); + virtual void finish(); +}; + +class ImageDocumentElement : public HTMLImageElement { +public: + static PassRefPtr<ImageDocumentElement> create(ImageDocument*); + +private: + ImageDocumentElement(ImageDocument* document) + : HTMLImageElement(imgTag, document) + , m_imageDocument(document) + { + } + + virtual ~ImageDocumentElement(); + virtual void willMoveToNewOwnerDocument(); + + ImageDocument* m_imageDocument; +}; + +inline PassRefPtr<ImageDocumentElement> ImageDocumentElement::create(ImageDocument* document) +{ + return adoptRef(new ImageDocumentElement(document)); +} + +// -------- + +static float pageZoomFactor(Document* document) +{ + FrameView* view = document->view(); + return view ? view->pageZoomFactor() : 1; +} + +void ImageDocumentParser::appendBytes(DocumentWriter*, const char*, int, bool) +{ + Frame* frame = document()->frame(); + Settings* settings = frame->settings(); + if (!frame->loader()->client()->allowImages(!settings || settings->areImagesEnabled())) + return; + + CachedImage* cachedImage = document()->cachedImage(); + cachedImage->data(frame->loader()->documentLoader()->mainResourceData(), false); + + document()->imageChanged(); +} + +void ImageDocumentParser::finish() +{ + if (!isStopped() && document()->imageElement()) { + CachedImage* cachedImage = document()->cachedImage(); + RefPtr<SharedBuffer> data = document()->frame()->loader()->documentLoader()->mainResourceData(); + + // If this is a multipart image, make a copy of the current part, since the resource data + // will be overwritten by the next part. + if (document()->frame()->loader()->documentLoader()->isLoadingMultipartContent()) + data = data->copy(); + + cachedImage->data(data.release(), true); + cachedImage->finish(); + + cachedImage->setResponse(document()->frame()->loader()->documentLoader()->response()); + + // Report the natural image size in the page title, regardless of zoom + // level. + IntSize size = cachedImage->imageSize(1.0f); + if (size.width()) { + // Compute the title, we use the decoded filename of the resource, falling + // back on the (decoded) hostname if there is no path. + String fileName = decodeURLEscapeSequences(document()->url().lastPathComponent()); + if (fileName.isEmpty()) + fileName = document()->url().host(); + document()->setTitle(imageTitle(fileName, size)); + } + + document()->imageChanged(); + } + + document()->finishedParsing(); +} + +// -------- + +ImageDocument::ImageDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) + , m_imageElement(0) + , m_imageSizeIsKnown(false) + , m_didShrinkImage(false) + , m_shouldShrinkImage(shouldShrinkToFit()) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> ImageDocument::createParser() +{ + return ImageDocumentParser::create(this); +} + +void ImageDocument::createDocumentStructure() +{ + ExceptionCode ec; + + RefPtr<Element> rootElement = Document::createElement(htmlTag, false); + appendChild(rootElement, ec); + + if (frame() && frame()->loader()) + frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr<Element> body = Document::createElement(bodyTag, false); + body->setAttribute(styleAttr, "margin: 0px;"); + + rootElement->appendChild(body, ec); + + RefPtr<ImageDocumentElement> imageElement = ImageDocumentElement::create(this); + + imageElement->setAttribute(styleAttr, "-webkit-user-select: none"); + imageElement->setLoadManually(true); + imageElement->setSrc(url().string()); + + body->appendChild(imageElement, ec); + + if (shouldShrinkToFit()) { + // Add event listeners + RefPtr<EventListener> listener = ImageEventListener::create(this); + if (DOMWindow* domWindow = this->domWindow()) + domWindow->addEventListener("resize", listener, false); + imageElement->addEventListener("click", listener.release(), false); + } + + m_imageElement = imageElement.get(); +} + +float ImageDocument::scale() const +{ + if (!m_imageElement) + return 1.0f; + + FrameView* view = frame()->view(); + if (!view) + return 1; + + IntSize imageSize = m_imageElement->cachedImage()->imageSize(view->pageZoomFactor()); + IntSize windowSize = IntSize(view->width(), view->height()); + + float widthScale = (float)windowSize.width() / imageSize.width(); + float heightScale = (float)windowSize.height() / imageSize.height(); + + return min(widthScale, heightScale); +} + +void ImageDocument::resizeImageToFit() +{ + if (!m_imageElement) + return; + + IntSize imageSize = m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)); + + float scale = this->scale(); + m_imageElement->setWidth(static_cast<int>(imageSize.width() * scale)); + m_imageElement->setHeight(static_cast<int>(imageSize.height() * scale)); + + ExceptionCode ec; + m_imageElement->style()->setProperty("cursor", "-webkit-zoom-in", ec); +} + +void ImageDocument::imageClicked(int x, int y) +{ + if (!m_imageSizeIsKnown || imageFitsInWindow()) + return; + + m_shouldShrinkImage = !m_shouldShrinkImage; + + if (m_shouldShrinkImage) + windowSizeChanged(); + else { + restoreImageSize(); + + updateLayout(); + + float scale = this->scale(); + + int scrollX = static_cast<int>(x / scale - (float)frame()->view()->width() / 2); + int scrollY = static_cast<int>(y / scale - (float)frame()->view()->height() / 2); + + frame()->view()->setScrollPosition(IntPoint(scrollX, scrollY)); + } +} + +void ImageDocument::imageChanged() +{ + ASSERT(m_imageElement); + + if (m_imageSizeIsKnown) + return; + + if (m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)).isEmpty()) + return; + + m_imageSizeIsKnown = true; + + if (shouldShrinkToFit()) { + // Force resizing of the image + windowSizeChanged(); + } +} + +void ImageDocument::restoreImageSize() +{ + if (!m_imageElement || !m_imageSizeIsKnown) + return; + + m_imageElement->setWidth(m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)).width()); + m_imageElement->setHeight(m_imageElement->cachedImage()->imageSize(pageZoomFactor(this)).height()); + + ExceptionCode ec; + if (imageFitsInWindow()) + m_imageElement->style()->removeProperty("cursor", ec); + else + m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec); + + m_didShrinkImage = false; +} + +bool ImageDocument::imageFitsInWindow() const +{ + if (!m_imageElement) + return true; + + FrameView* view = frame()->view(); + + IntSize imageSize = m_imageElement->cachedImage()->imageSize(view->pageZoomFactor()); + IntSize windowSize = IntSize(view->width(), view->height()); + + return imageSize.width() <= windowSize.width() && imageSize.height() <= windowSize.height(); +} + +void ImageDocument::windowSizeChanged() +{ + if (!m_imageElement || !m_imageSizeIsKnown) + return; + + bool fitsInWindow = imageFitsInWindow(); + + // If the image has been explicitly zoomed in, restore the cursor if the image fits + // and set it to a zoom out cursor if the image doesn't fit + if (!m_shouldShrinkImage) { + ExceptionCode ec; + + if (fitsInWindow) + m_imageElement->style()->removeProperty("cursor", ec); + else + m_imageElement->style()->setProperty("cursor", "-webkit-zoom-out", ec); + return; + } + + if (m_didShrinkImage) { + // If the window has been resized so that the image fits, restore the image size + // otherwise update the restored image size. + if (fitsInWindow) + restoreImageSize(); + else + resizeImageToFit(); + } else { + // If the image isn't resized but needs to be, then resize it. + if (!fitsInWindow) { + resizeImageToFit(); + m_didShrinkImage = true; + } + } +} + +CachedImage* ImageDocument::cachedImage() +{ + if (!m_imageElement) + createDocumentStructure(); + + return m_imageElement->cachedImage(); +} + +bool ImageDocument::shouldShrinkToFit() const +{ + return frame()->page()->settings()->shrinksStandaloneImagesToFit() && + frame()->page()->mainFrame() == frame(); +} + +// -------- + +void ImageEventListener::handleEvent(ScriptExecutionContext*, Event* event) +{ + if (event->type() == eventNames().resizeEvent) + m_doc->windowSizeChanged(); + else if (event->type() == eventNames().clickEvent) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + m_doc->imageClicked(mouseEvent->x(), mouseEvent->y()); + } +} + +bool ImageEventListener::operator==(const EventListener& listener) +{ + if (const ImageEventListener* imageEventListener = ImageEventListener::cast(&listener)) + return m_doc == imageEventListener->m_doc; + return false; +} + +// -------- + +ImageDocumentElement::~ImageDocumentElement() +{ + if (m_imageDocument) + m_imageDocument->disconnectImageElement(); +} + +void ImageDocumentElement::willMoveToNewOwnerDocument() +{ + if (m_imageDocument) { + m_imageDocument->disconnectImageElement(); + m_imageDocument = 0; + } + HTMLImageElement::willMoveToNewOwnerDocument(); +} + +} diff --git a/WebCore/html/ImageDocument.h b/WebCore/html/ImageDocument.h new file mode 100644 index 0000000..5d00bd6 --- /dev/null +++ b/WebCore/html/ImageDocument.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ImageDocument_h +#define ImageDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class ImageDocumentElement; + +class ImageDocument : public HTMLDocument { +public: + static PassRefPtr<ImageDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new ImageDocument(frame, url)); + } + + CachedImage* cachedImage(); + ImageDocumentElement* imageElement() const { return m_imageElement; } + void disconnectImageElement() { m_imageElement = 0; } + + void windowSizeChanged(); + void imageChanged(); + void imageClicked(int x, int y); + +private: + ImageDocument(Frame*, const KURL&); + + virtual PassRefPtr<DocumentParser> createParser(); + virtual bool isImageDocument() const { return true; } + + void createDocumentStructure(); + void resizeImageToFit(); + void restoreImageSize(); + bool imageFitsInWindow() const; + bool shouldShrinkToFit() const; + float scale() const; + + ImageDocumentElement* m_imageElement; + + // Whether enough of the image has been loaded to determine its size + bool m_imageSizeIsKnown; + + // Whether the image is shrunk to fit or not + bool m_didShrinkImage; + + // Whether the image should be shrunk or not + bool m_shouldShrinkImage; +}; + +} + +#endif // ImageDocument_h diff --git a/WebCore/html/MediaDocument.cpp b/WebCore/html/MediaDocument.cpp new file mode 100644 index 0000000..875141b --- /dev/null +++ b/WebCore/html/MediaDocument.cpp @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "MediaDocument.h" + +#include "DocumentLoader.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLEmbedElement.h" +#include "HTMLNames.h" +#include "HTMLVideoElement.h" +#include "KeyboardEvent.h" +#include "MainResourceLoader.h" +#include "NodeList.h" +#include "RawDataDocumentParser.h" + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: Share more code with PluginDocumentParser. +class MediaDocumentParser : public RawDataDocumentParser { +public: + static PassRefPtr<MediaDocumentParser> create(MediaDocument* document) + { + return adoptRef(new MediaDocumentParser(document)); + } + +private: + MediaDocumentParser(Document* document) + : RawDataDocumentParser(document) + , m_mediaElement(0) + { + } + + virtual void appendBytes(DocumentWriter*, const char*, int, bool); + + void createDocumentStructure(); + + HTMLMediaElement* m_mediaElement; +}; + +void MediaDocumentParser::createDocumentStructure() +{ + ExceptionCode ec; + RefPtr<Element> rootElement = document()->createElement(htmlTag, false); + document()->appendChild(rootElement, ec); + + if (document()->frame()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr<Element> body = document()->createElement(bodyTag, false); + body->setAttribute(styleAttr, "background-color: rgb(38,38,38);"); + + rootElement->appendChild(body, ec); + + RefPtr<Element> mediaElement = document()->createElement(videoTag, false); + + m_mediaElement = static_cast<HTMLVideoElement*>(mediaElement.get()); + m_mediaElement->setAttribute(controlsAttr, ""); + m_mediaElement->setAttribute(autoplayAttr, ""); + m_mediaElement->setAttribute(styleAttr, "margin: auto; position: absolute; top: 0; right: 0; bottom: 0; left: 0;"); + + m_mediaElement->setAttribute(nameAttr, "media"); + m_mediaElement->setSrc(document()->url()); + + body->appendChild(mediaElement, ec); + + Frame* frame = document()->frame(); + if (!frame) + return; + + frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); +} + +void MediaDocumentParser::appendBytes(DocumentWriter*, const char*, int, bool) +{ + ASSERT(!m_mediaElement); + if (m_mediaElement) + return; + + createDocumentStructure(); + finish(); +} + +MediaDocument::MediaDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) + , m_replaceMediaElementTimer(this, &MediaDocument::replaceMediaElementTimerFired) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +MediaDocument::~MediaDocument() +{ + ASSERT(!m_replaceMediaElementTimer.isActive()); +} + +PassRefPtr<DocumentParser> MediaDocument::createParser() +{ + return MediaDocumentParser::create(this); +} + +void MediaDocument::defaultEventHandler(Event* event) +{ + // Match the default Quicktime plugin behavior to allow + // clicking and double-clicking to pause and play the media. + Node* targetNode = event->target()->toNode(); + if (targetNode && targetNode->hasTagName(videoTag)) { + HTMLVideoElement* video = static_cast<HTMLVideoElement*>(targetNode); + if (event->type() == eventNames().clickEvent) { + if (!video->canPlay()) { + video->pause(event->fromUserGesture()); + event->setDefaultHandled(); + } + } else if (event->type() == eventNames().dblclickEvent) { + if (video->canPlay()) { + video->play(event->fromUserGesture()); + event->setDefaultHandled(); + } + } + } + + if (event->type() == eventNames().keydownEvent && event->isKeyboardEvent()) { + HTMLVideoElement* video = 0; + if (targetNode) { + if (targetNode->hasTagName(videoTag)) + video = static_cast<HTMLVideoElement*>(targetNode); + else { + RefPtr<NodeList> nodeList = targetNode->getElementsByTagName("video"); + if (nodeList.get()->length() > 0) + video = static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); + } + } + if (video) { + KeyboardEvent* keyboardEvent = static_cast<KeyboardEvent*>(event); + if (keyboardEvent->keyIdentifier() == "U+0020") { // space + if (video->paused()) { + if (video->canPlay()) + video->play(event->fromUserGesture()); + } else + video->pause(event->fromUserGesture()); + event->setDefaultHandled(); + } + } + } +} + +void MediaDocument::mediaElementSawUnsupportedTracks() +{ + // The HTMLMediaElement was told it has something that the underlying + // MediaPlayer cannot handle so we should switch from <video> to <embed> + // and let the plugin handle this. Don't do it immediately as this + // function may be called directly from a media engine callback, and + // replaceChild will destroy the element, media player, and media engine. + m_replaceMediaElementTimer.startOneShot(0); +} + +void MediaDocument::replaceMediaElementTimerFired(Timer<MediaDocument>*) +{ + HTMLElement* htmlBody = body(); + if (!htmlBody) + return; + + // Set body margin width and height to 0 as that is what a PluginDocument uses. + htmlBody->setAttribute(marginwidthAttr, "0"); + htmlBody->setAttribute(marginheightAttr, "0"); + + RefPtr<NodeList> nodeList = htmlBody->getElementsByTagName("video"); + + if (nodeList.get()->length() > 0) { + HTMLVideoElement* videoElement = static_cast<HTMLVideoElement*>(nodeList.get()->item(0)); + + RefPtr<Element> element = Document::createElement(embedTag, false); + HTMLEmbedElement* embedElement = static_cast<HTMLEmbedElement*>(element.get()); + + embedElement->setAttribute(widthAttr, "100%"); + embedElement->setAttribute(heightAttr, "100%"); + embedElement->setAttribute(nameAttr, "plugin"); + embedElement->setAttribute(srcAttr, url().string()); + embedElement->setAttribute(typeAttr, frame()->loader()->writer()->mimeType()); + + ExceptionCode ec; + videoElement->parent()->replaceChild(embedElement, videoElement, ec); + } +} + +} +#endif diff --git a/WebCore/html/MediaDocument.h b/WebCore/html/MediaDocument.h new file mode 100644 index 0000000..2d81296 --- /dev/null +++ b/WebCore/html/MediaDocument.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2008,2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaDocument_h +#define MediaDocument_h + +#if ENABLE(VIDEO) + +#include "HTMLDocument.h" + +namespace WebCore { + +class MediaDocument : public HTMLDocument { +public: + static PassRefPtr<MediaDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new MediaDocument(frame, url)); + } + virtual ~MediaDocument(); + + void mediaElementSawUnsupportedTracks(); + +private: + MediaDocument(Frame*, const KURL&); + + virtual bool isMediaDocument() const { return true; } + virtual PassRefPtr<DocumentParser> createParser(); + + virtual void defaultEventHandler(Event*); + + void replaceMediaElementTimerFired(Timer<MediaDocument>*); + + Timer<MediaDocument> m_replaceMediaElementTimer; +}; + +} + +#endif +#endif diff --git a/WebCore/html/PluginDocument.cpp b/WebCore/html/PluginDocument.cpp new file mode 100644 index 0000000..ad11dfb --- /dev/null +++ b/WebCore/html/PluginDocument.cpp @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PluginDocument.h" + +#include "DocumentLoader.h" +#include "Frame.h" +#include "FrameLoaderClient.h" +#include "HTMLEmbedElement.h" +#include "HTMLNames.h" +#include "MainResourceLoader.h" +#include "Page.h" +#include "RawDataDocumentParser.h" +#include "RenderEmbeddedObject.h" +#include "Settings.h" + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: Share more code with MediaDocumentParser. +class PluginDocumentParser : public RawDataDocumentParser { +public: + static PassRefPtr<PluginDocumentParser> create(PluginDocument* document) + { + return adoptRef(new PluginDocumentParser(document)); + } + + static Widget* pluginWidgetFromDocument(Document*); + +private: + PluginDocumentParser(Document* document) + : RawDataDocumentParser(document) + , m_embedElement(0) + { + } + + virtual void appendBytes(DocumentWriter*, const char*, int, bool); + + void createDocumentStructure(); + + HTMLEmbedElement* m_embedElement; +}; + +Widget* PluginDocumentParser::pluginWidgetFromDocument(Document* doc) +{ + ASSERT(doc); + RefPtr<Element> body = doc->body(); + if (body) { + RefPtr<Node> node = body->firstChild(); + if (node && node->renderer()) { + ASSERT(node->renderer()->isEmbeddedObject()); + return toRenderEmbeddedObject(node->renderer())->widget(); + } + } + return 0; +} + +void PluginDocumentParser::createDocumentStructure() +{ + ExceptionCode ec; + RefPtr<Element> rootElement = document()->createElement(htmlTag, false); + document()->appendChild(rootElement, ec); + + if (document()->frame() && document()->frame()->loader()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr<Element> body = document()->createElement(bodyTag, false); + body->setAttribute(marginwidthAttr, "0"); + body->setAttribute(marginheightAttr, "0"); + body->setAttribute(bgcolorAttr, "rgb(38,38,38)"); + + rootElement->appendChild(body, ec); + + RefPtr<Element> embedElement = document()->createElement(embedTag, false); + + m_embedElement = static_cast<HTMLEmbedElement*>(embedElement.get()); + m_embedElement->setAttribute(widthAttr, "100%"); + m_embedElement->setAttribute(heightAttr, "100%"); + + m_embedElement->setAttribute(nameAttr, "plugin"); + m_embedElement->setAttribute(srcAttr, document()->url().string()); + m_embedElement->setAttribute(typeAttr, document()->frame()->loader()->writer()->mimeType()); + + body->appendChild(embedElement, ec); +} + +void PluginDocumentParser::appendBytes(DocumentWriter*, const char*, int, bool) +{ + ASSERT(!m_embedElement); + if (m_embedElement) + return; + + createDocumentStructure(); + + Frame* frame = document()->frame(); + if (!frame) + return; + Settings* settings = frame->settings(); + if (!settings || !frame->loader()->subframeLoader()->allowPlugins(NotAboutToInstantiatePlugin)) + return; + + document()->updateLayout(); + + if (RenderPart* renderer = m_embedElement->renderPart()) { + frame->loader()->client()->redirectDataToPlugin(renderer->widget()); + frame->loader()->activeDocumentLoader()->mainResourceLoader()->setShouldBufferData(false); + } + + finish(); +} + +PluginDocument::PluginDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> PluginDocument::createParser() +{ + return PluginDocumentParser::create(this); +} + +Widget* PluginDocument::pluginWidget() +{ + return PluginDocumentParser::pluginWidgetFromDocument(this); +} + +Node* PluginDocument::pluginNode() +{ + RefPtr<Element> body_element = body(); + if (body_element) + return body_element->firstChild(); + + return 0; +} + +} diff --git a/WebCore/html/PluginDocument.h b/WebCore/html/PluginDocument.h new file mode 100644 index 0000000..3bb5d99 --- /dev/null +++ b/WebCore/html/PluginDocument.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2006, 2008, 2009Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PluginDocument_h +#define PluginDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class Node; +class Widget; +class PluginDocument : public HTMLDocument { +public: + static PassRefPtr<PluginDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new PluginDocument(frame, url)); + } + + Widget* pluginWidget(); + Node* pluginNode(); + + virtual bool isPluginDocument() const { return true; } + +private: + PluginDocument(Frame*, const KURL&); + + virtual PassRefPtr<DocumentParser> createParser(); +}; + +} + +#endif // PluginDocument_h diff --git a/WebCore/html/TextDocument.cpp b/WebCore/html/TextDocument.cpp new file mode 100644 index 0000000..9334a39 --- /dev/null +++ b/WebCore/html/TextDocument.cpp @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextDocument.h" + +#include "TextDocumentParser.h" + +namespace WebCore { + +TextDocument::TextDocument(Frame* frame, const KURL& url) + : HTMLDocument(frame, url) +{ + setCompatibilityMode(QuirksMode); + lockCompatibilityMode(); +} + +PassRefPtr<DocumentParser> TextDocument::createParser() +{ + return TextDocumentParser::create(this); +} + +} diff --git a/WebCore/html/TextDocument.h b/WebCore/html/TextDocument.h new file mode 100644 index 0000000..2ea49f8 --- /dev/null +++ b/WebCore/html/TextDocument.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextDocument_h +#define TextDocument_h + +#include "HTMLDocument.h" + +namespace WebCore { + +class TextDocument : public HTMLDocument { +public: + static PassRefPtr<TextDocument> create(Frame* frame, const KURL& url) + { + return adoptRef(new TextDocument(frame, url)); + } + +private: + TextDocument(Frame*, const KURL&); + + virtual PassRefPtr<DocumentParser> createParser(); +}; + +} + +#endif diff --git a/WebCore/html/canvas/ArrayBufferView.cpp b/WebCore/html/canvas/ArrayBufferView.cpp index bd22f88..485d18b 100644 --- a/WebCore/html/canvas/ArrayBufferView.cpp +++ b/WebCore/html/canvas/ArrayBufferView.cpp @@ -59,6 +59,34 @@ void ArrayBufferView::setImpl(ArrayBufferView* array, unsigned byteOffset, Excep memmove(base + byteOffset, array->baseAddress(), array->byteLength()); } +void ArrayBufferView::setRangeImpl(const char* data, size_t dataByteLength, unsigned byteOffset, ExceptionCode& ec) +{ + if (byteOffset > byteLength() + || byteOffset + dataByteLength > byteLength() + || byteOffset + dataByteLength < byteOffset) { + // Out of range offset or overflow + ec = INDEX_SIZE_ERR; + return; + } + + char* base = static_cast<char*>(baseAddress()); + memmove(base + byteOffset, data, dataByteLength); +} + +void ArrayBufferView::zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength, ExceptionCode& ec) +{ + if (byteOffset > byteLength() + || byteOffset + rangeByteLength > byteLength() + || byteOffset + rangeByteLength < byteOffset) { + // Out of range offset or overflow + ec = INDEX_SIZE_ERR; + return; + } + + char* base = static_cast<char*>(baseAddress()); + memset(base + byteOffset, 0, rangeByteLength); +} + void ArrayBufferView::calculateOffsetAndLength(int start, int end, unsigned arraySize, unsigned* offset, unsigned* length) { diff --git a/WebCore/html/canvas/ArrayBufferView.h b/WebCore/html/canvas/ArrayBufferView.h index 29ad691..ee685b1 100644 --- a/WebCore/html/canvas/ArrayBufferView.h +++ b/WebCore/html/canvas/ArrayBufferView.h @@ -73,6 +73,10 @@ class ArrayBufferView : public RefCounted<ArrayBufferView> { void setImpl(ArrayBufferView* array, unsigned byteOffset, ExceptionCode& ec); + void setRangeImpl(const char* data, size_t dataByteLength, unsigned byteOffset, ExceptionCode& ec); + + void zeroRangeImpl(unsigned byteOffset, size_t rangeByteLength, ExceptionCode& ec); + static void calculateOffsetAndLength(int start, int end, unsigned arraySize, unsigned* offset, unsigned* length); diff --git a/WebCore/html/canvas/CanvasRenderingContext.cpp b/WebCore/html/canvas/CanvasRenderingContext.cpp index 1e897d3..e019332 100644 --- a/WebCore/html/canvas/CanvasRenderingContext.cpp +++ b/WebCore/html/canvas/CanvasRenderingContext.cpp @@ -25,9 +25,6 @@ #include "config.h" #include "CanvasRenderingContext.h" -#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(3D_CANVAS) -#include "GraphicsContext3D.h" -#endif #include "HTMLCanvasElement.h" namespace WebCore { @@ -47,13 +44,4 @@ void CanvasRenderingContext::deref() m_canvas->deref(); } -bool CanvasRenderingContext::paintsIntoCanvasBuffer() const -{ -#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(3D_CANVAS) - if (GraphicsContext3D* context3D = graphicsContext3D()) - return context3D->paintsIntoCanvasBuffer(); -#endif - return true; -} - } // namespace WebCore diff --git a/WebCore/html/canvas/CanvasRenderingContext.h b/WebCore/html/canvas/CanvasRenderingContext.h index 2cdbe1d..8499b47 100644 --- a/WebCore/html/canvas/CanvasRenderingContext.h +++ b/WebCore/html/canvas/CanvasRenderingContext.h @@ -26,12 +26,13 @@ #ifndef CanvasRenderingContext_h #define CanvasRenderingContext_h +#include "GraphicsLayer.h" + #include <wtf/Noncopyable.h> namespace WebCore { class WebGLObject; - class GraphicsContext3D; class HTMLCanvasElement; class CanvasRenderingContext : public Noncopyable { @@ -49,12 +50,12 @@ namespace WebCore { virtual bool is3d() const { return false; } virtual bool isAccelerated() const { return false; } - // For accelerated canvases, returns a pointer to the underlying GraphicsContext3D. - // For non accelerated canvases returns 0. - virtual GraphicsContext3D* graphicsContext3D() const { return 0; } - virtual void paintRenderingResultsToCanvas() {} - bool paintsIntoCanvasBuffer() const; + virtual bool paintsIntoCanvasBuffer() const { return true; } + +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const { return 0; } +#endif private: HTMLCanvasElement* m_canvas; diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/WebCore/html/canvas/CanvasRenderingContext2D.cpp index 6df6abf..338960c 100644..100755 --- a/WebCore/html/canvas/CanvasRenderingContext2D.cpp +++ b/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -25,7 +25,7 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" @@ -59,8 +59,12 @@ #include "TextMetrics.h" #if ENABLE(ACCELERATED_2D_CANVAS) +#include "Chrome.h" +#include "ChromeClient.h" +#include "DrawingBuffer.h" #include "FrameView.h" #include "GraphicsContext3D.h" +#include "SharedGraphicsContext3D.h" #if USE(ACCELERATED_COMPOSITING) #include "RenderLayer.h" #endif @@ -124,17 +128,11 @@ CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement* canvas, bo return; if (!p->settings()->accelerated2dCanvasEnabled()) return; - if (FrameView* view = canvas->document()->view()) { - if (ScrollView* rootView = view->root()) { - if (HostWindow* hostWindow = rootView->hostWindow()) { - // Set up our context - GraphicsContext3D::Attributes attr; - attr.stencil = true; - m_context3D = GraphicsContext3D::create(attr, hostWindow); - if (m_context3D) - if (GraphicsContext* c = drawingContext()) - c->setGraphicsContext3D(m_context3D.get(), IntSize(canvas->width(), canvas->height())); - } + m_context3D = p->chrome()->client()->getSharedGraphicsContext3D(); + if (m_context3D) { + if (GraphicsContext* c = drawingContext()) { + m_drawingBuffer = DrawingBuffer::create(m_context3D.get(), IntSize(canvas->width(), canvas->height())); + c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas->width(), canvas->height())); } } #endif @@ -153,6 +151,16 @@ bool CanvasRenderingContext2D::isAccelerated() const #endif } +bool CanvasRenderingContext2D::paintsIntoCanvasBuffer() const +{ +#if ENABLE(ACCELERATED_2D_CANVAS) + if (m_context3D) + return m_context3D->paintsIntoCanvasBuffer(); +#endif + return true; +} + + void CanvasRenderingContext2D::reset() { m_stateStack.resize(1); @@ -160,8 +168,10 @@ void CanvasRenderingContext2D::reset() m_path.clear(); #if ENABLE(ACCELERATED_2D_CANVAS) if (m_context3D) { - if (GraphicsContext* c = drawingContext()) - c->setGraphicsContext3D(m_context3D.get(), IntSize(canvas()->width(), canvas()->height())); + if (GraphicsContext* c = drawingContext()) { + m_drawingBuffer->reset(IntSize(canvas()->width(), canvas()->height())); + c->setSharedGraphicsContext3D(m_context3D.get(), m_drawingBuffer.get(), IntSize(canvas()->width(), canvas()->height())); + } } #endif } @@ -209,7 +219,7 @@ void CanvasRenderingContext2D::restore() c->restore(); } -void CanvasRenderingContext2D::setAllAttributesToDefault() +void CanvasRenderingContext2D::setAllAttributesToDefault() { state().m_globalAlpha = 1; state().m_shadowOffset = FloatSize(); @@ -431,6 +441,15 @@ void CanvasRenderingContext2D::setGlobalCompositeOperation(const String& operati if (!c) return; c->setCompositeOperation(op); +#if ENABLE(ACCELERATED_2D_CANVAS) + if (isAccelerated() && op != CompositeSourceOver) { + c->setSharedGraphicsContext3D(0, 0, IntSize()); + m_drawingBuffer.clear(); + m_context3D.clear(); + // Mark as needing a style recalc so our compositing layer can be removed. + canvas()->setNeedsStyleRecalc(SyntheticStyleChange); + } +#endif } void CanvasRenderingContext2D::scale(float sx, float sy) @@ -640,7 +659,7 @@ void CanvasRenderingContext2D::closePath() FloatRect boundRect = m_path.boundingRect(); if (boundRect.width() || boundRect.height()) - m_path.closeSubpath(); + m_path.closeCanvasSubpath(); } void CanvasRenderingContext2D::moveTo(float x, float y) @@ -924,7 +943,7 @@ void CanvasRenderingContext2D::strokeRect(float x, float y, float width, float h { if (!validateRectForCanvas(x, y, width, height)) return; - + if (!(lineWidth >= 0)) return; @@ -1257,7 +1276,7 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const return; if (!state().m_invertibleCTM) return; - + FloatRect sourceRect = c->roundToDevicePixels(srcRect); FloatRect destRect = c->roundToDevicePixels(dstRect); @@ -1269,7 +1288,16 @@ void CanvasRenderingContext2D::drawImage(HTMLCanvasElement* sourceCanvas, const if (!sourceCanvas->originClean()) canvas()->setOriginTainted(); +#if ENABLE(ACCELERATED_2D_CANVAS) + // If we're drawing from one accelerated canvas 2d to another, avoid calling sourceCanvas->makeRenderingResultsAvailable() + // as that will do a readback to software. + CanvasRenderingContext* sourceContext = sourceCanvas->renderingContext(); + // FIXME: Implement an accelerated path for drawing from a WebGL canvas to a 2d canvas when possible. + if (!isAccelerated() || !sourceContext || !sourceContext->isAccelerated() || !sourceContext->is2d()) + sourceCanvas->makeRenderingResultsAvailable(); +#else sourceCanvas->makeRenderingResultsAvailable(); +#endif c->drawImageBuffer(buffer, DeviceColorSpace, destRect, sourceRect, state().m_globalComposite); didDraw(destRect); @@ -1482,12 +1510,12 @@ void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options) return; FloatRect dirtyRect = r; - if (options & CanvasWillDrawApplyTransform) { + if (options & CanvasDidDrawApplyTransform) { AffineTransform ctm = state().m_transform; dirtyRect = ctm.mapRect(r); } - if (options & CanvasWillDrawApplyShadow && alphaChannel(state().m_shadowColor)) { + if (options & CanvasDidDrawApplyShadow && alphaChannel(state().m_shadowColor)) { // The shadow gets applied after transformation FloatRect shadowRect(dirtyRect); shadowRect.move(state().m_shadowOffset); @@ -1495,16 +1523,20 @@ void CanvasRenderingContext2D::didDraw(const FloatRect& r, unsigned options) dirtyRect.unite(shadowRect); } - if (options & CanvasWillDrawApplyClip) { + if (options & CanvasDidDrawApplyClip) { // FIXME: apply the current clip to the rectangle. Unfortunately we can't get the clip // back out of the GraphicsContext, so to take clip into account for incremental painting, // we'd have to keep the clip path around. } +#if ENABLE(ACCELERATED_2D_CANVAS) + if (isAccelerated()) + drawingContext()->markDirtyRect(enclosingIntRect(dirtyRect)); +#endif #if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) // If we are drawing to hardware and we have a composited layer, just call rendererContentChanged(). RenderBox* renderBox = canvas()->renderBox(); - if (m_context3D && renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) + if (isAccelerated() && renderBox && renderBox->hasLayer() && renderBox->layer()->hasAcceleratedCompositing()) renderBox->layer()->rendererContentChanged(); else #endif @@ -1577,7 +1609,7 @@ PassRefPtr<ImageData> CanvasRenderingContext2D::getImageData(float sx, float sy, scaledRect.setWidth(1); if (scaledRect.height() < 1) scaledRect.setHeight(1); - ImageBuffer* buffer = canvas() ? canvas()->buffer() : 0; + ImageBuffer* buffer = canvas()->buffer(); if (!buffer) return createEmptyImageData(scaledRect.size()); return buffer->getUnmultipliedImageData(scaledRect); @@ -1630,7 +1662,7 @@ void CanvasRenderingContext2D::putImageData(ImageData* data, float dx, float dy, IntPoint destPoint(destOffset.width(), destOffset.height()); buffer->putUnmultipliedImageData(data, sourceRect, destPoint); - didDraw(sourceRect, 0); // ignore transform, shadow and clip + didDraw(sourceRect, CanvasDidDrawApplyNone); // ignore transform, shadow and clip } String CanvasRenderingContext2D::font() const @@ -1850,11 +1882,11 @@ void CanvasRenderingContext2D::drawTextInternal(const String& text, float x, flo c->drawBidiText(font, textRun, location); if (fill) - canvas()->didDraw(textRect); + didDraw(textRect); else { // When stroking text, pointy miters can extend outside of textRect, so we // punt and dirty the whole canvas. - canvas()->didDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); + didDraw(FloatRect(0, 0, canvas()->width(), canvas()->height())); } #if PLATFORM(QT) @@ -1877,4 +1909,11 @@ void CanvasRenderingContext2D::paintRenderingResultsToCanvas() #endif } +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) +PlatformLayer* CanvasRenderingContext2D::platformLayer() const +{ + return m_drawingBuffer->platformLayer(); +} +#endif + } // namespace WebCore diff --git a/WebCore/html/canvas/CanvasRenderingContext2D.h b/WebCore/html/canvas/CanvasRenderingContext2D.h index 9857344..91b6549 100644 --- a/WebCore/html/canvas/CanvasRenderingContext2D.h +++ b/WebCore/html/canvas/CanvasRenderingContext2D.h @@ -20,7 +20,7 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CanvasRenderingContext2D_h @@ -61,7 +61,8 @@ class KURL; class TextMetrics; #if ENABLE(ACCELERATED_2D_CANVAS) -class GraphicsContext3D; +class DrawingBuffer; +class SharedGraphicsContext3D; #endif typedef int ExceptionCode; @@ -74,6 +75,7 @@ public: virtual bool is2d() const { return true; } virtual bool isAccelerated() const; + virtual bool paintsIntoCanvasBuffer() const; CanvasStyle* strokeStyle() const; void setStrokeStyle(PassRefPtr<CanvasStyle>); @@ -223,8 +225,8 @@ public: virtual void paintRenderingResultsToCanvas(); -#if ENABLE(ACCELERATED_2D_CANVAS) - virtual GraphicsContext3D* graphicsContext3D() const { return m_context3D.get(); } +#if ENABLE(ACCELERATED_2D_CANVAS) && USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const; #endif private: @@ -262,14 +264,15 @@ private: void applyShadow(); - enum CanvasWillDrawOption { - CanvasWillDrawApplyTransform = 1, - CanvasWillDrawApplyShadow = 1 << 1, - CanvasWillDrawApplyClip = 1 << 2, - CanvasWillDrawApplyAll = 0xffffffff + enum CanvasDidDrawOption { + CanvasDidDrawApplyNone = 0, + CanvasDidDrawApplyTransform = 1, + CanvasDidDrawApplyShadow = 1 << 1, + CanvasDidDrawApplyClip = 1 << 2, + CanvasDidDrawApplyAll = 0xffffffff }; - void didDraw(const FloatRect&, unsigned options = CanvasWillDrawApplyAll); + void didDraw(const FloatRect&, unsigned options = CanvasDidDrawApplyAll); GraphicsContext* drawingContext() const; @@ -298,7 +301,8 @@ private: #endif #if ENABLE(ACCELERATED_2D_CANVAS) - OwnPtr<GraphicsContext3D> m_context3D; + OwnPtr<DrawingBuffer> m_drawingBuffer; + RefPtr<SharedGraphicsContext3D> m_context3D; #endif }; diff --git a/WebCore/html/canvas/TypedArrayBase.h b/WebCore/html/canvas/TypedArrayBase.h index e69c2b5..77283df 100644 --- a/WebCore/html/canvas/TypedArrayBase.h +++ b/WebCore/html/canvas/TypedArrayBase.h @@ -42,6 +42,16 @@ class TypedArrayBase : public ArrayBufferView { setImpl(array, offset * sizeof(T), ec); } + void setRange(const T* data, size_t dataLength, unsigned offset, ExceptionCode& ec) + { + setRangeImpl(reinterpret_cast<const char*>(data), dataLength * sizeof(T), offset * sizeof(T), ec); + } + + void zeroRange(unsigned offset, size_t length, ExceptionCode& ec) + { + zeroRangeImpl(offset * sizeof(T), length * sizeof(T), ec); + } + // Overridden from ArrayBufferView. This must be public because of // rules about inheritance of members in template classes, and // because it is accessed via pointers to subclasses. diff --git a/WebCore/html/canvas/WebGLRenderingContext.cpp b/WebCore/html/canvas/WebGLRenderingContext.cpp index 2a1464a..bda3569 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.cpp +++ b/WebCore/html/canvas/WebGLRenderingContext.cpp @@ -170,6 +170,11 @@ void WebGLRenderingContext::paintRenderingResultsToCanvas() m_context->paintRenderingResultsToCanvas(this); } +bool WebGLRenderingContext::paintsIntoCanvasBuffer() const +{ + return m_context->paintsIntoCanvasBuffer(); +} + void WebGLRenderingContext::reshape(int width, int height) { if (m_needsUpdate) { diff --git a/WebCore/html/canvas/WebGLRenderingContext.h b/WebCore/html/canvas/WebGLRenderingContext.h index 66ec8d8..d812c69 100644 --- a/WebCore/html/canvas/WebGLRenderingContext.h +++ b/WebCore/html/canvas/WebGLRenderingContext.h @@ -63,6 +63,7 @@ public: virtual bool is3d() const { return true; } virtual bool isAccelerated() const { return true; } + virtual bool paintsIntoCanvasBuffer() const; void activeTexture(unsigned long texture, ExceptionCode& ec); void attachShader(WebGLProgram*, WebGLShader*, ExceptionCode& ec); @@ -277,7 +278,10 @@ public: void viewport(long x, long y, unsigned long width, unsigned long height); - virtual GraphicsContext3D* graphicsContext3D() const { return m_context.get(); } + GraphicsContext3D* graphicsContext3D() const { return m_context.get(); } +#if USE(ACCELERATED_COMPOSITING) + virtual PlatformLayer* platformLayer() const { return m_context->platformLayer(); } +#endif void reshape(int width, int height); diff --git a/WebCore/html/parser/CSSPreloadScanner.cpp b/WebCore/html/parser/CSSPreloadScanner.cpp index 729103e..6ac923d 100644 --- a/WebCore/html/parser/CSSPreloadScanner.cpp +++ b/WebCore/html/parser/CSSPreloadScanner.cpp @@ -30,7 +30,7 @@ #include "CSSHelper.h" #include "CachedCSSStyleSheet.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" #include "HTMLToken.h" @@ -151,7 +151,7 @@ void CSSPreloadScanner::emitRule() String value(m_ruleValue.data(), m_ruleValue.size()); String url = deprecatedParseURL(value); if (!url.isEmpty()) - m_document->docLoader()->preload(CachedResource::CSSStyleSheet, url, String(), m_scanningBody); + m_document->cachedResourceLoader()->preload(CachedResource::CSSStyleSheet, url, String(), m_scanningBody); } m_rule.clear(); m_ruleValue.clear(); diff --git a/WebCore/html/parser/HTMLConstructionSite.cpp b/WebCore/html/parser/HTMLConstructionSite.cpp index 975b1af..0172b3d 100644 --- a/WebCore/html/parser/HTMLConstructionSite.cpp +++ b/WebCore/html/parser/HTMLConstructionSite.cpp @@ -213,7 +213,7 @@ void HTMLConstructionSite::insertDoctype(AtomicHTMLToken& token) void HTMLConstructionSite::insertComment(AtomicHTMLToken& token) { ASSERT(token.type() == HTMLToken::Comment); - attach(currentElement(), Comment::create(m_document, token.comment())); + attach(currentElement(), Comment::create(currentElement()->document(), token.comment())); } void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token) @@ -225,7 +225,8 @@ void HTMLConstructionSite::insertCommentOnDocument(AtomicHTMLToken& token) void HTMLConstructionSite::insertCommentOnHTMLHtmlElement(AtomicHTMLToken& token) { ASSERT(token.type() == HTMLToken::Comment); - attach(m_openElements.htmlElement(), Comment::create(m_document, token.comment())); + Element* parent = m_openElements.htmlElement(); + attach(parent, Comment::create(parent->document(), token.comment())); } PassRefPtr<Element> HTMLConstructionSite::attachToCurrent(PassRefPtr<Element> child) @@ -293,7 +294,7 @@ void HTMLConstructionSite::insertFormattingElement(AtomicHTMLToken& token) void HTMLConstructionSite::insertScriptElement(AtomicHTMLToken& token) { - RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, m_document, true); + RefPtr<HTMLScriptElement> element = HTMLScriptElement::create(scriptTag, currentElement()->document(), true); if (m_fragmentScriptingPermission == FragmentScriptingAllowed) element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); m_openElements.push(attachToCurrent(element.release())); @@ -326,13 +327,13 @@ void HTMLConstructionSite::insertTextNode(const String& characters) return; } - attachAtSite(site, Text::create(m_document, characters)); + attachAtSite(site, Text::create(site.parent->document(), characters)); } PassRefPtr<Element> HTMLConstructionSite::createElement(AtomicHTMLToken& token, const AtomicString& namespaceURI) { QualifiedName tagName(nullAtom, token.name(), namespaceURI); - RefPtr<Element> element = m_document->createElement(tagName, true); + RefPtr<Element> element = currentElement()->document()->createElement(tagName, true); element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); return element.release(); } @@ -343,7 +344,7 @@ PassRefPtr<Element> HTMLConstructionSite::createHTMLElement(AtomicHTMLToken& tok // FIXME: This can't use HTMLConstructionSite::createElement because we // have to pass the current form element. We should rework form association // to occur after construction to allow better code sharing here. - RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, m_document, form(), true); + RefPtr<Element> element = HTMLElementFactory::createHTMLElement(tagName, currentElement()->document(), form(), true); element->setAttributeMap(token.takeAtributes(), m_fragmentScriptingPermission); ASSERT(element->isHTMLElement()); return element.release(); diff --git a/WebCore/html/parser/HTMLDocumentParser.cpp b/WebCore/html/parser/HTMLDocumentParser.cpp index 0a1208d..a442d54 100644 --- a/WebCore/html/parser/HTMLDocumentParser.cpp +++ b/WebCore/html/parser/HTMLDocumentParser.cpp @@ -146,6 +146,32 @@ void HTMLDocumentParser::stopParsing() m_parserScheduler.clear(); // Deleting the scheduler will clear any timers. } +// This kicks off "Once the user agent stops parsing" as described by: +// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-end.html#the-end +void HTMLDocumentParser::prepareToStopParsing() +{ + ASSERT(!hasInsertionPoint()); + + // pumpTokenizer can cause this parser to be detached from the Document, + // but we need to ensure it isn't deleted yet. + RefPtr<HTMLDocumentParser> protect(this); + + // NOTE: This pump should only ever emit buffered character tokens, + // so ForceSynchronous vs. AllowYield should be meaningless. + pumpTokenizerIfPossible(ForceSynchronous); + + if (isStopped()) + return; + + DocumentParser::prepareToStopParsing(); + + // We will not have a scriptRunner when parsing a DocumentFragment. + if (m_scriptRunner) + document()->setReadyState(Document::Interactive); + + attemptToRunDeferredScriptsAndEnd(); +} + bool HTMLDocumentParser::processingData() const { return isScheduledForResume() || inWrite(); @@ -153,7 +179,7 @@ bool HTMLDocumentParser::processingData() const void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) { - if (m_parserStopped || m_treeBuilder->isPaused()) + if (isStopped() || m_treeBuilder->isPaused()) return; // Once a resume is scheduled, HTMLParserScheduler controls when we next pump. @@ -197,8 +223,7 @@ bool HTMLDocumentParser::runScriptsForPausedTreeBuilder() void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) { - ASSERT(!isDetached()); - ASSERT(!m_parserStopped); + ASSERT(!isStopped()); ASSERT(!m_treeBuilder->isPaused()); ASSERT(!isScheduledForResume()); // ASSERT that this object is both attached to the Document and protected. @@ -218,7 +243,7 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) m_token.clear(); // JavaScript may have stopped or detached the parser. - if (isDetached() || m_parserStopped) + if (isStopped()) return; // The parser will pause itself when waiting on a script to load or run. @@ -230,7 +255,7 @@ void HTMLDocumentParser::pumpTokenizer(SynchronousMode mode) m_treeBuilder->setPaused(!shouldContinueParsing); // JavaScript may have stopped or detached the parser. - if (isDetached() || m_parserStopped) + if (isStopped()) return; if (!shouldContinueParsing) @@ -279,7 +304,7 @@ bool HTMLDocumentParser::hasInsertionPoint() void HTMLDocumentParser::insert(const SegmentedString& source) { - if (m_parserStopped) + if (isStopped()) return; #ifdef ANDROID_INSTRUMENT @@ -304,7 +329,7 @@ void HTMLDocumentParser::insert(const SegmentedString& source) void HTMLDocumentParser::append(const SegmentedString& source) { - if (m_parserStopped) + if (isStopped()) return; // pumpTokenizer can cause this parser to be detached from the Document, @@ -342,18 +367,19 @@ void HTMLDocumentParser::end() ASSERT(!isDetached()); ASSERT(!isScheduledForResume()); - // pumpTokenizer can cause this parser to be detached from the Document, - // but we need to ensure it isn't deleted yet. - RefPtr<HTMLDocumentParser> protect(this); - - // NOTE: This pump should only ever emit buffered character tokens, - // so ForceSynchronous vs. AllowYield should be meaningless. - pumpTokenizerIfPossible(ForceSynchronous); - // Informs the the rest of WebCore that parsing is really finished (and deletes this). m_treeBuilder->finished(); } +void HTMLDocumentParser::attemptToRunDeferredScriptsAndEnd() +{ + ASSERT(isStopping()); + ASSERT(!hasInsertionPoint()); + if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing()) + return; + end(); +} + void HTMLDocumentParser::attemptToEnd() { // finish() indicates we will not receive any more data. If we are waiting on @@ -363,7 +389,7 @@ void HTMLDocumentParser::attemptToEnd() m_endWasDelayed = true; return; } - end(); + prepareToStopParsing(); } void HTMLDocumentParser::endIfDelayed() @@ -376,7 +402,7 @@ void HTMLDocumentParser::endIfDelayed() return; m_endWasDelayed = false; - end(); + prepareToStopParsing(); } void HTMLDocumentParser::finish() @@ -467,6 +493,11 @@ void HTMLDocumentParser::notifyFinished(CachedResource* cachedResource) ASSERT(m_scriptRunner); ASSERT(!inScriptExecution()); + if (isStopping()) { + attemptToRunDeferredScriptsAndEnd(); + return; + } + ASSERT(m_treeBuilder->isPaused()); // Note: We only ever wait on one script at a time, so we always know this // is the one we were waiting on and can un-pause the tree builder. diff --git a/WebCore/html/parser/HTMLDocumentParser.h b/WebCore/html/parser/HTMLDocumentParser.h index da21a2b..6d5b6d7 100644 --- a/WebCore/html/parser/HTMLDocumentParser.h +++ b/WebCore/html/parser/HTMLDocumentParser.h @@ -69,18 +69,22 @@ public: protected: virtual void insert(const SegmentedString&); + virtual void append(const SegmentedString&); virtual void finish(); HTMLDocumentParser(HTMLDocument*, bool reportErrors); HTMLDocumentParser(DocumentFragment*, Element* contextElement, FragmentScriptingPermission); + HTMLTokenizer* tokenizer() const { return m_tokenizer.get(); } + HTMLTreeBuilder* treeBuilder() const { return m_treeBuilder.get(); } + private: // DocumentParser virtual void detach(); virtual bool hasInsertionPoint(); - virtual void append(const SegmentedString&); virtual bool finishWasCalled(); virtual bool processingData() const; + virtual void prepareToStopParsing(); virtual void stopParsing(); virtual bool isWaitingForScripts() const; virtual bool isExecutingScript() const; @@ -113,6 +117,7 @@ private: void begin(); void attemptToEnd(); void endIfDelayed(); + void attemptToRunDeferredScriptsAndEnd(); void end(); bool isScheduledForResume() const; diff --git a/WebCore/html/HTMLInputStream.h b/WebCore/html/parser/HTMLInputStream.h index a709bd9..a709bd9 100644 --- a/WebCore/html/HTMLInputStream.h +++ b/WebCore/html/parser/HTMLInputStream.h diff --git a/WebCore/html/parser/HTMLPreloadScanner.cpp b/WebCore/html/parser/HTMLPreloadScanner.cpp index 7aafd90..5283fa3 100644 --- a/WebCore/html/parser/HTMLPreloadScanner.cpp +++ b/WebCore/html/parser/HTMLPreloadScanner.cpp @@ -29,7 +29,7 @@ #include "HTMLPreloadScanner.h" #include "CSSHelper.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Document.h" #include "HTMLTokenizer.h" #include "HTMLTreeBuilder.h" @@ -98,13 +98,13 @@ public: if (m_urlToLoad.isEmpty()) return; - DocLoader* docLoader = document->docLoader(); + CachedResourceLoader* cachedResourceLoader = document->cachedResourceLoader(); if (m_tagName == scriptTag) - docLoader->preload(CachedResource::Script, m_urlToLoad, m_charset, scanningBody); + cachedResourceLoader->preload(CachedResource::Script, m_urlToLoad, m_charset, scanningBody); else if (m_tagName == imgTag) - docLoader->preload(CachedResource::ImageResource, m_urlToLoad, String(), scanningBody); + cachedResourceLoader->preload(CachedResource::ImageResource, m_urlToLoad, String(), scanningBody); else if (m_tagName == linkTag && m_linkIsStyleSheet) - docLoader->preload(CachedResource::CSSStyleSheet, m_urlToLoad, m_charset, scanningBody); + cachedResourceLoader->preload(CachedResource::CSSStyleSheet, m_urlToLoad, m_charset, scanningBody); } const AtomicString& tagName() const { return m_tagName; } diff --git a/WebCore/html/parser/HTMLScriptRunner.cpp b/WebCore/html/parser/HTMLScriptRunner.cpp index 6d470a0..e1fc120 100644 --- a/WebCore/html/parser/HTMLScriptRunner.cpp +++ b/WebCore/html/parser/HTMLScriptRunner.cpp @@ -28,7 +28,7 @@ #include "Attribute.h" #include "CachedScript.h" -#include "DocLoader.h" +#include "CachedResourceLoader.h" #include "Element.h" #include "Event.h" #include "Frame.h" @@ -75,6 +75,12 @@ HTMLScriptRunner::~HTMLScriptRunner() // FIXME: Should we be passed a "done loading/parsing" callback sooner than destruction? if (m_parsingBlockingScript.cachedScript() && m_parsingBlockingScript.watchingForLoad()) stopWatchingForLoad(m_parsingBlockingScript); + + while (!m_scriptsToExecuteAfterParsing.isEmpty()) { + PendingScript pendingScript = m_scriptsToExecuteAfterParsing.takeFirst(); + if (pendingScript.cachedScript() && pendingScript.watchingForLoad()) + stopWatchingForLoad(pendingScript); + } } void HTMLScriptRunner::detach() @@ -129,10 +135,6 @@ void HTMLScriptRunner::executeParsingBlockingScript() ASSERT(m_document->haveStylesheetsLoaded()); ASSERT(isPendingScriptReady(m_parsingBlockingScript)); - // 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); } @@ -142,6 +144,10 @@ void HTMLScriptRunner::executePendingScriptAndDispatchEvent(PendingScript& pendi bool errorOccurred = false; ScriptSourceCode sourceCode = sourceFromPendingScript(pendingScript, errorOccurred); + // Stop watching loads before executeScript to prevent recursion if the script reloads itself. + if (pendingScript.cachedScript() && pendingScript.watchingForLoad()) + stopWatchingForLoad(pendingScript); + // Clear the pending script before possible rentrancy from executeScript() RefPtr<Element> scriptElement = pendingScript.releaseElementAndClear(); { @@ -238,6 +244,24 @@ bool HTMLScriptRunner::executeScriptsWaitingForStylesheets() return executeParsingBlockingScripts(); } +bool HTMLScriptRunner::executeScriptsWaitingForParsing() +{ + while (!m_scriptsToExecuteAfterParsing.isEmpty()) { + ASSERT(!m_scriptNestingLevel); + ASSERT(!haveParsingBlockingScript()); + ASSERT(m_scriptsToExecuteAfterParsing.first().cachedScript()); + if (!m_scriptsToExecuteAfterParsing.first().cachedScript()->isLoaded()) { + watchForLoad(m_scriptsToExecuteAfterParsing.first()); + return false; + } + PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst(); + executePendingScriptAndDispatchEvent(first); + if (!m_document) + return false; + } + return true; +} + void HTMLScriptRunner::requestParsingBlockingScript(Element* element) { if (!requestPendingScript(m_parsingBlockingScript, element)) @@ -252,6 +276,16 @@ void HTMLScriptRunner::requestParsingBlockingScript(Element* element) watchForLoad(m_parsingBlockingScript); } +void HTMLScriptRunner::requestDeferredScript(Element* element) +{ + PendingScript pendingScript; + if (!requestPendingScript(pendingScript, element)) + return; + + ASSERT(pendingScript.cachedScript()); + m_scriptsToExecuteAfterParsing.append(pendingScript); +} + bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Element* script) const { ASSERT(!pendingScript.element()); @@ -264,7 +298,7 @@ bool HTMLScriptRunner::requestPendingScript(PendingScript& pendingScript, Elemen 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()); + CachedScript* cachedScript = m_document->cachedResourceLoader()->requestScript(srcValue, toScriptElement(script)->scriptCharset()); if (!cachedScript) { notImplemented(); // Dispatch error event. return false; @@ -287,8 +321,13 @@ void HTMLScriptRunner::runScript(Element* script, int startingLineNumber) notImplemented(); // event for support if (script->hasAttribute(srcAttr)) { - // FIXME: Handle defer and async - requestParsingBlockingScript(script); + if (script->hasAttribute(asyncAttr)) // Async takes precendence over defer. + return; // Asynchronous scripts handle themselves. + + if (script->hasAttribute(deferAttr)) + requestDeferredScript(script); + else + requestParsingBlockingScript(script); } else { // FIXME: We do not block inline <script> tags on stylesheets to match the // old parser for now. When we do, the ASSERT below should be added. diff --git a/WebCore/html/parser/HTMLScriptRunner.h b/WebCore/html/parser/HTMLScriptRunner.h index ead9289..47c96fd 100644 --- a/WebCore/html/parser/HTMLScriptRunner.h +++ b/WebCore/html/parser/HTMLScriptRunner.h @@ -27,6 +27,7 @@ #define HTMLScriptRunner_h #include "PendingScript.h" +#include <wtf/Deque.h> #include <wtf/Noncopyable.h> #include <wtf/PassRefPtr.h> @@ -56,6 +57,7 @@ public: bool executeScriptsWaitingForLoad(CachedResource*); bool hasScriptsWaitingForStylesheets() const { return m_hasScriptsWaitingForStylesheets; } bool executeScriptsWaitingForStylesheets(); + bool executeScriptsWaitingForParsing(); bool isExecutingScript() const { return !!m_scriptNestingLevel; } @@ -71,6 +73,7 @@ private: bool executeParsingBlockingScripts(); void requestParsingBlockingScript(Element*); + void requestDeferredScript(Element*); bool requestPendingScript(PendingScript&, Element*) const; void runScript(Element*, int startingLineNumber); @@ -84,6 +87,7 @@ private: Document* m_document; HTMLScriptRunnerHost* m_host; PendingScript m_parsingBlockingScript; + Deque<PendingScript> m_scriptsToExecuteAfterParsing; // http://www.whatwg.org/specs/web-apps/current-work/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing unsigned m_scriptNestingLevel; // We only want stylesheet loads to trigger script execution if script diff --git a/WebCore/html/parser/HTMLTokenizer.cpp b/WebCore/html/parser/HTMLTokenizer.cpp index 5791842..f5405ff 100644 --- a/WebCore/html/parser/HTMLTokenizer.cpp +++ b/WebCore/html/parser/HTMLTokenizer.cpp @@ -293,7 +293,9 @@ bool HTMLTokenizer::nextToken(SegmentedString& source, HTMLToken& token) ADVANCE_TO(DataState); if (m_state == RCDATAState) ADVANCE_TO(RCDATAState); - ASSERT_NOT_REACHED(); + // When parsing text/plain documents, we run the tokenizer in the + // PLAINTEXTState and ignore m_skipLeadingNewLineForListing. + ASSERT(m_state == PLAINTEXTState); } } diff --git a/WebCore/html/parser/HTMLTreeBuilder.cpp b/WebCore/html/parser/HTMLTreeBuilder.cpp index 8c76fc0..406bb6c 100644 --- a/WebCore/html/parser/HTMLTreeBuilder.cpp +++ b/WebCore/html/parser/HTMLTreeBuilder.cpp @@ -463,6 +463,11 @@ HTMLTokenizer::State HTMLTreeBuilder::adjustedLexerState(HTMLTokenizer::State st void HTMLTreeBuilder::constructTreeFromToken(HTMLToken& rawToken) { AtomicHTMLToken token(rawToken); + constructTreeFromAtomicToken(token); +} + +void HTMLTreeBuilder::constructTreeFromAtomicToken(AtomicHTMLToken& token) +{ processToken(token); // Swallowing U+0000 characters isn't in the HTML5 spec, but turning all diff --git a/WebCore/html/parser/HTMLTreeBuilder.h b/WebCore/html/parser/HTMLTreeBuilder.h index c30e6b8..4634f0a 100644 --- a/WebCore/html/parser/HTMLTreeBuilder.h +++ b/WebCore/html/parser/HTMLTreeBuilder.h @@ -68,6 +68,8 @@ public: // The token really should be passed as a const& since it's never modified. void constructTreeFromToken(HTMLToken&); + void constructTreeFromAtomicToken(AtomicHTMLToken&); + // Must be called when parser is paused before calling the parser again. PassRefPtr<Element> takeScriptToProcess(int& scriptStartLine); diff --git a/WebCore/html/parser/HTMLViewSourceParser.h b/WebCore/html/parser/HTMLViewSourceParser.h index 34caf43..abe55b4 100644 --- a/WebCore/html/parser/HTMLViewSourceParser.h +++ b/WebCore/html/parser/HTMLViewSourceParser.h @@ -50,9 +50,12 @@ public: } virtual ~HTMLViewSourceParser(); -private: - HTMLViewSourceParser(HTMLViewSourceDocument*); +protected: + explicit HTMLViewSourceParser(HTMLViewSourceDocument*); + + HTMLTokenizer* tokenizer() const { return m_tokenizer.get(); } +private: // DocumentParser virtual void insert(const SegmentedString&); virtual void append(const SegmentedString&); diff --git a/WebCore/html/parser/TextDocumentParser.cpp b/WebCore/html/parser/TextDocumentParser.cpp new file mode 100644 index 0000000..d03b744 --- /dev/null +++ b/WebCore/html/parser/TextDocumentParser.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextDocumentParser.h" + +#include "HTMLDocument.h" +#include "HTMLNames.h" +#include "HTMLTokenizer.h" +#include "HTMLTreeBuilder.h" + +namespace WebCore { + +using namespace HTMLNames; + +TextDocumentParser::TextDocumentParser(HTMLDocument* document) + : HTMLDocumentParser(document, false) + , m_haveInsertedFakePreElement(false) +{ + tokenizer()->setState(HTMLTokenizer::PLAINTEXTState); +} + +TextDocumentParser::~TextDocumentParser() +{ +} + +void TextDocumentParser::append(const SegmentedString& text) +{ + if (!m_haveInsertedFakePreElement) + insertFakePreElement(); + HTMLDocumentParser::append(text); +} + +void TextDocumentParser::insertFakePreElement() +{ + // In principle, we should create a specialized tree builder for + // TextDocuments, but instead we re-use the existing HTMLTreeBuilder. + // We create a fake token and give it to the tree builder rather than + // sending fake bytes through the front-end of the parser to avoid + // distrubing the line/column number calculations. + + RefPtr<Attribute> styleAttribute = Attribute::createMapped("style", "word-wrap: break-word; white-space: pre-wrap;"); + RefPtr<NamedNodeMap> attributes = NamedNodeMap::create(); + attributes->insertAttribute(styleAttribute.release(), false); + AtomicHTMLToken fakePre(HTMLToken::StartTag, preTag.localName(), attributes.release()); + + treeBuilder()->constructTreeFromAtomicToken(fakePre); + m_haveInsertedFakePreElement = true; +} + +} diff --git a/WebCore/html/parser/TextDocumentParser.h b/WebCore/html/parser/TextDocumentParser.h new file mode 100644 index 0000000..1cccc5b --- /dev/null +++ b/WebCore/html/parser/TextDocumentParser.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef TextDocumentParser_h +#define TextDocumentParser_h + +#include "HTMLDocumentParser.h" + +namespace WebCore { + +class TextDocumentParser : public HTMLDocumentParser { +public: + static PassRefPtr<TextDocumentParser> create(HTMLDocument* document) + { + return adoptRef(new TextDocumentParser(document)); + } + virtual ~TextDocumentParser(); + +private: + explicit TextDocumentParser(HTMLDocument*); + + virtual void append(const SegmentedString&); + void insertFakePreElement(); + + bool m_haveInsertedFakePreElement; +}; + +} + +#endif diff --git a/WebCore/html/parser/TextViewSourceParser.cpp b/WebCore/html/parser/TextViewSourceParser.cpp new file mode 100644 index 0000000..d7e6e3d --- /dev/null +++ b/WebCore/html/parser/TextViewSourceParser.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextViewSourceParser.h" + +#include "HTMLTokenizer.h" + +namespace WebCore { + +TextViewSourceParser::TextViewSourceParser(HTMLViewSourceDocument* document) + : HTMLViewSourceParser(document) +{ + tokenizer()->setState(HTMLTokenizer::PLAINTEXTState); +} + +TextViewSourceParser::~TextViewSourceParser() +{ +} + +} diff --git a/WebCore/html/parser/TextViewSourceParser.h b/WebCore/html/parser/TextViewSourceParser.h new file mode 100644 index 0000000..e4170ed --- /dev/null +++ b/WebCore/html/parser/TextViewSourceParser.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextViewSourceParser_h +#define TextViewSourceParser_h + +#include "HTMLViewSourceParser.h" + +namespace WebCore { + +class TextViewSourceParser : public HTMLViewSourceParser { +public: + static PassRefPtr<TextViewSourceParser> create(HTMLViewSourceDocument* document) + { + return adoptRef(new TextViewSourceParser(document)); + } + virtual ~TextViewSourceParser(); + +private: + explicit TextViewSourceParser(HTMLViewSourceDocument*); +}; + +} + +#endif |