From 5abb8606fa57c3ebfc8b3c3dbc3fa4a25d2ae306 Mon Sep 17 00:00:00 2001 From: Iain Merrick Date: Mon, 13 Sep 2010 16:35:48 +0100 Subject: Merge WebKit at r67178 : Initial merge by git. Change-Id: I57e01163b6866cb029cdadf405a0394a3918bc18 --- WebCore/html/FTPDirectoryDocument.cpp | 451 +++++++++++++++++++++++ WebCore/html/FTPDirectoryDocument.h | 48 +++ WebCore/html/HTMLEmbedElement.cpp | 46 +++ WebCore/html/HTMLEmbedElement.h | 6 +- WebCore/html/HTMLImageElement.cpp | 18 +- WebCore/html/HTMLInputElement.cpp | 28 +- WebCore/html/HTMLInputElement.h | 2 + WebCore/html/HTMLInputStream.h | 151 -------- WebCore/html/HTMLLinkElement.cpp | 6 +- WebCore/html/HTMLMediaElement.cpp | 263 +++++++++++-- WebCore/html/HTMLMediaElement.h | 14 +- WebCore/html/HTMLObjectElement.cpp | 168 +++++++++ WebCore/html/HTMLObjectElement.h | 10 +- WebCore/html/HTMLPlugInImageElement.cpp | 58 ++- WebCore/html/HTMLPlugInImageElement.h | 20 +- WebCore/html/HTMLSelectElement.cpp | 11 +- WebCore/html/HTMLSelectElement.h | 2 +- WebCore/html/HTMLViewSourceDocument.cpp | 12 +- WebCore/html/HTMLViewSourceDocument.h | 3 - WebCore/html/ImageDocument.cpp | 414 +++++++++++++++++++++ WebCore/html/ImageDocument.h | 76 ++++ WebCore/html/MediaDocument.cpp | 215 +++++++++++ WebCore/html/MediaDocument.h | 61 +++ WebCore/html/PluginDocument.cpp | 161 ++++++++ WebCore/html/PluginDocument.h | 54 +++ WebCore/html/TextDocument.cpp | 44 +++ WebCore/html/TextDocument.h | 47 +++ WebCore/html/canvas/ArrayBufferView.cpp | 28 ++ WebCore/html/canvas/ArrayBufferView.h | 4 + WebCore/html/canvas/CanvasRenderingContext.cpp | 12 - WebCore/html/canvas/CanvasRenderingContext.h | 13 +- WebCore/html/canvas/CanvasRenderingContext2D.cpp | 91 +++-- WebCore/html/canvas/CanvasRenderingContext2D.h | 26 +- WebCore/html/canvas/TypedArrayBase.h | 10 + WebCore/html/canvas/WebGLRenderingContext.cpp | 5 + WebCore/html/canvas/WebGLRenderingContext.h | 6 +- WebCore/html/parser/CSSPreloadScanner.cpp | 4 +- WebCore/html/parser/HTMLConstructionSite.cpp | 13 +- WebCore/html/parser/HTMLDocumentParser.cpp | 65 +++- WebCore/html/parser/HTMLDocumentParser.h | 7 +- WebCore/html/parser/HTMLInputStream.h | 151 ++++++++ WebCore/html/parser/HTMLPreloadScanner.cpp | 10 +- WebCore/html/parser/HTMLScriptRunner.cpp | 55 ++- WebCore/html/parser/HTMLScriptRunner.h | 4 + WebCore/html/parser/HTMLTokenizer.cpp | 4 +- WebCore/html/parser/HTMLTreeBuilder.cpp | 5 + WebCore/html/parser/HTMLTreeBuilder.h | 2 + WebCore/html/parser/HTMLViewSourceParser.h | 7 +- WebCore/html/parser/TextDocumentParser.cpp | 72 ++++ WebCore/html/parser/TextDocumentParser.h | 52 +++ WebCore/html/parser/TextViewSourceParser.cpp | 43 +++ WebCore/html/parser/TextViewSourceParser.h | 47 +++ 52 files changed, 2805 insertions(+), 320 deletions(-) create mode 100644 WebCore/html/FTPDirectoryDocument.cpp create mode 100644 WebCore/html/FTPDirectoryDocument.h delete mode 100644 WebCore/html/HTMLInputStream.h create mode 100644 WebCore/html/ImageDocument.cpp create mode 100644 WebCore/html/ImageDocument.h create mode 100644 WebCore/html/MediaDocument.cpp create mode 100644 WebCore/html/MediaDocument.h create mode 100644 WebCore/html/PluginDocument.cpp create mode 100644 WebCore/html/PluginDocument.h create mode 100644 WebCore/html/TextDocument.cpp create mode 100644 WebCore/html/TextDocument.h mode change 100644 => 100755 WebCore/html/canvas/CanvasRenderingContext2D.cpp create mode 100644 WebCore/html/parser/HTMLInputStream.h create mode 100644 WebCore/html/parser/TextDocumentParser.cpp create mode 100644 WebCore/html/parser/TextDocumentParser.h create mode 100644 WebCore/html/parser/TextViewSourceParser.cpp create mode 100644 WebCore/html/parser/TextViewSourceParser.h (limited to 'WebCore/html') 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 +#include +#include + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +class FTPDirectoryDocumentParser : public HTMLDocumentParser { +public: + static PassRefPtr 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(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 createTDForFilename(const String&); + + RefPtr 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(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 rowElement = m_tableElement->insertRow(-1, ec); + rowElement->setAttribute("class", "ftpDirectoryEntryRow", ec); + + RefPtr 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 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 anchorElement = document()->createElement(aTag, false); + anchorElement->setAttribute("href", fullURL, ec); + anchorElement->appendChild(Text::create(document(), filename), ec); + + RefPtr 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(bytes)/1000); + + if (bytes < 1000000000) + return String::format("%.2f MB", static_cast(bytes)/1000000); + + return String::format("%.2f GB", static_cast(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 createTemplateDocumentData(Settings* settings) +{ + RefPtr 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, 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 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(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(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 bodyElement = document()->createElement(bodyTag, false); + + ExceptionCode ec; + document()->appendChild(bodyElement, ec); + + RefPtr tableElement = document()->createElement(tableTag, false); + m_tableElement = static_cast(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 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 create(Frame* frame, const KURL& url) + { + return adoptRef(new FTPDirectoryDocument(frame, url)); + } + +private: + FTPDirectoryDocument(Frame*, const KURL&); + virtual PassRefPtr 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& paramNames, Vector& 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 + // 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 paramNames; + Vector 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 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&) const; + + void parametersForPlugin(Vector& paramNames, Vector& 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(evt))) { + && document()->frame()->editor()->doTextFieldCommandFromEvent(this, static_cast(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/HTMLInputStream.h b/WebCore/html/HTMLInputStream.h deleted file mode 100644 index a709bd9..0000000 --- a/WebCore/html/HTMLInputStream.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * 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 HTMLInputStream_h -#define HTMLInputStream_h - -#include "SegmentedString.h" - -namespace WebCore { - -// The InputStream is made up of a sequence of SegmentedStrings: -// -// [--current--][--next--][--next--] ... [--next--] -// /\ (also called m_last) -// L_ current insertion point -// -// The current segmented string is stored in InputStream. Each of the -// afterInsertionPoint buffers are stored in InsertionPointRecords on the -// stack. -// -// We remove characters from the "current" string in the InputStream. -// document.write() will add characters at the current insertion point, -// which appends them to the "current" string. -// -// m_last is a pointer to the last of the afterInsertionPoint strings. -// The network adds data at the end of the InputStream, which appends -// them to the "last" string. -class HTMLInputStream : public Noncopyable { -public: - HTMLInputStream() - : m_last(&m_first) - { - } - - void appendToEnd(const SegmentedString& string) - { - m_last->append(string); - } - - void insertAtCurrentInsertionPoint(const SegmentedString& string) - { - m_first.append(string); - } - - bool hasInsertionPoint() const - { - if (&m_first != m_last) - return true; - if (!haveSeenEndOfFile()) { - // FIXME: Somehow we need to understand the difference between - // input streams that are coming off the network and streams that - // were created with document.open(). In the later case, we always - // have an isertion point at the end of the stream until someone - // calls document.close(). - return true; - } - return false; - } - - void markEndOfFile() - { - // FIXME: This should use InputStreamPreprocessor::endOfFileMarker - // once InputStreamPreprocessor is split off into its own header. - static const UChar endOfFileMarker = 0; - m_last->append(SegmentedString(String(&endOfFileMarker, 1))); - m_last->close(); - } - - bool haveSeenEndOfFile() const - { - return m_last->isClosed(); - } - - SegmentedString& current() { return m_first; } - - void splitInto(SegmentedString& next) - { - next = m_first; - m_first = SegmentedString(); - if (m_last == &m_first) { - // We used to only have one SegmentedString in the InputStream - // but now we have two. That means m_first is no longer also - // the m_last string, |next| is now the last one. - m_last = &next; - } - } - - void mergeFrom(SegmentedString& next) - { - m_first.append(next); - if (m_last == &next) { - // The string |next| used to be the last SegmentedString in - // the InputStream. Now that it's been merged into m_first, - // that makes m_first the last one. - m_last = &m_first; - } - if (next.isClosed()) { - // We also need to merge the "closed" state from next to - // m_first. Arguably, this work could be done in append(). - m_first.close(); - } - } - -private: - SegmentedString m_first; - SegmentedString* m_last; -}; - -class InsertionPointRecord : public Noncopyable { -public: - explicit InsertionPointRecord(HTMLInputStream& inputStream) - : m_inputStream(&inputStream) - { - m_inputStream->splitInto(m_next); - } - - ~InsertionPointRecord() - { - m_inputStream->mergeFrom(m_next); - } - -private: - HTMLInputStream* m_inputStream; - SegmentedString m_next; -}; - -} - -#endif 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*) 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 err) { + LOG(Media, "HTMLMediaElement::mediaEngineError(%d)", static_cast(err->code())); + // 1 - The user agent should cancel the fetching process. stopPeriodicTimers(); m_loadState = WaitingForSource; @@ -794,6 +849,8 @@ void HTMLMediaElement::mediaEngineError(PassRefPtr 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(state), static_cast(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) // 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 "); scheduleNextSourceChild(); - else + } else { + LOG(Media, "HTMLMediaElement::setNetworkState no more 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(state), static_cast(m_readyState)); + // Set "wasPotentiallyPlaying" BEFORE updating m_readyState, potentiallyPlaying() uses it bool wasPotentiallyPlaying = potentiallyPlaying(); @@ -1004,18 +1068,22 @@ void HTMLMediaElement::progressEventTimerFired(Timer*) 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 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 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 paramNames; Vector paramValues; @@ -2016,10 +2209,26 @@ void HTMLMediaElement::createMediaPlayerProxy() if (m_proxyWidget) m_needWidgetUpdate = false; } + +void HTMLMediaElement::updateWidget(bool) +{ + mediaElement->setNeedWidgetUpdate(false); + + Vector paramNames; + Vector 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& names, Vector& 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 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* paramNames, Vector* 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& paramNames, Vector& paramValues, String& url, String& serviceType) +{ + HashSet 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(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 . 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 element into arrays, but don't override 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 tags, any other content is fallback content. + if (child->isTextNode()) { + if (!static_cast(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 paramNames; + Vector 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 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&) 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 and . + void parametersForPlugin(Vector& paramNames, Vector& 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 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 uses depending on 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(n)->updateWidget(); + static_cast(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 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 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 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 create(ImageDocument* document) { return adoptRef(new ImageEventListener(document)); } + static const ImageEventListener* cast(const EventListener* listener) + { + return listener->type() == ImageEventListenerType + ? static_cast(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 create(ImageDocument* document) + { + return adoptRef(new ImageDocumentParser(document)); + } + + ImageDocument* document() const + { + return static_cast(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 create(ImageDocument*); + +private: + ImageDocumentElement(ImageDocument* document) + : HTMLImageElement(imgTag, document) + , m_imageDocument(document) + { + } + + virtual ~ImageDocumentElement(); + virtual void willMoveToNewOwnerDocument(); + + ImageDocument* m_imageDocument; +}; + +inline PassRefPtr 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 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 ImageDocument::createParser() +{ + return ImageDocumentParser::create(this); +} + +void ImageDocument::createDocumentStructure() +{ + ExceptionCode ec; + + RefPtr rootElement = Document::createElement(htmlTag, false); + appendChild(rootElement, ec); + + if (frame() && frame()->loader()) + frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr body = Document::createElement(bodyTag, false); + body->setAttribute(styleAttr, "margin: 0px;"); + + rootElement->appendChild(body, ec); + + RefPtr 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 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(imageSize.width() * scale)); + m_imageElement->setHeight(static_cast(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(x / scale - (float)frame()->view()->width() / 2); + int scrollY = static_cast(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(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 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 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 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 rootElement = document()->createElement(htmlTag, false); + document()->appendChild(rootElement, ec); + + if (document()->frame()) + document()->frame()->loader()->dispatchDocumentElementAvailable(); + + RefPtr body = document()->createElement(bodyTag, false); + body->setAttribute(styleAttr, "background-color: rgb(38,38,38);"); + + rootElement->appendChild(body, ec); + + RefPtr mediaElement = document()->createElement(videoTag, false); + + m_mediaElement = static_cast(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 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(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(targetNode); + else { + RefPtr nodeList = targetNode->getElementsByTagName("video"); + if (nodeList.get()->length() > 0) + video = static_cast(nodeList.get()->item(0)); + } + } + if (video) { + KeyboardEvent* keyboardEvent = static_cast(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