diff options
Diffstat (limited to 'WebCore/platform')
331 files changed, 14382 insertions, 5022 deletions
diff --git a/WebCore/platform/ContentType.cpp b/WebCore/platform/ContentType.cpp new file mode 100644 index 0000000..3dce7b5 --- /dev/null +++ b/WebCore/platform/ContentType.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/) + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ContentType.h" + +namespace WebCore { + +ContentType::ContentType(const String& contentType) + : m_type(contentType) +{ +} + +String ContentType::parameter(const String& parameterName) const +{ + String parameterValue; + String strippedType = m_type.stripWhiteSpace(); + + // a MIME type can have one or more "param=value" after a semi-colon, and separated from each other by semi-colons + int semi = strippedType.find(';'); + if (semi != -1) { + int start = strippedType.find(parameterName, semi + 1, false); + if (start != -1) { + start = strippedType.find('=', start + 6); + if (start != -1) { + int end = strippedType.find(';', start + 6); + if (end == -1) + end = strippedType.length(); + parameterValue = strippedType.substring(start + 1, end - (start + 1)).stripWhiteSpace(); + } + } + } + + return parameterValue; +} + +String ContentType::type() const +{ + String strippedType = m_type.stripWhiteSpace(); + + // "type" can have parameters after a semi-colon, strip them + int semi = strippedType.find(';'); + if (semi != -1) + strippedType = strippedType.left(semi).stripWhiteSpace(); + + return strippedType; +} + +} // namespace WebCore diff --git a/WebCore/platform/text/TextDecoder.h b/WebCore/platform/ContentType.h index 171cb59..bf8fc4c 100644 --- a/WebCore/platform/text/TextDecoder.h +++ b/WebCore/platform/ContentType.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -20,45 +21,27 @@ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TextDecoder_h -#define TextDecoder_h +#ifndef ContentType_h +#define ContentType_h #include "PlatformString.h" -#include "TextCodec.h" -#include "TextEncoding.h" -#include <wtf/OwnPtr.h> namespace WebCore { - class TextCodec; - - class TextDecoder { + class ContentType { public: - TextDecoder(const TextEncoding&); - void reset(const TextEncoding&); - const TextEncoding& encoding() const { return m_encoding; }; - - String decode(const char* data, size_t length, bool flush, bool stopOnError, bool& sawError) - { - if (!m_checkedForBOM) - return checkForBOM(data, length, flush, stopOnError, sawError); - return m_codec->decode(data, length, flush, stopOnError, sawError); - } + ContentType(const String& type); + String parameter (const String& parameterName) const; + String type() const; + const String& raw() const { return m_type; } private: - String checkForBOM(const char*, size_t length, bool flush, bool stopOnError, bool& sawError); - - TextEncoding m_encoding; - OwnPtr<TextCodec> m_codec; - - bool m_checkedForBOM; - unsigned char m_numBufferedBytes; - unsigned char m_bufferedBytes[3]; + String m_type; }; } // namespace WebCore -#endif // TextDecoder_h +#endif // ContentType_h diff --git a/WebCore/platform/ContextMenu.cpp b/WebCore/platform/ContextMenu.cpp index f086b63..f66a891 100644 --- a/WebCore/platform/ContextMenu.cpp +++ b/WebCore/platform/ContextMenu.cpp @@ -192,7 +192,7 @@ static void createAndAppendTextDirectionSubMenu(const HitTestResult& result, Con static bool selectionContainsPossibleWord(Frame* frame) { // Current algorithm: look for a character that's not just a separator. - for (TextIterator it(frame->selection()->toRange().get()); !it.atEnd(); it.advance()) { + for (TextIterator it(frame->selection()->toNormalizedRange().get()); !it.atEnd(); it.advance()) { int length = it.length(); const UChar* characters = it.characters(); for (int i = 0; i < length; ++i) @@ -252,7 +252,7 @@ void ContextMenu::populate() if (!node) return; #if PLATFORM(GTK) - if (!result.isContentEditable() && node->isControl()) + if (!result.isContentEditable() && (node->isElementNode() && static_cast<Element*>(node)->isFormControlElement())) return; #endif Frame* frame = node->document()->frame(); @@ -328,12 +328,10 @@ void ContextMenu::populate() if (!inPasswordField) { // Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range // is never considered a misspelling and bad grammar at the same time) - bool misspelling = frame->editor()->isSelectionMisspelled(); - bool badGrammar = !misspelling && (frame->editor()->isGrammarCheckingEnabled() && frame->editor()->isSelectionUngrammatical()); - + bool misspelling; + bool badGrammar; + Vector<String> guesses = frame->editor()->guessesForMisspelledOrUngrammaticalSelection(misspelling, badGrammar); if (misspelling || badGrammar) { - Vector<String> guesses = misspelling ? frame->editor()->guessesForMisspelledSelection() - : frame->editor()->guessesForUngrammaticalSelection(); size_t size = guesses.size(); if (size == 0) { // If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions @@ -427,7 +425,7 @@ void ContextMenu::populate() if (Page* page = frame->page()) { if (Settings* settings = page->settings()) { bool includeTextDirectionSubmenu = settings->textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAlwaysIncluded - || settings->textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAutomaticallyIncluded && frame->editor()->hasBidiSelection(); + || (settings->textDirectionSubmenuInclusionBehavior() == TextDirectionSubmenuAutomaticallyIncluded && frame->editor()->hasBidiSelection()); if (includeTextDirectionSubmenu) { ContextMenuItem TextDirectionMenuItem(SubmenuType, ContextMenuItemTagTextDirectionMenu, contextMenuItemTagTextDirectionMenu()); diff --git a/WebCore/platform/CrossThreadCopier.cpp b/WebCore/platform/CrossThreadCopier.cpp new file mode 100644 index 0000000..9ca626f --- /dev/null +++ b/WebCore/platform/CrossThreadCopier.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "CrossThreadCopier.h" + +#include "PlatformString.h" +#include "ResourceError.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" + +namespace WebCore { + +CrossThreadCopierBase<false, String>::Type CrossThreadCopierBase<false, String>::copy(const String& str) +{ + return str.copy(); +} + +CrossThreadCopierBase<false, ResourceError>::Type CrossThreadCopierBase<false, ResourceError>::copy(const ResourceError& error) +{ + return error.copy(); +} + +CrossThreadCopierBase<false, ResourceRequest>::Type CrossThreadCopierBase<false, ResourceRequest>::copy(const ResourceRequest& request) +{ + return request.copyData(); +} + +CrossThreadCopierBase<false, ResourceResponse>::Type CrossThreadCopierBase<false, ResourceResponse>::copy(const ResourceResponse& response) +{ + return response.copyData(); +} + +} // namespace WebCore diff --git a/WebCore/platform/CrossThreadCopier.h b/WebCore/platform/CrossThreadCopier.h new file mode 100644 index 0000000..e7b4c0e --- /dev/null +++ b/WebCore/platform/CrossThreadCopier.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CrossThreadCopier_h +#define CrossThreadCopier_h + +#include <memory> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Threading.h> +#include <wtf/TypeTraits.h> + +namespace WebCore { + + class ResourceError; + struct ResourceRequest; + class ResourceResponse; + class String; + struct CrossThreadResourceResponseData; + struct CrossThreadResourceRequestData; + + template<typename T> struct CrossThreadCopierPassThrough { + typedef T Type; + static Type copy(const T& parameter) + { + return parameter; + } + }; + + template<bool isConvertibleToInteger, typename T> struct CrossThreadCopierBase; + + // Integers get passed through without any changes. + template<typename T> struct CrossThreadCopierBase<true, T> : public CrossThreadCopierPassThrough<T> { + }; + + // Pointers get passed through without any significant changes. + template<typename T> struct CrossThreadCopierBase<false, T*> : public CrossThreadCopierPassThrough<T*> { + }; + + // Custom copy methods. + template<typename T> struct CrossThreadCopierBase<false, RefPtr<ThreadSafeShared<T> > > { + typedef PassRefPtr<T> Type; + static Type copy(const RefPtr<ThreadSafeShared<T> >& refPtr) + { + return PassRefPtr<T>(static_cast<T*>(refPtr.get())); + } + }; + + template<typename T> struct CrossThreadCopierBase<false, std::auto_ptr<T> > { + typedef std::auto_ptr<T> Type; + static Type copy(const std::auto_ptr<T>& autoPtr) + { + return std::auto_ptr<T>(*const_cast<std::auto_ptr<T>*>(&autoPtr)); + } + }; + + template<> struct CrossThreadCopierBase<false, String> { + typedef String Type; + static Type copy(const String&); + }; + + template<> struct CrossThreadCopierBase<false, ResourceError> { + typedef ResourceError Type; + static Type copy(const ResourceError&); + }; + + template<> struct CrossThreadCopierBase<false, ResourceRequest> { + typedef std::auto_ptr<CrossThreadResourceRequestData> Type; + static Type copy(const ResourceRequest&); + }; + + template<> struct CrossThreadCopierBase<false, ResourceResponse> { + typedef std::auto_ptr<CrossThreadResourceResponseData> Type; + static Type copy(const ResourceResponse&); + }; + + template<typename T> struct CrossThreadCopier : public CrossThreadCopierBase<WTF::IsConvertibleToInteger<T>::value, T> { + }; + +} // namespace WebCore + +#endif // CrossThreadCopier_h diff --git a/WebCore/platform/FileSystem.h b/WebCore/platform/FileSystem.h index 66dbc20..8ccefab 100644 --- a/WebCore/platform/FileSystem.h +++ b/WebCore/platform/FileSystem.h @@ -97,9 +97,6 @@ const PlatformFileHandle invalidPlatformFileHandle = 0; #if defined(Q_WS_MAC) typedef CFBundleRef PlatformModule; typedef unsigned PlatformModuleVersion; -#elif defined(Q_WS_X11) || defined(Q_WS_QWS) || defined(Q_WS_S60) -typedef QLibrary* PlatformModule; -typedef unsigned PlatformModuleVersion; #elif defined(Q_OS_WIN) typedef HMODULE PlatformModule; struct PlatformModuleVersion { @@ -119,6 +116,9 @@ struct PlatformModuleVersion { } }; +#else +typedef QLibrary* PlatformModule; +typedef unsigned PlatformModuleVersion; #endif #else diff --git a/WebCore/platform/GeolocationService.cpp b/WebCore/platform/GeolocationService.cpp index dfd5a5c..9b362c8 100644 --- a/WebCore/platform/GeolocationService.cpp +++ b/WebCore/platform/GeolocationService.cpp @@ -26,9 +26,7 @@ #include "config.h" #include "GeolocationService.h" -#include "Geoposition.h" -#include "PositionError.h" -#include "PositionOptions.h" +#include <wtf/Assertions.h> namespace WebCore { diff --git a/WebCore/platform/GeolocationService.h b/WebCore/platform/GeolocationService.h index 90d52eb..74a6ead 100644 --- a/WebCore/platform/GeolocationService.h +++ b/WebCore/platform/GeolocationService.h @@ -38,21 +38,21 @@ class PositionOptions; class GeolocationServiceClient { public: virtual ~GeolocationServiceClient() { } - virtual void geolocationServicePositionChanged(GeolocationService*) { } - virtual void geolocationServiceErrorOccurred(GeolocationService*) { } + virtual void geolocationServicePositionChanged(GeolocationService*) = 0; + virtual void geolocationServiceErrorOccurred(GeolocationService*) = 0; }; class GeolocationService : public Noncopyable { public: static GeolocationService* create(GeolocationServiceClient*); - virtual ~GeolocationService() {} + virtual ~GeolocationService() { } virtual bool startUpdating(PositionOptions*) { return false; } - virtual void stopUpdating() {} + virtual void stopUpdating() { } virtual void suspend() { } virtual void resume() { } - + virtual Geoposition* lastPosition() const { return 0; } virtual PositionError* lastError() const { return 0; } @@ -65,7 +65,7 @@ protected: private: GeolocationServiceClient* m_geolocationServiceClient; }; - + } // namespace WebCore #endif // GeolocationService_h diff --git a/WebCore/platform/KURL.cpp b/WebCore/platform/KURL.cpp index 6fcb9b9..6901782 100644 --- a/WebCore/platform/KURL.cpp +++ b/WebCore/platform/KURL.cpp @@ -289,6 +289,7 @@ inline bool KURL::protocolIs(const String& string, const char* protocol) void KURL::invalidate() { m_isValid = false; + m_protocolInHTTPFamily = false; m_schemeEnd = 0; m_userStart = 0; m_userEnd = 0; @@ -604,6 +605,11 @@ bool KURL::hasRef() const return m_fragmentEnd != m_queryEnd; } +String KURL::baseAsString() const +{ + return m_string.left(m_pathAfterLastSlash); +} + #ifdef NDEBUG static inline void assertProtocolIsGood(const char*) @@ -638,7 +644,10 @@ bool KURL::protocolIs(const char* protocol) const String KURL::query() const { - return m_string.substring(m_pathEnd, m_queryEnd - m_pathEnd); + if (m_queryEnd == m_pathEnd) + return String(); + + return m_string.substring(m_pathEnd + 1, m_queryEnd - (m_pathEnd + 1)); } String KURL::path() const @@ -677,13 +686,13 @@ void KURL::setPort(unsigned short i) if (!m_isValid) return; - // FIXME: Non-ASCII characters must be encoded and escaped to match parse() expectations, - // and to avoid changing more than just the port. - - bool colonNeeded = m_portEnd == m_hostEnd; - int portStart = (colonNeeded ? m_hostEnd : m_hostEnd + 1); + if (i) { + bool colonNeeded = m_portEnd == m_hostEnd; + int portStart = (colonNeeded ? m_hostEnd : m_hostEnd + 1); - parse(m_string.left(portStart) + (colonNeeded ? ":" : "") + String::number(i) + m_string.substring(m_portEnd)); + parse(m_string.left(portStart) + (colonNeeded ? ":" : "") + String::number(i) + m_string.substring(m_portEnd)); + } else + parse(m_string.left(m_hostEnd) + m_string.substring(m_portEnd)); } void KURL::setHostAndPort(const String& hostAndPort) @@ -822,7 +831,11 @@ String KURL::prettyURL() const } append(result, path()); - append(result, query()); + + if (m_pathEnd != m_queryEnd) { + result.append('?'); + append(result, query()); + } if (m_fragmentEnd != m_queryEnd) { result.append('#'); @@ -1047,7 +1060,7 @@ void KURL::parse(const char* url, const String* originalString) && matchLetter(url[2], 'l') && matchLetter(url[3], 'e'); - bool isHTTPorHTTPS = matchLetter(url[0], 'h') + m_protocolInHTTPFamily = matchLetter(url[0], 'h') && matchLetter(url[1], 't') && matchLetter(url[2], 't') && matchLetter(url[3], 'p') @@ -1129,7 +1142,7 @@ void KURL::parse(const char* url, const String* originalString) return; } - if (userStart == portEnd && !isHTTPorHTTPS && !isFile) { + if (userStart == portEnd && !m_protocolInHTTPFamily && !isFile) { // No authority found, which means that this is not a net_path, but rather an abs_path whose first two // path segments are empty. For file, http and https only, an empty authority is allowed. userStart -= 2; @@ -1253,7 +1266,7 @@ void KURL::parse(const char* url, const String* originalString) // For canonicalization, ensure we have a '/' for no path. // Only do this for http and https. - if (isHTTPorHTTPS && pathEnd - pathStart == 0) + if (m_protocolInHTTPFamily && pathEnd - pathStart == 0) *p++ = '/'; // add path, escaping bad characters @@ -1291,7 +1304,7 @@ void KURL::parse(const char* url, const String* originalString) // If we didn't end up actually changing the original string and // it was already in a String, reuse it to avoid extra allocation. - if (originalString && strncmp(buffer.data(), url, m_fragmentEnd) == 0) + if (originalString && originalString->length() == static_cast<unsigned>(m_fragmentEnd) && strncmp(buffer.data(), url, m_fragmentEnd) == 0) m_string = *originalString; else m_string = String(buffer.data(), m_fragmentEnd); diff --git a/WebCore/platform/KURL.h b/WebCore/platform/KURL.h index 7f6b102..46b72c7 100644 --- a/WebCore/platform/KURL.h +++ b/WebCore/platform/KURL.h @@ -47,7 +47,7 @@ QT_END_NAMESPACE #endif #if USE(GOOGLEURL) -#include "GoogleURLPrivate.h" +#include "KURLGooglePrivate.h" #endif namespace WebCore { @@ -84,7 +84,7 @@ public: // For conversions for other structures that have already parsed and // canonicalized the URL. The input must be exactly what KURL would have // done with the same input. - KURL(const char* canonicalSpec, int canonicalSpecLen, + KURL(const CString& canonicalSpec, const url_parse::Parsed& parsed, bool isValid); #endif @@ -119,16 +119,19 @@ public: String pass() const; String path() const; String lastPathComponent() const; - String query() const; // Includes the "?". - String ref() const; // Does *not* include the "#". + String query() const; + String ref() const; bool hasRef() const; + String baseAsString() const; + String prettyURL() const; String fileSystemPath() const; // Returns true if the current URL's protocol is the same as the null- // terminated ASCII argument. The argument must be lower-case. bool protocolIs(const char*) const; + bool protocolInHTTPFamily() const; bool isLocalFile() const; void setProtocol(const String&); @@ -203,10 +206,10 @@ private: bool isHierarchical() const; static bool protocolIs(const String&, const char*); #if USE(GOOGLEURL) - friend class GoogleURLPrivate; + friend class KURLGooglePrivate; void parse(const char* url, const String* originalString); // KURLMac calls this. void copyToBuffer(Vector<char, 512>& buffer) const; // KURLCFNet uses this. - GoogleURLPrivate m_url; + KURLGooglePrivate m_url; #else // !USE(GOOGLEURL) void init(const KURL&, const String&, const TextEncoding&); void copyToBuffer(Vector<char, 512>& buffer) const; @@ -218,7 +221,9 @@ private: void parse(const char* url, const String* originalString); String m_string; - bool m_isValid; + bool m_isValid : 1; + bool m_protocolInHTTPFamily : 1; + int m_schemeEnd; int m_userStart; int m_userEnd; @@ -311,6 +316,11 @@ inline bool KURL::isValid() const return m_isValid; } +inline bool KURL::protocolInHTTPFamily() const +{ + return m_protocolInHTTPFamily; +} + inline unsigned KURL::hostStart() const { return (m_passwordEnd == m_userStart) ? m_passwordEnd : m_passwordEnd + 1; diff --git a/WebCore/platform/KURLGoogle.cpp b/WebCore/platform/KURLGoogle.cpp new file mode 100644 index 0000000..c2e8272 --- /dev/null +++ b/WebCore/platform/KURLGoogle.cpp @@ -0,0 +1,944 @@ +/* + * Copyright (C) 2008, 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(GOOGLEURL) +#include "KURL.h" + +#ifndef NDEBUG +#include <stdio.h> +#endif + +#include "CString.h" +#include "NotImplemented.h" +#include "TextEncoding.h" +#include <wtf/Vector.h> + +#include <googleurl/src/url_canon_internal.h> +#include <googleurl/src/url_util.h> + +using WTF::isASCIILower; +using WTF::toASCIILower; + +namespace WebCore { + +// Wraps WebCore's text encoding in a character set converter for the +// canonicalizer. +class KURLCharsetConverter : public url_canon::CharsetConverter { +public: + // The encoding parameter may be NULL, but in this case the object must not + // be called. + KURLCharsetConverter(const TextEncoding* encoding) + : m_encoding(encoding) + { + } + + virtual void ConvertFromUTF16(const url_parse::UTF16Char* input, int inputLength, + url_canon::CanonOutput* output) + { + CString encoded = m_encoding->encode(input, inputLength, URLEncodedEntitiesForUnencodables); + output->Append(encoded.data(), static_cast<int>(encoded.length())); + } + +private: + const TextEncoding* m_encoding; +}; + +// Note that this function must be named differently than the one in KURL.cpp +// since our unit tests evilly include both files, and their local definition +// will be ambiguous. +static inline void assertProtocolIsGood(const char* protocol) +{ +#ifndef NDEBUG + const char* p = protocol; + while (*p) { + ASSERT(*p > ' ' && *p < 0x7F && !(*p >= 'A' && *p <= 'Z')); + ++p; + } +#endif +} + +// Returns the characters for the given string, or a pointer to a static empty +// string if the input string is NULL. This will always ensure we have a non- +// NULL character pointer since ReplaceComponents has special meaning for NULL. +static inline const url_parse::UTF16Char* CharactersOrEmpty(const String& str) +{ + static const url_parse::UTF16Char zero = 0; + return str.characters() ? + reinterpret_cast<const url_parse::UTF16Char*>(str.characters()) : + &zero; +} + +static inline bool isUnicodeEncoding(const TextEncoding* encoding) +{ + return encoding->encodingForFormSubmission() == UTF8Encoding(); +} + +static bool lowerCaseEqualsASCII(const char* begin, const char* end, const char* str) +{ + while (begin != end && *str) { + ASSERT(isASCIILower(*str)); + if (toASCIILower(*begin++) != *str++) + return false; + } + + // Both strings are equal (ignoring case) if and only if all of the characters were equal, + // and the end of both has been reached. + return begin == end && !*str; +} + + +// KURLGooglePrivate ----------------------------------------------------------- + +KURLGooglePrivate::KURLGooglePrivate() + : m_isValid(false) + , m_protocolInHTTPFamily(false) + , m_utf8IsASCII(true) + , m_stringIsValid(false) +{ +} + +KURLGooglePrivate::KURLGooglePrivate(const url_parse::Parsed& parsed, bool isValid) + : m_isValid(isValid) + , m_protocolInHTTPFamily(false) + , m_parsed(parsed) + , m_utf8IsASCII(true) + , m_stringIsValid(false) +{ +} + +// Setters for the data. Using the ASCII version when you know the +// data is ASCII will be slightly more efficient. The UTF-8 version +// will always be correct if the caller is unsure. +void KURLGooglePrivate::setUtf8(const CString& str) +{ + const char* data = str.data(); + unsigned dataLength = str.length(); + + // The m_utf8IsASCII must always be correct since the DeprecatedString + // getter must create it with the proper constructor. This test can be + // removed when DeprecatedString is gone, but it still might be a + // performance win. + m_utf8IsASCII = true; + for (unsigned i = 0; i < dataLength; i++) { + if (static_cast<unsigned char>(data[i]) >= 0x80) { + m_utf8IsASCII = false; + break; + } + } + + m_utf8 = str; + m_stringIsValid = false; + initProtocolInHTTPFamily(); +} + +void KURLGooglePrivate::setAscii(const CString& str) +{ + m_utf8 = str; + m_utf8IsASCII = true; + m_stringIsValid = false; + initProtocolInHTTPFamily(); +} + +void KURLGooglePrivate::init(const KURL& base, + const String& relative, + const TextEncoding* queryEncoding) +{ + init(base, relative.characters(), relative.length(), queryEncoding); +} + +// Note: code mostly duplicated below. +void KURLGooglePrivate::init(const KURL& base, const char* rel, int relLength, + const TextEncoding* queryEncoding) +{ + // As a performance optimization, we do not use the charset converter if + // encoding is UTF-8 or other Unicode encodings. Note that this is + // per HTML5 2.5.3 (resolving URL). The URL canonicalizer will be + // more efficient with no charset converter object because it + // can do UTF-8 internally with no extra copies. + + // We feel free to make the charset converter object every time since it's + // just a wrapper around a reference. + KURLCharsetConverter charsetConverterObject(queryEncoding); + KURLCharsetConverter* charsetConverter = + (!queryEncoding || isUnicodeEncoding(queryEncoding)) ? 0 : + &charsetConverterObject; + + url_canon::RawCanonOutputT<char> output; + const CString& baseStr = base.m_url.utf8String(); + m_isValid = url_util::ResolveRelative(baseStr.data(), baseStr.length(), + base.m_url.m_parsed, rel, relLength, + charsetConverter, + &output, &m_parsed); + + // See FIXME in KURLGooglePrivate in the header. If canonicalization has not + // changed the string, we can avoid an extra allocation by using assignment. + // + // When KURL encounters an error such that the URL is invalid and empty + // (for example, resolving a relative URL on a non-hierarchical base), it + // will produce an isNull URL, and calling setUtf8 will produce an empty + // non-null URL. This is unlikely to affect anything, but we preserve this + // just in case. + if (m_isValid || output.length()) { + // Without ref, the whole url is guaranteed to be ASCII-only. + if (m_parsed.ref.is_nonempty()) + setUtf8(CString(output.data(), output.length())); + else + setAscii(CString(output.data(), output.length())); + } else { + // WebCore expects resolved URLs to be empty rather than NULL. + setUtf8(CString("", 0)); + } +} + +// Note: code mostly duplicated above. See FIXMEs and comments there. +void KURLGooglePrivate::init(const KURL& base, const UChar* rel, int relLength, + const TextEncoding* queryEncoding) +{ + KURLCharsetConverter charsetConverterObject(queryEncoding); + KURLCharsetConverter* charsetConverter = + (!queryEncoding || isUnicodeEncoding(queryEncoding)) ? 0 : + &charsetConverterObject; + + url_canon::RawCanonOutputT<char> output; + const CString& baseStr = base.m_url.utf8String(); + m_isValid = url_util::ResolveRelative(baseStr.data(), baseStr.length(), + base.m_url.m_parsed, rel, relLength, + charsetConverter, + &output, &m_parsed); + + if (m_isValid || output.length()) { + if (m_parsed.ref.is_nonempty()) + setUtf8(CString(output.data(), output.length())); + else + setAscii(CString(output.data(), output.length())); + } else + setUtf8(CString("", 0)); +} + +void KURLGooglePrivate::initProtocolInHTTPFamily() +{ + if (!m_isValid) { + m_protocolInHTTPFamily = false; + return; + } + + const char* scheme = m_utf8.data() + m_parsed.scheme.begin; + if (m_parsed.scheme.len == 4) + m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 4, "http"); + else if (m_parsed.scheme.len == 5) + m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 5, "https"); + else + m_protocolInHTTPFamily = false; +} + +void KURLGooglePrivate::copyTo(KURLGooglePrivate* dest) const +{ + dest->m_isValid = m_isValid; + dest->m_protocolInHTTPFamily = m_protocolInHTTPFamily; + dest->m_parsed = m_parsed; + + // Don't copy the 16-bit string since that will be regenerated as needed. + dest->m_utf8 = CString(m_utf8.data(), m_utf8.length()); + dest->m_utf8IsASCII = m_utf8IsASCII; + dest->m_stringIsValid = false; +} + +String KURLGooglePrivate::componentString(const url_parse::Component& comp) const +{ + if (!m_isValid || comp.len <= 0) { + // KURL returns a NULL string if the URL is itself a NULL string, and an + // empty string for other nonexistant entities. + if (utf8String().isNull()) + return String(); + return String("", 0); + } + // begin and len are in terms of bytes which do not match + // if string() is UTF-16 and input contains non-ASCII characters. + // However, the only part in urlString that can contain non-ASCII + // characters is 'ref' at the end of the string. In that case, + // begin will always match the actual value and len (in terms of + // byte) will be longer than what's needed by 'mid'. However, mid + // truncates len to avoid go past the end of a string so that we can + // get away withtout doing anything here. + return string().substring(comp.begin, comp.len); +} + +void KURLGooglePrivate::replaceComponents(const Replacements& replacements) +{ + url_canon::RawCanonOutputT<char> output; + url_parse::Parsed newParsed; + + m_isValid = url_util::ReplaceComponents(utf8String().data(), + utf8String().length(), m_parsed, replacements, 0, &output, &newParsed); + + m_parsed = newParsed; + if (m_parsed.ref.is_nonempty()) + setUtf8(CString(output.data(), output.length())); + else + setAscii(CString(output.data(), output.length())); +} + +const String& KURLGooglePrivate::string() const +{ + if (!m_stringIsValid) { + // Must special case the NULL case, since constructing the + // string like we do below will generate an empty rather than + // a NULL string. + if (m_utf8.isNull()) + m_string = String(); + else if (m_utf8IsASCII) + m_string = String(m_utf8.data(), m_utf8.length()); + else + m_string = String::fromUTF8(m_utf8.data(), m_utf8.length()); + m_stringIsValid = true; + } + return m_string; +} + +// KURL ------------------------------------------------------------------------ + +// Creates with NULL-terminated string input representing an absolute URL. +// WebCore generally calls this only with hardcoded strings, so the input is +// ASCII. We treat is as UTF-8 just in case. +KURL::KURL(const char *url) +{ + // FIXME The Mac code checks for beginning with a slash and converting to a + // file: URL. We will want to add this as well once we can compile on a + // system like that. + m_url.init(KURL(), url, strlen(url), 0); + + // The one-argument constructors should never generate a NULL string. + // This is a funny quirk of KURL.cpp (probably a bug) which we preserve. + if (m_url.utf8String().isNull()) + m_url.setAscii(CString("", 0)); +} + +// Initializes with a string representing an absolute URL. No encoding +// information is specified. This generally happens when a KURL is converted +// to a string and then converted back. In this case, the URL is already +// canonical and in proper escaped form so needs no encoding. We treat it was +// UTF-8 just in case. +KURL::KURL(const String& url) +{ + if (!url.isNull()) + m_url.init(KURL(), url, 0); + else { + // WebCore expects us to preserve the nullness of strings when this + // constructor is used. In all other cases, it expects a non-null + // empty string, which is what init() will create. + m_url.m_isValid = false; + m_url.m_protocolInHTTPFamily = false; + } +} + +// Constructs a new URL given a base URL and a possibly relative input URL. +// This assumes UTF-8 encoding. +KURL::KURL(const KURL& base, const String& relative) +{ + m_url.init(base, relative, 0); +} + +// Constructs a new URL given a base URL and a possibly relative input URL. +// Any query portion of the relative URL will be encoded in the given encoding. +KURL::KURL(const KURL& base, + const String& relative, + const TextEncoding& encoding) +{ + m_url.init(base, relative, &encoding.encodingForFormSubmission()); +} + +KURL::KURL(const CString& canonicalSpec, + const url_parse::Parsed& parsed, bool isValid) + : m_url(parsed, isValid) +{ + // We know the reference fragment is the only part that can be UTF-8, so + // we know it's ASCII when there is no ref. + if (parsed.ref.is_nonempty()) + m_url.setUtf8(canonicalSpec); + else + m_url.setAscii(canonicalSpec); +} + +#if PLATFORM(CF) +KURL::KURL(CFURLRef) +{ + notImplemented(); + invalidate(); +} + +CFURLRef KURL::createCFURL() const +{ + notImplemented(); + return 0; +} +#endif + +KURL KURL::copy() const +{ + KURL result = *this; + m_url.copyTo(&result.m_url); + return result; +} + +bool KURL::isNull() const +{ + return m_url.utf8String().isNull(); +} + +bool KURL::isEmpty() const +{ + return !m_url.utf8String().length(); +} + +bool KURL::isValid() const +{ + return m_url.m_isValid; +} + +bool KURL::protocolInHTTPFamily() const +{ + return m_url.m_protocolInHTTPFamily; +} + +bool KURL::hasPath() const +{ + // Note that http://www.google.com/" has a path, the path is "/". This can + // return false only for invalid or nonstandard URLs. + return m_url.m_parsed.path.len >= 0; +} + +// We handle "parameters" separated by a semicolon, while KURL.cpp does not, +// which can lead to different results in some cases. +String KURL::lastPathComponent() const +{ + // When the output ends in a slash, WebCore has different expectations than + // the GoogleURL library. For "/foo/bar/" the library will return the empty + // string, but WebCore wants "bar". + url_parse::Component path = m_url.m_parsed.path; + if (path.len > 0 && m_url.utf8String().data()[path.end() - 1] == '/') + path.len--; + + url_parse::Component file; + url_parse::ExtractFileName(m_url.utf8String().data(), path, &file); + + // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns + // a null string when the path is empty, which we duplicate here. + if (!file.is_nonempty()) + return String(); + return m_url.componentString(file); +} + +String KURL::protocol() const +{ + return m_url.componentString(m_url.m_parsed.scheme); +} + +String KURL::host() const +{ + // Note: KURL.cpp unescapes here. + return m_url.componentString(m_url.m_parsed.host); +} + +// Returns 0 when there is no port or it is invalid. +// +// We treat URL's with out-of-range port numbers as invalid URLs, and they will +// be rejected by the canonicalizer. KURL.cpp will allow them in parsing, but +// return 0 from this port() function, so we mirror that behavior here. +unsigned short KURL::port() const +{ + if (!m_url.m_isValid || m_url.m_parsed.port.len <= 0) + return 0; + int port = url_parse::ParsePort(m_url.utf8String().data(), m_url.m_parsed.port); + if (port == url_parse::PORT_UNSPECIFIED) + return 0; + return static_cast<unsigned short>(port); +} + +// Returns the empty string if there is no password. +String KURL::pass() const +{ + // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns + // a null string when the password is empty, which we duplicate here. + if (!m_url.m_parsed.password.is_nonempty()) + return String(); + + // Note: KURL.cpp unescapes here. + return m_url.componentString(m_url.m_parsed.password); +} + +// Returns the empty string if there is no username. +String KURL::user() const +{ + // Note: KURL.cpp unescapes here. + return m_url.componentString(m_url.m_parsed.username); +} + +String KURL::ref() const +{ + // Empty but present refs ("foo.com/bar#") should result in the empty + // string, which m_url.componentString will produce. Nonexistant refs should be + // the NULL string. + if (!m_url.m_parsed.ref.is_valid()) + return String(); + + // Note: KURL.cpp unescapes here. + return m_url.componentString(m_url.m_parsed.ref); +} + +bool KURL::hasRef() const +{ + // Note: KURL.cpp unescapes here. + // FIXME determine if KURL.cpp agrees about an empty ref + return m_url.m_parsed.ref.len >= 0; +} + +String KURL::query() const +{ + if (m_url.m_parsed.query.len >= 0) + return m_url.componentString(m_url.m_parsed.query); + + // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns + // an empty string when the query is empty rather than a null (not sure + // which is right). + return String("", 0); +} + +String KURL::path() const +{ + // Note: KURL.cpp unescapes here. + return m_url.componentString(m_url.m_parsed.path); +} + +void KURL::setProtocol(const String& protocol) +{ + KURLGooglePrivate::Replacements replacements; + replacements.SetScheme(CharactersOrEmpty(protocol), + url_parse::Component(0, protocol.length())); + m_url.replaceComponents(replacements); +} + +void KURL::setHost(const String& host) +{ + KURLGooglePrivate::Replacements replacements; + replacements.SetHost(CharactersOrEmpty(host), + url_parse::Component(0, host.length())); + m_url.replaceComponents(replacements); +} + +// This function is used only in the JSC build. +void KURL::setHostAndPort(const String& s) +{ + String newhost = s.left(s.find(":")); + String newport = s.substring(s.find(":") + 1); + + KURLGooglePrivate::Replacements replacements; + // Host can't be removed, so we always set. + replacements.SetHost(CharactersOrEmpty(newhost), + url_parse::Component(0, newhost.length())); + + if (newport.isEmpty()) // Port may be removed, so we support clearing. + replacements.ClearPort(); + else + replacements.SetPort(CharactersOrEmpty(newport), url_parse::Component(0, newport.length())); + m_url.replaceComponents(replacements); +} + +void KURL::setPort(unsigned short i) +{ + KURLGooglePrivate::Replacements replacements; + String portStr; + if (i) { + portStr = String::number(static_cast<int>(i)); + replacements.SetPort( + reinterpret_cast<const url_parse::UTF16Char*>(portStr.characters()), + url_parse::Component(0, portStr.length())); + + } else { + // Clear any existing port when it is set to 0. + replacements.ClearPort(); + } + m_url.replaceComponents(replacements); +} + +void KURL::setUser(const String& user) +{ + // This function is commonly called to clear the username, which we + // normally don't have, so we optimize this case. + if (user.isEmpty() && !m_url.m_parsed.username.is_valid()) + return; + + // The canonicalizer will clear any usernames that are empty, so we + // don't have to explicitly call ClearUsername() here. + KURLGooglePrivate::Replacements replacements; + replacements.SetUsername(CharactersOrEmpty(user), + url_parse::Component(0, user.length())); + m_url.replaceComponents(replacements); +} + +void KURL::setPass(const String& pass) +{ + // This function is commonly called to clear the password, which we + // normally don't have, so we optimize this case. + if (pass.isEmpty() && !m_url.m_parsed.password.is_valid()) + return; + + // The canonicalizer will clear any passwords that are empty, so we + // don't have to explicitly call ClearUsername() here. + KURLGooglePrivate::Replacements replacements; + replacements.SetPassword(CharactersOrEmpty(pass), + url_parse::Component(0, pass.length())); + m_url.replaceComponents(replacements); +} + +void KURL::setRef(const String& ref) +{ + // This function is commonly called to clear the ref, which we + // normally don't have, so we optimize this case. + if (ref.isNull() && !m_url.m_parsed.ref.is_valid()) + return; + + KURLGooglePrivate::Replacements replacements; + if (ref.isNull()) + replacements.ClearRef(); + else + replacements.SetRef(CharactersOrEmpty(ref), url_parse::Component(0, ref.length())); + m_url.replaceComponents(replacements); +} + +void KURL::removeRef() +{ + KURLGooglePrivate::Replacements replacements; + replacements.ClearRef(); + m_url.replaceComponents(replacements); +} + +void KURL::setQuery(const String& query) +{ + KURLGooglePrivate::Replacements replacements; + if (query.isNull()) { + // KURL.cpp sets to NULL to clear any query. + replacements.ClearQuery(); + } else if (query.length() > 0 && query[0] == '?') { + // WebCore expects the query string to begin with a question mark, but + // GoogleURL doesn't. So we trim off the question mark when setting. + replacements.SetQuery(CharactersOrEmpty(query), + url_parse::Component(1, query.length() - 1)); + } else { + // When set with the empty string or something that doesn't begin with + // a question mark, KURL.cpp will add a question mark for you. The only + // way this isn't compatible is if you call this function with an empty + // string. KURL.cpp will leave a '?' with nothing following it in the + // URL, whereas we'll clear it. + // FIXME We should eliminate this difference. + replacements.SetQuery(CharactersOrEmpty(query), + url_parse::Component(0, query.length())); + } + m_url.replaceComponents(replacements); +} + +void KURL::setPath(const String& path) +{ + // Empty paths will be canonicalized to "/", so we don't have to worry + // about calling ClearPath(). + KURLGooglePrivate::Replacements replacements; + replacements.SetPath(CharactersOrEmpty(path), + url_parse::Component(0, path.length())); + m_url.replaceComponents(replacements); +} + +// On Mac, this just seems to return the same URL, but with "/foo/bar" for +// file: URLs instead of file:///foo/bar. We don't bother with any of this, +// at least for now. +String KURL::prettyURL() const +{ + if (!m_url.m_isValid) + return String(); + return m_url.string(); +} + +// We copied the KURL version here on Sept 12, 2008 while doing a WebKit +// merge. +// +// FIXME Somehow share this with KURL? Like we'd theoretically merge with +// decodeURLEscapeSequences below? +String mimeTypeFromDataURL(const String& url) +{ + ASSERT(protocolIs(url, "data")); + int index = url.find(';'); + if (index == -1) + index = url.find(','); + if (index != -1) { + int len = index - 5; + if (len > 0) + return url.substring(5, len); + return "text/plain"; // Data URLs with no MIME type are considered text/plain. + } + return ""; +} + +String decodeURLEscapeSequences(const String& str) +{ + return decodeURLEscapeSequences(str, UTF8Encoding()); +} + +// In KURL.cpp's implementation, this is called by every component getter. +// It will unescape every character, including NULL. This is scary, and may +// cause security holes. We never call this function for components, and +// just return the ASCII versions instead. +// +// However, this static function is called directly in some cases. It appears +// that this only happens for javascript: URLs, so this is essentially the +// JavaScript URL decoder. It assumes UTF-8 encoding. +// +// IE doesn't unescape %00, forcing you to use \x00 in JS strings, so we do +// the same. This also eliminates NULL-related problems should a consumer +// incorrectly call this function for non-JavaScript. +// +// FIXME These should be merged to the KURL.cpp implementation. +String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) +{ + // FIXME We can probably use KURL.cpp's version of this function + // without modification. However, I'm concerned about + // https://bugs.webkit.org/show_bug.cgi?id=20559 so am keeping this old + // custom code for now. Using their version will also fix the bug that + // we ignore the encoding. + // + // FIXME b/1350291: This does not get called very often. We just convert + // first to 8-bit UTF-8, then unescape, then back to 16-bit. This kind of + // sucks, and we don't use the encoding properly, which will make some + // obscure anchor navigations fail. + CString cstr = str.utf8(); + + const char* input = cstr.data(); + int inputLength = cstr.length(); + url_canon::RawCanonOutputT<char> unescaped; + for (int i = 0; i < inputLength; i++) { + if (input[i] == '%') { + unsigned char ch; + if (url_canon::DecodeEscaped(input, &i, inputLength, &ch)) { + if (!ch) { + // Never unescape NULLs. + unescaped.push_back('%'); + unescaped.push_back('0'); + unescaped.push_back('0'); + } else + unescaped.push_back(ch); + } else { + // Invalid escape sequence, copy the percent literal. + unescaped.push_back('%'); + } + } else { + // Regular non-escaped 8-bit character. + unescaped.push_back(input[i]); + } + } + + // Convert that 8-bit to UTF-16. It's not clear IE does this at all to + // JavaScript URLs, but Firefox and Safari do. + url_canon::RawCanonOutputT<url_parse::UTF16Char> utf16; + for (int i = 0; i < unescaped.length(); i++) { + unsigned char uch = static_cast<unsigned char>(unescaped.at(i)); + if (uch < 0x80) { + // Non-UTF-8, just append directly + utf16.push_back(uch); + } else { + // next_ch will point to the last character of the decoded + // character. + int nextCharacter = i; + unsigned codePoint; + if (url_canon::ReadUTFChar(unescaped.data(), &nextCharacter, + unescaped.length(), &codePoint)) { + // Valid UTF-8 character, convert to UTF-16. + url_canon::AppendUTF16Value(codePoint, &utf16); + i = nextCharacter; + } else { + // KURL.cpp strips any sequences that are not valid UTF-8. This + // sounds scary. Instead, we just keep those invalid code + // points and promote to UTF-16. We copy all characters from + // the current position to the end of the identified sqeuqnce. + while (i < nextCharacter) { + utf16.push_back(static_cast<unsigned char>(unescaped.at(i))); + i++; + } + utf16.push_back(static_cast<unsigned char>(unescaped.at(i))); + } + } + } + + return String(reinterpret_cast<UChar*>(utf16.data()), utf16.length()); +} + +bool KURL::protocolIs(const char* protocol) const +{ + assertProtocolIsGood(protocol); + if (m_url.m_parsed.scheme.len <= 0) + return !protocol; + return lowerCaseEqualsASCII( + m_url.utf8String().data() + m_url.m_parsed.scheme.begin, + m_url.utf8String().data() + m_url.m_parsed.scheme.end(), + protocol); +} + +bool KURL::isLocalFile() const +{ + return protocolIs("file"); +} + +// This is called to escape a URL string. It is only used externally when +// constructing mailto: links to set the query section. Since our query setter +// will automatically do the correct escaping, this function does not have to +// do any work. +// +// There is a possibility that a future called may use this function in other +// ways, and may expect to get a valid URL string. The dangerous thing we want +// to protect against here is accidentally getting NULLs in a string that is +// not supposed to have NULLs. Therefore, we escape NULLs here to prevent this. +String encodeWithURLEscapeSequences(const String& notEncodedString) +{ + CString utf8 = UTF8Encoding().encode( + reinterpret_cast<const UChar*>(notEncodedString.characters()), + notEncodedString.length(), + URLEncodedEntitiesForUnencodables); + const char* input = utf8.data(); + int inputLength = utf8.length(); + + Vector<char, 2048> buffer; + for (int i = 0; i < inputLength; i++) { + if (!input[i]) + buffer.append("%00", 3); + else + buffer.append(input[i]); + } + return String(buffer.data(), buffer.size()); +} + +bool KURL::isHierarchical() const +{ + if (!m_url.m_parsed.scheme.is_nonempty()) + return false; + return url_util::IsStandard( + &m_url.utf8String().data()[m_url.m_parsed.scheme.begin], + m_url.utf8String().length(), + m_url.m_parsed.scheme); +} + +#ifndef NDEBUG +void KURL::print() const +{ + printf("%s\n", m_url.utf8String().data()); +} +#endif + +void KURL::invalidate() +{ + // This is only called from the constructor so resetting the (automatically + // initialized) string and parsed structure would be a waste of time. + m_url.m_isValid = false; + m_url.m_protocolInHTTPFamily = false; +} + +// Equal up to reference fragments, if any. +bool equalIgnoringRef(const KURL& a, const KURL& b) +{ + // Compute the length of each URL without its ref. Note that the reference + // begin (if it exists) points to the character *after* the '#', so we need + // to subtract one. + int aLength = a.m_url.utf8String().length(); + if (a.m_url.m_parsed.ref.len >= 0) + aLength = a.m_url.m_parsed.ref.begin - 1; + + int bLength = b.m_url.utf8String().length(); + if (b.m_url.m_parsed.ref.len >= 0) + bLength = b.m_url.m_parsed.ref.begin - 1; + + return aLength == bLength + && !strncmp(a.m_url.utf8String().data(), b.m_url.utf8String().data(), aLength); +} + +unsigned KURL::hostStart() const +{ + return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::HOST, false); +} + +unsigned KURL::hostEnd() const +{ + return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PORT, true); +} + +unsigned KURL::pathStart() const +{ + return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PATH, false); +} + +unsigned KURL::pathEnd() const +{ + return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::QUERY, true); +} + +unsigned KURL::pathAfterLastSlash() const +{ + // When there's no path, ask for what would be the beginning of it. + if (!m_url.m_parsed.path.is_valid()) + return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PATH, false); + + url_parse::Component filename; + url_parse::ExtractFileName(m_url.utf8String().data(), m_url.m_parsed.path, + &filename); + return filename.begin; +} + +const KURL& blankURL() +{ + static KURL staticBlankURL("about:blank"); + return staticBlankURL; +} + +bool protocolIs(const String& url, const char* protocol) +{ + // Do the comparison without making a new string object. + assertProtocolIsGood(protocol); + for (int i = 0; ; ++i) { + if (!protocol[i]) + return url[i] == ':'; + if (toASCIILower(url[i]) != protocol[i]) + return false; + } +} + +inline bool KURL::protocolIs(const String& string, const char* protocol) +{ + return WebCore::protocolIs(string, protocol); +} + +} // namespace WebCore + +#endif // USE(GOOGLEURL) diff --git a/WebCore/platform/KURLGooglePrivate.h b/WebCore/platform/KURLGooglePrivate.h new file mode 100644 index 0000000..a70cce5 --- /dev/null +++ b/WebCore/platform/KURLGooglePrivate.h @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef KURLGooglePrivate_h +#define KURLGooglePrivate_h + +#include "CString.h" + +#include <googleurl/src/url_parse.h> +#include <googleurl/src/url_canon.h> + +namespace WebCore { + + class KURL; + class TextEncoding; + + // Wraps the internals related to using Google-URL as the bnackend for KURL. + // This maintains the state and has auxiliary functions so that we don't need + // to uglify KURL.h while allowing Google-URL to be evaluated. + class KURLGooglePrivate { + public: + KURLGooglePrivate(); + KURLGooglePrivate(const url_parse::Parsed&, bool isValid); + + // Initializes the object. This will call through to one of the backend + // initializers below depending on whether the string's internal + // representation is 8 or 16 bit. + void init(const KURL& base, const String& relative, + const TextEncoding* queryEncoding); + + // Backend initializers. The query encoding parameters are optional and can + // be NULL (this implies UTF-8). These initializers require that the object + // has just been created and the strings are NULL. Do not call on an + // already-constructed object. + void init(const KURL& base, const char* rel, int relLength, + const TextEncoding* queryEncoding); + void init(const KURL& base, const UChar* rel, int relLength, + const TextEncoding* queryEncoding); + + // Does a deep copy to the given output object. + void copyTo(KURLGooglePrivate* dest) const; + + // Returns the substring of the input identified by the given component. + String componentString(const url_parse::Component&) const; + + // Replaces the given components, modifying the current URL. The current + // URL must be valid. + typedef url_canon::Replacements<url_parse::UTF16Char> Replacements; + void replaceComponents(const Replacements&); + + // Setters for the data. Using the ASCII version when you know the + // data is ASCII will be slightly more efficient. The UTF-8 version + // will always be correct if the caller is unsure. + void setUtf8(const CString&); + void setAscii(const CString&); + + // TODO(brettw) we can support an additional optimization. Make this + // buffer support both optinal Strings and UTF-8 data. This way, we can use + // the optimization from the original KURL which uses = with the original + // string when canonicalization did not change it. This allows the strings + // to share a buffer internally, and saves a malloc. + + // Getters for the data. + const CString& utf8String() const { return m_utf8; } + const String& string() const; + + bool m_isValid; + bool m_protocolInHTTPFamily; + url_parse::Parsed m_parsed; // Indexes into the UTF-8 version of the string. + + private: + void initProtocolInHTTPFamily(); + + CString m_utf8; + + // Set to true when the caller set us using the ASCII setter. We can + // be more efficient when we know there is no UTF-8 to worry about. + // This flag is currently always correct, but should be changed to be a + // hint (see setUtf8). + bool m_utf8IsASCII; + + mutable bool m_stringIsValid; + mutable String m_string; + }; + +} // namespace WebCore + +#endif diff --git a/WebCore/platform/MIMETypeRegistry.h b/WebCore/platform/MIMETypeRegistry.h index be741a4..fca467f 100644 --- a/WebCore/platform/MIMETypeRegistry.h +++ b/WebCore/platform/MIMETypeRegistry.h @@ -38,6 +38,7 @@ public: static String getMIMETypeForExtension(const String& ext); static Vector<String> getExtensionsForMIMEType(const String& type); static String getPreferredExtensionForMIMEType(const String& type); + static String getMIMETypeForPath(const String& path); // Check to see if a mime type is suitable for being loaded inline as an diff --git a/WebCore/platform/PlatformWheelEvent.h b/WebCore/platform/PlatformWheelEvent.h index fc954ed..9395e93 100644 --- a/WebCore/platform/PlatformWheelEvent.h +++ b/WebCore/platform/PlatformWheelEvent.h @@ -59,23 +59,13 @@ class wxPoint; namespace WebCore { - // Wheel events come in three flavors: - // The ScrollByPixelWheelEvent is a fine-grained event that specifies the precise number of pixels to scroll. It is sent by MacBook touchpads on OS X. - // For ScrollByPixelWheelEvents, the delta values contain the precise number of pixels to scroll. - // The ScrollByLineWheelEvent (the normal wheel event) sends a delta that can be corrected by a line multiplier to determine how many lines to scroll. - // If the platform has configurable line sensitivity (Windows), then the number of lines to scroll is used in order to behave like the platform. - // If the platform does not have configurable line sensitivity, then WebCore's default behavior is used (which scrolls 3 * the wheel line delta). - // For ScrollByLineWheelEvents, the delta values represent the number of lines to scroll. - // The ScrollByPageWheelEvent indicates that the wheel event should scroll an entire page instead. In this case WebCore's built in paging behavior is used to page + // Wheel events come in two flavors: + // The ScrollByPixelWheelEvent is a fine-grained event that specifies the precise number of pixels to scroll. It is sent directly by MacBook touchpads on OS X, + // and synthesized in other cases where platforms generate line-by-line scrolling events. + // The ScrollByPageWheelEvent indicates that the wheel event should scroll an entire page. In this case WebCore's built in paging behavior is used to page // up and down (you get the same behavior as if the user was clicking in a scrollbar track to page up or page down). Page scrolling only works in the vertical direction. - enum PlatformWheelEventGranularity { ScrollByLineWheelEvent, ScrollByPageWheelEvent, ScrollByPixelWheelEvent }; + enum PlatformWheelEventGranularity { ScrollByPageWheelEvent, ScrollByPixelWheelEvent }; - // WebCore uses a line multiple of ~3 (40px per line step) when doing arrowing with a scrollbar or line stepping via the arrow keys. The delta for wheeling is expressed - // as a # of actual lines (40 / 3 = 1 wheel line). We use the horizontalLineMultiplier and verticalLineMultiplier methods to incorporate the line multiplier into the deltas. On - // platforms that do not support wheel sensitivity, we use this hardcoded constant value of 3 to ensure that wheeling by default matches the WebCore multiplier you - // get when doing other kinds of line stepping. - const int cLineMultiplier = 3; - class PlatformWheelEvent { public: const IntPoint& pos() const { return m_position; } // PlatformWindow coordinates. @@ -84,6 +74,9 @@ namespace WebCore { float deltaX() const { return m_deltaX; } float deltaY() const { return m_deltaY; } + float wheelTicksX() const { return m_wheelTicksX; } + float wheelTicksY() const { return m_wheelTicksY; } + PlatformWheelEventGranularity granularity() const { return m_granularity; } bool isAccepted() const { return m_isAccepted; } @@ -104,7 +97,7 @@ namespace WebCore { PlatformWheelEvent(NSEvent*); #endif #if PLATFORM(WIN) - PlatformWheelEvent(HWND, WPARAM, LPARAM, bool isHorizontal); + PlatformWheelEvent(HWND, WPARAM, LPARAM, bool isMouseHWheel); #endif #if PLATFORM(GTK) PlatformWheelEvent(GdkEventScroll*); @@ -117,18 +110,12 @@ namespace WebCore { #endif protected: -#if !PLATFORM(WIN) - int horizontalLineMultiplier() const { return cLineMultiplier; } - int verticalLineMultiplier() const { return cLineMultiplier; } -#else - int horizontalLineMultiplier() const; - int verticalLineMultiplier() const; -#endif - IntPoint m_position; IntPoint m_globalPosition; float m_deltaX; float m_deltaY; + float m_wheelTicksX; + float m_wheelTicksY; PlatformWheelEventGranularity m_granularity; bool m_isAccepted; bool m_shiftKey; diff --git a/WebCore/platform/PopupMenuStyle.h b/WebCore/platform/PopupMenuStyle.h index 89a7a74..5325ff3 100644 --- a/WebCore/platform/PopupMenuStyle.h +++ b/WebCore/platform/PopupMenuStyle.h @@ -28,16 +28,20 @@ #include "Color.h" #include "Font.h" +#include "Length.h" +#include "TextDirection.h" namespace WebCore { class PopupMenuStyle { public: - PopupMenuStyle(const Color& foreground, const Color& background, const Font& font, bool visible) + PopupMenuStyle(const Color& foreground, const Color& background, const Font& font, bool visible, Length textIndent, TextDirection textDirection) : m_foregroundColor(foreground) , m_backgroundColor(background) , m_font(font) , m_visible(visible) + , m_textIndent(textIndent) + , m_textDirection(textDirection) { } @@ -45,12 +49,16 @@ public: const Color& backgroundColor() const { return m_backgroundColor; } const Font& font() const { return m_font; } bool isVisible() const { return m_visible; } + Length textIndent() const { return m_textIndent; } + TextDirection textDirection() const { return m_textDirection; } private: Color m_foregroundColor; Color m_backgroundColor; Font m_font; bool m_visible; + Length m_textIndent; + TextDirection m_textDirection; }; } // namespace WebCore diff --git a/WebCore/platform/RunLoopTimer.h b/WebCore/platform/RunLoopTimer.h new file mode 100644 index 0000000..96eb8d8 --- /dev/null +++ b/WebCore/platform/RunLoopTimer.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS 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 RunLoopTimer_h +#define RunLoopTimer_h + +#include "SchedulePair.h" +#include <wtf/Noncopyable.h> +#include <wtf/RetainPtr.h> + +namespace WebCore { + +// Time intervals are all in seconds. + +class RunLoopTimerBase : Noncopyable { +public: + virtual ~RunLoopTimerBase(); + + void schedule(const SchedulePair*); + void schedule(const SchedulePairHashSet&); + + void start(double nextFireInterval, double repeatInterval); + + void startRepeating(double repeatInterval) { start(repeatInterval, repeatInterval); } + void startOneShot(double interval) { start(interval, 0); } + + void stop(); + bool isActive() const; + + virtual void fired() = 0; + +private: +#if PLATFORM(CF) + RetainPtr<CFRunLoopTimerRef> m_timer; +#endif +}; + +template <typename TimerFiredClass> class RunLoopTimer : public RunLoopTimerBase { +public: + typedef void (TimerFiredClass::*TimerFiredFunction)(RunLoopTimer*); + + RunLoopTimer(TimerFiredClass* o, TimerFiredFunction f) + : m_object(o), m_function(f) { } + + virtual void fired() { (m_object->*m_function)(this); } + +private: + TimerFiredClass* m_object; + TimerFiredFunction m_function; +}; + +} // namespace WebCore + +#endif diff --git a/WebCore/platform/ScrollView.cpp b/WebCore/platform/ScrollView.cpp index 573dabe..5a12304 100644 --- a/WebCore/platform/ScrollView.cpp +++ b/WebCore/platform/ScrollView.cpp @@ -441,6 +441,9 @@ const int panIconSizeLength = 20; void ScrollView::scrollContents(const IntSize& scrollDelta) { + if (!hostWindow()) + return; + // Since scrolling is double buffered, we will be blitting the scroll view's intersection // with the clip rect every time to keep it smooth. IntRect clipRect = windowClipRect(); @@ -531,8 +534,8 @@ void ScrollView::adjustScrollbarsAvoidingResizerCount(int overlapDelta) // If we went from n to 0 or from 0 to n and we're the outermost view, // we need to invalidate the windowResizerRect(), since it will now need to paint // differently. - if (oldCount > 0 && m_scrollbarsAvoidingResizer == 0 || - oldCount == 0 && m_scrollbarsAvoidingResizer > 0) + if ((oldCount > 0 && m_scrollbarsAvoidingResizer == 0) || + (oldCount == 0 && m_scrollbarsAvoidingResizer > 0)) invalidateRect(windowResizerRect()); } } @@ -615,10 +618,7 @@ void ScrollView::wheelEvent(PlatformWheelEvent& e) e.accept(); float deltaX = e.deltaX(); float deltaY = e.deltaY(); - if (e.granularity() == ScrollByLineWheelEvent) { - deltaX *= cMouseWheelPixelsPerLineStep; - deltaY *= cMouseWheelPixelsPerLineStep; - } else if (e.granularity() == ScrollByPageWheelEvent) { + if (e.granularity() == ScrollByPageWheelEvent) { ASSERT(deltaX == 0); bool negative = deltaY < 0; deltaY = max(0, visibleHeight() - cAmountToKeepWhenPaging); diff --git a/WebCore/platform/Scrollbar.h b/WebCore/platform/Scrollbar.h index 9238a77..2c5b274 100644 --- a/WebCore/platform/Scrollbar.h +++ b/WebCore/platform/Scrollbar.h @@ -42,8 +42,7 @@ class ScrollbarTheme; class PlatformMouseEvent; // These match the numbers we use over in WebKit (WebFrameView.m). -const int cScrollbarPixelsPerLineStep = 40; -const float cMouseWheelPixelsPerLineStep = 40.0f / 3.0f; +const int cScrollbarPixelsPerLineStep = 40; const int cAmountToKeepWhenPaging = 40; class Scrollbar : public Widget, public RefCounted<Scrollbar> { diff --git a/WebCore/platform/SharedTimer.h b/WebCore/platform/SharedTimer.h index 4cc90a2..a005add 100644 --- a/WebCore/platform/SharedTimer.h +++ b/WebCore/platform/SharedTimer.h @@ -28,17 +28,46 @@ namespace WebCore { - // Single timer, shared to implement all the timers managed by the Timer class. + // Each thread has its own single instance of shared timer, which implements this interface. + // This instance is shared by all timers in the thread. // Not intended to be used directly; use the Timer class instead. + class SharedTimer { + public: + virtual ~SharedTimer() {} + virtual void setFiredFunction(void (*)()) = 0; - void setSharedTimerFiredFunction(void (*)()); + // The fire time is relative to the classic POSIX epoch of January 1, 1970, + // as the result of currentTime() is. + virtual void setFireTime(double) = 0; + virtual void stop() = 0; + }; - // The fire time is relative to the classic POSIX epoch of January 1, 1970, - // as the result of currentTime() is. - void setSharedTimerFireTime(double fireTime); + // Implemented by port (since it provides the run loop for the main thread). + // FIXME: make ports implement MainThreadSharedTimer directly instead. + void setSharedTimerFiredFunction(void (*)()); + void setSharedTimerFireTime(double); void stopSharedTimer(); -} + // Implementation of SharedTimer for the main thread. + class MainThreadSharedTimer : public SharedTimer { + public: + virtual void setFiredFunction(void (*function)()) + { + setSharedTimerFiredFunction(function); + } + + virtual void setFireTime(double fireTime) + { + setSharedTimerFireTime(fireTime); + } + + virtual void stop() + { + stopSharedTimer(); + } + }; + +} // namespace WebCore -#endif +#endif // SharedTimer_h diff --git a/WebCore/platform/ThreadCheck.h b/WebCore/platform/ThreadCheck.h index d1ff4a4..07eb463 100644 --- a/WebCore/platform/ThreadCheck.h +++ b/WebCore/platform/ThreadCheck.h @@ -33,12 +33,18 @@ namespace WebCore { LogOnThreadViolation, RaiseExceptionOnThreadViolation }; - void setDefaultThreadViolationBehavior(ThreadViolationBehavior); - void reportThreadViolation(const char* function); + enum ThreadViolationRound { + ThreadViolationRoundOne = 0, + ThreadViolationRoundTwo, + MaximumThreadViolationRound + }; + void setDefaultThreadViolationBehavior(ThreadViolationBehavior, ThreadViolationRound); + void reportThreadViolation(const char* function, ThreadViolationRound); } -extern "C" void WebCoreReportThreadViolation(const char* function); +extern "C" void WebCoreReportThreadViolation(const char* function, WebCore::ThreadViolationRound); -#define WebCoreThreadViolationCheck() ::WebCore::reportThreadViolation(WTF_PRETTY_FUNCTION) +#define WebCoreThreadViolationCheckRoundOne() ::WebCore::reportThreadViolation(WTF_PRETTY_FUNCTION, WebCore::ThreadViolationRoundOne) +#define WebCoreThreadViolationCheckRoundTwo() ::WebCore::reportThreadViolation(WTF_PRETTY_FUNCTION, WebCore::ThreadViolationRoundTwo) #endif diff --git a/WebCore/platform/ThreadGlobalData.cpp b/WebCore/platform/ThreadGlobalData.cpp index 9bf0bf2..903af66 100644 --- a/WebCore/platform/ThreadGlobalData.cpp +++ b/WebCore/platform/ThreadGlobalData.cpp @@ -29,6 +29,7 @@ #include "EventNames.h" #include "StringImpl.h" +#include "ThreadTimers.h" #include <wtf/UnusedParam.h> #if USE(ICU_UNICODE) @@ -70,6 +71,7 @@ ThreadGlobalData::ThreadGlobalData() : m_emptyString(new StringImpl) , m_atomicStringTable(new HashSet<StringImpl*>) , m_eventNames(new EventNames) + , m_threadTimers(new ThreadTimers) #if USE(ICU_UNICODE) , m_cachedConverterICU(new ICUConverterWrapper) #endif @@ -90,6 +92,7 @@ ThreadGlobalData::~ThreadGlobalData() delete m_eventNames; delete m_atomicStringTable; + delete m_threadTimers; ASSERT(isMainThread() || m_emptyString->hasOneRef()); // We intentionally don't clean up static data on application quit, so there will be many strings remaining on the main thread. delete m_emptyString; diff --git a/WebCore/platform/ThreadGlobalData.h b/WebCore/platform/ThreadGlobalData.h index 17637aa..7faca36 100644 --- a/WebCore/platform/ThreadGlobalData.h +++ b/WebCore/platform/ThreadGlobalData.h @@ -36,6 +36,7 @@ namespace WebCore { class EventNames; struct ICUConverterWrapper; struct TECConverterWrapper; + class ThreadTimers; class ThreadGlobalData : Noncopyable { public: @@ -45,6 +46,7 @@ namespace WebCore { EventNames& eventNames() { return *m_eventNames; } StringImpl* emptyString() { return m_emptyString; } HashSet<StringImpl*>& atomicStringTable() { return *m_atomicStringTable; } + ThreadTimers& threadTimers() { return *m_threadTimers; } #if USE(ICU_UNICODE) ICUConverterWrapper& cachedConverterICU() { return *m_cachedConverterICU; } @@ -58,6 +60,7 @@ namespace WebCore { StringImpl* m_emptyString; HashSet<StringImpl*>* m_atomicStringTable; EventNames* m_eventNames; + ThreadTimers* m_threadTimers; #if USE(ICU_UNICODE) ICUConverterWrapper* m_cachedConverterICU; diff --git a/WebCore/platform/ThreadTimers.cpp b/WebCore/platform/ThreadTimers.cpp new file mode 100644 index 0000000..71a06b0 --- /dev/null +++ b/WebCore/platform/ThreadTimers.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ThreadTimers.h" + +#include "SharedTimer.h" +#include "ThreadGlobalData.h" +#include "Timer.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +// Timers are created, started and fired on the same thread, and each thread has its own ThreadTimers +// copy to keep the heap and a set of currently firing timers. + +static MainThreadSharedTimer* mainThreadSharedTimer() +{ + static MainThreadSharedTimer* timer = new MainThreadSharedTimer; + return timer; +} + +ThreadTimers::ThreadTimers() + : m_sharedTimer(0) + , m_firingTimers(false) +{ + if (isMainThread()) + setSharedTimer(mainThreadSharedTimer()); +} + +// A worker thread may initialize SharedTimer after some timers are created. +// Also, SharedTimer can be replaced with 0 before all timers are destroyed. +void ThreadTimers::setSharedTimer(SharedTimer* sharedTimer) +{ + if (m_sharedTimer) { + m_sharedTimer->setFiredFunction(0); + m_sharedTimer->stop(); + } + + m_sharedTimer = sharedTimer; + + if (sharedTimer) { + m_sharedTimer->setFiredFunction(ThreadTimers::sharedTimerFired); + updateSharedTimer(); + } +} + +void ThreadTimers::updateSharedTimer() +{ + if (!m_sharedTimer) + return; + + if (m_firingTimers || m_timerHeap.isEmpty()) + m_sharedTimer->stop(); + else + m_sharedTimer->setFireTime(m_timerHeap.first()->m_nextFireTime); +} + + +void ThreadTimers::collectFiringTimers(double fireTime, Vector<TimerBase*>& firingTimers) +{ + while (!m_timerHeap.isEmpty() && m_timerHeap.first()->m_nextFireTime <= fireTime) { + TimerBase* timer = m_timerHeap.first(); + firingTimers.append(timer); + m_timersReadyToFire.add(timer); + timer->m_nextFireTime = 0; + timer->heapDeleteMin(); + } +} + +void ThreadTimers::fireTimers(double fireTime, const Vector<TimerBase*>& firingTimers) +{ + size_t size = firingTimers.size(); + for (size_t i = 0; i != size; ++i) { + TimerBase* timer = firingTimers[i]; + + // If not in the set, this timer has been deleted or re-scheduled in another timer's fired function. + // So either we don't want to fire it at all or we will fire it next time the shared timer goes off. + // It might even have been deleted; that's OK because we won't do anything else with the pointer. + if (!m_timersReadyToFire.contains(timer)) + continue; + + // Setting the next fire time has a side effect of removing the timer from the firing timers set. + double interval = timer->repeatInterval(); + timer->setNextFireTime(interval ? fireTime + interval : 0); + + // Once the timer has been fired, it may be deleted, so do nothing else with it after this point. + timer->fired(); + + // Catch the case where the timer asked timers to fire in a nested event loop. + if (!m_firingTimers) + break; + } +} + +void ThreadTimers::sharedTimerFired() +{ + // Redirect to non-static method. + threadGlobalData().threadTimers().sharedTimerFiredInternal(); +} + +void ThreadTimers::sharedTimerFiredInternal() +{ + // Do a re-entrancy check. + if (m_firingTimers) + return; + m_firingTimers = true; + + double fireTime = currentTime(); + Vector<TimerBase*> firingTimers; + + // m_timersReadyToFire will initially contain the same set as firingTimers, but + // as timers fire some mat become re-scheduled or deleted. They get removed from + // m_timersReadyToFire so we can avoid firing them. + ASSERT(m_timersReadyToFire.isEmpty()); + + collectFiringTimers(fireTime, firingTimers); + fireTimers(fireTime, firingTimers); + + m_timersReadyToFire.clear(); + m_firingTimers = false; + + updateSharedTimer(); +} + +void ThreadTimers::fireTimersInNestedEventLoop() +{ + // Reset the reentrancy guard so the timers can fire again. + m_firingTimers = false; + m_timersReadyToFire.clear(); + updateSharedTimer(); +} + +} // namespace WebCore + diff --git a/WebCore/platform/ThreadTimers.h b/WebCore/platform/ThreadTimers.h new file mode 100644 index 0000000..366c320 --- /dev/null +++ b/WebCore/platform/ThreadTimers.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * 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 ThreadTimer_h +#define ThreadTimer_h + +#include <wtf/Noncopyable.h> +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +namespace WebCore { + + class SharedTimer; + class TimerBase; + + // A collection of timers per thread. Kept in ThreadGlobalData. + class ThreadTimers : Noncopyable { + public: + ThreadTimers(); + + // On a thread different then main, we should set the thread's instance of the SharedTimer. + void setSharedTimer(SharedTimer*); + + Vector<TimerBase*>& timerHeap() { return m_timerHeap; } + HashSet<const TimerBase*>& timersReadyToFire() { return m_timersReadyToFire; } + + void updateSharedTimer(); + void fireTimersInNestedEventLoop(); + + private: + static void sharedTimerFired(); + + void fireTimers(double fireTime, const Vector<TimerBase*>&); + void collectFiringTimers(double fireTime, Vector<TimerBase*>&); + void sharedTimerFiredInternal(); + void fireTimersInNestedEventLoopInternal(); + + Vector<TimerBase*> m_timerHeap; + HashSet<const TimerBase*> m_timersReadyToFire; // Temporarily holds a pointer to a stack object. No ownership. + SharedTimer* m_sharedTimer; // External object, can be a run loop on a worker thread. Normally set/reset by worker thread. + bool m_firingTimers; // Reentrancy guard. + }; + +} + +#endif diff --git a/WebCore/platform/Timer.cpp b/WebCore/platform/Timer.cpp index a8fcbb8..353a2a7 100644 --- a/WebCore/platform/Timer.cpp +++ b/WebCore/platform/Timer.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,6 +28,8 @@ #include "Timer.h" #include "SharedTimer.h" +#include "ThreadGlobalData.h" +#include "ThreadTimers.h" #include <limits.h> #include <limits> #include <math.h> @@ -44,22 +47,28 @@ namespace WebCore { // // When a timer's "next fire time" changes, we need to move it around in the priority queue. -// ---------------- - -#ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it -static bool deferringTimers; -#endif -static Vector<TimerBase*>* timerHeap; -static HashSet<const TimerBase*>* timersReadyToFire; +// Simple accessors to thread-specific data. +static Vector<TimerBase*>& timerHeap() +{ + return threadGlobalData().threadTimers().timerHeap(); +} -// ---------------- +static HashSet<const TimerBase*>& timersReadyToFire() +{ + return threadGlobalData().threadTimers().timersReadyToFire(); +} // Class to represent elements in the heap when calling the standard library heap algorithms. // Maintains the m_heapIndex value in the timers themselves, which allows us to do efficient // modification of the heap. class TimerHeapElement { public: - explicit TimerHeapElement(int i) : m_index(i), m_timer((*timerHeap)[m_index]) { checkConsistency(); } + explicit TimerHeapElement(int i) + : m_index(i) + , m_timer(timerHeap()[m_index]) + { + checkConsistency(); + } TimerHeapElement(const TimerHeapElement&); TimerHeapElement& operator=(const TimerHeapElement&); @@ -69,7 +78,7 @@ public: void checkConsistency() const { ASSERT(m_index >= 0); - ASSERT(m_index < (timerHeap ? static_cast<int>(timerHeap->size()) : 0)); + ASSERT(m_index < static_cast<int>(timerHeap().size())); } private: @@ -90,7 +99,7 @@ inline TimerHeapElement& TimerHeapElement::operator=(const TimerHeapElement& o) m_timer = t; if (m_index != -1) { checkConsistency(); - (*timerHeap)[m_index] = t; + timerHeap()[m_index] = t; t->m_heapIndex = m_index; } return *this; @@ -137,7 +146,7 @@ public: void checkConsistency(int offset = 0) const { ASSERT_UNUSED(offset, m_index + offset >= 0); - ASSERT_UNUSED(offset, m_index + offset <= (timerHeap ? static_cast<int>(timerHeap->size()) : 0)); + ASSERT_UNUSED(offset, m_index + offset <= static_cast<int>(timerHeap().size())); } private: @@ -156,31 +165,16 @@ inline int operator-(TimerHeapIterator a, TimerHeapIterator b) { return a.index( // ---------------- -void updateSharedTimer() -{ -#ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it - if (timersReadyToFire || deferringTimers || !timerHeap || timerHeap->isEmpty()) -#else - if (timersReadyToFire || !timerHeap || timerHeap->isEmpty()) -#endif - stopSharedTimer(); - else - setSharedTimerFireTime(timerHeap->first()->m_nextFireTime); -} - -// ---------------- - TimerBase::TimerBase() - : m_nextFireTime(0), m_repeatInterval(0), m_heapIndex(-1) + : m_nextFireTime(0) + , m_repeatInterval(0) + , m_heapIndex(-1) { - // We only need to do this once, but probably not worth trying to optimize it. - setSharedTimerFiredFunction(sharedTimerFired); } TimerBase::~TimerBase() { stop(); - ASSERT(!inHeap()); } @@ -202,7 +196,7 @@ void TimerBase::stop() bool TimerBase::isActive() const { - return m_nextFireTime || (timersReadyToFire && timersReadyToFire->contains(this)); + return m_nextFireTime || timersReadyToFire().contains(this); } double TimerBase::nextFireInterval() const @@ -216,11 +210,10 @@ double TimerBase::nextFireInterval() const inline void TimerBase::checkHeapIndex() const { - ASSERT(timerHeap); - ASSERT(!timerHeap->isEmpty()); + ASSERT(!timerHeap().isEmpty()); ASSERT(m_heapIndex >= 0); - ASSERT(m_heapIndex < static_cast<int>(timerHeap->size())); - ASSERT((*timerHeap)[m_heapIndex] == this); + ASSERT(m_heapIndex < static_cast<int>(timerHeap().size())); + ASSERT(timerHeap()[m_heapIndex] == this); } inline void TimerBase::checkConsistency() const @@ -243,15 +236,15 @@ inline void TimerBase::heapDelete() { ASSERT(m_nextFireTime == 0); heapPop(); - timerHeap->removeLast(); + timerHeap().removeLast(); m_heapIndex = -1; } -inline void TimerBase::heapDeleteMin() +void TimerBase::heapDeleteMin() { ASSERT(m_nextFireTime == 0); heapPopMin(); - timerHeap->removeLast(); + timerHeap().removeLast(); m_heapIndex = -1; } @@ -265,10 +258,8 @@ inline void TimerBase::heapIncreaseKey() inline void TimerBase::heapInsert() { ASSERT(!inHeap()); - if (!timerHeap) - timerHeap = new Vector<TimerBase*>; - timerHeap->append(this); - m_heapIndex = timerHeap->size() - 1; + timerHeap().append(this); + m_heapIndex = timerHeap().size() - 1; heapDecreaseKey(); } @@ -284,19 +275,18 @@ inline void TimerBase::heapPop() void TimerBase::heapPopMin() { - ASSERT(this == timerHeap->first()); + ASSERT(this == timerHeap().first()); checkHeapIndex(); - pop_heap(TimerHeapIterator(0), TimerHeapIterator(timerHeap->size())); + pop_heap(TimerHeapIterator(0), TimerHeapIterator(timerHeap().size())); checkHeapIndex(); - ASSERT(this == timerHeap->last()); + ASSERT(this == timerHeap().last()); } void TimerBase::setNextFireTime(double newTime) { // Keep heap valid while changing the next-fire time. - if (timersReadyToFire) - timersReadyToFire->remove(this); + timersReadyToFire().remove(this); double oldTime = m_nextFireTime; if (oldTime != newTime) { @@ -318,89 +308,16 @@ void TimerBase::setNextFireTime(double newTime) bool isFirstTimerInHeap = m_heapIndex == 0; if (wasFirstTimerInHeap || isFirstTimerInHeap) - updateSharedTimer(); + threadGlobalData().threadTimers().updateSharedTimer(); } checkConsistency(); } -void TimerBase::collectFiringTimers(double fireTime, Vector<TimerBase*>& firingTimers) -{ - while (!timerHeap->isEmpty() && timerHeap->first()->m_nextFireTime <= fireTime) { - TimerBase* timer = timerHeap->first(); - firingTimers.append(timer); - timersReadyToFire->add(timer); - timer->m_nextFireTime = 0; - timer->heapDeleteMin(); - } -} - -void TimerBase::fireTimers(double fireTime, const Vector<TimerBase*>& firingTimers) -{ - int size = firingTimers.size(); - for (int i = 0; i != size; ++i) { - TimerBase* timer = firingTimers[i]; - - // If not in the set, this timer has been deleted or re-scheduled in another timer's fired function. - // So either we don't want to fire it at all or we will fire it next time the shared timer goes off. - // It might even have been deleted; that's OK because we won't do anything else with the pointer. - if (!timersReadyToFire->contains(timer)) - continue; - - // Setting the next fire time has a side effect of removing the timer from the firing timers set. - double interval = timer->repeatInterval(); - timer->setNextFireTime(interval ? fireTime + interval : 0); - - // Once the timer has been fired, it may be deleted, so do nothing else with it after this point. - timer->fired(); - - // Catch the case where the timer asked timers to fire in a nested event loop. - if (!timersReadyToFire) - break; - } -} - -void TimerBase::sharedTimerFired() -{ - // Do a re-entrancy check. - if (timersReadyToFire) - return; - - double fireTime = currentTime(); - Vector<TimerBase*> firingTimers; - HashSet<const TimerBase*> firingTimersSet; - - timersReadyToFire = &firingTimersSet; - - collectFiringTimers(fireTime, firingTimers); - fireTimers(fireTime, firingTimers); - - timersReadyToFire = 0; - - updateSharedTimer(); -} - void TimerBase::fireTimersInNestedEventLoop() { - timersReadyToFire = 0; - updateSharedTimer(); -} - -#ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it -// ---------------- - -bool isDeferringTimers() -{ - return deferringTimers; -} - -void setDeferringTimers(bool shouldDefer) -{ - if (shouldDefer == deferringTimers) - return; - deferringTimers = shouldDefer; - updateSharedTimer(); + // Redirect to ThreadTimers. + threadGlobalData().threadTimers().fireTimersInNestedEventLoop(); } -#endif -} +} // namespace WebCore diff --git a/WebCore/platform/Timer.h b/WebCore/platform/Timer.h index a2589f7..aab52c2 100644 --- a/WebCore/platform/Timer.h +++ b/WebCore/platform/Timer.h @@ -27,7 +27,6 @@ #define Timer_h #include <wtf/Noncopyable.h> -#include <wtf/Vector.h> namespace WebCore { @@ -73,20 +72,13 @@ private: void heapPop(); void heapPopMin(); - static void collectFiringTimers(double fireTime, Vector<TimerBase*>&); - static void fireTimers(double fireTime, const Vector<TimerBase*>&); - static void sharedTimerFired(); - double m_nextFireTime; // 0 if inactive double m_repeatInterval; // 0 if not repeating int m_heapIndex; // -1 if not in heap unsigned m_heapInsertionOrder; // Used to keep order among equal-fire-time timers - friend void updateSharedTimer(); -#ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it - friend void setDeferringTimers(bool); -#endif friend class TimerHeapElement; + friend class ThreadTimers; friend bool operator<(const TimerHeapElement&, const TimerHeapElement&); }; @@ -104,13 +96,6 @@ private: TimerFiredFunction m_function; }; -#ifdef ANDROID_FIX // it is removed in http://trac.webkit.org/changeset/40080, but Android needs it -// Set to true to prevent any timers from firing. -// When set back to false, timers that were deferred will fire. -bool isDeferringTimers(); -void setDeferringTimers(bool); -#endif - } #endif diff --git a/WebCore/platform/Widget.h b/WebCore/platform/Widget.h index 6684eb2..459d615 100644 --- a/WebCore/platform/Widget.h +++ b/WebCore/platform/Widget.h @@ -142,7 +142,6 @@ public: virtual void setFocus(); void setCursor(const Cursor&); - Cursor cursor(); virtual void show(); virtual void hide(); @@ -198,7 +197,7 @@ private: IntRect m_frame; // Not used when a native widget exists. -#if PLATFORM(MAC) || PLATFORM(GTK) +#if PLATFORM(MAC) WidgetPrivate* m_data; #endif #if PLATFORM(ANDROID) diff --git a/WebCore/platform/android/FileChooserAndroid.cpp b/WebCore/platform/android/FileChooserAndroid.cpp index a8c4150..ec1b758 100644 --- a/WebCore/platform/android/FileChooserAndroid.cpp +++ b/WebCore/platform/android/FileChooserAndroid.cpp @@ -32,9 +32,8 @@ namespace WebCore { String FileChooser::basenameForWidth(const Font& font, int width) const { - if (m_filenames.size() == 0) { + if (!m_filenames.size()) return String(); - } // FIXME: This could be a lot faster, but assuming the data will not // often be much longer than the provided width, this may be fast enough. String output = m_filenames[0].copy(); diff --git a/WebCore/platform/android/FileSystemAndroid.cpp b/WebCore/platform/android/FileSystemAndroid.cpp index fcb0413..f2665a2 100644 --- a/WebCore/platform/android/FileSystemAndroid.cpp +++ b/WebCore/platform/android/FileSystemAndroid.cpp @@ -29,6 +29,7 @@ #include "FileSystem.h" #include "CString.h" +#include "StringBuilder.h" #include <fnmatch.h> #include <dlfcn.h> #include <dirent.h> @@ -42,7 +43,8 @@ namespace WebCore { // This is set in WebSettings.cpp String sPluginPath; -CString fileSystemRepresentation(const String& path) { +CString fileSystemRepresentation(const String& path) +{ return path.utf8(); } @@ -51,19 +53,20 @@ CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) int number = rand() % 10000 + 1; CString filename; do { - String path = sPluginPath; - path.append("/"); - path.append(prefix); - path.append(String::number(number)); - filename = path.utf8(); - const char *fstr = filename.data(); + StringBuilder builder; + builder.append(sPluginPath); + builder.append('/'); + builder.append(prefix); + builder.append(String::number(number)); + filename = builder.toString().utf8(); + const char* fstr = filename.data(); handle = open(filename.data(), O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); number++; - } while (handle == -1 && errno == EEXIST); + + if (handle != -1) + return filename; + } while (errno == EEXIST); - if (handle != -1) { - return filename; - } return CString(); } @@ -84,7 +87,7 @@ int writeToFile(PlatformFileHandle handle, const char* data, int length) { int totalBytesWritten = 0; while (totalBytesWritten < length) { - int bytesWritten = write(handle, data, length - totalBytesWritten); + int bytesWritten = write(handle, data, (size_t)(length - totalBytesWritten)); if (bytesWritten < 0 && errno != EINTR) return -1; else if (bytesWritten > 0) @@ -94,13 +97,11 @@ int writeToFile(PlatformFileHandle handle, const char* data, int length) return totalBytesWritten; } -// new as of SVN change 36269, Sept 8, 2008 String homeDirectoryPath() { return sPluginPath; } -// new as of webkit4, Feb 28, 2009 Vector<String> listDirectory(const String& path, const String& filter) { Vector<String> entries; @@ -108,15 +109,13 @@ Vector<String> listDirectory(const String& path, const String& filter) CString cfilter = filter.utf8(); DIR* dir = opendir(cpath.data()); if (dir) { - struct dirent * dp; - while ((dp = readdir(dir)) != NULL) { + struct dirent* dp; + while (dp = readdir(dir)) { const char* name = dp->d_name; - if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) { + if (!strcmp(name, ".") || !strcmp(name, "..")) continue; - } - if (fnmatch(cfilter.data(), name, 0) != 0) { + if (fnmatch(cfilter.data(), name, 0)) continue; - } char filePath[1024]; if ((int) (sizeof(filePath) - 1) < snprintf(filePath, sizeof(filePath), "%s/%s", cpath.data(), name)) { @@ -129,4 +128,4 @@ Vector<String> listDirectory(const String& path, const String& filter) return entries; } -} +} // namespace WebCore diff --git a/WebCore/platform/android/KeyEventAndroid.cpp b/WebCore/platform/android/KeyEventAndroid.cpp index 5496bbc..ab848bd 100644 --- a/WebCore/platform/android/KeyEventAndroid.cpp +++ b/WebCore/platform/android/KeyEventAndroid.cpp @@ -1,5 +1,10 @@ /* * Copyright 2007, The Android Open Source Project + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com + * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2008 Collabora, Ltd. All rights reserved. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -33,7 +38,8 @@ namespace WebCore { // compare to same function in gdk/KeyEventGdk.cpp -static int windowsKeyCodeForKeyEvent(unsigned int keyCode) { +static int windowsKeyCodeForKeyEvent(unsigned int keyCode) +{ // Does not provide all key codes, and does not handle all keys. switch(keyCode) { case kKeyCodeDel: @@ -190,7 +196,7 @@ static String keyIdentifierForAndroidKeyCode(int keyCode) } } -static inline String singleCharacterString(int c) +static inline String singleCharacterString(UChar32 c) { if (!c) return String(); @@ -215,10 +221,10 @@ PlatformKeyboardEvent::PlatformKeyboardEvent(int keyCode, UChar32 unichar, , m_windowsVirtualKeyCode(windowsKeyCodeForKeyEvent(keyCode)) , m_nativeVirtualKeyCode(keyCode) , m_isKeypad(false) - , m_shiftKey((mods & ShiftKey) != 0) - , m_ctrlKey((mods & CtrlKey) != 0) - , m_altKey((mods & AltKey) != 0) - , m_metaKey((mods & MetaKey) != 0) + , m_shiftKey((mods & ShiftKey)) + , m_ctrlKey((mods & CtrlKey)) + , m_altKey((mods & AltKey)) + , m_metaKey((mods & MetaKey)) // added for android , m_repeatCount(repeatCount) , m_unichar(unichar) diff --git a/WebCore/platform/android/LocalizedStringsAndroid.cpp b/WebCore/platform/android/LocalizedStringsAndroid.cpp index 02f135f..07cf229 100644 --- a/WebCore/platform/android/LocalizedStringsAndroid.cpp +++ b/WebCore/platform/android/LocalizedStringsAndroid.cpp @@ -27,6 +27,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#define LOG_TAG "WebCore" + #include "config.h" #include "LocalizedStrings.h" @@ -35,14 +37,16 @@ namespace WebCore { +// *** The following strings should be localized *** // + String contextMenuItemTagInspectElement() { - return String::fromUTF8("Inspect Element"); + return String("Inspect Element"); } String unknownFileSizeText() { - return String::fromUTF8("Unknown"); + return String("Unknown"); } String imageTitle(const String& filename, const IntSize& size) @@ -51,4 +55,254 @@ String imageTitle(const String& filename, const IntSize& size) return String(); } +// The following functions are used to fetch localized text for HTML form +// elements submit and reset. These strings are used when the page author +// has not specified any text for these buttons. +String submitButtonDefaultLabel() +{ + verifiedOk(); + return String("Submit"); +} + +String resetButtonDefaultLabel() +{ + verifiedOk(); + return String("Reset"); +} + +// The alt text for an input element is not used visually, but rather is +// used for accessability - eg reading the web page. See +// HTMLInputElement::altText() for more information. +String inputElementAltText() +{ + notImplemented(); + return String(); +} + +// This is the string that appears before an input box when the HTML element +// <ISINDEX> is used. The returned string is used if no PROMPT attribute is +// provided. +// note: Safari and FireFox use (too long for us imho) "This is a searchable index. Enter search keywords:" +String searchableIndexIntroduction() +{ + verifiedOk(); + return String("Enter search:"); +} + +String contextMenuItemTagOpenLinkInNewWindow() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagDownloadLinkToDisk() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCopyLinkToClipboard() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagOpenImageInNewWindow() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagDownloadImageToDisk() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCopyImageToClipboard() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagOpenFrameInNewWindow() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCopy() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagGoBack() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagGoForward() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagStop() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagReload() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCut() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagPaste() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagNoGuessesFound() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagIgnoreSpelling() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagLearnSpelling() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagSearchWeb() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagLookUpInDictionary() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagOpenLink() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagIgnoreGrammar() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagSpellingMenu() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagShowSpellingPanel(bool) +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCheckSpelling() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCheckSpellingWhileTyping() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagCheckGrammarWithSpelling() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagFontMenu() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagBold() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagItalic() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagUnderline() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagOutline() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagWritingDirectionMenu() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagDefaultDirection() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagLeftToRight() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagRightToLeft() +{ + ASSERT_NOT_REACHED(); + return String(); +} + +String contextMenuItemTagTextDirectionMenu() +{ + ASSERT_NOT_REACHED(); + return String(); +} + } // namespace WebCore diff --git a/WebCore/platform/android/PopupMenuAndroid.cpp b/WebCore/platform/android/PopupMenuAndroid.cpp index 4d1fe04..8a1ed07 100644 --- a/WebCore/platform/android/PopupMenuAndroid.cpp +++ b/WebCore/platform/android/PopupMenuAndroid.cpp @@ -39,7 +39,6 @@ PopupMenu::~PopupMenu() void PopupMenu::show(const IntRect&, FrameView*, int) { - } void PopupMenu::hide() diff --git a/WebCore/platform/android/RenderThemeAndroid.cpp b/WebCore/platform/android/RenderThemeAndroid.cpp index e104c8e..a1e8bf6 100644 --- a/WebCore/platform/android/RenderThemeAndroid.cpp +++ b/WebCore/platform/android/RenderThemeAndroid.cpp @@ -26,28 +26,33 @@ #include "config.h" #include "RenderThemeAndroid.h" +#include "Color.h" +#include "FormControlElement.h" +#include "GraphicsContext.h" +#include "PlatformGraphicsContext.h" #include "RenderSkinAndroid.h" #include "RenderSkinButton.h" #include "RenderSkinCombo.h" #include "RenderSkinRadio.h" - -#include "GraphicsContext.h" -#include "PlatformGraphicsContext.h" - #include "SkCanvas.h" -#define MAX_COMBO_HEIGHT 20 +namespace WebCore { + +const int MAX_COMBO_HEIGHT = 20; -// Add a constant amount of padding to the textsize to get the final height of buttons, -// so that our button images are large enough to properly fit the text. -#define BUTTON_PADDING 18 +// Add a constant amount of padding to the textsize to get the final height +// of buttons, so that our button images are large enough to properly fit +// the text. +const int BUTTON_PADDING = 18; // Add padding to the fontSize of ListBoxes to get their maximum sizes. -// Listboxes often have a specified size. Since we change them into dropdowns, -// we want a much smaller height, which encompasses the text. -#define LISTBOX_PADDING 5 +// Listboxes often have a specified size. Since we change them into +// dropdowns, we want a much smaller height, which encompasses the text. +const int LISTBOX_PADDING = 5; -namespace WebCore { +// This is the color of selection in a textfield. It was obtained by checking +// the color of selection in TextViews in the system. +const RGBA32 SELECTION_COLOR = makeRGB(255, 146, 0); static SkCanvas* getCanvasFromInfo(const RenderObject::PaintInfo& info) { @@ -72,10 +77,10 @@ void RenderThemeAndroid::close() { } -bool RenderThemeAndroid::stateChanged(RenderObject* o, ControlState state) const +bool RenderThemeAndroid::stateChanged(RenderObject* obj, ControlState state) const { if (CheckedState == state) { - o->repaint(); + obj->repaint(); return true; } return false; @@ -83,12 +88,12 @@ bool RenderThemeAndroid::stateChanged(RenderObject* o, ControlState state) const Color RenderThemeAndroid::platformActiveSelectionBackgroundColor() const { - return Color(46, 251, 0); + return Color(SELECTION_COLOR); } Color RenderThemeAndroid::platformInactiveSelectionBackgroundColor() const { - return Color(255, 255, 0, 255); + return Color(Color::transparent); } Color RenderThemeAndroid::platformActiveSelectionForegroundColor() const @@ -103,7 +108,7 @@ Color RenderThemeAndroid::platformInactiveSelectionForegroundColor() const Color RenderThemeAndroid::platformTextSearchHighlightColor() const { - return Color(192, 192, 192); + return Color(Color::transparent); } int RenderThemeAndroid::baselinePosition(const RenderObject* obj) const @@ -145,18 +150,18 @@ void RenderThemeAndroid::addIntrinsicMargins(RenderStyle* style) const bool RenderThemeAndroid::supportsFocus(ControlPart appearance) { switch (appearance) { - case PushButtonPart: - case ButtonPart: - case TextFieldPart: - return true; - default: - return false; + case PushButtonPart: + case ButtonPart: + case TextFieldPart: + return true; + default: + return false; } return false; } -void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const { // Padding code is taken from RenderThemeSafari.cpp // It makes sure we have enough space for the button text. @@ -166,29 +171,30 @@ void RenderThemeAndroid::adjustButtonStyle(CSSStyleSelector* selector, RenderSty style->setMinHeight(Length(style->fontSize() + BUTTON_PADDING, Fixed)); } -bool RenderThemeAndroid::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintCheckbox(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { - RenderSkinRadio::Draw(getCanvasFromInfo(i), o->element(), ir, true); + RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, true); return false; } -bool RenderThemeAndroid::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintButton(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { // If it is a disabled button, simply paint it to the master picture. - Node* element = o->element(); - if (!element->isEnabled()) { - RenderSkinButton::Draw(getCanvasFromInfo(i), ir, RenderSkinAndroid::kDisabled); - } else { + Node* node = obj->node(); + FormControlElement* formControlElement = toFormControlElement(static_cast<Element*>(node)); + if (formControlElement && !formControlElement->isEnabled()) + RenderSkinButton::Draw(getCanvasFromInfo(info), rect, RenderSkinAndroid::kDisabled); + else // Store all the important information in the platform context. - i.context->platformContext()->storeButtonInfo(element, ir); - } + info.context->platformContext()->storeButtonInfo(node, rect); + // We always return false so we do not request to be redrawn. return false; } -bool RenderThemeAndroid::paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintRadio(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { - RenderSkinRadio::Draw(getCanvasFromInfo(i), o->element(), ir, false); + RenderSkinRadio::Draw(getCanvasFromInfo(info), obj->node(), rect, false); return false; } @@ -204,74 +210,85 @@ void RenderThemeAndroid::setRadioSize(RenderStyle* style) const setCheckboxSize(style); } -void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +void RenderThemeAndroid::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const { addIntrinsicMargins(style); } -bool RenderThemeAndroid::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } -void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const +void RenderThemeAndroid::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, WebCore::Element*) const { addIntrinsicMargins(style); } -bool RenderThemeAndroid::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintTextArea(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { - if (o->isMenuList()) { - return paintCombo(o, i, ir); - } + if (obj->isMenuList()) + return paintCombo(obj, info, rect); return true; } -void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeAndroid::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { addIntrinsicMargins(style); } -bool RenderThemeAndroid::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } -void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeAndroid::adjustListboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const { style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); style->setMaxHeight(Length(style->fontSize() + LISTBOX_PADDING, Fixed)); addIntrinsicMargins(style); } -void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +static void adjustMenuListStyleCommon(RenderStyle* style, Element* e) { + // Added to make room for our arrow. style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); + // Code copied from RenderThemeMac.mm + // Makes sure that the text shows up on our treatment + bool isEnabled = true; + if (FormControlElement* formControlElement = toFormControlElement(e)) + isEnabled = formControlElement->isEnabled(); + style->setColor(isEnabled ? Color::black : Color::darkGray); +} + +void RenderThemeAndroid::adjustMenuListStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const +{ + adjustMenuListStyleCommon(style, e); addIntrinsicMargins(style); } -bool RenderThemeAndroid::paintCombo(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintCombo(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { - if (o->style() && o->style()->backgroundColor().alpha() == 0) + if (obj->style() && !obj->style()->backgroundColor().alpha()) return true; - Node* element = o->element(); - int height = ir.height(); - int y = ir.y(); + Node* node = obj->node(); + int height = rect.height(); + int y = rect.y(); // If the combo box is too large, leave it at its max height, and center it. if (height > MAX_COMBO_HEIGHT) { y += (height - MAX_COMBO_HEIGHT) >> 1; height = MAX_COMBO_HEIGHT; } - return RenderSkinCombo::Draw(getCanvasFromInfo(i), element, ir.x(), y, - ir.width(), height); + return RenderSkinCombo::Draw(getCanvasFromInfo(info), node, rect.x(), y, + rect.width(), height); } -bool RenderThemeAndroid::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintMenuList(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { - return paintCombo(o, i, ir); + return paintCombo(obj, info, rect); } -void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element* e) const { // Copied from RenderThemeSafari. const float baseFontSize = 11.0f; @@ -289,30 +306,29 @@ void RenderThemeAndroid::adjustMenuListButtonStyle(CSSStyleSelector* selector, R const int padding = 4; style->setPaddingTop(Length(padding, Fixed)); style->setPaddingLeft(Length(padding, Fixed)); - // Added to make room for our arrow. - style->setPaddingRight(Length(RenderSkinCombo::extraWidth(), Fixed)); + adjustMenuListStyleCommon(style, e); } -bool RenderThemeAndroid::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir) +bool RenderThemeAndroid::paintMenuListButton(RenderObject* obj, const RenderObject::PaintInfo& info, const IntRect& rect) { - return paintCombo(o, i, ir); + return paintCombo(obj, info, rect); } bool RenderThemeAndroid::supportsFocusRing(const RenderStyle* style) const { - return (style->opacity() > 0 && style->hasAppearance() - && style->appearance() != TextFieldPart - && style->appearance() != SearchFieldPart - && style->appearance() != TextAreaPart - && style->appearance() != CheckboxPart - && style->appearance() != RadioPart - && style->appearance() != PushButtonPart - && style->appearance() != SquareButtonPart - && style->appearance() != ButtonPart - && style->appearance() != ButtonBevelPart - && style->appearance() != MenulistPart - && style->appearance() != MenulistButtonPart - ); -} - -} + return style->opacity() > 0 + && style->hasAppearance() + && style->appearance() != TextFieldPart + && style->appearance() != SearchFieldPart + && style->appearance() != TextAreaPart + && style->appearance() != CheckboxPart + && style->appearance() != RadioPart + && style->appearance() != PushButtonPart + && style->appearance() != SquareButtonPart + && style->appearance() != ButtonPart + && style->appearance() != ButtonBevelPart + && style->appearance() != MenulistPart + && style->appearance() != MenulistButtonPart; +} + +} // namespace WebCore diff --git a/WebCore/platform/android/RenderThemeAndroid.h b/WebCore/platform/android/RenderThemeAndroid.h index 3b6e9cb..056b2d6 100644 --- a/WebCore/platform/android/RenderThemeAndroid.h +++ b/WebCore/platform/android/RenderThemeAndroid.h @@ -35,7 +35,11 @@ class RenderSkinRadio; class RenderSkinCombo; struct ThemeData { - ThemeData() :m_part(0), m_state(0) {} + ThemeData() + : m_part(0) + , m_state(0) + { + } unsigned m_part; unsigned m_state; @@ -48,7 +52,7 @@ public: virtual bool stateChanged(RenderObject*, ControlState) const; - virtual bool supportsFocusRing(const RenderStyle* style) const; + virtual bool supportsFocusRing(const RenderStyle*) const; // A method asking if the theme's controls actually care about redrawing when hovered. virtual bool supportsHover(const RenderStyle* style) const { return style->affectedByHoverRules(); } @@ -65,38 +69,38 @@ public: virtual int minimumMenuListSize(RenderStyle*) const { return 0; } protected: - virtual bool paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); - virtual void setCheckboxSize(RenderStyle* style) const; + virtual bool paintCheckbox(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void setCheckboxSize(RenderStyle*) const; - virtual bool paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); - virtual void setRadioSize(RenderStyle* style) const; + virtual bool paintRadio(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void setRadioSize(RenderStyle*) const; - virtual void adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const; - virtual bool paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, WebCore::Element*) const; + virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustTextFieldStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const; - virtual bool paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, WebCore::Element*) const; + virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustTextAreaStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const; - virtual bool paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, WebCore::Element*) const; + virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - bool paintCombo(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& ir); + bool paintCombo(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustListboxStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; - virtual void adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; - virtual bool paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void adjustListboxStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); - virtual void adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; - virtual bool paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); private: - void addIntrinsicMargins(RenderStyle* style) const; + void addIntrinsicMargins(RenderStyle*) const; void close(); - bool supportsFocus(ControlPart appearance); + bool supportsFocus(ControlPart); }; } // namespace WebCore diff --git a/WebCore/platform/android/ScrollViewAndroid.cpp b/WebCore/platform/android/ScrollViewAndroid.cpp index 5622d8b..a59faf3 100644 --- a/WebCore/platform/android/ScrollViewAndroid.cpp +++ b/WebCore/platform/android/ScrollViewAndroid.cpp @@ -28,6 +28,7 @@ #include "ScrollView.h" #include "FloatRect.h" +#include "Frame.h" #include "FrameView.h" #include "IntRect.h" #include "WebCoreFrameBridge.h" @@ -67,9 +68,13 @@ IntSize ScrollView::platformContentsSize() const void ScrollView::platformSetScrollPosition(const WebCore::IntPoint& pt) { - if (parent()) // don't attempt to scroll subframes; they're fully visible + android::WebViewCore* webviewCore = android::WebViewCore::getWebViewCore(this); + // don't attempt to scroll subframes; they're fully visible. + // as this can be called before the view is added to the parent in iframe + // creation, we can't depend on parent() checking. + if (webviewCore->mainFrame()->view() != this) return; - android::WebViewCore::getWebViewCore(this)->scrollTo(pt.x(), pt.y()); + webviewCore->scrollTo(pt.x(), pt.y()); } void ScrollView::platformScrollbarModes(ScrollbarMode& h, ScrollbarMode& v) const @@ -98,7 +103,10 @@ void ScrollView::platformRepaintContentRectangle(const IntRect &rect, bool now) #ifdef ANDROID_CAPTURE_OFFSCREEN_PAINTS void ScrollView::platformOffscreenContentRectangle(const IntRect& rect) { - android::WebViewCore::getWebViewCore(this)->offInvalidate(rect); + android::WebViewCore* core = android::WebViewCore::getWebViewCore(this); + if (!core) + return; + core->offInvalidate(rect); } #endif diff --git a/WebCore/platform/android/SystemTimeAndroid.cpp b/WebCore/platform/android/SystemTimeAndroid.cpp index a9c862a..1ecbfc7 100644 --- a/WebCore/platform/android/SystemTimeAndroid.cpp +++ b/WebCore/platform/android/SystemTimeAndroid.cpp @@ -31,7 +31,7 @@ namespace WebCore { float userIdleTime() { // Needed for PageCache, which we currently have disabled. - return 0.0F; + return 0.0f; } } // namespace WebCore diff --git a/WebCore/platform/android/TemporaryLinkStubs.cpp b/WebCore/platform/android/TemporaryLinkStubs.cpp index 5d71dd0..446b078 100644 --- a/WebCore/platform/android/TemporaryLinkStubs.cpp +++ b/WebCore/platform/android/TemporaryLinkStubs.cpp @@ -41,7 +41,6 @@ #include "CString.h" #include "Cursor.h" #include "Database.h" -//#include "DebuggerCallFrame.h" #include "DocumentFragment.h" #include "DocumentLoader.h" #include "EditCommand.h" @@ -61,12 +60,15 @@ #include "IconDatabase.h" #include "IconLoader.h" #include "IntPoint.h" + +#if USE(JSC) #include "JavaScriptCallFrame.h" #include "JavaScriptDebugServer.h" #include "API/JSClassRef.h" -#include "JavaScriptCallFrame.h" #include "JavaScriptProfile.h" #include "jni_utility.h" +#endif + #include "KURL.h" #include "Language.h" #include "loader.h" @@ -90,14 +92,6 @@ using namespace WebCore; -// This function is called when the frame view has changed the state of it's border. -// iFrames, which are have a FrameView, are drawn with a 1px left/right border and 2px top/bottom border -// Check function _shouldDrawBorder in WebFrameView.mm -// We don't draw borders unless css draws them. -//void FrameView::updateBorder() { verifiedOk(); } -//int WebCore::screenDepthPerComponent(Widget*) { ASSERT(0); notImplemented(); return 0; } -//bool WebCore::screenIsMonochrome(Widget*) { ASSERT(0); notImplemented(); return false; } - /********************************************************/ /* Completely empty stubs (mostly to allow DRT to run): */ /********************************************************/ @@ -124,7 +118,7 @@ namespace WebCore { // of a plugin can be used to render a mimetype that is not native to the browser. PluginInfo* PluginInfoStore::createPluginInfoForPluginAtIndex(unsigned) { - ASSERT(0); + ASSERT_NOT_REACHED(); return 0; } @@ -157,7 +151,7 @@ void refreshPlugins(bool) // the app may update progress with the amount of data loaded. void CheckCacheObjectStatus(DocLoader*, CachedResource*) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } @@ -168,42 +162,6 @@ void CheckCacheObjectStatus(DocLoader*, CachedResource*) Icon::~Icon() { } void Icon::paint(GraphicsContext*, const IntRect&) { } -// *** The following strings should be localized *** // - -// The following functions are used to fetch localized text for HTML form -// elements submit and reset. These strings are used when the page author -// has not specified any text for these buttons. -String submitButtonDefaultLabel() -{ - verifiedOk(); - return "Submit"; -} - -String resetButtonDefaultLabel() -{ - verifiedOk(); - return "Reset"; -} - -// The alt text for an input element is not used visually, but rather is -// used for accessability - eg reading the web page. See -// HTMLInputElement::altText() for more information. -String inputElementAltText() -{ - notImplemented(); - return String(); -} - -// This is the string that appears before an input box when the HTML element -// <ISINDEX> is used. The returned string is used if no PROMPT attribute is -// provided. -// note: Safari and FireFox use (too long for us imho) "This is a searchable index. Enter search keywords:" -String searchableIndexIntroduction() -{ - verifiedOk(); - return String("Enter search:"); -} - // This function provides the default value for the CSS property: // -webkit-focus-ring-color // It is also related to the CSS property outline-color: @@ -213,223 +171,6 @@ Color focusRingColor() return 0xFF0000FF; } -// LocalizedStrings -String contextMenuItemTagOpenLinkInNewWindow() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagDownloadLinkToDisk() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCopyLinkToClipboard() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagOpenImageInNewWindow() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagDownloadImageToDisk() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCopyImageToClipboard() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagOpenFrameInNewWindow() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCopy() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagGoBack() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagGoForward() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagStop() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagReload() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCut() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagPaste() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagNoGuessesFound() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagIgnoreSpelling() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagLearnSpelling() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagSearchWeb() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagLookUpInDictionary() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagOpenLink() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagIgnoreGrammar() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagSpellingMenu() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagShowSpellingPanel(bool) -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCheckSpelling() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCheckSpellingWhileTyping() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagCheckGrammarWithSpelling() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagFontMenu() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagBold() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagItalic() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagUnderline() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagOutline() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagWritingDirectionMenu() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagDefaultDirection() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagLeftToRight() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagRightToLeft() -{ - ASSERT(0); - return String(); -} - -String contextMenuItemTagTextDirectionMenu() -{ - ASSERT(0); - return String(); -} - } // namespace WebCore // FIXME, no support for spelling yet. @@ -484,126 +225,126 @@ Pasteboard::~Pasteboard() ContextMenu::ContextMenu(const HitTestResult& result) : m_hitTestResult(result) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } ContextMenu::~ContextMenu() { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } void ContextMenu::appendItem(ContextMenuItem&) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } void ContextMenu::setPlatformDescription(PlatformMenuDescription menu) { - ASSERT(0); + ASSERT_NOT_REACHED(); m_platformDescription = menu; } PlatformMenuDescription ContextMenu::platformDescription() const { - ASSERT(0); + ASSERT_NOT_REACHED(); return m_platformDescription; } ContextMenuItem::ContextMenuItem(PlatformMenuItemDescription) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } ContextMenuItem::ContextMenuItem(ContextMenu*) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } ContextMenuItem::ContextMenuItem(ContextMenuItemType, ContextMenuAction, const String&, ContextMenu*) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } ContextMenuItem::~ContextMenuItem() { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } PlatformMenuItemDescription ContextMenuItem::releasePlatformDescription() { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); return m_platformDescription; } ContextMenuItemType ContextMenuItem::type() const { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); return ActionType; } void ContextMenuItem::setType(ContextMenuItemType) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } ContextMenuAction ContextMenuItem::action() const { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); return ContextMenuItemTagNoAction; } void ContextMenuItem::setAction(ContextMenuAction) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } String ContextMenuItem::title() const { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); return String(); } void ContextMenuItem::setTitle(const String&) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } PlatformMenuDescription ContextMenuItem::platformSubMenu() const { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); return 0; } void ContextMenuItem::setSubMenu(ContextMenu*) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } void ContextMenuItem::setChecked(bool) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } void ContextMenuItem::setEnabled(bool) { - ASSERT(0); + ASSERT_NOT_REACHED(); notImplemented(); } @@ -614,16 +355,6 @@ void systemBeep() notImplemented(); } -// functions new to Jun-07 tip of tree merge: - -// void WebCore::CachedPage::close() {} - -//void WebCore::Frame::print() {} -// void WebCore::Frame::issueTransposeCommand() {} -//void WebCore::Frame::cleanupPlatformScriptObjects() {} -// void WebCore::Frame::dashboardRegionsChanged() {} -//bool WebCore::Frame::isCharacterSmartReplaceExempt(unsigned short, bool) { return false; } - void* WebCore::Frame::dragImageForSelection() { return 0; @@ -641,7 +372,7 @@ namespace WebCore { IntSize dragImageSize(void*) { - return IntSize(0, 0); + return IntSize(); } void deleteDragImage(void*) {} @@ -709,13 +440,6 @@ String signedPublicKeyAndChallengeString(unsigned int, String const&, WebCore::K } // namespace WebCore -// added for Nov-16-07 ToT integration -//namespace WebCore { -//void Frame::clearPlatformScriptObjects() { notImplemented(); } - -//} - -// functions new to Feb-19 tip of tree merge: namespace WebCore { // isCharacterSmartReplaceExempt is defined in SmartReplaceICU.cpp; in theory, we could use that one // but we don't support all of the required icu functions @@ -729,7 +453,6 @@ bool isCharacterSmartReplaceExempt(UChar32, bool) int MakeDataExecutable; -// functions new to Mar-2 tip of tree merge: String KURL::fileSystemPath() const { notImplemented(); @@ -737,7 +460,6 @@ String KURL::fileSystemPath() const } -// functions new to Jun-1 tip of tree merge: PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String&) { notImplemented(); @@ -745,6 +467,7 @@ PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String&) } +#if USE(JSC) namespace JSC { namespace Bindings { bool dispatchJNICall(ExecState*, const void* targetAppletView, jobject obj, bool isStatic, JNIType returnType, jmethodID methodID, jvalue* args, jvalue& result, const char* callingURL, JSValuePtr& exceptionDescription) @@ -754,6 +477,7 @@ bool dispatchJNICall(ExecState*, const void* targetAppletView, jobject obj, bool } } } // namespace Bindings +#endif char* dirname(const char*) { @@ -761,14 +485,6 @@ char* dirname(const char*) return 0; } - // new as of SVN change 36269, Sept 8, 2008 -const String& Database::databaseInfoTableName() -{ - notImplemented(); - static const String dummy; - return dummy; -} - // new as of SVN change 38068, Nov 5, 2008 namespace WebCore { void prefetchDNS(const String&) @@ -839,6 +555,7 @@ void AXObjectCache::remove(RenderObject*) notImplemented(); } +#if USE(JSC) using namespace JSC; @@ -962,3 +679,4 @@ void JavaScriptDebugServer::willExecuteProgram(const DebuggerCallFrame&, int, in { notImplemented(); } +#endif diff --git a/WebCore/platform/android/TextBoundaries.cpp b/WebCore/platform/android/TextBoundaries.cpp deleted file mode 100644 index 457a36e..0000000 --- a/WebCore/platform/android/TextBoundaries.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2009, The Android Open Source Project - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "TextBoundaries.h" - -#include <unicode/ubrk.h> - -#include "TextBreakIterator.h" - -namespace WebCore { - -int findNextWordFromIndex(const UChar* chars, int len, int position, bool forward) -{ - UBreakIterator* it = wordBreakIterator(chars, len); - - int newPosition = position; - - if (forward) { - position = ubrk_following(it, position); - while (position != UBRK_DONE) { - // We stop searching when the character preceeding the break - // is alphanumeric. - if (position < len && u_isalnum(chars[position - 1])) - return position; - - position = ubrk_following(it, position); - } - - return len; - } else { - position = ubrk_preceding(it, position); - while (position != UBRK_DONE) { - // We stop searching when the character following the break - // is alphanumeric. - if (position > 0 && u_isalnum(chars[position])) - return position; - - position = ubrk_preceding(it, position); - } - - return 0; - } -} - -void findWordBoundary(const UChar* chars, int len, int position, int* start, int* end) -{ - UBreakIterator* it = wordBreakIterator(chars, len); - *end = ubrk_following(it, position); - if (*end < 0) - *end = ubrk_last(it); - *start = ubrk_previous(it); -} - -} // namespace WebCore diff --git a/WebCore/platform/android/WidgetAndroid.cpp b/WebCore/platform/android/WidgetAndroid.cpp index 84ab26c..fcfea2e 100644 --- a/WebCore/platform/android/WidgetAndroid.cpp +++ b/WebCore/platform/android/WidgetAndroid.cpp @@ -51,7 +51,7 @@ IntRect Widget::frameRect() const { // FIXME: use m_frame instead? if (!platformWidget()) - return IntRect(0, 0, 0, 0); + return IntRect(); return platformWidget()->getBounds(); } @@ -118,9 +118,11 @@ int Widget::screenWidth() const } if (!widget) return 0; - - return android::WebViewCore::getWebViewCore( - static_cast<const ScrollView*>(widget))->screenWidth(); + android::WebViewCore* core = android::WebViewCore::getWebViewCore( + static_cast<const ScrollView*>(widget)); + if (!core) + return 0; + return core->screenWidth(); } } // WebCore namepsace diff --git a/WebCore/platform/animation/Animation.cpp b/WebCore/platform/animation/Animation.cpp index 5df4480..05761f8 100644 --- a/WebCore/platform/animation/Animation.cpp +++ b/WebCore/platform/animation/Animation.cpp @@ -25,13 +25,13 @@ namespace WebCore { Animation::Animation() - : m_delay(initialAnimationDelay()) - , m_direction(initialAnimationDirection()) - , m_duration(initialAnimationDuration()) - , m_iterationCount(initialAnimationIterationCount()) - , m_name(initialAnimationName()) + : m_name(initialAnimationName()) , m_property(initialAnimationProperty()) + , m_iterationCount(initialAnimationIterationCount()) + , m_delay(initialAnimationDelay()) + , m_duration(initialAnimationDuration()) , m_timingFunction(initialAnimationTimingFunction()) + , m_direction(initialAnimationDirection()) , m_playState(initialAnimationPlayState()) , m_delaySet(false) , m_directionSet(false) @@ -47,13 +47,13 @@ Animation::Animation() Animation::Animation(const Animation& o) : RefCounted<Animation>() - , m_delay(o.m_delay) - , m_direction(o.m_direction) - , m_duration(o.m_duration) - , m_iterationCount(o.m_iterationCount) , m_name(o.m_name) , m_property(o.m_property) + , m_iterationCount(o.m_iterationCount) + , m_delay(o.m_delay) + , m_duration(o.m_duration) , m_timingFunction(o.m_timingFunction) + , m_direction(o.m_direction) , m_playState(o.m_playState) , m_delaySet(o.m_delaySet) , m_directionSet(o.m_directionSet) @@ -69,14 +69,14 @@ Animation::Animation(const Animation& o) Animation& Animation::operator=(const Animation& o) { - m_delay = o.m_delay; - m_direction = o.m_direction; - m_duration = o.m_duration; - m_iterationCount = o.m_iterationCount; m_name = o.m_name; - m_playState = o.m_playState; m_property = o.m_property; + m_iterationCount = o.m_iterationCount; + m_delay = o.m_delay; + m_duration = o.m_duration; m_timingFunction = o.m_timingFunction; + m_direction = o.m_direction; + m_playState = o.m_playState; m_delaySet = o.m_delaySet; m_directionSet = o.m_directionSet; @@ -86,7 +86,6 @@ Animation& Animation::operator=(const Animation& o) m_playStateSet = o.m_playStateSet; m_propertySet = o.m_propertySet; m_timingFunctionSet = o.m_timingFunctionSet; - m_isNone = o.m_isNone; return *this; @@ -101,13 +100,13 @@ bool Animation::animationsMatch(const Animation* o, bool matchPlayStates) const if (!o) return false; - bool result = m_delay == o->m_delay && - m_direction == o->m_direction && - m_duration == o->m_duration && - m_iterationCount == o->m_iterationCount && - m_name == o->m_name && + bool result = m_name == o->m_name && m_property == o->m_property && + m_iterationCount == o->m_iterationCount && + m_delay == o->m_delay && + m_duration == o->m_duration && m_timingFunction == o->m_timingFunction && + m_direction == o->m_direction && m_delaySet == o->m_delaySet && m_directionSet == o->m_directionSet && m_durationSet == o->m_durationSet && diff --git a/WebCore/platform/animation/Animation.h b/WebCore/platform/animation/Animation.h index 294acdf..9e1e8e1 100644 --- a/WebCore/platform/animation/Animation.h +++ b/WebCore/platform/animation/Animation.h @@ -35,6 +35,12 @@ namespace WebCore { const int cAnimateNone = 0; const int cAnimateAll = -2; +// These were in RenderStyle, but have been moved here as +// animation-play-state is in the process of being removed. + +const unsigned AnimPlayStatePlaying = 0; +const unsigned AnimPlayStatePaused = 1; + class Animation : public RefCounted<Animation> { public: ~Animation(); @@ -77,8 +83,13 @@ public: void clearTimingFunction() { m_timingFunctionSet = false; } double delay() const { return m_delay; } - bool direction() const { return m_direction; } + + enum AnimationDirection { AnimationDirectionNormal, AnimationDirectionAlternate }; + AnimationDirection direction() const { return m_direction; } + double duration() const { return m_duration; } + + enum { IterationCountInfinite = -1 }; int iterationCount() const { return m_iterationCount; } const String& name() const { return m_name; } unsigned playState() const { return m_playState; } @@ -86,7 +97,7 @@ public: const TimingFunction& timingFunction() const { return m_timingFunction; } void setDelay(double c) { m_delay = c; m_delaySet = true; } - void setDirection(bool d) { m_direction = d; m_directionSet = true; } + void setDirection(AnimationDirection d) { m_direction = d; m_directionSet = true; } void setDuration(double d) { ASSERT(d >= 0); m_duration = d; m_durationSet = true; } void setIterationCount(int c) { m_iterationCount = c; m_iterationCountSet = true; } void setName(const String& n) { m_name = n; m_nameSet = true; } @@ -109,13 +120,13 @@ private: Animation(); Animation(const Animation& o); - double m_delay; - bool m_direction; - double m_duration; - int m_iterationCount; String m_name; int m_property; + int m_iterationCount; + double m_delay; + double m_duration; TimingFunction m_timingFunction; + AnimationDirection m_direction : 1; unsigned m_playState : 2; @@ -132,7 +143,7 @@ private: public: static float initialAnimationDelay() { return 0; } - static bool initialAnimationDirection() { return false; } + static AnimationDirection initialAnimationDirection() { return AnimationDirectionNormal; } static double initialAnimationDuration() { return 0; } static int initialAnimationIterationCount() { return 1; } static String initialAnimationName() { return String("none"); } diff --git a/WebCore/platform/cf/RunLoopTimerCF.cpp b/WebCore/platform/cf/RunLoopTimerCF.cpp new file mode 100644 index 0000000..c97dd21 --- /dev/null +++ b/WebCore/platform/cf/RunLoopTimerCF.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RunLoopTimer.h" + +#if PLATFORM(MAC) + +namespace WebCore { + +RunLoopTimerBase::~RunLoopTimerBase() +{ + stop(); +} + +static void timerFired(CFRunLoopTimerRef, void* context) +{ + RunLoopTimerBase* timer = static_cast<RunLoopTimerBase*>(context); + timer->fired(); +} + +void RunLoopTimerBase::start(double nextFireInterval, double repeatInterval) +{ + if (m_timer) + CFRunLoopTimerInvalidate(m_timer.get()); + CFRunLoopTimerContext context = { 0, this, 0, 0, 0 }; + m_timer.adoptCF(CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + nextFireInterval, repeatInterval, 0, 0, timerFired, &context)); +} + +void RunLoopTimerBase::schedule(const SchedulePair* schedulePair) +{ + ASSERT_ARG(schedulePair, schedulePair); + ASSERT_WITH_MESSAGE(m_timer, "Timer must have one of the start functions called before calling schedule()."); + CFRunLoopAddTimer(schedulePair->runLoop(), m_timer.get(), schedulePair->mode()); +} + +void RunLoopTimerBase::schedule(const SchedulePairHashSet& schedulePairs) +{ + SchedulePairHashSet::const_iterator end = schedulePairs.end(); + for (SchedulePairHashSet::const_iterator it = schedulePairs.begin(); it != end; ++it) + schedule((*it).get()); +} + +void RunLoopTimerBase::stop() +{ + if (!m_timer) + return; + CFRunLoopTimerInvalidate(m_timer.get()); + m_timer = 0; +} + +bool RunLoopTimerBase::isActive() const +{ + return m_timer && CFRunLoopTimerIsValid(m_timer.get()); +} + +} // namespace WebCore + +#endif diff --git a/WebCore/platform/chromium/ChromiumBridge.h b/WebCore/platform/chromium/ChromiumBridge.h index dd36c1a..3e5c404 100644 --- a/WebCore/platform/chromium/ChromiumBridge.h +++ b/WebCore/platform/chromium/ChromiumBridge.h @@ -92,6 +92,7 @@ namespace WebCore { // JavaScript --------------------------------------------------------- static void notifyJSOutOfMemory(Frame*); + static bool allowScriptDespiteSettings(const KURL& documentURL); // Language ----------------------------------------------------------- static String computedDefaultLanguage(); @@ -100,11 +101,10 @@ namespace WebCore { static bool layoutTestMode(); // MimeType ----------------------------------------------------------- - static bool isSupportedImageMIMEType(const char* mimeType); - static bool isSupportedJavascriptMIMEType(const char* mimeType); - static bool isSupportedNonImageMIMEType(const char* mimeType); - static bool matchesMIMEType(const String& pattern, const String& type); - static String mimeTypeForExtension(const String& ext); + static bool isSupportedImageMIMEType(const String& mimeType); + static bool isSupportedJavaScriptMIMEType(const String& mimeType); + static bool isSupportedNonImageMIMEType(const String& mimeType); + static String mimeTypeForExtension(const String& fileExtension); static String mimeTypeFromFile(const String& filePath); static String preferredExtensionForMIMEType(const String& mimeType); @@ -114,7 +114,7 @@ namespace WebCore { static bool popupsAllowed(NPP); // Protocol ----------------------------------------------------------- - static String uiResourceProtocol(); + static String uiResourceProtocol(); // deprecated // Resources ---------------------------------------------------------- static PassRefPtr<Image> loadPlatformImageResource(const char* name); @@ -134,7 +134,6 @@ namespace WebCore { // StatsCounters ------------------------------------------------------ static void decrementStatsCounter(const char* name); static void incrementStatsCounter(const char* name); - static void initV8CounterFunction(); // SystemTime --------------------------------------------------------- static double currentTime(); @@ -153,15 +152,14 @@ namespace WebCore { GraphicsContext*, int part, int state, int classicState, const IntRect&, const IntRect& alignRect); static void paintTextField( GraphicsContext*, int part, int state, int classicState, const IntRect&, const Color&, bool fillContentArea, bool drawEdges); + static void paintTrackbar( + GraphicsContext*, int part, int state, int classicState, const IntRect&); #endif // Trace Event -------------------------------------------------------- static void traceEventBegin(const char* name, void* id, const char* extra); static void traceEventEnd(const char* name, void* id, const char* extra); - // URL ---------------------------------------------------------------- - static KURL inspectorURL(); - // Visited links ------------------------------------------------------ static LinkHash visitedLinkHash(const UChar* url, unsigned length); static LinkHash visitedLinkHash(const KURL& base, const AtomicString& attributeURL); diff --git a/WebCore/platform/chromium/ChromiumDataObject.cpp b/WebCore/platform/chromium/ChromiumDataObject.cpp index 67e9d00..dee4568 100644 --- a/WebCore/platform/chromium/ChromiumDataObject.cpp +++ b/WebCore/platform/chromium/ChromiumDataObject.cpp @@ -37,6 +37,7 @@ void ChromiumDataObject::clear() { url = KURL(); urlTitle = ""; + fileExtension = ""; filenames.clear(); plainText = ""; textHtml = ""; @@ -49,6 +50,7 @@ void ChromiumDataObject::clear() bool ChromiumDataObject::hasData() { return !url.isEmpty() + || !fileExtension.isEmpty() || !filenames.isEmpty() || !plainText.isEmpty() || !textHtml.isEmpty() diff --git a/WebCore/platform/chromium/ChromiumDataObject.h b/WebCore/platform/chromium/ChromiumDataObject.h index 448e763..19b91c4 100644 --- a/WebCore/platform/chromium/ChromiumDataObject.h +++ b/WebCore/platform/chromium/ChromiumDataObject.h @@ -54,6 +54,7 @@ namespace WebCore { KURL url; String urlTitle; + String fileExtension; Vector<String> filenames; String plainText; diff --git a/WebCore/platform/chromium/ClipboardChromium.cpp b/WebCore/platform/chromium/ClipboardChromium.cpp index 7fc156e..b28503d 100644 --- a/WebCore/platform/chromium/ClipboardChromium.cpp +++ b/WebCore/platform/chromium/ClipboardChromium.cpp @@ -268,15 +268,15 @@ static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* elem // use the alt tag if one exists, otherwise we fall back on the suggested // filename in the http header, and finally we resort to using the filename // in the URL. - String extension("."); - extension += MIMETypeRegistry::getPreferredExtensionForMIMEType( + String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType( cachedImage->response().mimeType()); + dataObject->fileExtension = extension.isEmpty() ? "" : "." + extension; String title = element->getAttribute(altAttr); - if (title.isEmpty()) { + if (title.isEmpty()) title = cachedImage->response().suggestedFilename(); - // FIXME: If title is empty, get the filename from the URL. - } - dataObject->fileContentFilename = title + extension; + + title = ClipboardChromium::validateFileName(title, dataObject); + dataObject->fileContentFilename = title + dataObject->fileExtension; } void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) diff --git a/WebCore/platform/chromium/ClipboardChromium.h b/WebCore/platform/chromium/ClipboardChromium.h index 1864c1a..53699da 100644 --- a/WebCore/platform/chromium/ClipboardChromium.h +++ b/WebCore/platform/chromium/ClipboardChromium.h @@ -47,6 +47,12 @@ namespace WebCore { static PassRefPtr<ClipboardChromium> create( bool isForDragging, PassRefPtr<ChromiumDataObject>, ClipboardAccessPolicy); + // Returns the file name (not including the extension). This removes any + // invalid file system characters as well as making sure the + // path + extension is not bigger than allowed by the file system. + // This may change the file extension in dataObject. + static String validateFileName(const String& title, ChromiumDataObject* dataObject); + virtual void clearData(const String& type); void clearAllData(); String getData(const String& type, bool& success) const; diff --git a/WebCore/platform/chromium/ClipboardChromiumLinux.cpp b/WebCore/platform/chromium/ClipboardChromiumLinux.cpp new file mode 100644 index 0000000..2c89f6e --- /dev/null +++ b/WebCore/platform/chromium/ClipboardChromiumLinux.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClipboardChromium.h" + +#include "ChromiumDataObject.h" +#include "NotImplemented.h" + +namespace WebCore { + +String ClipboardChromium::validateFileName(const String& title, ChromiumDataObject* dataObject) +{ + notImplemented(); + return title; +} + +} // namespace WebCore diff --git a/WebCore/platform/chromium/ClipboardChromiumMac.cpp b/WebCore/platform/chromium/ClipboardChromiumMac.cpp new file mode 100644 index 0000000..2c89f6e --- /dev/null +++ b/WebCore/platform/chromium/ClipboardChromiumMac.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClipboardChromium.h" + +#include "ChromiumDataObject.h" +#include "NotImplemented.h" + +namespace WebCore { + +String ClipboardChromium::validateFileName(const String& title, ChromiumDataObject* dataObject) +{ + notImplemented(); + return title; +} + +} // namespace WebCore diff --git a/WebCore/platform/chromium/ClipboardChromiumWin.cpp b/WebCore/platform/chromium/ClipboardChromiumWin.cpp new file mode 100644 index 0000000..b4a2c21 --- /dev/null +++ b/WebCore/platform/chromium/ClipboardChromiumWin.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ClipboardChromium.h" + +#include "ChromiumDataObject.h" + +#include <shlwapi.h> + +namespace WebCore { + +// Returns true if the specified character is not valid in a file name. This +// is intended for use with removeCharacters. +static bool isInvalidFileCharacter(UChar c) +{ + return (PathGetCharType(c) & (GCT_LFNCHAR | GCT_SHORTCHAR)) == 0; +} + +String ClipboardChromium::validateFileName(const String& title, ChromiumDataObject* dataObject) +{ + // Remove any invalid file system characters. + String result = title.removeCharacters(&isInvalidFileCharacter); + if (result.length() + dataObject->fileExtension.length() + 1 >= MAX_PATH) { + if (dataObject->fileExtension.length() + 1 >= MAX_PATH) + dataObject->fileExtension = ""; + if (result.length() + dataObject->fileExtension.length() + 1 >= MAX_PATH) + result = result.substring(0, MAX_PATH - dataObject->fileExtension.length() - 1); + } + return result; +} + +} // namespace WebCore diff --git a/WebCore/platform/chromium/MimeTypeRegistryChromium.cpp b/WebCore/platform/chromium/MimeTypeRegistryChromium.cpp index 5d6f426..1aac5ec 100644 --- a/WebCore/platform/chromium/MimeTypeRegistryChromium.cpp +++ b/WebCore/platform/chromium/MimeTypeRegistryChromium.cpp @@ -83,8 +83,7 @@ String MIMETypeRegistry::getMIMETypeForPath(const String& path) bool MIMETypeRegistry::isSupportedImageMIMEType(const String& mimeType) { - return !mimeType.isEmpty() - && ChromiumBridge::isSupportedImageMIMEType(mimeType.latin1().data()); + return ChromiumBridge::isSupportedImageMIMEType(mimeType); } bool MIMETypeRegistry::isSupportedImageResourceMIMEType(const String& mimeType) @@ -100,14 +99,12 @@ bool MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(const String& mimeTyp bool MIMETypeRegistry::isSupportedJavaScriptMIMEType(const String& mimeType) { - return !mimeType.isEmpty() - && ChromiumBridge::isSupportedJavascriptMIMEType(mimeType.latin1().data()); + return ChromiumBridge::isSupportedJavaScriptMIMEType(mimeType); } bool MIMETypeRegistry::isSupportedNonImageMIMEType(const String& mimeType) { - return !mimeType.isEmpty() - && ChromiumBridge::isSupportedNonImageMIMEType(mimeType.latin1().data()); + return ChromiumBridge::isSupportedNonImageMIMEType(mimeType); } bool MIMETypeRegistry::isSupportedMediaMIMEType(const String& mimeType) diff --git a/WebCore/platform/chromium/PasteboardChromium.cpp b/WebCore/platform/chromium/PasteboardChromium.cpp index e7b2203..9b32bae 100644 --- a/WebCore/platform/chromium/PasteboardChromium.cpp +++ b/WebCore/platform/chromium/PasteboardChromium.cpp @@ -98,6 +98,15 @@ void Pasteboard::writeURL(const KURL& url, const String& titleStr, Frame* frame) void Pasteboard::writeImage(Node* node, const KURL&, const String& title) { + ASSERT(node); + ASSERT(node->renderer()); + ASSERT(node->renderer()->isImage()); + RenderImage* renderer = static_cast<RenderImage*>(node->renderer()); + CachedImage* cachedImage = static_cast<CachedImage*>(renderer->cachedImage()); + ASSERT(cachedImage); + Image* image = cachedImage->image(); + ASSERT(image); + // If the image is wrapped in a link, |url| points to the target of the // link. This isn't useful to us, so get the actual image URL. AtomicString urlString; @@ -113,16 +122,6 @@ void Pasteboard::writeImage(Node* node, const KURL&, const String& title) } KURL url = urlString.isEmpty() ? KURL() : node->document()->completeURL(parseURL(urlString)); - ASSERT(node); - ASSERT(node->renderer()); - ASSERT(node->renderer()->isImage()); - - RenderImage* renderer = static_cast<RenderImage*>(node->renderer()); - CachedImage* cachedImage = static_cast<CachedImage*>(renderer->cachedImage()); - ASSERT(cachedImage); - Image* image = cachedImage->image(); - ASSERT(image); - NativeImageSkia* bitmap = 0; #if !PLATFORM(CG) bitmap = image->nativeImageForCurrentFrame(); diff --git a/WebCore/platform/chromium/PlatformKeyboardEventChromium.cpp b/WebCore/platform/chromium/PlatformKeyboardEventChromium.cpp index 6840bdf..ae55afe 100644 --- a/WebCore/platform/chromium/PlatformKeyboardEventChromium.cpp +++ b/WebCore/platform/chromium/PlatformKeyboardEventChromium.cpp @@ -42,7 +42,7 @@ void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCom #if PLATFORM(WIN_OS) // No KeyDown events on Windows to disambiguate. ASSERT_NOT_REACHED(); -#elif PLATFORM(DARWIN) +#else // Can only change type from KeyDown to RawKeyDown or Char, as we lack information for other conversions. ASSERT(m_type == KeyDown); ASSERT(type == RawKeyDown || type == Char); @@ -56,6 +56,7 @@ void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCom } else { m_keyIdentifier = String(); m_windowsVirtualKeyCode = 0; +#if PLATFORM(DARWIN) if (m_text.length() == 1 && (m_text[0U] >= 0xF700 && m_text[0U] <= 0xF7FF)) { // According to NSEvents.h, OpenStep reserves the range 0xF700-0xF8FF for function keys. However, some actual private use characters // happen to be in this range, e.g. the Apple logo (Option+Shift+K). @@ -63,6 +64,7 @@ void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool backwardCom m_text = String(); m_unmodifiedText = String(); } +#endif } #endif } diff --git a/WebCore/platform/chromium/PlatformWidget.h b/WebCore/platform/chromium/PlatformWidget.h index e4e6a18..b9dddf3 100644 --- a/WebCore/platform/chromium/PlatformWidget.h +++ b/WebCore/platform/chromium/PlatformWidget.h @@ -31,10 +31,12 @@ #ifndef PlatformWidget_h #define PlatformWidget_h +#include <wtf/StdLibExtras.h> // for intptr_t + // PlatformWidget is an opaque identifier corresponding to whatever native // view type the embedder may use. PlatformWidget CANNOT be assumed to be // a valid pointer. Some embedders may not use this identifier at all. -typedef void* PlatformWidget; +typedef intptr_t PlatformWidget; #endif diff --git a/WebCore/platform/chromium/PopupMenuChromium.cpp b/WebCore/platform/chromium/PopupMenuChromium.cpp index fad0a6b..53f565a 100644 --- a/WebCore/platform/chromium/PopupMenuChromium.cpp +++ b/WebCore/platform/chromium/PopupMenuChromium.cpp @@ -69,10 +69,24 @@ static const int kMaxHeight = 500; static const int kBorderSize = 1; static const TimeStamp kTypeAheadTimeoutMs = 1000; +// The settings used for the drop down menu. +// This is the delegate used if none is provided. +static const PopupContainerSettings dropDownSettings = { + true, // focusOnShow + true, // setTextOnIndexChange + true, // acceptOnAbandon + false // loopSelectionNavigation +}; + // This class uses WebCore code to paint and handle events for a drop-down list // box ("combobox" on Windows). class PopupListBox : public FramelessScrollView, public RefCounted<PopupListBox> { public: + static PassRefPtr<PopupListBox> create(PopupMenuClient* client, const PopupContainerSettings& settings) + { + return adoptRef(new PopupListBox(client, settings)); + } + // FramelessScrollView virtual void paint(GraphicsContext*, const IntRect&); virtual bool handleMouseDownEvent(const PlatformMouseEvent&); @@ -84,9 +98,6 @@ public: // ScrollView virtual HostWindow* hostWindow() const; - // Widget - virtual void invalidateRect(const IntRect&); - // PopupListBox methods // Shows the popup @@ -125,20 +136,6 @@ public: // Returns whether the popup wants to process events for the passed key. bool isInterestedInEventForKey(int keyCode); - // Sets whether the PopupMenuClient should be told to change its text when a - // new item is selected (by using the arrow keys). Default is true. - void setTextOnIndexChange(bool value) { m_setTextOnIndexChange = value; } - - // Sets whether we should accept the selected index when the popup is - // abandonned. - void setAcceptOnAbandon(bool value) { m_shouldAcceptOnAbandon = value; } - - // Sets whether pressing the down/up arrow when the last/first row is - // selected clears the selection on the first key press and then selects the - // first/last row on the next key press. If false, the selected row stays - // the last/first row. - void setLoopSelectionNavigation(bool value) { m_loopSelectionNavigation = value; } - private: friend class PopupContainer; friend class RefCounted<PopupListBox>; @@ -159,17 +156,15 @@ private: int y; // y offset of this item, relative to the top of the popup. }; - PopupListBox(PopupMenuClient* client) - : m_originalIndex(0) + PopupListBox(PopupMenuClient* client, const PopupContainerSettings& settings) + : m_settings(settings) + , m_originalIndex(0) , m_selectedIndex(0) - , m_shouldAcceptOnAbandon(true) , m_willAcceptOnAbandon(false) , m_visibleRows(0) , m_popupClient(client) , m_repeatingChar(0) , m_lastCharTime(0) - , m_setTextOnIndexChange(true) - , m_loopSelectionNavigation(false) { setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff); } @@ -228,6 +223,9 @@ private: void selectPreviousRow(); void selectNextRow(); + // The settings that specify the behavior for this Popup window. + PopupContainerSettings m_settings; + // This is the index of the item marked as "selected" - i.e. displayed in the widget on the // page. int m_originalIndex; @@ -237,15 +235,10 @@ private: // enter yet however. int m_selectedIndex; - // Whether we should accept the selectedIndex as chosen when the popup is - // "abandoned". This value is set through its setter and is useful as - // select popup menu and form autofill popup menu have different behaviors. - bool m_shouldAcceptOnAbandon; - // True if we should accept the selectedIndex as chosen, even if the popup // is "abandoned". This is used for keyboard navigation, where we want the - // selection to change immediately, and is only used if - // m_shouldAcceptOnAbandon is true. + // selection to change immediately, and is only used if the settings + // acceptOnAbandon field is true. bool m_willAcceptOnAbandon; // This is the number of rows visible in the popup. The maximum number visible at a time is @@ -277,10 +270,6 @@ private: // The last time the user hit a key. Used for typeAheadFind. TimeStamp m_lastCharTime; - - bool m_setTextOnIndexChange; - - bool m_loopSelectionNavigation; }; static PlatformMouseEvent constructRelativeMouseEvent(const PlatformMouseEvent& e, @@ -316,19 +305,15 @@ static PlatformWheelEvent constructRelativeWheelEvent(const PlatformWheelEvent& // static PassRefPtr<PopupContainer> PopupContainer::create(PopupMenuClient* client, - bool focusOnShow) + const PopupContainerSettings& settings) { - return adoptRef(new PopupContainer(client, focusOnShow)); + return adoptRef(new PopupContainer(client, settings)); } -PopupContainer::PopupContainer(PopupMenuClient* client, bool focusOnShow) - : m_listBox(new PopupListBox(client)), - m_focusOnShow(focusOnShow) +PopupContainer::PopupContainer(PopupMenuClient* client, const PopupContainerSettings& settings) + : m_listBox(PopupListBox::create(client, settings)) + , m_settings(settings) { - // FrameViews are created with a refcount of 1 so it needs releasing after we - // assign it to a RefPtr. - m_listBox->deref(); - setScrollbarModes(ScrollbarAlwaysOff, ScrollbarAlwaysOff); } @@ -352,19 +337,19 @@ void PopupContainer::showPopup(FrameView* view) if (chromeClient) { // If the popup would extend past the bottom of the screen, open upwards // instead. - FloatRect screen = screenRect(view); + FloatRect screen = screenAvailableRect(view); IntRect widgetRect = chromeClient->windowToScreen(frameRect()); if (widgetRect.bottom() > static_cast<int>(screen.bottom())) widgetRect.move(0, -(widgetRect.height() + selectHeight)); - chromeClient->popupOpened(this, widgetRect, m_focusOnShow); + chromeClient->popupOpened(this, widgetRect, m_settings.focusOnShow); } - // Must get called after we have a client and containingWindow. - addChild(m_listBox.get()); + if (!m_listBox->parent()) + addChild(m_listBox.get()); - // Enable scrollbars after the listbox is inserted into the hierarchy, so - // it has a proper WidgetClient. + // Enable scrollbars after the listbox is inserted into the hierarchy, + // so it has a proper WidgetClient. m_listBox->setVerticalScrollbarMode(ScrollbarAuto); m_listBox->scrollToRevealSelection(); @@ -374,12 +359,6 @@ void PopupContainer::showPopup(FrameView* view) void PopupContainer::hidePopup() { - invalidate(); - - m_listBox->disconnectClient(); - removeChild(m_listBox.get()); - m_listBox = 0; - if (client()) client()->popupClosed(this); } @@ -495,27 +474,17 @@ void PopupContainer::show(const IntRect& r, FrameView* v, int index) showPopup(v); } -void PopupContainer::setTextOnIndexChange(bool value) -{ - listBox()->setTextOnIndexChange(value); -} - -void PopupContainer::setAcceptOnAbandon(bool value) -{ - listBox()->setAcceptOnAbandon(value); -} - -void PopupContainer::setLoopSelectionNavigation(bool value) -{ - listBox()->setLoopSelectionNavigation(value); -} - void PopupContainer::refresh() { listBox()->updateFromElement(); layout(); } +int PopupContainer::selectedIndex() const +{ + return m_listBox->selectedIndex(); +} + /////////////////////////////////////////////////////////////////////////////// // PopupListBox implementation @@ -658,13 +627,14 @@ bool PopupListBox::handleKeyEvent(const PlatformKeyboardEvent& event) // want to fire the onchange event until the popup is closed, to match // IE). We change the original index so we revert to that when the // popup is closed. - if (m_shouldAcceptOnAbandon) + if (m_settings.acceptOnAbandon) m_willAcceptOnAbandon = true; setOriginalIndex(m_selectedIndex); - if (m_setTextOnIndexChange) + if (m_settings.setTextOnIndexChange) m_popupClient->setTextFromItem(m_selectedIndex); - } else if (!m_setTextOnIndexChange && event.windowsVirtualKeyCode() == VKEY_TAB) { + } else if (!m_settings.setTextOnIndexChange && + event.windowsVirtualKeyCode() == VKEY_TAB) { // TAB is a special case as it should select the current item if any and // advance focus. if (m_selectedIndex >= 0) @@ -683,15 +653,6 @@ HostWindow* PopupListBox::hostWindow() const return parent() ? parent()->hostWindow() : 0; } -void PopupListBox::invalidateRect(const IntRect& rect) -{ - // Since we are returning the HostWindow of our parent as our own in - // hostWindow(), we need to invalidate in our parent's coordinates. - IntRect newRect(rect); - newRect.move(kBorderSize, kBorderSize); - FramelessScrollView::invalidateRect(newRect); -} - // From HTMLSelectElement.cpp static String stripLeadingWhiteSpace(const String& string) { @@ -855,12 +816,10 @@ void PopupListBox::abandon() m_selectedIndex = m_originalIndex; + m_popupClient->hidePopup(); + if (m_willAcceptOnAbandon) m_popupClient->valueChanged(m_selectedIndex); - - // valueChanged may have torn down the popup! - if (m_popupClient) - m_popupClient->hidePopup(); } int PopupListBox::pointToRowIndex(const IntPoint& point) @@ -892,12 +851,11 @@ void PopupListBox::acceptIndex(int index) if (isSelectableItem(index)) { RefPtr<PopupListBox> keepAlive(this); - // Tell the <select> PopupMenuClient what index was selected, and hide ourself. - m_popupClient->valueChanged(index); + // Hide ourselves first since valueChanged may have numerous side-effects. + m_popupClient->hidePopup(); - // valueChanged may have torn down the popup! - if (m_popupClient) - m_popupClient->hidePopup(); + // Tell the <select> PopupMenuClient what index was selected. + m_popupClient->valueChanged(index); } } @@ -940,7 +898,9 @@ void PopupListBox::invalidateRow(int index) if (index < 0) return; - invalidateRect(getRowBounds(index)); + // Invalidate in the window contents, as FramelessScrollView::invalidateRect + // paints in the window coordinates. + invalidateRect(contentsToWindow(getRowBounds(index))); } void PopupListBox::scrollToRevealRow(int index) @@ -959,7 +919,8 @@ void PopupListBox::scrollToRevealRow(int index) } } -bool PopupListBox::isSelectableItem(int index) { +bool PopupListBox::isSelectableItem(int index) +{ return m_items[index]->type == TypeOption && m_popupClient->itemIsEnabled(index); } @@ -973,7 +934,7 @@ void PopupListBox::clearSelection() void PopupListBox::selectNextRow() { - if (!m_loopSelectionNavigation || m_selectedIndex != numItems() - 1) { + if (!m_settings.loopSelectionNavigation || m_selectedIndex != numItems() - 1) { adjustSelectedIndex(1); return; } @@ -984,7 +945,7 @@ void PopupListBox::selectNextRow() void PopupListBox::selectPreviousRow() { - if (!m_loopSelectionNavigation || m_selectedIndex > 0) { + if (!m_settings.loopSelectionNavigation || m_selectedIndex > 0) { adjustSelectedIndex(-1); return; } @@ -1156,16 +1117,15 @@ PopupMenu::~PopupMenu() void PopupMenu::show(const IntRect& r, FrameView* v, int index) { - p.popup = PopupContainer::create(client(), true); + if (!p.popup) + p.popup = PopupContainer::create(client(), dropDownSettings); p.popup->show(r, v, index); } void PopupMenu::hide() { - if (p.popup) { + if (p.popup) p.popup->hidePopup(); - p.popup = 0; - } } void PopupMenu::updateFromElement() diff --git a/WebCore/platform/chromium/PopupMenuChromium.h b/WebCore/platform/chromium/PopupMenuChromium.h index a57383d..cd13c22 100644 --- a/WebCore/platform/chromium/PopupMenuChromium.h +++ b/WebCore/platform/chromium/PopupMenuChromium.h @@ -44,7 +44,7 @@ namespace WebCore { // FIXME: Our FramelessScrollView classes should probably implement HostWindow! - // This class holds a PopupListBox (see cpp file). Its sole purpose is to be + // The PopupContainer class holds a PopupListBox (see cpp file). Its sole purpose is to be // able to draw a border around its child. All its paint/event handling is // just forwarded to the child listBox (with the appropriate transforms). // NOTE: this class is exposed so it can be instantiated direcly for the @@ -52,9 +52,30 @@ namespace WebCore { // autofill popup should not be focused when shown and we want to forward the // key events to it (through handleKeyEvent). + struct PopupContainerSettings { + // Whether the popup should get the focus when displayed. + bool focusOnShow; + + // Whether the PopupMenuClient should be told to change its text when a + // new item is selected by using the arrow keys. + bool setTextOnIndexChange; + + // Whether the selection should be accepted when the popup menu is + // closed (through ESC being pressed or the focus going away). + // Note that when TAB is pressed, the selection is always accepted + // regardless of this setting. + bool acceptOnAbandon; + + // Whether the we should move the selection to the first/last item when + // the user presses down/up arrow keys and the last/first item is + // selected. + bool loopSelectionNavigation; + }; + class PopupContainer : public FramelessScrollView, public RefCounted<PopupContainer> { public: - static PassRefPtr<PopupContainer> create(PopupMenuClient*, bool focusOnShow); + static PassRefPtr<PopupContainer> create(PopupMenuClient*, + const PopupContainerSettings&); // Whether a key event should be sent to this popup. virtual bool isInterestedInEventForKey(int keyCode); @@ -84,31 +105,19 @@ namespace WebCore { // Compute size of widget and children. void layout(); - // Sets whether the PopupMenuClient should be told to change its text when a - // new item is selected (by using the arrow keys). Default is true. - void setTextOnIndexChange(bool); - - // Sets whether the selection should be accepted when the popup menu is - // closed (through ESC being pressed or the focus going away). Default - // is true. Note that when TAB is pressed, the selection is always - // accepted regardless of this setting. - void setAcceptOnAbandon(bool); - - // Sets whether we should move the selection to the first/last item - // when the user presses down/up arrow keys and the last/first item is - // selected. Default is false, causing the first/last item to stay - // selected. - void setLoopSelectionNavigation(bool); - PopupListBox* listBox() const { return m_listBox.get(); } + // Gets the index of the item that the user is currently moused-over or + // has selected with the keyboard up/down arrows. + int selectedIndex() const; + // Refresh the popup values from the PopupMenuClient. void refresh(); private: friend class WTF::RefCounted<PopupContainer>; - PopupContainer(PopupMenuClient*, bool focusOnShow); + PopupContainer(PopupMenuClient*, const PopupContainerSettings&); ~PopupContainer(); // Paint the border. @@ -116,8 +125,7 @@ namespace WebCore { RefPtr<PopupListBox> m_listBox; - // Whether the window showing this popup should be focused when shown. - bool m_focusOnShow; + PopupContainerSettings m_settings; }; } // namespace WebCore diff --git a/WebCore/platform/chromium/ScrollbarThemeChromium.cpp b/WebCore/platform/chromium/ScrollbarThemeChromium.cpp index de40572..426a078 100644 --- a/WebCore/platform/chromium/ScrollbarThemeChromium.cpp +++ b/WebCore/platform/chromium/ScrollbarThemeChromium.cpp @@ -96,7 +96,7 @@ IntRect ScrollbarThemeChromium::forwardButtonRect(Scrollbar* scrollbar, Scrollba IntRect ScrollbarThemeChromium::trackRect(Scrollbar* scrollbar, bool) { IntSize bs = buttonSize(scrollbar); - int thickness = scrollbarThickness(); + int thickness = scrollbarThickness(scrollbar->controlSize()); if (scrollbar->orientation() == HorizontalScrollbar) { if (scrollbar->width() < 2 * thickness) return IntRect(); @@ -167,26 +167,26 @@ bool ScrollbarThemeChromium::shouldCenterOnThumb(Scrollbar*, const PlatformMouse IntSize ScrollbarThemeChromium::buttonSize(Scrollbar* scrollbar) { +#if defined(__linux__) + // On Linux, we don't use buttons + return IntSize(0, 0); +#endif + // Our desired rect is essentially thickness by thickness. // Our actual rect will shrink to half the available space when we have < 2 // times thickness pixels left. This allows the scrollbar to scale down // and function even at tiny sizes. - int thickness = scrollbarThickness(); + int thickness = scrollbarThickness(scrollbar->controlSize()); -#if !defined(__linux__) // In layout test mode, we force the button "girth" (i.e., the length of // the button along the axis of the scrollbar) to be a fixed size. // FIXME: This is retarded! scrollbarThickness is already fixed in layout // test mode so that should be enough to result in repeatable results, but // preserving this hack avoids having to rebaseline pixel tests. const int kLayoutTestModeGirth = 17; - int girth = ChromiumBridge::layoutTestMode() ? kLayoutTestModeGirth : thickness; -#else - int girth = thickness; -#endif if (scrollbar->orientation() == HorizontalScrollbar) { int width = scrollbar->width() < 2 * girth ? scrollbar->width() / 2 : girth; diff --git a/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp b/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp index 95d0f78..a99d778 100644 --- a/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp +++ b/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp @@ -1,10 +1,10 @@ /* * Copyright (c) 2008, 2009, Google Inc. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: - * + * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above @@ -14,7 +14,7 @@ * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR @@ -37,112 +37,106 @@ #include "Scrollbar.h" #include "TransformationMatrix.h" -#include "gtkdrawing.h" -#include <gtk/gtk.h> -#include "skia/ext/GdkSkia.h" - namespace WebCore { int ScrollbarThemeChromium::scrollbarThickness(ScrollbarControlSize controlSize) { - static int size = 0; - if (!size) { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - size = metrics.slider_width; - } - return size; + return 15; } bool ScrollbarThemeChromium::invalidateOnMouseEnterExit() { - notImplemented(); return false; } -// Given an uninitialised widget state object, set the members such that it's -// sane for drawing scrollbars -static void initMozState(GtkWidgetState* mozState) +static void drawVertLine(SkCanvas* canvas, int x, int y1, int y2, const SkPaint& paint) { - mozState->active = true; - mozState->focused = false; - mozState->inHover = false; - mozState->disabled = false; - mozState->isDefault = false; - mozState->canDefault = false; - mozState->depressed = false; - mozState->curpos = 0; - mozState->maxpos = 0; + SkIRect skrect; + skrect.set(x, y1, x + 1, y2 + 1); + canvas->drawIRect(skrect, paint); } -// Paint a GTK widget -// gc: context to draw onto -// rect: the area of the widget -// widget_type: the type of widget to draw -// flags: widget dependent flags (e.g. direction of scrollbar arrows etc) -// -// See paintMozWiget in RenderThemeGtk.cpp for an explanation of the clipping. -static void paintScrollbarWidget(GraphicsContext* gc, const IntRect& rect, - GtkThemeWidgetType widget_type, gint flags) +static void drawHorizLine(SkCanvas* canvas, int x1, int x2, int y, const SkPaint& paint) { - PlatformContextSkia* pcs = gc->platformContext(); - - GdkRectangle gdkRect = { rect.x(), rect.y(), rect.width(), rect.height() }; - - const SkIRect clip_region = pcs->canvas()->getTotalClip().getBounds(); - TransformationMatrix ctm = gc->getCTM().inverse(); - IntPoint pos = ctm.mapPoint( - IntPoint(SkScalarRound(clip_region.fLeft), SkScalarRound(clip_region.fTop))); - GdkRectangle gdkClipRect; - gdkClipRect.x = pos.x(); - gdkClipRect.y = pos.y(); - gdkClipRect.width = clip_region.width(); - gdkClipRect.height = clip_region.height(); - - gdk_rectangle_intersect(&gdkRect, &gdkClipRect, &gdkClipRect); - - GtkWidgetState mozState; - initMozState(&mozState); + SkIRect skrect; + skrect.set(x1, y, x2 + 1, y + 1); + canvas->drawIRect(skrect, paint); +} - moz_gtk_widget_paint(widget_type, pcs->gdk_skia(), &gdkRect, &gdkClipRect, - &mozState, flags, GTK_TEXT_DIR_LTR); +static void drawBox(SkCanvas* canvas, const IntRect& rect, const SkPaint& paint) +{ + const int right = rect.x() + rect.width() - 1; + const int bottom = rect.y() + rect.height() - 1; + drawHorizLine(canvas, rect.x(), right, rect.y(), paint); + drawVertLine(canvas, right, rect.y(), bottom, paint); + drawHorizLine(canvas, rect.x(), right, bottom, paint); + drawVertLine(canvas, rect.x(), rect.y(), bottom, paint); } void ScrollbarThemeChromium::paintTrackPiece(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart partType) { - const bool horz = scrollbar->orientation() == HorizontalScrollbar; - const GtkThemeWidgetType track_type = - horz ? MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL : MOZ_GTK_SCROLLBAR_TRACK_VERTICAL; - paintScrollbarWidget(gc, rect, track_type, 0); + SkCanvas* const canvas = gc->platformContext()->canvas(); + SkPaint paint; + SkIRect skrect; + + skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); + paint.setARGB(0xff, 0xe3, 0xdd, 0xd8); + canvas->drawIRect(skrect, paint); + + paint.setARGB(0xff, 0xc5, 0xba, 0xb0); + drawBox(canvas, rect, paint); } void ScrollbarThemeChromium::paintButton(GraphicsContext* gc, Scrollbar* scrollbar, - const IntRect& rect, ScrollbarPart part) + const IntRect& rect, ScrollbarPart part) { - // FIXME: It appears the either we're upsetting GTK by forcing WebKit sizes - // on it, or the buttons expect the track to be drawn under them. Either - // way, we end up with unpainted pixels which are upsetting the pixel - // tests. Thus we paint green under the buttons to, at least, make the - // pixel output the same between debug and opt builds. - SkPaint paint; - paint.setARGB(255, 0, 255, 128); - SkRect skrect; - skrect.set(rect.x(), rect.y(), rect.x() + rect.width() - 1, rect.y() + rect.height() - 1); - gc->platformContext()->canvas()->drawRect(skrect, paint); - - const bool horz = scrollbar->orientation() == HorizontalScrollbar; - gint flags = horz ? 0 : MOZ_GTK_STEPPER_VERTICAL; - flags |= ForwardButtonEndPart == part ? MOZ_GTK_STEPPER_DOWN : 0; - paintScrollbarWidget(gc, rect, MOZ_GTK_SCROLLBAR_BUTTON, flags); + // We don't use buttons } void ScrollbarThemeChromium::paintThumb(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect) { - const bool horz = scrollbar->orientation() == HorizontalScrollbar; - const GtkThemeWidgetType thumb_type = - horz ? MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL : MOZ_GTK_SCROLLBAR_THUMB_VERTICAL; - paintScrollbarWidget(gc, rect, thumb_type, 0); + const bool hovered = scrollbar->hoveredPart() == ThumbPart; + const int midx = rect.x() + rect.width() / 2; + const int midy = rect.y() + rect.height() / 2; + const bool vertical = scrollbar->orientation() == VerticalScrollbar; + SkCanvas* const canvas = gc->platformContext()->canvas(); + + SkPaint paint; + if (hovered) + paint.setARGB(0xff, 0xff, 0xff, 0xff); + else + paint.setARGB(0xff, 0xf4, 0xf2, 0xef); + + SkIRect skrect; + if (vertical) + skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height()); + else + skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1); + + canvas->drawIRect(skrect, paint); + + if (hovered) + paint.setARGB(0xff, 0xf4, 0xf2, 0xef); + else + paint.setARGB(0xff, 0xea, 0xe5, 0xe0); + + if (vertical) + skrect.set(midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); + else + skrect.set(rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height()); + + canvas->drawIRect(skrect, paint); + + paint.setARGB(0xff, 0x9d, 0x96, 0x8e); + drawBox(canvas, rect, paint); + + if (rect.height() > 10 && rect.width() > 10) { + paint.setARGB(0xff, 0x9d, 0x96, 0x8e); + drawHorizLine(canvas, midx - 1, midx + 3, midy, paint); + drawHorizLine(canvas, midx - 1, midx + 3, midy - 3, paint); + drawHorizLine(canvas, midx - 1, midx + 3, midy + 3, paint); + } } } // namespace WebCore diff --git a/WebCore/platform/chromium/TemporaryLinkStubs.cpp b/WebCore/platform/chromium/TemporaryLinkStubs.cpp index 4a028da..f6e77d4 100644 --- a/WebCore/platform/chromium/TemporaryLinkStubs.cpp +++ b/WebCore/platform/chromium/TemporaryLinkStubs.cpp @@ -44,11 +44,3 @@ String KURL::fileSystemPath() const { notImplemented(); return String(); } PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String&) { notImplemented(); return 0; } } // namespace WebCore - -namespace WTF { - -#if !defined(__linux__) -void scheduleDispatchFunctionsOnMainThread() { notImplemented(); } -#endif - -} // namespace WTF diff --git a/WebCore/platform/graphics/BitmapImage.cpp b/WebCore/platform/graphics/BitmapImage.cpp index 68863df..1d97632 100644 --- a/WebCore/platform/graphics/BitmapImage.cpp +++ b/WebCore/platform/graphics/BitmapImage.cpp @@ -53,6 +53,7 @@ BitmapImage::BitmapImage(ImageObserver* observer) , m_repetitionsComplete(0) , m_desiredFrameStartTime(0) , m_isSolidColor(false) + , m_checkedForSolidColor(false) , m_animationFinished(false) , m_allDataReceived(false) , m_haveSize(false) diff --git a/WebCore/platform/graphics/BitmapImage.h b/WebCore/platform/graphics/BitmapImage.h index 110aec4..db05d1c 100644 --- a/WebCore/platform/graphics/BitmapImage.h +++ b/WebCore/platform/graphics/BitmapImage.h @@ -166,7 +166,7 @@ protected: virtual void drawFrameMatchingSourceSize(GraphicsContext*, const FloatRect& dstRect, const IntSize& srcSize, CompositeOperator); #endif virtual void draw(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, CompositeOperator); -#if PLATFORM(QT) || PLATFORM(WX) +#if PLATFORM(WX) virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #endif @@ -218,9 +218,18 @@ protected: void invalidatePlatformData(); // Checks to see if the image is a 1x1 solid color. We optimize these images and just do a fill rect instead. + // This check should happen regardless whether m_checkedForSolidColor is already set, as the frame may have + // changed. void checkForSolidColor(); - virtual bool mayFillWithSolidColor() const { return m_isSolidColor && m_currentFrame == 0; } + virtual bool mayFillWithSolidColor() + { + if (!m_checkedForSolidColor && frameCount() > 0) { + checkForSolidColor(); + ASSERT(m_checkedForSolidColor); + } + return m_isSolidColor && m_currentFrame == 0; + } virtual Color solidColor() const { return m_solidColor; } ImageSource m_source; @@ -242,6 +251,7 @@ protected: Color m_solidColor; // If we're a 1x1 solid color, this is the color to use to fill. bool m_isSolidColor; // Whether or not we are a 1x1 solid image. + bool m_checkedForSolidColor; // Whether we've checked the frame for solid color. bool m_animationFinished; // Whether or not we've completed the entire animation. diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index c7e11ee..e85ac00 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -116,6 +116,15 @@ RGBA32 makeRGBAFromHSLA(double hue, double saturation, double lightness, double static_cast<int>(alpha * scaleFactor)); } +RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a) +{ + double colors = 1 - k; + int r = static_cast<int>(nextafter(256, 0) * (colors * (1 - c))); + int g = static_cast<int>(nextafter(256, 0) * (colors * (1 - m))); + int b = static_cast<int>(nextafter(256, 0) * (colors * (1 - y))); + return makeRGBA(r, g, b, static_cast<float>(nextafter(256, 0) * a)); +} + // originally moved here from the CSS parser bool Color::parseHexColor(const String& name, RGBA32& rgb) { @@ -312,4 +321,34 @@ void Color::getRGBA(double& r, double& g, double& b, double& a) const a = alpha() / 255.0; } +Color colorFromPremultipliedARGB(unsigned pixelColor) +{ + RGBA32 rgba; + + if (unsigned alpha = (pixelColor & 0xFF000000) >> 24) { + rgba = makeRGBA(((pixelColor & 0x00FF0000) >> 16) * 255 / alpha, + ((pixelColor & 0x0000FF00) >> 8) * 255 / alpha, + (pixelColor & 0x000000FF) * 255 / alpha, + alpha); + } else + rgba = pixelColor; + + return Color(rgba); +} + +unsigned premultipliedARGBFromColor(const Color& color) +{ + unsigned pixelColor; + + if (unsigned alpha = color.alpha()) { + pixelColor = alpha << 24 | + ((color.red() * alpha + 254) / 255) << 16 | + ((color.green() * alpha + 254) / 255) << 8 | + ((color.blue() * alpha + 254) / 255); + } else + pixelColor = color.rgb(); + + return pixelColor; +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index 61fc74c..3c889f9 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -60,6 +60,7 @@ RGBA32 makeRGBA(int r, int g, int b, int a); RGBA32 colorWithOverrideAlpha(RGBA32 color, float overrideAlpha); RGBA32 makeRGBA32FromFloats(float r, float g, float b, float a); RGBA32 makeRGBAFromHSLA(double h, double s, double l, double a); +RGBA32 makeRGBAFromCMYKA(float c, float m, float y, float k, float a); int differenceSquared(const Color&, const Color&); @@ -71,6 +72,8 @@ public: Color(int r, int g, int b, int a) : m_color(makeRGBA(r, g, b, a)), m_valid(true) { } // Color is currently limited to 32bit RGBA, perhaps some day we'll support better colors Color(float r, float g, float b, float a) : m_color(makeRGBA32FromFloats(r, g, b, a)), m_valid(true) { } + // Creates a new color from the specific CMYK and alpha values. + Color(float c, float m, float y, float k, float a) : m_color(makeRGBAFromCMYKA(c, m, y, k, a)), m_valid(true) { } explicit Color(const String&); explicit Color(const char*); @@ -146,9 +149,11 @@ inline bool operator!=(const Color& a, const Color& b) } Color focusRingColor(); +Color colorFromPremultipliedARGB(unsigned); +unsigned premultipliedARGBFromColor(const Color&); #if PLATFORM(CG) -CGColorRef cgColor(const Color&); +CGColorRef createCGColor(const Color&); #endif } // namespace WebCore diff --git a/WebCore/platform/graphics/FloatPoint.cpp b/WebCore/platform/graphics/FloatPoint.cpp index 564ea86..7765ba9 100644 --- a/WebCore/platform/graphics/FloatPoint.cpp +++ b/WebCore/platform/graphics/FloatPoint.cpp @@ -40,7 +40,7 @@ FloatPoint::FloatPoint(const IntPoint& p) : m_x(p.x()), m_y(p.y()) FloatPoint FloatPoint::matrixTransform(const TransformationMatrix& transform) const { double newX, newY; - transform.map(static_cast<double>(m_x), static_cast<double>(m_y), &newX, &newY); + transform.map(static_cast<double>(m_x), static_cast<double>(m_y), newX, newY); return narrowPrecision(newX, newY); } diff --git a/WebCore/platform/graphics/FloatPoint.h b/WebCore/platform/graphics/FloatPoint.h index 35b3036..a3fbeea 100644 --- a/WebCore/platform/graphics/FloatPoint.h +++ b/WebCore/platform/graphics/FloatPoint.h @@ -55,7 +55,7 @@ QT_END_NAMESPACE class TPoint; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) struct SkPoint; #endif @@ -99,7 +99,7 @@ public: FloatPoint(const TPoint&); #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) operator SkPoint() const; FloatPoint(const SkPoint&); #endif diff --git a/WebCore/platform/graphics/FloatPoint3D.cpp b/WebCore/platform/graphics/FloatPoint3D.cpp index e3ba422..8c21ef3 100644 --- a/WebCore/platform/graphics/FloatPoint3D.cpp +++ b/WebCore/platform/graphics/FloatPoint3D.cpp @@ -21,7 +21,6 @@ #include "config.h" -#if ENABLE(SVG) #include <math.h> #include "FloatPoint.h" #include "FloatPoint3D.h" @@ -62,4 +61,3 @@ void FloatPoint3D::normalize() } // namespace WebCore -#endif // ENABLE(SVG) diff --git a/WebCore/platform/graphics/FloatPoint3D.h b/WebCore/platform/graphics/FloatPoint3D.h index 184e914..2e71ddd 100644 --- a/WebCore/platform/graphics/FloatPoint3D.h +++ b/WebCore/platform/graphics/FloatPoint3D.h @@ -22,8 +22,6 @@ #ifndef FloatPoint3D_h #define FloatPoint3D_h -#if ENABLE(SVG) - namespace WebCore { class FloatPoint; @@ -53,6 +51,4 @@ private: } // namespace WebCore -#endif // ENABLE(SVG) - #endif // FloatPoint3D_h diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index a87c949..786fb3e 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -51,7 +51,7 @@ QT_END_NAMESPACE class wxRect2DDouble; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) struct SkRect; #endif @@ -143,7 +143,7 @@ public: operator wxRect2DDouble() const; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) FloatRect(const SkRect&); operator SkRect() const; #endif diff --git a/WebCore/platform/graphics/FontCache.cpp b/WebCore/platform/graphics/FontCache.cpp index 2d219be..130313d 100644 --- a/WebCore/platform/graphics/FontCache.cpp +++ b/WebCore/platform/graphics/FontCache.cpp @@ -139,8 +139,13 @@ static const AtomicString& alternateFamilyName(const AtomicString& familyName) DEFINE_STATIC_LOCAL(AtomicString, courierNew, ("Courier New")); if (equalIgnoringCase(familyName, courier)) return courierNew; +#if !PLATFORM(WIN_OS) + // On Windows, Courier New (truetype font) is always present and + // Courier is a bitmap font. So, we don't want to map Courier New to + // Courier. if (equalIgnoringCase(familyName, courierNew)) return courier; +#endif // Alias Times and Times New Roman. DEFINE_STATIC_LOCAL(AtomicString, times, ("Times")); @@ -158,6 +163,21 @@ static const AtomicString& alternateFamilyName(const AtomicString& familyName) if (equalIgnoringCase(familyName, helvetica)) return arial; +#if PLATFORM(WIN_OS) + // On Windows, bitmap fonts are blocked altogether so that we have to + // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) + DEFINE_STATIC_LOCAL(AtomicString, msSans, ("MS Sans Serif")); + DEFINE_STATIC_LOCAL(AtomicString, microsoftSans, ("Microsoft Sans Serif")); + if (equalIgnoringCase(familyName, msSans)) + return microsoftSans; + + // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no + // 'Microsoft Sans Serif-equivalent' for Serif. + static AtomicString msSerif("MS Serif"); + if (equalIgnoringCase(familyName, msSerif)) + return timesNewRoman; +#endif + return emptyAtom; } @@ -216,7 +236,7 @@ struct FontDataCacheKeyTraits : WTF::GenericHashTraits<FontPlatformData> { static const bool needsDestruction = true; static const FontPlatformData& emptyValue() { - DEFINE_STATIC_LOCAL(FontPlatformData, key, ()); + DEFINE_STATIC_LOCAL(FontPlatformData, key, (0.f, false, false)); return key; } static void constructDeletedValue(FontPlatformData& slot) @@ -304,7 +324,7 @@ void FontCache::purgeInactiveFontData(int count) } Vector<FontPlatformDataCacheKey> keysToRemove; - keysToRemove.reserveCapacity(gFontPlatformDataCache->size()); + keysToRemove.reserveInitialCapacity(gFontPlatformDataCache->size()); FontPlatformDataCache::iterator platformDataEnd = gFontPlatformDataCache->end(); for (FontPlatformDataCache::iterator platformData = gFontPlatformDataCache->begin(); platformData != platformDataEnd; ++platformData) { if (platformData->second && !gFontDataCache->contains(*platformData->second)) @@ -424,7 +444,7 @@ void FontCache::invalidate() Vector<RefPtr<FontSelector> > clients; size_t numClients = gClients->size(); - clients.reserveCapacity(numClients); + clients.reserveInitialCapacity(numClients); HashSet<FontSelector*>::iterator end = gClients->end(); for (HashSet<FontSelector*>::iterator it = gClients->begin(); it != end; ++it) clients.append(*it); diff --git a/WebCore/platform/graphics/GlyphBuffer.h b/WebCore/platform/graphics/GlyphBuffer.h index fdb306f..dcda419 100644 --- a/WebCore/platform/graphics/GlyphBuffer.h +++ b/WebCore/platform/graphics/GlyphBuffer.h @@ -37,7 +37,7 @@ #include <ApplicationServices/ApplicationServices.h> #endif -#if PLATFORM(CAIRO) +#if PLATFORM(CAIRO) || (PLATFORM(WX) && defined(__WXGTK__)) #include <cairo.h> #endif diff --git a/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/WebCore/platform/graphics/GlyphPageTreeNode.cpp index 6b9d23d..bd838de 100644 --- a/WebCore/platform/graphics/GlyphPageTreeNode.cpp +++ b/WebCore/platform/graphics/GlyphPageTreeNode.cpp @@ -218,6 +218,7 @@ void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNu } haveGlyphs |= pageToFill->fill(from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData()); if (scratchPage) { + ASSERT(to <= static_cast<int>(GlyphPage::size)); for (int j = from; j < to; j++) { if (!m_page->m_glyphs[j].glyph && pageToFill->m_glyphs[j].glyph) m_page->m_glyphs[j] = pageToFill->m_glyphs[j]; diff --git a/WebCore/platform/graphics/GlyphWidthMap.cpp b/WebCore/platform/graphics/GlyphWidthMap.cpp index 6e8d68d..43cab65 100644 --- a/WebCore/platform/graphics/GlyphWidthMap.cpp +++ b/WebCore/platform/graphics/GlyphWidthMap.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 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 @@ -29,43 +29,22 @@ #include "config.h" #include "GlyphWidthMap.h" -namespace WebCore -{ - -float GlyphWidthMap::widthForGlyph(Glyph g) -{ - unsigned pageNumber = (g / GlyphWidthPage::size); - GlyphWidthPage* page = locatePage(pageNumber); - if (page) - return page->widthForGlyph(g); - return cGlyphWidthUnknown; -} - -void GlyphWidthMap::setWidthForGlyph(Glyph glyph, float width) -{ - unsigned pageNumber = (glyph / GlyphWidthPage::size); - GlyphWidthPage* page = locatePage(pageNumber); - if (page) - page->setWidthForGlyph(glyph, width); -} +namespace WebCore { -inline GlyphWidthMap::GlyphWidthPage* GlyphWidthMap::locatePage(unsigned pageNumber) +GlyphWidthMap::GlyphWidthPage* GlyphWidthMap::locatePageSlowCase(unsigned pageNumber) { GlyphWidthPage* page; if (pageNumber == 0) { - if (m_filledPrimaryPage) - return &m_primaryPage; + ASSERT(!m_filledPrimaryPage); page = &m_primaryPage; m_filledPrimaryPage = true; } else { if (m_pages) { - GlyphWidthPage* result = m_pages->get(pageNumber); - if (result) - return result; - } + if ((page = m_pages->get(pageNumber))) + return page; + } else + m_pages.set(new HashMap<int, GlyphWidthPage*>); page = new GlyphWidthPage; - if (!m_pages) - m_pages = new HashMap<int, GlyphWidthPage*>; m_pages->set(pageNumber, page); } diff --git a/WebCore/platform/graphics/GlyphWidthMap.h b/WebCore/platform/graphics/GlyphWidthMap.h index 1633769..e194ecf 100644 --- a/WebCore/platform/graphics/GlyphWidthMap.h +++ b/WebCore/platform/graphics/GlyphWidthMap.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 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 @@ -29,9 +29,9 @@ #ifndef GlyphWidthMap_h #define GlyphWidthMap_h -#include <wtf/unicode/Unicode.h> -#include <wtf/Noncopyable.h> #include <wtf/HashMap.h> +#include <wtf/OwnPtr.h> +#include <wtf/unicode/Unicode.h> namespace WebCore { @@ -41,34 +41,49 @@ const float cGlyphWidthUnknown = -1; class GlyphWidthMap : Noncopyable { public: - GlyphWidthMap() : m_filledPrimaryPage(false), m_pages(0) {} - ~GlyphWidthMap() { if (m_pages) { deleteAllValues(*m_pages); delete m_pages; } } + GlyphWidthMap() : m_filledPrimaryPage(false) { } + ~GlyphWidthMap() { if (m_pages) { deleteAllValues(*m_pages); } } + + float widthForGlyph(Glyph glyph) + { + return locatePage(glyph / GlyphWidthPage::size)->widthForGlyph(glyph); + } - float widthForGlyph(Glyph); - void setWidthForGlyph(Glyph, float); + void setWidthForGlyph(Glyph glyph, float width) + { + locatePage(glyph / GlyphWidthPage::size)->setWidthForGlyph(glyph, width); + } private: struct GlyphWidthPage { static const size_t size = 256; // Usually covers Latin-1 in a single page. float m_widths[size]; - float widthForGlyph(Glyph g) const { return m_widths[g % size]; } - void setWidthForGlyph(Glyph g, float w) + float widthForGlyph(Glyph glyph) const { return m_widths[glyph % size]; } + void setWidthForGlyph(Glyph glyph, float width) { - setWidthForIndex(g % size, w); + setWidthForIndex(glyph % size, width); } - void setWidthForIndex(unsigned index, float w) + void setWidthForIndex(unsigned index, float width) { - m_widths[index] = w; + m_widths[index] = width; } }; - GlyphWidthPage* locatePage(unsigned page); + GlyphWidthPage* locatePage(unsigned pageNumber) + { + if (!pageNumber && m_filledPrimaryPage) + return &m_primaryPage; + return locatePageSlowCase(pageNumber); + } + + GlyphWidthPage* locatePageSlowCase(unsigned pageNumber); bool m_filledPrimaryPage; GlyphWidthPage m_primaryPage; // We optimize for the page that contains glyph indices 0-255. - HashMap<int, GlyphWidthPage*>* m_pages; + OwnPtr<HashMap<int, GlyphWidthPage*> > m_pages; }; } + #endif diff --git a/WebCore/platform/graphics/Gradient.cpp b/WebCore/platform/graphics/Gradient.cpp index 2e6a5d2..24e8bbf 100644 --- a/WebCore/platform/graphics/Gradient.cpp +++ b/WebCore/platform/graphics/Gradient.cpp @@ -39,6 +39,7 @@ Gradient::Gradient(const FloatPoint& p0, const FloatPoint& p1) , m_r1(0) , m_stopsSorted(false) , m_lastStop(0) + , m_spreadMethod(SpreadMethodPad) { platformInit(); } @@ -146,4 +147,11 @@ int Gradient::findStop(float value) const return m_lastStop; } +void Gradient::setSpreadMethod(GradientSpreadMethod spreadMethod) +{ + // FIXME: Should it become necessary, allow calls to this method after m_gradient has been set. + ASSERT(m_gradient == 0); + m_spreadMethod = spreadMethod; +} + } //namespace diff --git a/WebCore/platform/graphics/Gradient.h b/WebCore/platform/graphics/Gradient.h index 00ef2b6..764deee 100644 --- a/WebCore/platform/graphics/Gradient.h +++ b/WebCore/platform/graphics/Gradient.h @@ -29,6 +29,8 @@ #include "FloatPoint.h" #include "Generator.h" +#include "GraphicsTypes.h" +#include "TransformationMatrix.h" #include <wtf/PassRefPtr.h> #include <wtf/Vector.h> @@ -93,6 +95,12 @@ namespace WebCore { void setStopsSorted(bool s) { m_stopsSorted = s; } + void setSpreadMethod(GradientSpreadMethod); + GradientSpreadMethod spreadMethod() { return m_spreadMethod; } + void setGradientSpaceTransform(const TransformationMatrix& gradientSpaceTransformation) { m_gradientSpaceTransformation = gradientSpaceTransformation; } + // Qt and CG transform the gradient at draw time + TransformationMatrix gradientSpaceTransform() { return m_gradientSpaceTransformation; } + virtual void fill(GraphicsContext*, const FloatRect&); private: @@ -112,6 +120,8 @@ namespace WebCore { mutable Vector<ColorStop> m_stops; mutable bool m_stopsSorted; mutable int m_lastStop; + GradientSpreadMethod m_spreadMethod; + TransformationMatrix m_gradientSpaceTransformation; PlatformGradient m_gradient; }; diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 8426011..8cad794 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -128,6 +128,11 @@ void GraphicsContext::setStrokeColor(const Color& color) setPlatformStrokeColor(color); } +ColorSpace GraphicsContext::strokeColorSpace() const +{ + return m_common->state.strokeColorSpace; +} + void GraphicsContext::setShadow(const IntSize& size, int blur, const Color& color) { m_common->state.shadowSize = size; @@ -178,16 +183,6 @@ void GraphicsContext::setFillRule(WindRule fillRule) m_common->state.fillRule = fillRule; } -GradientSpreadMethod GraphicsContext::spreadMethod() const -{ - return m_common->state.spreadMethod; -} - -void GraphicsContext::setSpreadMethod(GradientSpreadMethod spreadMethod) -{ - m_common->state.spreadMethod = spreadMethod; -} - void GraphicsContext::setFillColor(const Color& color) { m_common->state.fillColorSpace = SolidColorSpace; @@ -255,6 +250,31 @@ void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) m_common->state.fillGradient = gradient; } +Gradient* GraphicsContext::fillGradient() const +{ + return m_common->state.fillGradient.get(); +} + +ColorSpace GraphicsContext::fillColorSpace() const +{ + return m_common->state.fillColorSpace; +} + +Gradient* GraphicsContext::strokeGradient() const +{ + return m_common->state.strokeGradient.get(); +} + +Pattern* GraphicsContext::fillPattern() const +{ + return m_common->state.fillPattern.get(); +} + +Pattern* GraphicsContext::strokePattern() const +{ + return m_common->state.strokePattern.get(); +} + void GraphicsContext::setShadowsIgnoreTransforms(bool ignoreTransforms) { m_common->state.shadowsIgnoreTransforms = ignoreTransforms; diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index c27f38f..7c1c4b0 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -123,6 +123,18 @@ namespace WebCore { DashedStroke }; +// FIXME: This is a place-holder until we decide to add +// real color space support to WebCore. At that time, ColorSpace will be a +// class and instances will be held off of Colors. There will be +// special singleton Gradient and Pattern color spaces to mark when +// a fill or stroke is using a gradient or pattern instead of a solid color. +// https://bugs.webkit.org/show_bug.cgi?id=20558 + enum ColorSpace { + SolidColorSpace, + PatternColorSpace, + GradientColorSpace + }; + enum InterpolationQuality { InterpolationDefault, InterpolationNone, @@ -131,14 +143,6 @@ namespace WebCore { InterpolationHigh }; - // FIXME: Currently these constants have to match the values used in the SVG - // DOM API. That's a mistake. We need to make cut that dependency. - enum GradientSpreadMethod { - SpreadMethodPad = 1, - SpreadMethodReflect = 2, - SpreadMethodRepeat = 3 - }; - class GraphicsContext : Noncopyable { public: GraphicsContext(PlatformGraphicsContext*); @@ -152,17 +156,28 @@ namespace WebCore { void setStrokeStyle(const StrokeStyle& style); Color strokeColor() const; void setStrokeColor(const Color&); + + ColorSpace strokeColorSpace() const; + void setStrokePattern(PassRefPtr<Pattern>); + Pattern* strokePattern() const; + void setStrokeGradient(PassRefPtr<Gradient>); + Gradient* strokeGradient() const; WindRule fillRule() const; void setFillRule(WindRule); - GradientSpreadMethod spreadMethod() const; - void setSpreadMethod(GradientSpreadMethod); Color fillColor() const; void setFillColor(const Color&); + void setFillPattern(PassRefPtr<Pattern>); + Pattern* fillPattern() const; + void setFillGradient(PassRefPtr<Gradient>); + Gradient* fillGradient() const; + + ColorSpace fillColorSpace() const; + void setShadowsIgnoreTransforms(bool); void setShouldAntialias(bool); @@ -206,6 +221,9 @@ namespace WebCore { void restore(); // These draw methods will do both stroking and filling. + // FIXME: ...except drawRect(), which fills properly but always strokes + // using a 1-pixel stroke inset from the rect borders (of the correct + // stroke color). void drawRect(const IntRect&); void drawLine(const IntPoint&, const IntPoint&); void drawEllipse(const IntRect&); @@ -402,3 +420,4 @@ namespace WebCore { } // namespace WebCore #endif // GraphicsContext_h + diff --git a/WebCore/platform/graphics/GraphicsContextPrivate.h b/WebCore/platform/graphics/GraphicsContextPrivate.h index 87123eb..98baab1 100644 --- a/WebCore/platform/graphics/GraphicsContextPrivate.h +++ b/WebCore/platform/graphics/GraphicsContextPrivate.h @@ -33,18 +33,6 @@ namespace WebCore { -// FIXME: This is a place-holder until we decide to add -// real color space support to WebCore. At that time, ColorSpace will be a -// class and instances will be held off of Colors. There will be -// special singleton Gradient and Pattern color spaces to mark when -// a fill or stroke is using a gradient or pattern instead of a solid color. -// https://bugs.webkit.org/show_bug.cgi?id=20558 - enum ColorSpace { - SolidColorSpace, - PatternColorSpace, - GradientColorSpace - }; - struct GraphicsContextState { GraphicsContextState() : textDrawingMode(cTextFill) @@ -80,7 +68,6 @@ namespace WebCore { RefPtr<Pattern> strokePattern; WindRule fillRule; - GradientSpreadMethod spreadMethod; ColorSpace fillColorSpace; Color fillColor; RefPtr<Gradient> fillGradient; diff --git a/WebCore/platform/graphics/GraphicsLayer.cpp b/WebCore/platform/graphics/GraphicsLayer.cpp new file mode 100644 index 0000000..0c442a2 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsLayer.cpp @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" + +#include "FloatPoint.h" +#include "RotateTransformOperation.h" +#include "TextStream.h" + +namespace WebCore { + +void GraphicsLayer::FloatValue::set(float key, float value, const TimingFunction* timingFunction) +{ + m_key = key; + m_value = value; + if (timingFunction != m_timingFunction) { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + else + m_timingFunction.clear(); + } +} + +void GraphicsLayer::TransformValue::set(float key, const TransformOperations* value, const TimingFunction* timingFunction) +{ + m_key = key; + if (value != m_value) { + if (value) + m_value.set(new TransformOperations(*value)); + else + m_value.clear(); + } + if (timingFunction != m_timingFunction) { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + else + m_timingFunction.clear(); + } +} + +void GraphicsLayer::FloatValueList::insert(float key, float value, const TimingFunction* timingFunction) +{ + for (size_t i = 0; i < m_values.size(); ++i) { + FloatValue& curFloatValue = m_values[i]; + if (curFloatValue.key() == key) { + curFloatValue.set(key, value, timingFunction); + return; + } + if (curFloatValue.key() > key) { + // insert before + m_values.insert(i, FloatValue(key, value, timingFunction)); + return; + } + } + + // append + m_values.append(FloatValue(key, value, timingFunction)); +} + +void GraphicsLayer::TransformValueList::insert(float key, const TransformOperations* value, const TimingFunction* timingFunction) +{ + for (size_t i = 0; i < m_values.size(); ++i) { + TransformValue& curTransValue = m_values[i]; + if (curTransValue.key() == key) { + curTransValue.set(key, value, timingFunction); + return; + } + if (curTransValue.key() > key) { + // insert before + m_values.insert(i, TransformValue(key, value, timingFunction)); + return; + } + } + + // append + m_values.append(TransformValue(key, value, timingFunction)); +} + +// An "invalid" list is one whose functions don't match, and therefore has to be animated as a Matrix +// The hasBigRotation flag will always return false if isValid is false. Otherwise hasBigRotation is +// true if the rotation between any two keyframes is >= 180 degrees. +void GraphicsLayer::TransformValueList::makeFunctionList(FunctionList& list, bool& isValid, bool& hasBigRotation) const +{ + list.clear(); + isValid = false; + hasBigRotation = false; + + if (m_values.size() < 2) + return; + + // empty transforms match anything, so find the first non-empty entry as the reference + size_t firstIndex = 0; + for ( ; firstIndex < m_values.size(); ++firstIndex) { + if (m_values[firstIndex].value()->operations().size() > 0) + break; + } + + if (firstIndex >= m_values.size()) + return; + + const TransformOperations* firstVal = m_values[firstIndex].value(); + + // see if the keyframes are valid + for (size_t i = firstIndex + 1; i < m_values.size(); ++i) { + const TransformOperations* val = m_values[i].value(); + + // a null transform matches anything + if (val->operations().isEmpty()) + continue; + + if (firstVal->operations().size() != val->operations().size()) + return; + + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + if (!firstVal->operations().at(j)->isSameType(*val->operations().at(j))) + return; + } + } + + // keyframes are valid, fill in the list + isValid = true; + + double lastRotAngle = 0.0; + double maxRotAngle = -1.0; + + list.resize(firstVal->operations().size()); + for (size_t j = 0; j < firstVal->operations().size(); ++j) { + TransformOperation::OperationType type = firstVal->operations().at(j)->getOperationType(); + list[j] = type; + + // if this is a rotation entry, we need to see if any angle differences are >= 180 deg + if (type == TransformOperation::ROTATE_X || + type == TransformOperation::ROTATE_Y || + type == TransformOperation::ROTATE_Z || + type == TransformOperation::ROTATE_3D) { + lastRotAngle = static_cast<RotateTransformOperation*>(firstVal->operations().at(j).get())->angle(); + + if (maxRotAngle < 0) + maxRotAngle = fabs(lastRotAngle); + + for (size_t i = firstIndex + 1; i < m_values.size(); ++i) { + const TransformOperations* val = m_values[i].value(); + double rotAngle = val->operations().isEmpty() ? 0 : (static_cast<RotateTransformOperation*>(val->operations().at(j).get())->angle()); + double diffAngle = fabs(rotAngle - lastRotAngle); + if (diffAngle > maxRotAngle) + maxRotAngle = diffAngle; + lastRotAngle = rotAngle; + } + } + } + + hasBigRotation = maxRotAngle >= 180.0; +} + +GraphicsLayer::GraphicsLayer(GraphicsLayerClient* client) + : m_client(client) + , m_anchorPoint(0.5f, 0.5f, 0) + , m_opacity(1) +#ifndef NDEBUG + , m_zPosition(0) +#endif + , m_backgroundColorSet(false) + , m_contentsOpaque(false) + , m_preserves3D(false) + , m_backfaceVisibility(true) + , m_usingTiledLayer(false) + , m_masksToBounds(false) + , m_drawsContent(false) + , m_paintingPhase(GraphicsLayerPaintAllMask) + , m_parent(0) +#ifndef NDEBUG + , m_repaintCount(0) +#endif +{ +} + +GraphicsLayer::~GraphicsLayer() +{ + removeAllAnimations(); + + removeAllChildren(); + removeFromParent(); +} + +void GraphicsLayer::addChild(GraphicsLayer* childLayer) +{ + ASSERT(childLayer != this); + + if (childLayer->parent()) + childLayer->removeFromParent(); + + childLayer->setParent(this); + m_children.append(childLayer); +} + +void GraphicsLayer::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + ASSERT(childLayer != this); + + if (childLayer->parent()) + childLayer->removeFromParent(); + + childLayer->setParent(this); + m_children.insert(index, childLayer); +} + +void GraphicsLayer::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + ASSERT(childLayer != this); + childLayer->removeFromParent(); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i, childLayer); + found = true; + break; + } + } + + childLayer->setParent(this); + + if (!found) + m_children.append(childLayer); +} + +void GraphicsLayer::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + childLayer->removeFromParent(); + ASSERT(childLayer != this); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i+1, childLayer); + found = true; + break; + } + } + + childLayer->setParent(this); + + if (!found) + m_children.append(childLayer); +} + +bool GraphicsLayer::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + ASSERT(!newChild->parent()); + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (oldChild == m_children[i]) { + m_children[i] = newChild; + found = true; + break; + } + } + if (found) { + oldChild->setParent(0); + + newChild->removeFromParent(); + newChild->setParent(this); + return true; + } + return false; +} + +void GraphicsLayer::removeAllChildren() +{ + while (m_children.size()) { + GraphicsLayer* curLayer = m_children[0]; + ASSERT(curLayer->parent()); + curLayer->removeFromParent(); + } +} + +void GraphicsLayer::removeFromParent() +{ + if (m_parent) { + unsigned i; + for (i = 0; i < m_parent->m_children.size(); i++) { + if (this == m_parent->m_children[i]) { + m_parent->m_children.remove(i); + break; + } + } + + setParent(0); + } +} + +void GraphicsLayer::setBackgroundColor(const Color& inColor, const Animation*, double /*beginTime*/) +{ + m_backgroundColor = inColor; + m_backgroundColorSet = true; +} + +void GraphicsLayer::clearBackgroundColor() +{ + m_backgroundColor = Color(); + m_backgroundColorSet = false; +} + +bool GraphicsLayer::setOpacity(float opacity, const Animation*, double) +{ + m_opacity = opacity; + return false; // not animating +} + +void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const IntRect& clip) +{ + m_client->paintContents(this, context, m_paintingPhase, clip); +} + +String GraphicsLayer::propertyIdToString(AnimatedPropertyID property) +{ + switch (property) { + case AnimatedPropertyWebkitTransform: + return "transform"; + case AnimatedPropertyOpacity: + return "opacity"; + case AnimatedPropertyBackgroundColor: + return "backgroundColor"; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + } + ASSERT_NOT_REACHED(); + return ""; +} + +int GraphicsLayer::findAnimationEntry(AnimatedPropertyID property, short index) const +{ + for (size_t i = 0; i < m_animations.size(); ++i) { + if (m_animations[i].matches(property, index)) + return static_cast<int>(i); + } + return -1; +} + +void GraphicsLayer::addAnimationEntry(AnimatedPropertyID property, short index, bool isTransition, const Animation* transition) +{ + int i = findAnimationEntry(property, index); + + if (i >= 0) + m_animations[i].reset(transition, isTransition); + else + m_animations.append(AnimationEntry(transition, property, index, isTransition)); +} + +void GraphicsLayer::removeAllAnimations() +{ + size_t size = m_animations.size(); + for (size_t i = 0; i < size; ++i) + removeAnimation(0, true); +} + +void GraphicsLayer::removeAllAnimationsForProperty(AnimatedPropertyID property) +{ + for (short j = 0; ; ++j) { + int i = findAnimationEntry(property, j); + if (i < 0) + break; + removeAnimation(i, false); + } +} + +void GraphicsLayer::removeFinishedAnimations(const String& name, int /*index*/, bool reset) +{ + size_t size = m_animations.size(); + for (size_t i = 0; i < size; ) { + AnimationEntry& anim = m_animations[i]; + if (!anim.isTransition() && anim.animation()->name() == name) { + removeAnimation(i, reset); + --size; + } else + ++i; + } +} + +void GraphicsLayer::removeFinishedTransitions(AnimatedPropertyID property) +{ + size_t size = m_animations.size(); + for (size_t i = 0; i < size; ) { + AnimationEntry& anim = m_animations[i]; + if (anim.isTransition() && property == anim.property()) { + removeAnimation(i, false); + --size; + } else + ++i; + } +} + +void GraphicsLayer::suspendAnimations() +{ +} + +void GraphicsLayer::resumeAnimations() +{ +} + +#ifndef NDEBUG +void GraphicsLayer::updateDebugIndicators() +{ + if (GraphicsLayer::showDebugBorders()) { + if (drawsContent()) { + if (m_usingTiledLayer) + setDebugBorder(Color(0, 255, 0, 204), 2.0f); // tiled layer: green + else + setDebugBorder(Color(255, 0, 0, 204), 2.0f); // normal layer: red + } else if (masksToBounds()) { + setDebugBorder(Color(128, 255, 255, 178), 2.0f); // masking layer: pale blue + if (GraphicsLayer::showDebugBorders()) + setDebugBackgroundColor(Color(128, 255, 255, 52)); + } else + setDebugBorder(Color(255, 255, 0, 204), 2.0f); // container: yellow + } +} + +void GraphicsLayer::setZPosition(float position) +{ + m_zPosition = position; +} +#endif + +static void writeIndent(TextStream& ts, int indent) +{ + for (int i = 0; i != indent; ++i) + ts << " "; +} + +void GraphicsLayer::dumpLayer(TextStream& ts, int indent) const +{ + writeIndent(ts, indent); + ts << "(" << "GraphicsLayer" << " " << static_cast<void*>(const_cast<GraphicsLayer*>(this)); + ts << " \"" << m_name << "\"\n"; + dumpProperties(ts, indent); + writeIndent(ts, indent); + ts << ")\n"; +} + +void GraphicsLayer::dumpProperties(TextStream& ts, int indent) const +{ + writeIndent(ts, indent + 1); + ts << "(position " << m_position.x() << " " << m_position.y() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(anchor " << m_anchorPoint.x() << " " << m_anchorPoint.y() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(bounds " << m_size.width() << " " << m_size.height() << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(opacity " << m_opacity << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(usingTiledLayer " << m_usingTiledLayer << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(m_preserves3D " << m_preserves3D << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(drawsContent " << m_drawsContent << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(m_backfaceVisibility " << (m_backfaceVisibility ? "visible" : "hidden") << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(client "; + if (m_client) + ts << static_cast<void*>(m_client); + else + ts << "none"; + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(backgroundColor "; + if (!m_backgroundColorSet) + ts << "none"; + else + ts << m_backgroundColor.name(); + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(transform "; + if (m_transform.isIdentity()) + ts << "identity"; + else { + ts << "[" << m_transform.m11() << " " << m_transform.m12() << " " << m_transform.m13() << " " << m_transform.m14() << "] "; + ts << "[" << m_transform.m21() << " " << m_transform.m22() << " " << m_transform.m23() << " " << m_transform.m24() << "] "; + ts << "[" << m_transform.m31() << " " << m_transform.m32() << " " << m_transform.m33() << " " << m_transform.m34() << "] "; + ts << "[" << m_transform.m41() << " " << m_transform.m42() << " " << m_transform.m43() << " " << m_transform.m44() << "]"; + } + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(childrenTransform "; + if (m_childrenTransform.isIdentity()) + ts << "identity"; + else { + ts << "[" << m_childrenTransform.m11() << " " << m_childrenTransform.m12() << " " << m_childrenTransform.m13() << " " << m_childrenTransform.m14() << "] "; + ts << "[" << m_childrenTransform.m21() << " " << m_childrenTransform.m22() << " " << m_childrenTransform.m23() << " " << m_childrenTransform.m24() << "] "; + ts << "[" << m_childrenTransform.m31() << " " << m_childrenTransform.m32() << " " << m_childrenTransform.m33() << " " << m_childrenTransform.m34() << "] "; + ts << "[" << m_childrenTransform.m41() << " " << m_childrenTransform.m42() << " " << m_childrenTransform.m43() << " " << m_childrenTransform.m44() << "]"; + } + ts << ")\n"; + + writeIndent(ts, indent + 1); + ts << "(children " << m_children.size() << "\n"; + + unsigned i; + for (i = 0; i < m_children.size(); i++) + m_children[i]->dumpLayer(ts, indent+2); + writeIndent(ts, indent + 1); + ts << ")\n"; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/GraphicsLayer.h b/WebCore/platform/graphics/GraphicsLayer.h new file mode 100644 index 0000000..f928ce8 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsLayer.h @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayer_h +#define GraphicsLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "Animation.h" +#include "Color.h" +#include "FloatPoint.h" +#include "FloatPoint3D.h" +#include "FloatSize.h" +#include "GraphicsLayerClient.h" +#include "TransformationMatrix.h" +#include "TransformOperations.h" +#include <wtf/OwnPtr.h> + +#if PLATFORM(MAC) +#ifdef __OBJC__ +@class WebLayer; +@class CALayer; +typedef WebLayer PlatformLayer; +typedef CALayer* NativeLayer; +#else +typedef void* PlatformLayer; +typedef void* NativeLayer; +#endif +#else +typedef void* PlatformLayer; +typedef void* NativeLayer; +#endif + +namespace WebCore { + +class FloatPoint3D; +class GraphicsContext; +class Image; +class TextStream; +class TimingFunction; + +// GraphicsLayer is an abstraction for a rendering surface with backing store, +// which may have associated transformation and animations. + +class GraphicsLayer { +public: + // Used to store one float value of a keyframe animation. + class FloatValue { + public: + FloatValue(float key, float value, const TimingFunction* timingFunction = 0) + : m_key(key), m_value(value), m_timingFunction(0) + { + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + } + + FloatValue(const FloatValue& other) + : m_key(other.key()), m_value(other.value()), m_timingFunction(0) + { + if (other.timingFunction()) + m_timingFunction.set(new TimingFunction(*other.timingFunction())); + } + + const FloatValue& operator=(const FloatValue& other) + { + if (&other != this) + set(other.key(), other.value(), other.timingFunction()); + return *this; + } + + void set(float key, float value, const TimingFunction*); + + float key() const { return m_key; } + float value() const { return m_value; } + const TimingFunction* timingFunction() const { return m_timingFunction.get(); } + + private: + float m_key; + float m_value; + OwnPtr<TimingFunction> m_timingFunction; + }; + + + class FloatValueList { + public: + void insert(float key, float value, const TimingFunction* timingFunction); + + size_t size() const { return m_values.size(); } + const FloatValue& at(size_t i) const { return m_values.at(i); } + const Vector<FloatValue>& values() const { return m_values; } + + private: + Vector<FloatValue> m_values; + }; + + // Used to store one transform in a keyframe list. + class TransformValue { + public: + TransformValue(float key = NAN, const TransformOperations* value = 0, const TimingFunction* timingFunction = 0) + : m_key(key) + { + if (value) + m_value.set(new TransformOperations(*value)); + if (timingFunction) + m_timingFunction.set(new TimingFunction(*timingFunction)); + } + + TransformValue(const TransformValue& other) + : m_key(other.key()) + { + if (other.value()) + m_value.set(new TransformOperations(*other.value())); + if (other.timingFunction()) + m_timingFunction.set(new TimingFunction(*other.timingFunction())); + } + + const TransformValue& operator=(const TransformValue& other) + { + if (&other != this) + set(other.key(), other.value(), other.timingFunction()); + return *this; + } + + void set(float key, const TransformOperations* value, const TimingFunction* timingFunction); + + float key() const { return m_key; } + const TransformOperations* value() const { return m_value.get(); } + const TimingFunction* timingFunction() const { return m_timingFunction.get(); } + + private: + float m_key; + OwnPtr<TransformOperations> m_value; + OwnPtr<TimingFunction> m_timingFunction; + }; + + // Used to store a series of transforms in a keyframe list. + class TransformValueList { + public: + typedef Vector<TransformOperation::OperationType> FunctionList; + + size_t size() const { return m_values.size(); } + const TransformValue& at(size_t i) const { return m_values.at(i); } + const Vector<TransformValue>& values() const { return m_values; } + + void insert(float key, const TransformOperations* value, const TimingFunction* timingFunction); + + // return a list of the required functions. List is empty if keyframes are not valid + // If return value is true, functions contain rotations of >= 180 degrees + void makeFunctionList(FunctionList& list, bool& isValid, bool& hasBigRotation) const; + private: + Vector<TransformValue> m_values; + }; + + static GraphicsLayer* createGraphicsLayer(GraphicsLayerClient*); + + virtual ~GraphicsLayer(); + + GraphicsLayerClient* client() const { return m_client; } + + // Layer name. Only used to identify layers in debug output + const String& name() const { return m_name; } + virtual void setName(const String& name) { m_name = name; } + + // For hosting this GraphicsLayer in a native layer hierarchy. + virtual NativeLayer nativeLayer() const { return 0; } + + GraphicsLayer* parent() const { return m_parent; }; + void setParent(GraphicsLayer* layer) { m_parent = layer; } // Internal use only. + + const Vector<GraphicsLayer*>& children() const { return m_children; } + + // Add child layers. If the child is already parented, it will be removed from its old parent. + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + + void removeAllChildren(); + virtual void removeFromParent(); + + // Offset is origin of the renderer minus origin of the graphics layer (so either zero or negative). + IntSize offsetFromRenderer() const { return m_offsetFromRenderer; } + void setOffsetFromRenderer(const IntSize& offset) { m_offsetFromRenderer = offset; } + + // The position of the layer (the location of its top-left corner in its parent) + const FloatPoint& position() const { return m_position; } + virtual void setPosition(const FloatPoint& p) { m_position = p; } + + // Anchor point: (0, 0) is top left, (1, 1) is bottom right. The anchor point + // affects the origin of the transforms. + const FloatPoint3D& anchorPoint() const { return m_anchorPoint; } + virtual void setAnchorPoint(const FloatPoint3D& p) { m_anchorPoint = p; } + + // The bounds of the layer + const FloatSize& size() const { return m_size; } + virtual void setSize(const FloatSize& size) { m_size = size; } + + const TransformationMatrix& transform() const { return m_transform; } + virtual void setTransform(const TransformationMatrix& t) { m_transform = t; } + + const TransformationMatrix& childrenTransform() const { return m_childrenTransform; } + virtual void setChildrenTransform(const TransformationMatrix& t) { m_childrenTransform = t; } + + bool preserves3D() const { return m_preserves3D; } + virtual void setPreserves3D(bool b) { m_preserves3D = b; } + + bool masksToBounds() const { return m_masksToBounds; } + virtual void setMasksToBounds(bool b) { m_masksToBounds = b; } + + bool drawsContent() const { return m_drawsContent; } + virtual void setDrawsContent(bool b) { m_drawsContent = b; } + + // The color used to paint the layer backgrounds + const Color& backgroundColor() const { return m_backgroundColor; } + virtual void setBackgroundColor(const Color&, const Animation* = 0, double beginTime = 0); + virtual void clearBackgroundColor(); + bool backgroundColorSet() const { return m_backgroundColorSet; } + + // opaque means that we know the layer contents have no alpha + bool contentsOpaque() const { return m_contentsOpaque; } + virtual void setContentsOpaque(bool b) { m_contentsOpaque = b; } + + bool backfaceVisibility() const { return m_backfaceVisibility; } + virtual void setBackfaceVisibility(bool b) { m_backfaceVisibility = b; } + + float opacity() const { return m_opacity; } + // return true if we started an animation + virtual bool setOpacity(float o, const Animation* = 0, double beginTime = 0); + + // Some GraphicsLayers paint only the foreground or the background content + GraphicsLayerPaintingPhase drawingPhase() const { return m_paintingPhase; } + void setDrawingPhase(GraphicsLayerPaintingPhase phase) { m_paintingPhase = phase; } + + virtual void setNeedsDisplay() = 0; + // mark the given rect (in layer coords) as needing dispay. Never goes deep. + virtual void setNeedsDisplayInRect(const FloatRect&) = 0; + + virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition) = 0; + virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime) = 0; + + void removeFinishedAnimations(const String& name, int index, bool reset); + void removeFinishedTransitions(AnimatedPropertyID); + void removeAllAnimations(); + + virtual void suspendAnimations(); + virtual void resumeAnimations(); + + // Layer contents + virtual void setContentsToImage(Image*) { } + virtual void setContentsToVideo(PlatformLayer*) { } + virtual void setContentsBackgroundColor(const Color&) { } + virtual void clearContents() { } + + virtual void updateContentsRect() { } + + // Callback from the underlying graphics system to draw layer contents. + void paintGraphicsLayerContents(GraphicsContext&, const IntRect& clip); + + virtual PlatformLayer* platformLayer() const { return 0; } + + void dumpLayer(TextStream&, int indent = 0) const; + +#ifndef NDEBUG + int repaintCount() const { return m_repaintCount; } + int incrementRepaintCount() { return ++m_repaintCount; } +#endif + + // Platform behaviors + static bool graphicsContextsFlipped(); + +#ifndef NDEBUG + static bool showDebugBorders(); + static bool showRepaintCounter(); + + void updateDebugIndicators(); + + virtual void setDebugBackgroundColor(const Color&) { } + virtual void setDebugBorder(const Color&, float /*borderWidth*/) { } + // z-position is the z-equivalent of position(). It's only used for debugging purposes. + virtual float zPosition() const { return m_zPosition; } + virtual void setZPosition(float); +#endif + + static String propertyIdToString(AnimatedPropertyID); + +protected: + GraphicsLayer(GraphicsLayerClient*); + + void dumpProperties(TextStream&, int indent) const; + + // returns -1 if not found + int findAnimationEntry(AnimatedPropertyID, short index) const; + void addAnimationEntry(AnimatedPropertyID, short index, bool isTransition, const Animation*); + + virtual void removeAnimation(int /*index*/, bool /*reset*/) {} + void removeAllAnimationsForProperty(AnimatedPropertyID); + + GraphicsLayerClient* m_client; + String m_name; + + // Offset from the owning renderer + IntSize m_offsetFromRenderer; + + // Position is relative to the parent GraphicsLayer + FloatPoint m_position; + FloatPoint3D m_anchorPoint; + FloatSize m_size; + TransformationMatrix m_transform; + TransformationMatrix m_childrenTransform; + + Color m_backgroundColor; + float m_opacity; +#ifndef NDEBUG + float m_zPosition; +#endif + + bool m_backgroundColorSet : 1; + bool m_contentsOpaque : 1; + bool m_preserves3D: 1; + bool m_backfaceVisibility : 1; + bool m_usingTiledLayer : 1; + bool m_masksToBounds : 1; + bool m_drawsContent : 1; + + GraphicsLayerPaintingPhase m_paintingPhase; + + Vector<GraphicsLayer*> m_children; + GraphicsLayer* m_parent; + + // AnimationEntry represents an animation of a property on this layer. + // For transform only, there may be more than one, in which case 'index' + // is an index into the list of transforms. + class AnimationEntry { + public: + AnimationEntry(const Animation* animation, AnimatedPropertyID property, short index, bool isTransition) + : m_animation(const_cast<Animation*>(animation)) + , m_property(property) + , m_index(index) + , m_isCurrent(true) + , m_isTransition(isTransition) + { + } + + const Animation* animation() const { return m_animation.get(); } + AnimatedPropertyID property() const { return m_property; } + int index() const { return m_index; } + bool isCurrent() const { return m_isCurrent; } + void setIsCurrent(bool b = true) { m_isCurrent = b; } + bool isTransition() const { return m_isTransition; } + + bool matches(AnimatedPropertyID property, short index) const + { + return m_property == property && m_index == index; + } + + void reset(const Animation* animation, bool isTransition) + { + m_animation = const_cast<Animation*>(animation); + m_isTransition = isTransition; + m_isCurrent = true; + } + + private: + RefPtr<Animation> m_animation; + AnimatedPropertyID m_property : 14; + short m_index : 16; + bool m_isCurrent : 1; + bool m_isTransition : 1; + }; + + Vector<AnimationEntry> m_animations; // running animations/transitions + +#ifndef NDEBUG + int m_repaintCount; +#endif +}; + + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayer_h + diff --git a/WebCore/platform/graphics/GraphicsLayerClient.h b/WebCore/platform/graphics/GraphicsLayerClient.h new file mode 100644 index 0000000..46382f2 --- /dev/null +++ b/WebCore/platform/graphics/GraphicsLayerClient.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayerClient_h +#define GraphicsLayerClient_h + +#if USE(ACCELERATED_COMPOSITING) + +namespace WebCore { + +class GraphicsContext; +class GraphicsLayer; +class IntPoint; +class IntRect; +class FloatPoint; + +enum GraphicsLayerPaintingPhase { + GraphicsLayerPaintBackgroundMask = (1 << 0), + GraphicsLayerPaintForegroundMask = (1 << 1), + GraphicsLayerPaintAllMask = (GraphicsLayerPaintBackgroundMask | GraphicsLayerPaintForegroundMask) +}; + +enum AnimatedPropertyID { + AnimatedPropertyInvalid, + AnimatedPropertyWebkitTransform, + AnimatedPropertyOpacity, + AnimatedPropertyBackgroundColor +}; + +class GraphicsLayerClient { +public: + virtual ~GraphicsLayerClient() {} + + // Callbacks for when hardware-accelerated transitions and animation started + virtual void notifyAnimationStarted(const GraphicsLayer*, double time) = 0; + + virtual void paintContents(const GraphicsLayer*, GraphicsContext&, GraphicsLayerPaintingPhase, const IntRect& inClip) = 0; + + // Return a rect for the "contents" of the graphics layer, i.e. video or image content, in GraphicsLayer coordinates. + virtual IntRect contentsBox(const GraphicsLayer*) = 0; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerClient_h diff --git a/WebCore/platform/graphics/GraphicsTypes.h b/WebCore/platform/graphics/GraphicsTypes.h index cdf5e31..769207a 100644 --- a/WebCore/platform/graphics/GraphicsTypes.h +++ b/WebCore/platform/graphics/GraphicsTypes.h @@ -50,6 +50,14 @@ namespace WebCore { CompositePlusLighter }; + // FIXME: Currently these constants have to match the values used in the SVG + // DOM API. That's a mistake. We need to make cut that dependency. + enum GradientSpreadMethod { + SpreadMethodPad = 1, + SpreadMethodReflect = 2, + SpreadMethodRepeat = 3 + }; + enum LineCap { ButtCap, RoundCap, SquareCap }; enum LineJoin { MiterJoin, RoundJoin, BevelJoin }; diff --git a/WebCore/platform/graphics/Image.cpp b/WebCore/platform/graphics/Image.cpp index 49961e1..08d96b4 100644 --- a/WebCore/platform/graphics/Image.cpp +++ b/WebCore/platform/graphics/Image.cpp @@ -119,7 +119,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& destRect, const Fl FloatSize scale(scaledTileSize.width() / intrinsicTileSize.width(), scaledTileSize.height() / intrinsicTileSize.height()); - TransformationMatrix patternTransform = TransformationMatrix().scale(scale.width(), scale.height()); + TransformationMatrix patternTransform = TransformationMatrix().scaleNonUniform(scale.width(), scale.height()); FloatRect oneTileRect; oneTileRect.setX(destRect.x() + fmodf(fmodf(-srcPoint.x(), scaledTileSize.width()) - scaledTileSize.width(), scaledTileSize.width())); @@ -158,7 +158,7 @@ void Image::drawTiled(GraphicsContext* ctxt, const FloatRect& dstRect, const Flo vRule = RepeatTile; FloatSize scale = calculatePatternScale(dstRect, srcRect, hRule, vRule); - TransformationMatrix patternTransform = TransformationMatrix().scale(scale.width(), scale.height()); + TransformationMatrix patternTransform = TransformationMatrix().scaleNonUniform(scale.width(), scale.height()); // We want to construct the phase such that the pattern is centered (when stretch is not // set for a particular rule). diff --git a/WebCore/platform/graphics/Image.h b/WebCore/platform/graphics/Image.h index c3cf2e7..70f6d49 100644 --- a/WebCore/platform/graphics/Image.h +++ b/WebCore/platform/graphics/Image.h @@ -118,9 +118,9 @@ public: SharedBuffer* data() { return m_data.get(); } - // It may look unusual that there is no start animation call as public API. This is because - // we start and stop animating lazily. Animation begins whenever someone draws the image. It will - // automatically pause once all observers no longer want to render the image anywhere. + // Animation begins whenever someone draws the image, so startAnimation() is not normally called. + // It will automatically pause once all observers no longer want to render the image anywhere. + virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { } virtual void stopAnimation() {} virtual void resetAnimation() {} @@ -164,11 +164,9 @@ protected: void drawTiled(GraphicsContext*, const FloatRect& dstRect, const FloatRect& srcRect, TileRule hRule, TileRule vRule, CompositeOperator); // Supporting tiled drawing - virtual bool mayFillWithSolidColor() const { return false; } + virtual bool mayFillWithSolidColor() { return false; } virtual Color solidColor() const { return Color(); } - virtual void startAnimation(bool /*catchUpIfNecessary*/ = true) { } - virtual void drawPattern(GraphicsContext*, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator, const FloatRect& destRect); #if PLATFORM(CG) diff --git a/WebCore/platform/graphics/IntPoint.h b/WebCore/platform/graphics/IntPoint.h index cb24b4e..a05b1f0 100644 --- a/WebCore/platform/graphics/IntPoint.h +++ b/WebCore/platform/graphics/IntPoint.h @@ -59,7 +59,7 @@ class TPoint; class wxPoint; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) struct SkPoint; struct SkIPoint; #endif @@ -128,7 +128,7 @@ public: operator wxPoint() const; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) IntPoint(const SkIPoint&); operator SkIPoint() const; operator SkPoint() const; diff --git a/WebCore/platform/graphics/IntRect.h b/WebCore/platform/graphics/IntRect.h index 03784a3..1be98b8 100644 --- a/WebCore/platform/graphics/IntRect.h +++ b/WebCore/platform/graphics/IntRect.h @@ -58,7 +58,7 @@ class TRect; class wxRect; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) struct SkRect; struct SkIRect; #endif @@ -158,7 +158,7 @@ public: operator CGRect() const; #endif -#if PLATFORM(SKIA) +#if (PLATFORM(SKIA) || PLATFORM(SGL)) IntRect(const SkIRect&); operator SkRect() const; operator SkIRect() const; diff --git a/WebCore/platform/graphics/MediaPlayer.cpp b/WebCore/platform/graphics/MediaPlayer.cpp index 21e31fc..99d6aa4 100644 --- a/WebCore/platform/graphics/MediaPlayer.cpp +++ b/WebCore/platform/graphics/MediaPlayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * 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 @@ -27,7 +27,9 @@ #if ENABLE(VIDEO) #include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" +#include "ContentType.h" #include "IntRect.h" #include "MIMETypeRegistry.h" #include "FrameView.h" @@ -47,26 +49,196 @@ #endif namespace WebCore { + +// a null player to make MediaPlayer logic simpler + +class NullMediaPlayerPrivate : public MediaPlayerPrivateInterface { +public: + NullMediaPlayerPrivate(MediaPlayer*) { } + + virtual void load(const String&) { } + virtual void cancelLoad() { } + + virtual void play() { } + virtual void pause() { } + + virtual IntSize naturalSize() const { return IntSize(0, 0); } + + virtual bool hasVideo() const { return false; } + + virtual void setVisible(bool) { } + + virtual float duration() const { return 0; } + + virtual float currentTime() const { return 0; } + virtual void seek(float) { } + virtual bool seeking() const { return false; } + + virtual void setEndTime(float) { } + + virtual void setRate(float) { } + virtual bool paused() const { return false; } + + virtual void setVolume(float) { } + + virtual MediaPlayer::NetworkState networkState() const { return MediaPlayer::Empty; } + virtual MediaPlayer::ReadyState readyState() const { return MediaPlayer::HaveNothing; } + + virtual float maxTimeSeekable() const { return 0; } + virtual float maxTimeBuffered() const { return 0; } + + virtual int dataRate() const { return 0; } + + virtual bool totalBytesKnown() const { return false; } + virtual unsigned totalBytes() const { return 0; } + virtual unsigned bytesLoaded() const { return 0; } + + virtual void setSize(const IntSize&) { } + + virtual void paint(GraphicsContext*, const IntRect&) { } + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual void setPoster(const String& /*url*/) { } + virtual void deliverNotification(MediaPlayerProxyNotificationType) { } + virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) { } +#endif +}; + +static MediaPlayerPrivateInterface* createNullMediaPlayer(MediaPlayer* player) +{ + return new NullMediaPlayerPrivate(player); +} + + +// engine support + +struct MediaPlayerFactory { + MediaPlayerFactory(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsTypeAndCodecs) + : constructor(constructor) + , getSupportedTypes(getSupportedTypes) + , supportsTypeAndCodecs(supportsTypeAndCodecs) + { + } + + CreateMediaEnginePlayer constructor; + MediaEngineSupportedTypes getSupportedTypes; + MediaEngineSupportsType supportsTypeAndCodecs; +}; + +static void addMediaEngine(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType); +static MediaPlayerFactory* chooseBestEngineForTypeAndCodecs(const String& type, const String& codecs); + +static Vector<MediaPlayerFactory*>& installedMediaEngines() +{ + DEFINE_STATIC_LOCAL(Vector<MediaPlayerFactory*>, installedEngines, ()); + static bool enginesQueried = false; + + if (!enginesQueried) { + enginesQueried = true; + MediaPlayerPrivate::registerMediaEngine(addMediaEngine); + + // register additional engines here + } - MediaPlayer::MediaPlayer(MediaPlayerClient* client) + return installedEngines; +} + +static void addMediaEngine(CreateMediaEnginePlayer constructor, MediaEngineSupportedTypes getSupportedTypes, MediaEngineSupportsType supportsType) +{ + ASSERT(constructor); + ASSERT(getSupportedTypes); + ASSERT(supportsType); + installedMediaEngines().append(new MediaPlayerFactory(constructor, getSupportedTypes, supportsType)); +} + +static MediaPlayerFactory* chooseBestEngineForTypeAndCodecs(const String& type, const String& codecs) +{ + Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + + if (engines.isEmpty()) + return 0; + + MediaPlayerFactory* engine = 0; + MediaPlayer::SupportsType supported = MediaPlayer::IsNotSupported; + + unsigned count = engines.size(); + for (unsigned ndx = 0; ndx < count; ndx++) { + MediaPlayer::SupportsType engineSupport = engines[ndx]->supportsTypeAndCodecs(type, codecs); + if (engineSupport > supported) { + supported = engineSupport; + engine = engines[ndx]; + } + } + + return engine; +} + +// media player + +MediaPlayer::MediaPlayer(MediaPlayerClient* client) : m_mediaPlayerClient(client) - , m_private(new MediaPlayerPrivate(this)) + , m_private(createNullMediaPlayer(this)) + , m_currentMediaEngine(0) , m_frameView(0) , m_visible(false) , m_rate(1.0f) , m_volume(1.0f) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + , m_playerProxy(0) +#endif { +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + if (!engines.isEmpty()) { + m_currentMediaEngine = engines[0]; + m_private.clear(); + m_private.set(engines[0]->constructor(this)); + } +#endif } MediaPlayer::~MediaPlayer() { - delete m_private; } -void MediaPlayer::load(const String& url) +void MediaPlayer::load(const String& url, const ContentType& contentType) +{ + String type = contentType.type(); + String codecs = contentType.parameter("codecs"); + + // if we don't know the MIME type, see if the path can help + if (type.isEmpty()) + type = MIMETypeRegistry::getMIMETypeForPath(url); + + MediaPlayerFactory* engine = chooseBestEngineForTypeAndCodecs(type, codecs); + + // if we didn't find an engine that claims the MIME type, just use the first engine + if (!engine) + engine = installedMediaEngines()[0]; + + // don't delete and recreate the player unless it comes from a different engine + if (engine && m_currentMediaEngine != engine) { + m_currentMediaEngine = engine; + m_private.clear(); + m_private.set(engine->constructor(this)); +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + m_private->setMediaPlayerProxy(m_playerProxy); +#endif + + } + + if (m_private) + m_private->load(url); + else + m_private.set(createNullMediaPlayer(this)); +} + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +void MediaPlayer::setPoster(const String& url) { - m_private->load(url); + m_private->setPoster(url); } +#endif void MediaPlayer::cancelLoad() { @@ -90,7 +262,7 @@ float MediaPlayer::duration() const float MediaPlayer::currentTime() const { - return m_private->currentTime(); + return m_private->currentTime(); } void MediaPlayer::seek(float time) @@ -193,10 +365,10 @@ unsigned MediaPlayer::totalBytes() return m_private->totalBytes(); } -void MediaPlayer::setRect(const IntRect& r) +void MediaPlayer::setSize(const IntSize& size) { - m_rect = r; - m_private->setRect(r); + m_size = size; + m_private->setSize(size); } bool MediaPlayer::visible() const @@ -215,29 +387,47 @@ void MediaPlayer::paint(GraphicsContext* p, const IntRect& r) m_private->paint(p, r); } -bool MediaPlayer::supportsType(const String& type) +MediaPlayer::SupportsType MediaPlayer::supportsType(ContentType contentType) { - HashSet<String> types; - getSupportedTypes(types); - return MIMETypeRegistry::isSupportedMediaMIMEType(type) && types.contains(type); + String type = contentType.type(); + String codecs = contentType.parameter("codecs"); + MediaPlayerFactory* engine = chooseBestEngineForTypeAndCodecs(type, codecs); + + if (!engine) + return IsNotSupported; + + return engine->supportsTypeAndCodecs(type, codecs); } void MediaPlayer::getSupportedTypes(HashSet<String>& types) { - MediaPlayerPrivate::getSupportedTypes(types); + Vector<MediaPlayerFactory*>& engines = installedMediaEngines(); + if (engines.isEmpty()) + return; + + unsigned count = engines.size(); + for (unsigned ndx = 0; ndx < count; ndx++) + engines[ndx]->getSupportedTypes(types); } - + bool MediaPlayer::isAvailable() { - static bool availabityKnown = false; - static bool isAvailable; - if (!availabityKnown) { - isAvailable = MediaPlayerPrivate::isAvailable(); - availabityKnown = true; - } - return isAvailable; + return !installedMediaEngines().isEmpty(); } +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +void MediaPlayer::deliverNotification(MediaPlayerProxyNotificationType notification) +{ + m_private->deliverNotification(notification); +} + +void MediaPlayer::setMediaPlayerProxy(WebMediaPlayerProxy* proxy) +{ + m_playerProxy = proxy; + m_private->setMediaPlayerProxy(proxy); +} +#endif + void MediaPlayer::networkStateChanged() { if (m_mediaPlayerClient) @@ -262,11 +452,29 @@ void MediaPlayer::timeChanged() m_mediaPlayerClient->mediaPlayerTimeChanged(this); } +void MediaPlayer::sizeChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerSizeChanged(this); +} + void MediaPlayer::repaint() { if (m_mediaPlayerClient) m_mediaPlayerClient->mediaPlayerRepaint(this); } +void MediaPlayer::durationChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerDurationChanged(this); +} + +void MediaPlayer::rateChanged() +{ + if (m_mediaPlayerClient) + m_mediaPlayerClient->mediaPlayerRateChanged(this); +} + } #endif diff --git a/WebCore/platform/graphics/MediaPlayer.h b/WebCore/platform/graphics/MediaPlayer.h index 203f299..7d90e44 100644 --- a/WebCore/platform/graphics/MediaPlayer.h +++ b/WebCore/platform/graphics/MediaPlayer.h @@ -28,52 +28,78 @@ #if ENABLE(VIDEO) +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) +#include "MediaPlayerProxy.h" +#endif + #include "IntRect.h" #include "StringHash.h" #include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> #include <wtf/Noncopyable.h> namespace WebCore { +class ContentType; class FrameView; class GraphicsContext; +class IntRect; class IntSize; class MediaPlayer; -class MediaPlayerPrivate; +class MediaPlayerPrivateInterface; class String; class MediaPlayerClient { public: virtual ~MediaPlayerClient() { } + + // the network state has changed virtual void mediaPlayerNetworkStateChanged(MediaPlayer*) { } + + // the ready state has changed virtual void mediaPlayerReadyStateChanged(MediaPlayer*) { } + + // the volume or muted state has changed virtual void mediaPlayerVolumeChanged(MediaPlayer*) { } + + // time has jumped, eg. not as a result of normal playback virtual void mediaPlayerTimeChanged(MediaPlayer*) { } + + // a new frame of video is available virtual void mediaPlayerRepaint(MediaPlayer*) { } + + // the media file duration has changed, or is now known + virtual void mediaPlayerDurationChanged(MediaPlayer*) { } + + // the playback rate has changed + virtual void mediaPlayerRateChanged(MediaPlayer*) { } + + // the movie size has changed + virtual void mediaPlayerSizeChanged(MediaPlayer*) { } }; class MediaPlayer : Noncopyable { public: MediaPlayer(MediaPlayerClient*); virtual ~MediaPlayer(); - - static bool isAvailable(); - static bool supportsType(const String&); + + // media engine support + enum SupportsType { IsNotSupported, IsSupported, MayBeSupported }; + static MediaPlayer::SupportsType supportsType(ContentType contentType); static void getSupportedTypes(HashSet<String>&); + static bool isAvailable(); IntSize naturalSize(); bool hasVideo(); void setFrameView(FrameView* frameView) { m_frameView = frameView; } + FrameView* frameView() { return m_frameView; } bool inMediaDocument(); - // FIXME: it would be better to just have a getter and setter for size. - // This is currently an absolute rect, which is not appropriate for - // content with transforms - IntRect rect() const { return m_rect; } - void setRect(const IntRect& r); + IntSize size() const { return m_size; } + void setSize(const IntSize& size); - void load(const String& url); + void load(const String& url, const ContentType& contentType); void cancelLoad(); bool visible() const; @@ -96,7 +122,7 @@ public: float maxTimeBuffered(); float maxTimeSeekable(); - + unsigned bytesLoaded(); bool totalBytesKnown(); unsigned totalBytes(); @@ -108,33 +134,55 @@ public: void paint(GraphicsContext*, const IntRect&); - enum NetworkState { Empty, LoadFailed, Loading, LoadedMetaData, LoadedFirstFrame, Loaded }; + enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; NetworkState networkState(); - enum ReadyState { DataUnavailable, CanShowCurrentFrame, CanPlay, CanPlayThrough }; + enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; ReadyState readyState(); void networkStateChanged(); void readyStateChanged(); void volumeChanged(); void timeChanged(); + void sizeChanged(); + void rateChanged(); + void durationChanged(); void repaint(); - + + MediaPlayerClient* mediaPlayerClient() const { return m_mediaPlayerClient; } + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + void setPoster(const String& url); + void deliverNotification(MediaPlayerProxyNotificationType notification); + void setMediaPlayerProxy(WebMediaPlayerProxy* proxy); +#endif + private: - - friend class MediaPlayerPrivate; - + static void initializeMediaEngines(); + MediaPlayerClient* m_mediaPlayerClient; - MediaPlayerPrivate* m_private; + OwnPtr<MediaPlayerPrivateInterface*> m_private; + void* m_currentMediaEngine; FrameView* m_frameView; - IntRect m_rect; + IntSize m_size; bool m_visible; float m_rate; float m_volume; +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + WebMediaPlayerProxy* m_playerProxy; // not owned or used, passed to m_private +#endif }; +typedef MediaPlayerPrivateInterface* (*CreateMediaEnginePlayer)(MediaPlayer*); +typedef void (*MediaEngineSupportedTypes)(HashSet<String>& types); +typedef MediaPlayer::SupportsType (*MediaEngineSupportsType)(const String& type, const String& codecs); + +typedef void (*MediaEngineRegistrar)(CreateMediaEnginePlayer, MediaEngineSupportedTypes, MediaEngineSupportsType); + + } -#endif +#endif // ENABLE(VIDEO) + #endif diff --git a/WebCore/platform/graphics/MediaPlayerPrivate.h b/WebCore/platform/graphics/MediaPlayerPrivate.h new file mode 100644 index 0000000..2e73e7e --- /dev/null +++ b/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlayerPrivate_h +#define MediaPlayerPrivate_h + +#if ENABLE(VIDEO) + +#include "MediaPlayer.h" + +namespace WebCore { + +class IntRect; +class IntSize; +class String; + +class MediaPlayerPrivateInterface { +public: + virtual ~MediaPlayerPrivateInterface() { } + + virtual void load(const String& url) = 0; + virtual void cancelLoad() = 0; + + virtual void play() = 0; + virtual void pause() = 0; + + virtual IntSize naturalSize() const = 0; + + virtual bool hasVideo() const = 0; + + virtual void setVisible(bool) = 0; + + virtual float duration() const = 0; + + virtual float currentTime() const = 0; + virtual void seek(float time) = 0; + virtual bool seeking() const = 0; + + virtual void setEndTime(float time) = 0; + + virtual void setRate(float) = 0; + virtual bool paused() const = 0; + + virtual void setVolume(float) = 0; + + virtual MediaPlayer::NetworkState networkState() const = 0; + virtual MediaPlayer::ReadyState readyState() const = 0; + + virtual float maxTimeSeekable() const = 0; + virtual float maxTimeBuffered() const = 0; + + virtual int dataRate() const = 0; + + virtual bool totalBytesKnown() const { return totalBytes() > 0; } + virtual unsigned totalBytes() const = 0; + virtual unsigned bytesLoaded() const = 0; + + virtual void setSize(const IntSize&) = 0; + + virtual void paint(GraphicsContext*, const IntRect&) = 0 ; + +#if ENABLE(PLUGIN_PROXY_FOR_VIDEO) + virtual void setPoster(const String& url) = 0; + virtual void deliverNotification(MediaPlayerProxyNotificationType) = 0; + virtual void setMediaPlayerProxy(WebMediaPlayerProxy*) = 0; +#endif +}; + +} + +#endif +#endif diff --git a/WebCore/platform/graphics/Pattern.h b/WebCore/platform/graphics/Pattern.h index 716a645..6981748 100644 --- a/WebCore/platform/graphics/Pattern.h +++ b/WebCore/platform/graphics/Pattern.h @@ -30,6 +30,7 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/RefPtr.h> +#include "TransformationMatrix.h" #if PLATFORM(CG) typedef struct CGPattern* CGPatternRef; @@ -67,7 +68,9 @@ namespace WebCore { Image* tileImage() const { return m_tileImage.get(); } - PlatformPatternPtr createPlatformPattern(const TransformationMatrix& patternTransform) const; + // Pattern space is an abstract space that maps to the default user space by the transformation 'userSpaceTransformation' + PlatformPatternPtr createPlatformPattern(const TransformationMatrix& userSpaceTransformation) const; + void setPatternSpaceTransform(const TransformationMatrix& patternSpaceTransformation) { m_patternSpaceTransformation = patternSpaceTransformation; } private: Pattern(Image*, bool repeatX, bool repeatY); @@ -75,6 +78,7 @@ namespace WebCore { RefPtr<Image> m_tileImage; bool m_repeatX; bool m_repeatY; + TransformationMatrix m_patternSpaceTransformation; }; } //namespace diff --git a/WebCore/platform/graphics/SimpleFontData.cpp b/WebCore/platform/graphics/SimpleFontData.cpp index 9670b55..9f51037 100644 --- a/WebCore/platform/graphics/SimpleFontData.cpp +++ b/WebCore/platform/graphics/SimpleFontData.cpp @@ -136,20 +136,6 @@ SimpleFontData::~SimpleFontData() } } -#if !PLATFORM(QT) -float SimpleFontData::widthForGlyph(Glyph glyph) const -{ - float width = m_glyphToWidthMap.widthForGlyph(glyph); - if (width != cGlyphWidthUnknown) - return width; - - width = platformWidthForGlyph(glyph); - m_glyphToWidthMap.setWidthForGlyph(glyph, width); - - return width; -} -#endif - const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const { return this; diff --git a/WebCore/platform/graphics/SimpleFontData.h b/WebCore/platform/graphics/SimpleFontData.h index e572e30..d2dd0b9 100644 --- a/WebCore/platform/graphics/SimpleFontData.h +++ b/WebCore/platform/graphics/SimpleFontData.h @@ -131,7 +131,7 @@ public: #endif #if PLATFORM(WX) - wxFont getWxFont() const { return m_font.font(); } + wxFont* getWxFont() const { return m_font.font(); } #endif private: @@ -205,6 +205,21 @@ public: mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; #endif }; + + +#if !PLATFORM(QT) +ALWAYS_INLINE float SimpleFontData::widthForGlyph(Glyph glyph) const +{ + float width = m_glyphToWidthMap.widthForGlyph(glyph); + if (width != cGlyphWidthUnknown) + return width; + + width = platformWidthForGlyph(glyph); + m_glyphToWidthMap.setWidthForGlyph(glyph, width); + + return width; +} +#endif } // namespace WebCore diff --git a/WebCore/platform/graphics/android/FontPlatformData.h b/WebCore/platform/graphics/android/FontPlatformData.h index 2bb8834..56c7827 100644 --- a/WebCore/platform/graphics/android/FontPlatformData.h +++ b/WebCore/platform/graphics/android/FontPlatformData.h @@ -42,6 +42,8 @@ public: FontPlatformData(const FontPlatformData&); FontPlatformData(SkTypeface*, float textSize, bool fakeBold, bool fakeItalic); FontPlatformData(const FontPlatformData& src, float textSize); + FontPlatformData(float size, bool syntheticBold, bool syntheticOblique); + ~FontPlatformData(); FontPlatformData(WTF::HashTableDeletedValueType) @@ -54,6 +56,7 @@ public: bool operator==(const FontPlatformData& a) const; void setupPaint(SkPaint*) const; + float size() const { return mTextSize; } unsigned hash() const; private: diff --git a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp index e82c1f6..4496408 100644 --- a/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp +++ b/WebCore/platform/graphics/android/FontPlatformDataAndroid.cpp @@ -103,6 +103,13 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) trace(4); } +FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) + : mTypeface(NULL), mTextSize(size), mFakeBold(bold), mFakeItalic(oblique) +{ + inc_count(); + trace(5); +} + FontPlatformData::~FontPlatformData() { dec_count(); diff --git a/WebCore/platform/graphics/android/GradientAndroid.cpp b/WebCore/platform/graphics/android/GradientAndroid.cpp index 71b7f73..8334d1c 100644 --- a/WebCore/platform/graphics/android/GradientAndroid.cpp +++ b/WebCore/platform/graphics/android/GradientAndroid.cpp @@ -42,6 +42,7 @@ public: SkShader* m_shader; SkShader::TileMode m_tileMode; + int m_colorCountWhenShaderWasBuilt; }; namespace WebCore { @@ -57,45 +58,54 @@ static U8CPU F2B(float x) return (int)(x * 255); } -SkShader* Gradient::getShader(SkShader::TileMode mode) { - if (NULL == m_gradient) { +SkShader* Gradient::getShader(SkShader::TileMode mode) +{ + if (NULL == m_gradient) m_gradient = new PlatformGradientRec; - } else if (m_gradient->m_tileMode == mode) { + else if (mode == m_gradient->m_tileMode) return m_gradient->m_shader; + + // need to ensure that the m_stops array is sorted. We call getColor() + // which, as a side effect, does the sort. + // TODO: refactor Gradient.h to formally expose a sort method + { + float r, g, b, a; + this->getColor(0, &r, &g, &b, &a); } - SkPoint pts[2]; + SkPoint pts[2] = { m_p0, m_p1 }; // convert to SkPoint - android_setpt(&pts[0], m_p0); - android_setpt(&pts[1], m_p1); - size_t count = m_stops.size(); + const size_t count = m_stops.size(); SkAutoMalloc storage(count * (sizeof(SkColor) + sizeof(SkScalar))); SkColor* colors = (SkColor*)storage.get(); SkScalar* pos = (SkScalar*)(colors + count); Vector<ColorStop>::iterator iter = m_stops.begin(); - int i = -1; - while (i++, iter != m_stops.end()) { + for (int i = 0; iter != m_stops.end(); i++) { pos[i] = SkFloatToScalar(iter->stop); colors[i] = SkColorSetARGB(F2B(iter->alpha), F2B(iter->red), - F2B(iter->green), F2B(iter->blue)); + F2B(iter->green), F2B(iter->blue)); ++iter; } SkShader* s; - if (0 == count) { - // it seems the spec says a zero-size gradient draws transparent - s = new SkColorShader(0); - } else if (m_radial) { - s = SkGradientShader::CreateRadial(pts[0], SkFloatToScalar(m_r0), - colors, pos, count, mode); - } else { + if (m_radial) + // FIXME: SVG always passes 0 for m_r0 + s = SkGradientShader::CreateRadial(pts[0], + SkFloatToScalar(m_r0 ? m_r0 : m_r1), colors, pos, count, mode); + else s = SkGradientShader::CreateLinear(pts, colors, pos, count, mode); - } + if (NULL == s) + s = new SkColorShader(0); + + // zap our previous shader, if present m_gradient->m_shader->safeUnref(); m_gradient->m_shader = s; m_gradient->m_tileMode = mode; + SkMatrix matrix = m_gradientSpaceTransformation; + s->setLocalMatrix(matrix); + return s; } @@ -109,7 +119,7 @@ void Gradient::fill(GraphicsContext* context, const FloatRect& rect) paint.setAntiAlias(true); paint.setShader(this->getShader(mode)); - android_gc2canvas(context)->drawRect(*android_setrect(&r, rect), paint); + android_gc2canvas(context)->drawRect(rect, paint); } diff --git a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp index 90ce354..3b4d9a9 100644 --- a/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp +++ b/WebCore/platform/graphics/android/GraphicsContextAndroid.cpp @@ -46,6 +46,7 @@ #include "SkGradientShader.h" #include "SkBitmapRef.h" #include "SkString.h" +#include "SkiaUtils.h" using namespace std; @@ -84,6 +85,7 @@ public: struct State { SkPath* mPath; + SkPathEffect* mPathEffect; float mMiterLimit; float mAlpha; float mStrokeThickness; @@ -98,6 +100,7 @@ public: State() { mPath = NULL; // lazily allocated + mPathEffect = 0; mMiterLimit = 4; mAlpha = 1; mStrokeThickness = 0.0f; // Same as default in GraphicsContextPrivate.h @@ -114,10 +117,12 @@ public: State(const State& other) { memcpy(this, &other, sizeof(State)); mPath = deepCopyPtr<SkPath>(other.mPath); + mPathEffect->safeRef(); } ~State() { delete mPath; + mPathEffect->safeUnref(); } void setShadow(int radius, int dx, int dy, SkColor c) { @@ -269,6 +274,11 @@ public: rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); } + SkPathEffect* pe = mState->mPathEffect; + if (pe) { + paint->setPathEffect(pe); + return false; + } switch (mCG->strokeStyle()) { case NoStroke: case SolidStroke: @@ -284,7 +294,7 @@ public: if (width > 0) { SkScalar intervals[] = { width, width }; - SkPathEffect* pe = new SkDashPathEffect(intervals, 2, 0); + pe = new SkDashPathEffect(intervals, 2, 0); paint->setPathEffect(pe)->unref(); // return true if we're basically a dotted dash of squares return RoundToInt(width) == RoundToInt(paint->getStrokeWidth()); @@ -315,7 +325,8 @@ static SkShader::TileMode SpreadMethod2TileMode(GradientSpreadMethod sm) { } static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, - Gradient* grad, GradientSpreadMethod sm) { + Gradient* grad) +{ switch (cs) { case PatternColorSpace: // createPlatformPattern() returns a new inst @@ -324,6 +335,7 @@ static void extactShader(SkPaint* paint, ColorSpace cs, Pattern* pat, break; case GradientColorSpace: { // grad->getShader() returns a cached obj + GradientSpreadMethod sm = grad->spreadMethod(); paint->setShader(grad->getShader(SpreadMethod2TileMode(sm))); break; } @@ -400,9 +412,7 @@ void GraphicsContext::drawRect(const IntRect& rect) return; SkPaint paint; - SkRect r; - - android_setrect(&r, rect); + SkRect r(rect); if (fillColor().alpha()) { m_data->setup_paint_fill(&paint); @@ -488,9 +498,7 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) canvas->drawPoints(SkCanvas::kPoints_PointMode, count, verts, paint); canvas->restore(); } else { - SkPoint pts[2]; - android_setpt(&pts[0], point1); - android_setpt(&pts[1], point2); + SkPoint pts[2] = { point1, point2 }; canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint); } } @@ -546,10 +554,8 @@ void GraphicsContext::drawEllipse(const IntRect& rect) return; SkPaint paint; - SkRect oval; + SkRect oval(rect); - android_setrect(&oval, rect); - if (fillColor().rgb() & 0xFF000000) { m_data->setup_paint_fill(&paint); GC2Canvas(this)->drawOval(oval, paint); @@ -579,10 +585,8 @@ void GraphicsContext::strokeArc(const IntRect& r, int startAngle, int angleSpan) SkPath path; SkPaint paint; - SkRect oval; + SkRect oval(r); - android_setrect(&oval, r); - if (strokeStyle() == NoStroke) { m_data->setup_paint_fill(&paint); // we want the fill color paint.setStyle(SkPaint::kStroke_Style); @@ -644,7 +648,6 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef SkPaint paint; SkPath path; SkScalar radii[8]; - SkRect r; radii[0] = SkIntToScalar(topLeft.width()); radii[1] = SkIntToScalar(topLeft.height()); @@ -654,7 +657,7 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef radii[5] = SkIntToScalar(bottomRight.height()); radii[6] = SkIntToScalar(bottomLeft.width()); radii[7] = SkIntToScalar(bottomLeft.height()); - path.addRoundRect(*android_setrect(&r, rect), radii); + path.addRoundRect(rect, radii); m_data->setup_paint_fill(&paint); GC2Canvas(this)->drawPath(path, paint); @@ -663,16 +666,14 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef void GraphicsContext::fillRect(const FloatRect& rect) { SkPaint paint; - SkRect r; - android_setrect(&r, rect); m_data->setup_paint_fill(&paint); extactShader(&paint, m_common->state.fillColorSpace, m_common->state.fillPattern.get(), - m_common->state.fillGradient.get(), spreadMethod()); + m_common->state.fillGradient.get()); - GC2Canvas(this)->drawRect(r, paint); + GC2Canvas(this)->drawRect(rect, paint); } void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) @@ -682,13 +683,11 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color) if (color.rgb() & 0xFF000000) { SkPaint paint; - SkRect r; - android_setrect(&r, rect); m_data->setup_paint_common(&paint); paint.setColor(color.rgb()); // punch in the specified color paint.setShader(NULL); // in case we had one set - GC2Canvas(this)->drawRect(r, paint); + GC2Canvas(this)->drawRect(rect, paint); } } @@ -697,9 +696,7 @@ void GraphicsContext::clip(const FloatRect& rect) if (paintingDisabled()) return; - SkRect r; - - GC2Canvas(this)->clipRect(*android_setrect(&r, rect)); + GC2Canvas(this)->clipRect(rect); } void GraphicsContext::clip(const Path& path) @@ -720,8 +717,8 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness //printf("-------- addInnerRoundedRectClip: [%d %d %d %d] thickness=%d\n", rect.x(), rect.y(), rect.width(), rect.height(), thickness); SkPath path; - SkRect r; - android_setrect(&r, rect); + SkRect r(rect); + path.addOval(r, SkPath::kCW_Direction); // only perform the inset if we won't invert r if (2*thickness < rect.width() && 2*thickness < rect.height()) @@ -737,9 +734,7 @@ void GraphicsContext::clipOut(const IntRect& r) if (paintingDisabled()) return; - SkRect rect; - - GC2Canvas(this)->clipRect(*android_setrect(&rect, r), SkRegion::kDifference_Op); + GC2Canvas(this)->clipRect(r, SkRegion::kDifference_Op); } void GraphicsContext::clipOutEllipseInRect(const IntRect& r) @@ -747,13 +742,24 @@ void GraphicsContext::clipOutEllipseInRect(const IntRect& r) if (paintingDisabled()) return; - SkRect oval; - SkPath path; + SkPath path; - path.addOval(*android_setrect(&oval, r), SkPath::kCCW_Direction); + path.addOval(r, SkPath::kCCW_Direction); GC2Canvas(this)->clipPath(path, SkRegion::kDifference_Op); } +#if ENABLE(SVG) +void GraphicsContext::clipPath(WindRule clipRule) +{ + if (paintingDisabled()) + return; + const SkPath* oldPath = m_data->getPath(); + SkPath path(*oldPath); + path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); + GC2Canvas(this)->clipPath(path); +} +#endif + void GraphicsContext::clipOut(const Path& p) { if (paintingDisabled()) @@ -893,7 +899,7 @@ void GraphicsContext::setAlpha(float alpha) void GraphicsContext::setCompositeOperation(CompositeOperator op) { - m_data->mState->mPorterDuffMode = android_convert_compositeOp(op); + m_data->mState->mPorterDuffMode = WebCoreCompositeToSkiaComposite(op); } void GraphicsContext::clearRect(const FloatRect& rect) @@ -902,12 +908,10 @@ void GraphicsContext::clearRect(const FloatRect& rect) return; SkPaint paint; - SkRect r; - android_setrect(&r, rect); m_data->setup_paint_fill(&paint); paint.setPorterDuffXfermode(SkPorterDuff::kClear_Mode); - GC2Canvas(this)->drawRect(r, paint); + GC2Canvas(this)->drawRect(rect, paint); } void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) @@ -916,13 +920,10 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float lineWidth) return; SkPaint paint; - SkRect r; - - android_setrect(&r, rect); m_data->setup_paint_stroke(&paint, NULL); paint.setStrokeWidth(SkFloatToScalar(lineWidth)); - GC2Canvas(this)->drawRect(r, paint); + GC2Canvas(this)->drawRect(rect, paint); } void GraphicsContext::setLineCap(LineCap cap) @@ -943,6 +944,29 @@ void GraphicsContext::setLineCap(LineCap cap) } } +#if ENABLE(SVG) +void GraphicsContext::setLineDash(const DashArray& dashes, float dashOffset) +{ + if (paintingDisabled()) + return; + + size_t dashLength = dashes.size(); + if (!dashLength) + return; + + size_t count = (dashLength % 2) == 0 ? dashLength : dashLength * 2; + SkScalar* intervals = new SkScalar[count]; + + for (unsigned int i = 0; i < count; i++) + intervals[i] = SkFloatToScalar(dashes[i % dashLength]); + SkPathEffect **effectPtr = &m_data->mState->mPathEffect; + (*effectPtr)->safeUnref(); + *effectPtr = new SkDashPathEffect(intervals, count, SkFloatToScalar(dashOffset)); + + delete[] intervals; +} +#endif + void GraphicsContext::setLineJoin(LineJoin join) { switch (join) { @@ -997,8 +1021,7 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) return FloatRect(); const SkMatrix& matrix = GC2Canvas(this)->getTotalMatrix(); - SkRect r; - android_setrect(&r, rect); + SkRect r(rect); matrix.mapRect(&r); FloatRect result(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); return result; @@ -1041,7 +1064,13 @@ void GraphicsContext::setPlatformShouldAntialias(bool useAA) TransformationMatrix GraphicsContext::getCTM() const { - return TransformationMatrix(GC2Canvas(this)->getTotalMatrix()); + const SkMatrix& m = GC2Canvas(this)->getTotalMatrix(); + return TransformationMatrix(SkScalarToDouble(m.getScaleX()), // a + SkScalarToDouble(m.getSkewY()), // b + SkScalarToDouble(m.getSkewX()), // c + SkScalarToDouble(m.getScaleY()), // d + SkScalarToDouble(m.getTranslateX()), // e + SkScalarToDouble(m.getTranslateY())); // f } /////////////////////////////////////////////////////////////////////////////// @@ -1056,12 +1085,6 @@ void GraphicsContext::addPath(const Path& p) m_data->addPath(*p.platformPath()); } -void GraphicsContext::drawPath() -{ - this->fillPath(); - this->strokePath(); -} - void GraphicsContext::fillPath() { SkPath* path = m_data->getPath(); @@ -1082,7 +1105,7 @@ void GraphicsContext::fillPath() extactShader(&paint, m_common->state.fillColorSpace, m_common->state.fillPattern.get(), - m_common->state.fillGradient.get(), spreadMethod()); + m_common->state.fillGradient.get()); GC2Canvas(this)->drawPath(*path, paint); } @@ -1090,7 +1113,7 @@ void GraphicsContext::fillPath() void GraphicsContext::strokePath() { const SkPath* path = m_data->getPath(); - if (paintingDisabled() || !path || strokeStyle() == NoStroke) + if (paintingDisabled() || !path) return; SkPaint paint; @@ -1098,7 +1121,7 @@ void GraphicsContext::strokePath() extactShader(&paint, m_common->state.strokeColorSpace, m_common->state.strokePattern.get(), - m_common->state.strokeGradient.get(), spreadMethod()); + m_common->state.strokeGradient.get()); GC2Canvas(this)->drawPath(*path, paint); } diff --git a/WebCore/platform/graphics/android/ImageAndroid.cpp b/WebCore/platform/graphics/android/ImageAndroid.cpp index 769ac43..5d81b56 100644 --- a/WebCore/platform/graphics/android/ImageAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageAndroid.cpp @@ -42,6 +42,7 @@ #include "SkShader.h" #include "SkString.h" #include "SkTemplates.h" +#include "SkiaUtils.h" #include <utils/AssetManager.h> @@ -78,6 +79,7 @@ BitmapImage::BitmapImage(SkBitmapRef* ref, ImageObserver* observer) , m_frames(0) , m_frameTimer(0) , m_repetitionCount(0) + , m_repetitionCountStatus(Unknown) , m_repetitionsComplete(0) , m_isSolidColor(false) , m_animationFinished(true) @@ -111,6 +113,7 @@ void BitmapImage::invalidatePlatformData() void BitmapImage::checkForSolidColor() { + m_checkedForSolidColor = true; m_isSolidColor = false; if (frameCount() == 1) { SkBitmapRef* ref = frameAtIndex(0); @@ -148,12 +151,29 @@ void BitmapImage::checkForSolidColor() return; // keep solid == false } m_isSolidColor = true; - m_solidColor = android_SkPMColorToWebCoreColor(color); + m_solidColor = SkPMColorToWebCoreColor(color); } } +static void round(SkIRect* dst, const WebCore::FloatRect& src) +{ + dst->set(SkScalarRound(SkFloatToScalar(src.x())), + SkScalarRound(SkFloatToScalar(src.y())), + SkScalarRound(SkFloatToScalar((src.x() + src.width()))), + SkScalarRound(SkFloatToScalar((src.y() + src.height())))); +} + +static void round_scaled(SkIRect* dst, const WebCore::FloatRect& src, + float sx, float sy) +{ + dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)), + SkScalarRound(SkFloatToScalar(src.y() * sy)), + SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)), + SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy))); +} + void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, - const FloatRect& srcRect, CompositeOperator compositeOp) + const FloatRect& srcRect, CompositeOperator compositeOp) { startAnimation(); @@ -174,12 +194,11 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, } SkIRect srcR; - SkRect dstR; + SkRect dstR(dstRect); float invScaleX = (float)bitmap.width() / image->origWidth(); float invScaleY = (float)bitmap.height() / image->origHeight(); - android_setrect(&dstR, dstRect); - android_setrect_scaled(&srcR, srcRect, invScaleX, invScaleY); + round_scaled(&srcR, srcRect, invScaleX, invScaleY); if (srcR.isEmpty() || dstR.isEmpty()) { #ifdef TRACE_SKIPPED_BITMAPS SkDebugf("----- skip bitmapimage: [%d %d] src-empty %d dst-empty %d\n", @@ -194,7 +213,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, ctxt->setupFillPaint(&paint); // need global alpha among other things paint.setFilterBitmap(true); - paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); + paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp)); canvas->drawBitmapRect(bitmap, &srcR, dstR, &paint); #ifdef TRACE_SUBSAMPLED_BITMAPS @@ -214,7 +233,7 @@ void BitmapImage::setURL(const String& str) /////////////////////////////////////////////////////////////////////////////// -void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, +void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator compositeOp, const FloatRect& destRect) @@ -223,19 +242,40 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, if (!image) { // If it's too early we won't have an image yet. return; } - + // in case we get called with an incomplete bitmap - const SkBitmap& bitmap = image->bitmap(); - if (bitmap.getPixels() == NULL && bitmap.pixelRef() == NULL) { + const SkBitmap& origBitmap = image->bitmap(); + if (origBitmap.getPixels() == NULL && origBitmap.pixelRef() == NULL) { return; } - - SkRect dstR; - android_setrect(&dstR, destRect); + + SkRect dstR(destRect); if (dstR.isEmpty()) { return; } - + + SkIRect srcR; + // we may have to scale if the image has been subsampled (so save RAM) + bool imageIsSubSampled = image->origWidth() != origBitmap.width() || + image->origHeight() != origBitmap.height(); + float scaleX = 1; + float scaleY = 1; + if (imageIsSubSampled) { + scaleX = (float)image->origWidth() / origBitmap.width(); + scaleY = (float)image->origHeight() / origBitmap.height(); +// SkDebugf("----- subsampled %g %g\n", scaleX, scaleY); + round_scaled(&srcR, srcRect, 1 / scaleX, 1 / scaleY); + } else { + round(&srcR, srcRect); + } + + // now extract the proper subset of the src image + SkBitmap bitmap; + if (!origBitmap.extractSubset(&bitmap, srcR)) { + SkDebugf("--- Image::drawPattern calling extractSubset failed\n"); + return; + } + SkCanvas* canvas = ctxt->platformContext()->mCanvas; SkPaint paint; ctxt->setupFillPaint(&paint); // need global alpha among other things @@ -245,20 +285,32 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, SkShader::kRepeat_TileMode); paint.setShader(shader)->unref(); // now paint is the only owner of shader - paint.setPorterDuffXfermode(android_convert_compositeOp(compositeOp)); + paint.setPorterDuffXfermode(WebCoreCompositeToSkiaComposite(compositeOp)); paint.setFilterBitmap(true); - + SkMatrix matrix(patternTransform); - - float scaleX = (float)image->origWidth() / bitmap.width(); - float scaleY = (float)image->origHeight() / bitmap.height(); - matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY)); - - matrix.postTranslate(SkFloatToScalar(phase.x()), - SkFloatToScalar(phase.y())); + + if (imageIsSubSampled) { + matrix.preScale(SkFloatToScalar(scaleX), SkFloatToScalar(scaleY)); + } + // We also need to translate it such that the origin of the pattern is the + // origin of the destination rect, which is what WebKit expects. Skia uses + // the coordinate system origin as the base for the patter. If WebKit wants + // a shifted image, it will shift it from there using the patternTransform. + float tx = phase.x() + srcRect.x() * patternTransform.a(); + float ty = phase.y() + srcRect.y() * patternTransform.d(); + matrix.postTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty)); shader->setLocalMatrix(matrix); +#if 0 + SkDebugf("--- drawPattern: src [%g %g %g %g] dst [%g %g %g %g] transform [%g %g %g %g %g %g] matrix [%g %g %g %g %g %g]\n", + srcRect.x(), srcRect.y(), srcRect.width(), srcRect.height(), + destRect.x(), destRect.y(), destRect.width(), destRect.height(), + patternTransform.a(), patternTransform.b(), patternTransform.c(), + patternTransform.d(), patternTransform.e(), patternTransform.f(), + matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]); +#endif canvas->drawRect(dstR, paint); - + #ifdef TRACE_SUBSAMPLED_BITMAPS if (bitmap.width() != image->origWidth() || bitmap.height() != image->origHeight()) { diff --git a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp index 4c6a246..70730d8 100644 --- a/WebCore/platform/graphics/android/ImageSourceAndroid.cpp +++ b/WebCore/platform/graphics/android/ImageSourceAndroid.cpp @@ -329,7 +329,11 @@ SkBitmapRef* ImageSource::createFrameAtIndex(size_t index) m_decoder.m_gifDecoder->frameBufferAtIndex(index); if (!buffer || buffer->status() == RGBA32Buffer::FrameEmpty) return 0; - return new SkBitmapRef(buffer->bitmap()); + SkBitmap& bitmap = buffer->bitmap(); + SkPixelRef* pixelRef = bitmap.pixelRef(); + if (pixelRef) + pixelRef->setURI(m_decoder.m_url); + return new SkBitmapRef(bitmap); } #else SkASSERT(index == 0); diff --git a/WebCore/platform/graphics/android/PathAndroid.cpp b/WebCore/platform/graphics/android/PathAndroid.cpp index cfcd2bc..e7c61d1 100644 --- a/WebCore/platform/graphics/android/PathAndroid.cpp +++ b/WebCore/platform/graphics/android/PathAndroid.cpp @@ -31,6 +31,7 @@ #include "StrokeStyleApplier.h" #include "TransformationMatrix.h" +#include "SkPaint.h" #include "SkPath.h" #include "SkRegion.h" @@ -89,9 +90,7 @@ void Path::translate(const FloatSize& size) FloatRect Path::boundingRect() const { - SkRect r; - - m_path->computeBounds(&r, SkPath::kExact_BoundsType); + const SkRect& r = m_path->getBounds(); return FloatRect( SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), SkScalarToFloat(r.width()), @@ -196,18 +195,12 @@ void Path::addArc(const FloatPoint& p, float r, float sa, float ea, void Path::addRect(const FloatRect& rect) { - SkRect r; - - android_setrect(&r, rect); - m_path->addRect(r); + m_path->addRect(rect); } void Path::addEllipse(const FloatRect& rect) { - SkRect r; - - android_setrect(&r, rect); - m_path->addOval(r); + m_path->addOval(rect); } void Path::clear() @@ -254,7 +247,7 @@ void Path::apply(void* info, PathApplierFunction function) const break; case SkPath::kClose_Verb: elem.type = PathElementCloseSubpath; - elem.points = NULL; + elem.points = setfpts(fpts, 0, 0); break; case SkPath::kDone_Verb: return; @@ -268,6 +261,64 @@ void Path::transform(const TransformationMatrix& xform) m_path->transform(xform); } +#if ENABLE(SVG) +String Path::debugString() const +{ + String result; + + SkPath::Iter iter(*m_path, false); + SkPoint pts[4]; + + int numPoints = m_path->getPoints(0, 0); + SkPath::Verb verb; + + do { + verb = iter.next(pts); + switch (verb) { + case SkPath::kMove_Verb: + result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY); + numPoints -= 1; + break; + case SkPath::kLine_Verb: + if (!iter.isCloseLine()) { + result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY); + numPoints -= 1; + } + break; + case SkPath::kQuad_Verb: + result += String::format("Q%.2f,%.2f,%.2f,%.2f ", + pts[1].fX, pts[1].fY, + pts[2].fX, pts[2].fY); + numPoints -= 2; + break; + case SkPath::kCubic_Verb: + result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ", + pts[1].fX, pts[1].fY, + pts[2].fX, pts[2].fY, + pts[3].fX, pts[3].fY); + numPoints -= 3; + break; + case SkPath::kClose_Verb: + result += "Z "; + break; + case SkPath::kDone_Verb: + break; + } + } while (verb != SkPath::kDone_Verb); + + // If you have a path that ends with an M, Skia will not iterate the + // trailing M. That's nice of it, but Apple's paths output the trailing M + // and we want out layout dumps to look like theirs + if (numPoints) { + ASSERT(numPoints==1); + m_path->getLastPt(pts); + result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY); + } + + return result.stripWhiteSpace(); +} +#endif + /////////////////////////////////////////////////////////////////////////////// // Computes the bounding box for the stroke and style currently selected into @@ -283,9 +334,9 @@ static FloatRect boundingBoxForCurrentStroke(GraphicsContext* context) context->setupStrokePaint(&paint); SkPath fillPath; paint.getFillPath(*path, &fillPath); - SkRect r; - fillPath.computeBounds(&r, SkPath::kExact_BoundsType); - return FloatRect(r.fLeft, r.fTop, r.width(), r.height()); + const SkRect& r = fillPath.getBounds(); + return FloatRect(SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop), + SkScalarToFloat(r.width()), SkScalarToFloat(r.height())); } static GraphicsContext* scratchContext() @@ -313,4 +364,30 @@ FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) return r; } +#if ENABLE(SVG) +bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const +{ +#if 0 + ASSERT(applier); + GraphicsContext* scratch = scratchContext(); + scratch->save(); + + applier->strokeStyle(scratch); + + SkPaint paint; + scratch->platformContext()->setupPaintForStroking(&paint, 0, 0); + SkPath strokePath; + paint.getFillPath(*platformPath(), &strokePath); + bool contains = SkPathContainsPoint(&strokePath, point, + SkPath::kWinding_FillType); + + scratch->restore(); + return contains; +#else + // FIXME: + return false; +#endif +} +#endif + } diff --git a/WebCore/platform/graphics/android/PatternAndroid.cpp b/WebCore/platform/graphics/android/PatternAndroid.cpp index ffdbbb1..ff2b522 100644 --- a/WebCore/platform/graphics/android/PatternAndroid.cpp +++ b/WebCore/platform/graphics/android/PatternAndroid.cpp @@ -40,14 +40,13 @@ static SkShader::TileMode toTileMode(bool doRepeat) { return doRepeat ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; } -SkShader* Pattern::createPlatformPattern(const TransformationMatrix& transform) const +SkShader* Pattern::createPlatformPattern(const TransformationMatrix& ) const { SkBitmapRef* ref = tileImage()->nativeImageForCurrentFrame(); SkShader* s = SkShader::CreateBitmapShader(ref->bitmap(), toTileMode(m_repeatX), toTileMode(m_repeatY)); - - // TODO: do I treat transform as a local matrix??? + s->setLocalMatrix(m_patternSpaceTransformation); return s; } diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp index b13f45f..e0aecfa 100644 --- a/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.cpp @@ -35,12 +35,9 @@ PlatformGraphicsContext::PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<C { } -PlatformGraphicsContext::PlatformGraphicsContext() : m_deleteCanvas(true) +PlatformGraphicsContext::PlatformGraphicsContext() + : mCanvas(new SkCanvas), m_deleteCanvas(true), m_buttons(0) { - mCanvas = new SkCanvas; - // Since this is our own private SkCanvas, and has no relation to a picture - // storing button references would be meaningless. - m_buttons = NULL; } PlatformGraphicsContext::~PlatformGraphicsContext() diff --git a/WebCore/platform/graphics/android/PlatformGraphicsContext.h b/WebCore/platform/graphics/android/PlatformGraphicsContext.h index dce8ef3..4eeb4c1 100644 --- a/WebCore/platform/graphics/android/PlatformGraphicsContext.h +++ b/WebCore/platform/graphics/android/PlatformGraphicsContext.h @@ -34,7 +34,6 @@ #include "SkTDArray.h" class SkCanvas; -class WebCore::Node; class Container { public: @@ -149,9 +148,6 @@ public: PlatformGraphicsContext(SkCanvas* canvas, WTF::Vector<Container>* buttons); ~PlatformGraphicsContext(); - void setupFillPaint(GraphicsContext*, SkPaint*); - void setupStrokePaint(GraphicsContext*, SkPaint*); - SkCanvas* mCanvas; bool deleteUs() const { return m_deleteCanvas; } diff --git a/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp b/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp deleted file mode 100644 index 154d4f3..0000000 --- a/WebCore/platform/graphics/android/TransformationMatrixAndroid.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2007, The Android Open Source Project - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "TransformationMatrix.h" - -#include "FloatRect.h" -#include "IntRect.h" - -#include "android_graphics.h" - -namespace WebCore { - -static const double deg2rad = 0.017453292519943295769; // pi/180 - -TransformationMatrix::TransformationMatrix() -{ - m_transform.reset(); -} - -TransformationMatrix::TransformationMatrix(const SkMatrix& mat) : m_transform(mat) {} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform.reset(); - - m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); - m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); - m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); - - m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); - m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(c)); - m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform.set(SkMatrix::kMScaleX, SkDoubleToScalar(a)); - m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(b)); - m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(tx)); - - m_transform.set(SkMatrix::kMScaleY, SkDoubleToScalar(d)); - m_transform.set(SkMatrix::kMSkewX, SkDoubleToScalar(c)); - m_transform.set(SkMatrix::kMTransX, SkDoubleToScalar(ty)); -} - -void TransformationMatrix::map(double x, double y, double *x2, double *y2) const -{ - SkPoint pt; - - m_transform.mapXY(SkDoubleToScalar(x), SkDoubleToScalar(y), &pt); - *x2 = SkScalarToDouble(pt.fX); - *y2 = SkScalarToDouble(pt.fY); -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - SkRect src, dst; - SkIRect ir; - - android_setrect(&src, rect); - m_transform.mapRect(&dst, src); - // we round out to mimic enclosingIntRect() - dst.roundOut(&ir); - - return IntRect(ir.fLeft, ir.fTop, ir.width(), ir.height()); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ - SkRect r; - - android_setrect(&r, rect); - m_transform.mapRect(&r); - - return FloatRect(r.fLeft, r.fTop, r.width(), r.height()); -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -double TransformationMatrix::a() const -{ - return SkScalarToDouble(m_transform[0]); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.set(0, SkDoubleToScalar(a)); -} - -double TransformationMatrix::b() const -{ - return SkScalarToDouble(m_transform[1]); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.set(1, SkDoubleToScalar(b)); -} - -double TransformationMatrix::c() const -{ - return SkScalarToDouble(m_transform[3]); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.set(3, SkDoubleToScalar(c)); -} - -double TransformationMatrix::d() const { - return SkScalarToDouble(m_transform[4]); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.set(4, SkDoubleToScalar(d)); -} - -double TransformationMatrix::e() const -{ - return SkScalarToDouble(m_transform[2]); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.set(2, SkDoubleToScalar(e)); -} - -double TransformationMatrix::f() const { - return SkScalarToDouble(m_transform[5]); -} -void TransformationMatrix::setF(double f) { - m_transform.set(5, SkDoubleToScalar(f)); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform.preScale(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform.preRotate(SkDoubleToScalar(d)); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform.preTranslate(SkDoubleToScalar(tx), SkDoubleToScalar(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - m_transform.preSkew(SkDoubleToScalar(sx), SkDoubleToScalar(sy)); - return *this; -} - -double TransformationMatrix::det() const -{ - return SkScalarToDouble(m_transform[SkMatrix::kMScaleX]) * SkScalarToDouble(m_transform[SkMatrix::kMScaleY]) - - SkScalarToDouble(m_transform[SkMatrix::kMSkewX]) * SkScalarToDouble(m_transform[SkMatrix::kMSkewY]); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - // the constructor initializes inverse to the identity - TransformationMatrix inverse; - - // if we are not invertible, inverse will stay identity - m_transform.invert(&inverse.m_transform); - - return inverse; -} - -TransformationMatrix::operator SkMatrix() const -{ - return m_transform; -} - -bool TransformationMatrix::operator==(const TransformationMatrix &m2) const -{ - return m_transform == m2.m_transform; -} - -TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) -{ - m_transform.setConcat(m2.m_transform, m_transform); - return *this; -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) -{ - TransformationMatrix cat; - - cat.m_transform.setConcat(m2.m_transform, m_transform); - return cat; -} - -} diff --git a/WebCore/platform/graphics/android/android_graphics.cpp b/WebCore/platform/graphics/android/android_graphics.cpp index 1decfd8..cdd8afe 100644 --- a/WebCore/platform/graphics/android/android_graphics.cpp +++ b/WebCore/platform/graphics/android/android_graphics.cpp @@ -25,175 +25,38 @@ #include "config.h" #include "android_graphics.h" -#include "IntPoint.h" #include "IntRect.h" -#include "FloatPoint.h" -#include "FloatRect.h" #include "SkCanvas.h" -#include "SkColorPriv.h" #include "SkCornerPathEffect.h" -#include "SkGradientShader.h" #include "SkPath.h" #include "SkRegion.h" -SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src) -{ - dst->set(SkIntToScalar(src.x()), SkIntToScalar(src.y())); - return dst; -} - -SkPoint* android_setpt(SkPoint* dst, const WebCore::FloatPoint& src) -{ - dst->set(SkFloatToScalar(src.x()), SkFloatToScalar(src.y())); - return dst; -} - -SkRect* android_setrect(SkRect* dst, const WebCore::IntRect& src) -{ - dst->set(SkIntToScalar(src.x()), - SkIntToScalar(src.y()), - SkIntToScalar(src.x() + src.width()), - SkIntToScalar(src.y() + src.height())); - return dst; -} - -SkIRect* android_setrect(SkIRect* dst, const WebCore::IntRect& src) -{ - dst->set(src.x(), src.y(), - src.x() + src.width(), - src.y() + src.height()); - return dst; -} - -SkRect* android_setrect(SkRect* dst, const WebCore::FloatRect& src) -{ - dst->set(SkFloatToScalar(src.x()), - SkFloatToScalar(src.y()), - SkFloatToScalar(src.x() + src.width()), - SkFloatToScalar(src.y() + src.height())); - return dst; -} - -SkIRect* android_setrect(SkIRect* dst, const WebCore::FloatRect& src) -{ - dst->set(SkScalarRound(SkFloatToScalar(src.x())), - SkScalarRound(SkFloatToScalar(src.y())), - SkScalarRound(SkFloatToScalar(src.x() + src.width())), - SkScalarRound(SkFloatToScalar(src.y() + src.height()))); - return dst; -} - -SkIRect* android_setrect_scaled(SkIRect* dst, const WebCore::FloatRect& src, - float sx, float sy) -{ - dst->set(SkScalarRound(SkFloatToScalar(src.x() * sx)), - SkScalarRound(SkFloatToScalar(src.y() * sy)), - SkScalarRound(SkFloatToScalar((src.x() + src.width()) * sx)), - SkScalarRound(SkFloatToScalar((src.y() + src.height()) * sy))); - return dst; -} - -static const struct CompositOpToPorterDuffMode { - uint8_t mCompositOp; - uint8_t mPorterDuffMode; -} gMapCompositOpsToPorterDuffModes[] = { - { WebCore::CompositeClear, SkPorterDuff::kClear_Mode }, - { WebCore::CompositeCopy, SkPorterDuff::kSrc_Mode }, - { WebCore::CompositeSourceOver, SkPorterDuff::kSrcOver_Mode }, - { WebCore::CompositeSourceIn, SkPorterDuff::kSrcIn_Mode }, - { WebCore::CompositeSourceOut, SkPorterDuff::kSrcOut_Mode }, - { WebCore::CompositeSourceAtop, SkPorterDuff::kSrcATop_Mode }, - { WebCore::CompositeDestinationOver, SkPorterDuff::kDstOver_Mode }, - { WebCore::CompositeDestinationIn, SkPorterDuff::kDstIn_Mode }, - { WebCore::CompositeDestinationOut, SkPorterDuff::kDstOut_Mode }, - { WebCore::CompositeDestinationAtop, SkPorterDuff::kDstATop_Mode }, - { WebCore::CompositeXOR, SkPorterDuff::kXor_Mode }, - { WebCore::CompositePlusDarker, SkPorterDuff::kDarken_Mode }, - { WebCore::CompositeHighlight, SkPorterDuff::kSrcOver_Mode }, // TODO - { WebCore::CompositePlusLighter, SkPorterDuff::kLighten_Mode } -}; - -SkPorterDuff::Mode android_convert_compositeOp(WebCore::CompositeOperator op) -{ - const CompositOpToPorterDuffMode* table = gMapCompositOpsToPorterDuffModes; - - for (unsigned i = 0; i < SK_ARRAY_COUNT(gMapCompositOpsToPorterDuffModes); i++) { - if (table[i].mCompositOp == op) { - return (SkPorterDuff::Mode)table[i].mPorterDuffMode; - } - } - - SkDEBUGF(("GraphicsContext::setCompositeOperation uknown CompositOperator %d\n", op)); - return SkPorterDuff::kSrcOver_Mode; // fall-back -} - -SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule rule) -{ - // stretch == clamp - // repeat == repeat - // RoundTile??? - - return WebCore::Image::RepeatTile == rule ? SkShader::kRepeat_TileMode : SkShader::kClamp_TileMode; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////////// - -static U8CPU InvScaleByte(U8CPU component, uint32_t scale) -{ - SkASSERT(component == (uint8_t)component); - return (component * scale + 0x8000) >> 16; -} - -// move this guy into SkColor.h -static SkColor SkPMColorToColor(SkPMColor pm) -{ - if (0 == pm) - return 0; - - unsigned a = SkGetPackedA32(pm); - uint32_t scale = (255 << 16) / a; - - return SkColorSetARGB(a, - InvScaleByte(SkGetPackedR32(pm), scale), - InvScaleByte(SkGetPackedG32(pm), scale), - InvScaleByte(SkGetPackedB32(pm), scale)); -} - -WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm) -{ - SkColor c = SkPMColorToColor(pm); - - // need the cast to find the right constructor - return WebCore::Color((int)SkColorGetR(c), (int)SkColorGetG(c), - (int)SkColorGetB(c), (int)SkColorGetA(c)); -} +/////////////////////////////////////////////////////////////////////////////// -const static SkColor focusOuterColors[] = { - SkColorSetARGB(0xff, 0xB3, 0x3F, 0x08), // normal focus ring select - SkColorSetARGB(0xff, 0x46, 0xb0, 0x00), // fake focus ring select, for phone, email, text - SkColorSetARGB(0xff, 0xb0, 0x16, 0x00), // invalid focus ring color - SkColorSetARGB(0xff, 0xAD, 0x5C, 0x0A), // normal focus ring pressed - SkColorSetARGB(0xff, 0x36, 0xc0, 0x00) // fake focus ring pressed +const static SkColor cursorOuterColors[] = { + SkColorSetARGB(0xff, 0xB3, 0x3F, 0x08), // normal ring select + SkColorSetARGB(0xff, 0x46, 0xb0, 0x00), // fake ring select, for phone, email, text + SkColorSetARGB(0xff, 0xAD, 0x5C, 0x0A), // normal ring pressed + SkColorSetARGB(0xff, 0x36, 0xc0, 0x00) // fake ring pressed }; -const static SkColor focusInnerColors[] = { - SkColorSetARGB(0xff, 0xFE, 0x92, 0x30), // normal focus ring select - SkColorSetARGB(0xff, 0x8c, 0xd9, 0x00), // fake focus ring select, for phone, email, text - SkColorSetARGB(0xff, 0xd9, 0x2c, 0x00), // invalid focus ring color - SkColorSetARGB(0xff, 0xFE, 0xBD, 0x3A), // normal focus ring pressed - SkColorSetARGB(0xff, 0x7c, 0xe9, 0x00) // fake focus ring pressed +const static SkColor cursorInnerColors[] = { + SkColorSetARGB(0xff, 0xFE, 0x92, 0x30), // normal ring select + SkColorSetARGB(0xff, 0x8c, 0xd9, 0x00), // fake ring select, for phone, email, text + SkColorSetARGB(0xff, 0xFE, 0xBD, 0x3A), // normal ring pressed + SkColorSetARGB(0xff, 0x7c, 0xe9, 0x00) // fake ring pressed }; -const static SkColor focusPressedColors[] = { - SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B), // normal focus ring pressed - SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00) // fake focus ring pressed +const static SkColor cursorPressedColors[] = { + SkColorSetARGB(0x80, 0xFF, 0xC6, 0x4B), // normal ring pressed + SkColorSetARGB(0x80, 0x7c, 0xe9, 0x00) // fake ring pressed }; -#define FOCUS_RING_ROUNDEDNESS SkIntToScalar(5) // used to draw corners -#define FOCUS_RING_INNER_DIAMETER SkFixedToScalar(SkIntToFixed(3)>>1) // 3/2 == 1.5 -#define FOCUS_RING_OUTER_OUTSET 2 // used to inflate rects added to region +#define CURSOR_RING_ROUNDEDNESS SkIntToScalar(5) // used to draw corners +#define CURSOR_RING_INNER_DIAMETER SkFixedToScalar(SkIntToFixed(3)>>1) // 3/2 == 1.5 +#define CURSOR_RING_OUTER_OUTSET 2 // used to inflate rects added to region -void FocusRing::DrawRing(SkCanvas* canvas, +void CursorRing::DrawRing(SkCanvas* canvas, const Vector<WebCore::IntRect>& rects, Flavor flavor) { unsigned rectCount = rects.size(); @@ -201,27 +64,28 @@ void FocusRing::DrawRing(SkCanvas* canvas, SkPath path; for (unsigned i = 0; i < rectCount; i++) { - SkIRect r; + SkRect r(rects[i]); + SkIRect ir; - android_setrect(&r, rects[i]); - r.inset(-FOCUS_RING_OUTER_OUTSET, -FOCUS_RING_OUTER_OUTSET); - rgn.op(r, SkRegion::kUnion_Op); + r.round(&ir); + ir.inset(-CURSOR_RING_OUTER_OUTSET, -CURSOR_RING_OUTER_OUTSET); + rgn.op(ir, SkRegion::kUnion_Op); } rgn.getBoundaryPath(&path); SkPaint paint; paint.setAntiAlias(true); - paint.setPathEffect(new SkCornerPathEffect(FOCUS_RING_ROUNDEDNESS))->unref(); + paint.setPathEffect(new SkCornerPathEffect(CURSOR_RING_ROUNDEDNESS))->unref(); if (flavor >= NORMAL_ANIMATING) { // pressed - paint.setColor(focusPressedColors[flavor - NORMAL_ANIMATING]); + paint.setColor(cursorPressedColors[flavor - NORMAL_ANIMATING]); canvas->drawPath(path, paint); } paint.setStyle(SkPaint::kStroke_Style); - paint.setStrokeWidth(FOCUS_RING_OUTER_DIAMETER); - paint.setColor(focusOuterColors[flavor]); + paint.setStrokeWidth(CURSOR_RING_OUTER_DIAMETER); + paint.setColor(cursorOuterColors[flavor]); canvas->drawPath(path, paint); - paint.setStrokeWidth(FOCUS_RING_INNER_DIAMETER); - paint.setColor(focusInnerColors[flavor]); + paint.setStrokeWidth(CURSOR_RING_INNER_DIAMETER); + paint.setColor(cursorInnerColors[flavor]); canvas->drawPath(path, paint); } diff --git a/WebCore/platform/graphics/android/android_graphics.h b/WebCore/platform/graphics/android/android_graphics.h index 21492cf..348daf1 100644 --- a/WebCore/platform/graphics/android/android_graphics.h +++ b/WebCore/platform/graphics/android/android_graphics.h @@ -26,64 +26,39 @@ #ifndef android_graphics_DEFINED #define android_graphics_DEFINED -#include "Color.h" -#include "Image.h" #include "wtf/Vector.h" -#include "SkColor.h" -#include "SkPorterDuff.h" -#include "SkScalar.h" -#include "SkShader.h" +#include "SkTypes.h" class SkCanvas; -struct SkPoint; -struct SKRect; namespace WebCore { - class FloatRect; - class IntPoint; class IntRect; class GraphicsContext; } -SkPoint* android_setpt(SkPoint* dst, const WebCore::IntPoint& src); -SkPoint* android_setpt(SkPoint* dst, const WebCore::FloatPoint& src); -SkRect* android_setrect(SkRect* dst, const WebCore::IntRect& src); -SkRect* android_setrect(SkRect* dst, const WebCore::FloatRect& src); -SkIRect* android_setrect(SkIRect* dst, const WebCore::IntRect& src); -SkIRect* android_setrect(SkIRect* dst, const WebCore::FloatRect& src); -SkIRect* android_setrect_scaled(SkIRect* dst, const WebCore::FloatRect& src, - float sx, float sy); - -SkPorterDuff::Mode android_convert_compositeOp(WebCore::CompositeOperator); -SkShader::TileMode android_convert_TileRule(WebCore::Image::TileRule); - -WebCore::Color android_SkPMColorToWebCoreColor(SkPMColor pm); - SkCanvas* android_gc2canvas(WebCore::GraphicsContext* gc); -// Data and methods for focus rings +// Data and methods for cursor rings // used to inflate node cache entry -#define FOCUS_RING_HIT_TEST_RADIUS 5 +#define CURSOR_RING_HIT_TEST_RADIUS 5 -// used to inval rectangle enclosing pressed state of focus ring -#define FOCUS_RING_OUTER_DIAMETER SkFixedToScalar(SkIntToFixed(13)>>2) // 13/4 == 3.25 +// used to inval rectangle enclosing pressed state of ring +#define CURSOR_RING_OUTER_DIAMETER SkFixedToScalar(SkIntToFixed(13)>>2) // 13/4 == 3.25 -struct FocusRing { +struct CursorRing { public: enum Flavor { NORMAL_FLAVOR, FAKE_FLAVOR, - INVALID_FLAVOR, NORMAL_ANIMATING, FAKE_ANIMATING, ANIMATING_COUNT = 2 }; - - static void DrawRing(SkCanvas* , + + static void DrawRing(SkCanvas* , const Vector<WebCore::IntRect>& rects, Flavor ); }; #endif - diff --git a/WebCore/platform/graphics/cairo/FontCairo.cpp b/WebCore/platform/graphics/cairo/FontCairo.cpp index 9da9426..b23182d 100644 --- a/WebCore/platform/graphics/cairo/FontCairo.cpp +++ b/WebCore/platform/graphics/cairo/FontCairo.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,8 +30,11 @@ #include "Font.h" #include "GlyphBuffer.h" +#include "Gradient.h" #include "GraphicsContext.h" +#include "Pattern.h" #include "SimpleFontData.h" +#include "TransformationMatrix.h" namespace WebCore { @@ -78,18 +82,51 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons } if (context->textDrawingMode() & cTextFill) { - float red, green, blue, alpha; - fillColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); - + if (context->fillGradient()) { + cairo_set_source(cr, context->fillGradient()->platformGradient()); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else if (context->fillPattern()) { + TransformationMatrix affine; + cairo_set_source(cr, context->fillPattern()->createPlatformPattern(affine)); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else { + float red, green, blue, alpha; + fillColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); + } cairo_show_glyphs(cr, glyphs, numGlyphs); } if (context->textDrawingMode() & cTextStroke) { - Color strokeColor = context->strokeColor(); - float red, green, blue, alpha; - strokeColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); + if (context->strokeGradient()) { + cairo_set_source(cr, context->strokeGradient()->platformGradient()); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else if (context->strokePattern()) { + TransformationMatrix affine; + cairo_set_source(cr, context->strokePattern()->createPlatformPattern(affine)); + if (context->getAlpha() < 1.0f) { + cairo_push_group(cr); + cairo_paint_with_alpha(cr, context->getAlpha()); + cairo_pop_group_to_source(cr); + } + } else { + Color strokeColor = context->strokeColor(); + float red, green, blue, alpha; + strokeColor.getRGBA(red, green, blue, alpha); + cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); + } cairo_glyph_path(cr, glyphs, numGlyphs); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); diff --git a/WebCore/platform/graphics/cairo/GradientCairo.cpp b/WebCore/platform/graphics/cairo/GradientCairo.cpp index 7776424..72fb0c5 100644 --- a/WebCore/platform/graphics/cairo/GradientCairo.cpp +++ b/WebCore/platform/graphics/cairo/GradientCairo.cpp @@ -57,6 +57,22 @@ cairo_pattern_t* Gradient::platformGradient() ++stopIterator; } + switch (m_spreadMethod) { + case SpreadMethodPad: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_PAD); + break; + case SpreadMethodReflect: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_REFLECT); + break; + case SpreadMethodRepeat: + cairo_pattern_set_extend(m_gradient, CAIRO_EXTEND_REPEAT); + break; + } + + cairo_matrix_t matrix = m_gradientSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(m_gradient, &matrix); + return m_gradient; } diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index ef748cf..35ebd3c 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -87,22 +87,6 @@ static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const cairo_fill(cr); } -static inline cairo_pattern_t* applySpreadMethod(cairo_pattern_t* pattern, GradientSpreadMethod spreadMethod) -{ - switch (spreadMethod) { - case SpreadMethodPad: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_PAD); - break; - case SpreadMethodReflect: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REFLECT); - break; - case SpreadMethodRepeat: - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - break; - } - return pattern; -} - GraphicsContext::GraphicsContext(PlatformGraphicsContext* cr) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate) @@ -122,7 +106,7 @@ TransformationMatrix GraphicsContext::getCTM() const cairo_t* cr = platformContext(); cairo_matrix_t m; cairo_get_matrix(cr, &m); - return m; + return TransformationMatrix(m.xx, m.yx, m.xy, m.yy, m.x0, m.y0); } cairo_t* GraphicsContext::platformContext() const @@ -463,7 +447,6 @@ void GraphicsContext::fillPath() } case GradientColorSpace: cairo_pattern_t* pattern = m_common->state.fillGradient->platformGradient(); - pattern = applySpreadMethod(pattern, spreadMethod()); cairo_set_source(cr, pattern); cairo_clip(cr); cairo_paint_with_alpha(cr, m_common->state.globalAlpha); @@ -501,7 +484,6 @@ void GraphicsContext::strokePath() } case GradientColorSpace: cairo_pattern_t* pattern = m_common->state.strokeGradient->platformGradient(); - pattern = applySpreadMethod(pattern, spreadMethod()); cairo_set_source(cr, pattern); if (m_common->state.globalAlpha < 1.0f) { cairo_push_group(cr); @@ -750,8 +732,8 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) return; cairo_t* cr = m_data->cr; - const cairo_matrix_t* matrix = reinterpret_cast<const cairo_matrix_t*>(&transform); - cairo_transform(cr, matrix); + const cairo_matrix_t matrix = cairo_matrix_t(transform); + cairo_transform(cr, &matrix); m_data->concatCTM(transform); } diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 535f70d..55b2e25 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -51,6 +51,7 @@ public: // NOTE: These may note be needed: review and remove once Cairo implementation is complete , m_hdc(0) , m_transparencyCount(0) + , m_shouldIncludeChildWindows(false) #endif { } @@ -94,6 +95,7 @@ public: #elif PLATFORM(WIN) HDC m_hdc; unsigned m_transparencyCount; + bool m_shouldIncludeChildWindows; #endif }; diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 3e06669..dff39b7 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -1,7 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2007 Holger Hans Peter Freyther <zecke@selfish.org> - * Copyright (C) 2008 Dirk Schulze <vbs85@gmx.de> + * Copyright (C) 2008, 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,6 +30,7 @@ #include "Base64.h" #include "BitmapImage.h" +#include "Color.h" #include "GraphicsContext.h" #include "ImageData.h" #include "MIMETypeRegistry.h" @@ -126,17 +127,15 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const unsigned char* destRows = dataDst + desty * destBytesPerRow + destx * 4; for (int y = 0; y < numRows; ++y) { - unsigned char *row = dataSrc + stride * (y + originy); + unsigned* row = reinterpret_cast<unsigned*>(dataSrc + stride * (y + originy)); for (int x = 0; x < numColumns; x++) { - uint32_t *pixel = (uint32_t *) row + x + originx; int basex = x * 4; - if (unsigned int alpha = (*pixel & 0xff000000) >> 24) { - destRows[basex] = (*pixel & 0x00ff0000) >> 16; - destRows[basex + 1] = (*pixel & 0x0000ff00) >> 8; - destRows[basex + 2] = (*pixel & 0x000000ff); - destRows[basex + 3] = alpha; - } else - reinterpret_cast<uint32_t*>(destRows + basex)[0] = pixel[0]; + unsigned* pixel = row + x + originx; + Color pixelColor = colorFromPremultipliedARGB(*pixel); + destRows[basex] = pixelColor.red(); + destRows[basex + 1] = pixelColor.green(); + destRows[basex + 2] = pixelColor.blue(); + destRows[basex + 3] = pixelColor.alpha(); } destRows += destBytesPerRow; } @@ -181,14 +180,15 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, con unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; for (int y = 0; y < numRows; ++y) { - unsigned char *row = dataDst + stride * (y + desty); + unsigned* row = reinterpret_cast<unsigned*>(dataDst + stride * (y + desty)); for (int x = 0; x < numColumns; x++) { - uint32_t *pixel = (uint32_t *) row + x + destx; int basex = x * 4; - if (unsigned int alpha = srcRows[basex + 3]) { - *pixel = alpha << 24 | srcRows[basex] << 16 | srcRows[basex + 1] << 8 | srcRows[basex + 2]; - } else - pixel[0] = reinterpret_cast<uint32_t*>(srcRows + basex)[0]; + unsigned* pixel = row + x + destx; + Color pixelColor(srcRows[basex], + srcRows[basex + 1], + srcRows[basex + 2], + srcRows[basex + 3]); + *pixel = premultipliedARGBFromColor(pixelColor); } srcRows += srcBytesPerRow; } diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp index 2850488..224154e 100644 --- a/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,10 +30,11 @@ #if PLATFORM(CAIRO) -#include "TransformationMatrix.h" +#include "Color.h" #include "FloatRect.h" #include "GraphicsContext.h" #include "ImageObserver.h" +#include "TransformationMatrix.h" #include <cairo.h> #include <math.h> @@ -60,6 +62,7 @@ BitmapImage::BitmapImage(cairo_surface_t* surface, ImageObserver* observer) , m_repetitionCountStatus(Unknown) , m_repetitionsComplete(0) , m_isSolidColor(false) + , m_checkedForSolidColor(false) , m_animationFinished(true) , m_allDataReceived(true) , m_haveSize(true) @@ -180,8 +183,28 @@ void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, con void BitmapImage::checkForSolidColor() { - // FIXME: It's easy to implement this optimization. Just need to check the RGBA32 buffer to see if it is 1x1. m_isSolidColor = false; + m_checkedForSolidColor = true; + + if (frameCount() > 1) + return; + + cairo_surface_t* frameSurface = frameAtIndex(0); + if (!frameSurface) + return; + + ASSERT(cairo_surface_get_type(frameSurface) == CAIRO_SURFACE_TYPE_IMAGE); + + int width = cairo_image_surface_get_width(frameSurface); + int height = cairo_image_surface_get_height(frameSurface); + + if (width != 1 || height != 1) + return; + + unsigned* pixelColor = reinterpret_cast<unsigned*>(cairo_image_surface_get_data(frameSurface)); + m_solidColor = colorFromPremultipliedARGB(*pixelColor); + + m_isSolidColor = true; } } diff --git a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp index 6841599..c6d54f2 100644 --- a/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageSourceCairo.cpp @@ -111,7 +111,7 @@ void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* delete m_decoder; m_decoder = 0; if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } bool ImageSource::initialized() const diff --git a/WebCore/platform/graphics/cairo/PatternCairo.cpp b/WebCore/platform/graphics/cairo/PatternCairo.cpp index 7d75db3..58c5d00 100644 --- a/WebCore/platform/graphics/cairo/PatternCairo.cpp +++ b/WebCore/platform/graphics/cairo/PatternCairo.cpp @@ -26,23 +26,26 @@ #include "config.h" #include "Pattern.h" -#include "TransformationMatrix.h" #include "GraphicsContext.h" +#include "TransformationMatrix.h" #include <cairo.h> namespace WebCore { -cairo_pattern_t* Pattern::createPlatformPattern(const TransformationMatrix& patternTransform) const +cairo_pattern_t* Pattern::createPlatformPattern(const TransformationMatrix&) const { cairo_surface_t* surface = tileImage()->nativeImageForCurrentFrame(); if (!surface) return 0; cairo_pattern_t* pattern = cairo_pattern_create_for_surface(surface); - const TransformationMatrix& inverse = patternTransform.inverse(); - const cairo_matrix_t* pattern_matrix = reinterpret_cast<const cairo_matrix_t*>(&inverse); - cairo_pattern_set_matrix(pattern, pattern_matrix); + + // cairo merges patter space and user space itself + cairo_matrix_t matrix = m_patternSpaceTransformation; + cairo_matrix_invert(&matrix); + cairo_pattern_set_matrix(pattern, &matrix); + if (m_repeatX || m_repeatY) cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); return pattern; diff --git a/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp b/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp index b78620f..1b83a29 100644 --- a/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp +++ b/WebCore/platform/graphics/cairo/TransformationMatrixCairo.cpp @@ -32,248 +32,18 @@ namespace WebCore { -static const double deg2rad = 0.017453292519943295769; // pi/180 - -TransformationMatrix::TransformationMatrix() -{ - cairo_matrix_init_identity(&m_transform); -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) -{ - cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); -} - -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) -{ - m_transform = matrix; -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - cairo_matrix_init(&m_transform, a, b, c, d, tx, ty); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - *x2 = x; - *y2 = y; - cairo_matrix_transform_point(&m_transform, x2, y2); -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - FloatRect floatRect(rect); - FloatRect enclosingFloatRect = this->mapRect(floatRect); - - return enclosingIntRect(enclosingFloatRect); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ - double rectMinX = rect.x(); - double rectMaxX = rect.x() + rect.width(); - double rectMinY = rect.y(); - double rectMaxY = rect.y() + rect.height(); - - double px = rectMinX; - double py = rectMinY; - cairo_matrix_transform_point(&m_transform, &px, &py); - - double enclosingRectMinX = px; - double enclosingRectMinY = py; - double enclosingRectMaxX = px; - double enclosingRectMaxY = py; - - px = rectMaxX; - py = rectMinY; - cairo_matrix_transform_point(&m_transform, &px, &py); - if (px < enclosingRectMinX) - enclosingRectMinX = px; - else if (px > enclosingRectMaxX) - enclosingRectMaxX = px; - if (py < enclosingRectMinY) - enclosingRectMinY = py; - else if (py > enclosingRectMaxY) - enclosingRectMaxY = py; - - px = rectMaxX; - py = rectMaxY; - cairo_matrix_transform_point(&m_transform, &px, &py); - if (px < enclosingRectMinX) - enclosingRectMinX = px; - else if (px > enclosingRectMaxX) - enclosingRectMaxX = px; - if (py < enclosingRectMinY) - enclosingRectMinY = py; - else if (py > enclosingRectMaxY) - enclosingRectMaxY = py; - - px = rectMinX; - py = rectMaxY; - cairo_matrix_transform_point(&m_transform, &px, &py); - if (px < enclosingRectMinX) - enclosingRectMinX = px; - else if (px > enclosingRectMaxX) - enclosingRectMaxX = px; - if (py < enclosingRectMinY) - enclosingRectMinY = py; - else if (py > enclosingRectMaxY) - enclosingRectMaxY = py; - - - double enclosingRectWidth = enclosingRectMaxX - enclosingRectMinX; - double enclosingRectHeight = enclosingRectMaxY - enclosingRectMinY; - - return FloatRect(enclosingRectMinX, enclosingRectMinY, enclosingRectWidth, enclosingRectHeight); -} - -bool TransformationMatrix::isIdentity() const -{ - return ((m_transform.xx == 1) && (m_transform.yy == 1) - && (m_transform.xy == 0) && (m_transform.yx == 0) - && (m_transform.x0 == 0) && (m_transform.y0 == 0)); -} - -double TransformationMatrix::a() const -{ - return m_transform.xx; -} - -void TransformationMatrix::setA(double a) -{ - m_transform.xx = a; -} - -double TransformationMatrix::b() const -{ - return m_transform.yx; -} - -void TransformationMatrix::setB(double b) -{ - m_transform.yx = b; -} - -double TransformationMatrix::c() const -{ - return m_transform.xy; -} - -void TransformationMatrix::setC(double c) -{ - m_transform.xy = c; -} - -double TransformationMatrix::d() const -{ - return m_transform.yy; -} - -void TransformationMatrix::setD(double d) -{ - m_transform.yy = d; -} - -double TransformationMatrix::e() const -{ - return m_transform.x0; -} - -void TransformationMatrix::setE(double e) -{ - m_transform.x0 = e; -} - -double TransformationMatrix::f() const -{ - return m_transform.y0; -} - -void TransformationMatrix::setF(double f) -{ - m_transform.y0 = f; -} - -void TransformationMatrix::reset() -{ - cairo_matrix_init_identity(&m_transform); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - cairo_matrix_scale(&m_transform, sx, sy); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - cairo_matrix_rotate(&m_transform, d * deg2rad); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - cairo_matrix_translate(&m_transform, tx, ty); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - cairo_matrix_t shear; - cairo_matrix_init(&shear, 1, sy, sx, 1, 0, 0); - - cairo_matrix_t result; - cairo_matrix_multiply(&result, &shear, &m_transform); - m_transform = result; - - return *this; -} - -double TransformationMatrix::det() const -{ - return m_transform.xx * m_transform.yy - m_transform.xy * m_transform.yx; -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - if (!isInvertible()) return TransformationMatrix(); - - cairo_matrix_t result = m_transform; - cairo_matrix_invert(&result); - return TransformationMatrix(result); -} - TransformationMatrix::operator cairo_matrix_t() const { - return m_transform; -} + cairo_matrix_t m; -bool TransformationMatrix::operator== (const TransformationMatrix &m2) const -{ - return ((m_transform.xx == m2.m_transform.xx) - && (m_transform.yy == m2.m_transform.yy) - && (m_transform.xy == m2.m_transform.xy) - && (m_transform.yx == m2.m_transform.yx) - && (m_transform.x0 == m2.m_transform.x0) - && (m_transform.y0 == m2.m_transform.y0)); - -} - -TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) -{ - cairo_matrix_t result; - cairo_matrix_multiply(&result, &m_transform, &m2.m_transform); - m_transform = result; - - return *this; -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) -{ - cairo_matrix_t result; - cairo_matrix_multiply(&result, &m_transform, &m2.m_transform); - return result; + cairo_matrix_init (&m, + a(), + b(), + c(), + d(), + e(), + f()); + return m; } } diff --git a/WebCore/platform/graphics/cg/ColorCG.cpp b/WebCore/platform/graphics/cg/ColorCG.cpp index 48ce9f2..0465c0b 100644 --- a/WebCore/platform/graphics/cg/ColorCG.cpp +++ b/WebCore/platform/graphics/cg/ColorCG.cpp @@ -67,9 +67,9 @@ Color::Color(CGColorRef color) m_color = makeRGBA(r * 255, g * 255, b * 255, a * 255); } -#if !PLATFORM(MAC) +#if PLATFORM(WIN_OS) -CGColorRef cgColor(const Color& c) +CGColorRef createCGColor(const Color& c) { CGColorRef color = NULL; CMProfileRef prof = NULL; @@ -89,7 +89,7 @@ CGColorRef cgColor(const Color& c) return color; } -#endif // !PLATFORM(MAC) +#endif // PLATFORM(WIN_OS) } diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index 1cc55a4..4b8a555 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -504,6 +504,7 @@ void GraphicsContext::fillPath() CGContextEOClip(context); else CGContextClip(context); + CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform()); CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); CGContextRestoreGState(context); break; @@ -529,6 +530,7 @@ void GraphicsContext::strokePath() CGContextSaveGState(context); CGContextReplacePathWithStrokedPath(context); CGContextClip(context); + CGContextConcatCTM(context, m_common->state.strokeGradient->gradientSpaceTransform()); CGContextDrawShading(context, m_common->state.strokeGradient->platformGradient()); CGContextRestoreGState(context); break; @@ -552,6 +554,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) case GradientColorSpace: CGContextSaveGState(context); CGContextClipToRect(context, rect); + CGContextConcatCTM(context, m_common->state.fillGradient->gradientSpaceTransform()); CGContextDrawShading(context, m_common->state.fillGradient->platformGradient()); CGContextRestoreGState(context); break; @@ -734,7 +737,7 @@ void GraphicsContext::setPlatformShadow(const IntSize& size, int blur, const Col if (!color.isValid()) CGContextSetShadow(context, CGSizeMake(width, height), blurRadius); else { - CGColorRef colorCG = cgColor(color); + CGColorRef colorCG = createCGColor(color); CGContextSetShadowWithColor(context, CGSizeMake(width, height), blurRadius, @@ -907,7 +910,8 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) TransformationMatrix GraphicsContext::getCTM() const { - return CGContextGetCTM(platformContext()); + CGAffineTransform t = CGContextGetCTM(platformContext()); + return TransformationMatrix(t.a, t.b, t.c, t.d, t.tx, t.ty); } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index 13c8c07..dbf1d85 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -73,6 +73,7 @@ BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) , m_repetitionCountStatus(Unknown) , m_repetitionsComplete(0) , m_isSolidColor(false) + , m_checkedForSolidColor(false) , m_animationFinished(true) , m_allDataReceived(true) , m_haveSize(true) @@ -99,6 +100,7 @@ BitmapImage::BitmapImage(CGImageRef cgImage, ImageObserver* observer) void BitmapImage::checkForSolidColor() { + m_checkedForSolidColor = true; if (frameCount() > 1) m_isSolidColor = false; else { diff --git a/WebCore/platform/graphics/cg/ImageSourceCG.cpp b/WebCore/platform/graphics/cg/ImageSourceCG.cpp index 0b276cc..c059985 100644 --- a/WebCore/platform/graphics/cg/ImageSourceCG.cpp +++ b/WebCore/platform/graphics/cg/ImageSourceCG.cpp @@ -58,7 +58,7 @@ void ImageSource::clear(bool, size_t, SharedBuffer* data, bool allDataReceived) m_decoder = 0; } if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } static CFDictionaryRef imageSourceOptions() diff --git a/WebCore/platform/graphics/cg/PatternCG.cpp b/WebCore/platform/graphics/cg/PatternCG.cpp index 2b9c12f..697bc57 100644 --- a/WebCore/platform/graphics/cg/PatternCG.cpp +++ b/WebCore/platform/graphics/cg/PatternCG.cpp @@ -50,12 +50,13 @@ static void patternReleaseCallback(void* info) static_cast<Image*>(info)->deref(); } -CGPatternRef Pattern::createPlatformPattern(const TransformationMatrix& transform) const +CGPatternRef Pattern::createPlatformPattern(const TransformationMatrix& userSpaceTransformation) const { IntRect tileRect = tileImage()->rect(); - TransformationMatrix patternTransform = transform; - patternTransform.scale(1, -1); + TransformationMatrix patternTransform = m_patternSpaceTransformation; + patternTransform.multiply(userSpaceTransformation); + patternTransform.scaleNonUniform(1, -1); patternTransform.translate(0, -tileRect.height()); // If FLT_MAX should also be used for xStep or yStep, nothing is rendered. Using fractions of FLT_MAX also diff --git a/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp b/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp index 9b3181a..568a6b3 100644 --- a/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp +++ b/WebCore/platform/graphics/cg/TransformationMatrixCG.cpp @@ -28,187 +28,19 @@ #if PLATFORM(CG) +#include <CoreGraphics/CGAffineTransform.h> #include "FloatConversion.h" -#include "FloatRect.h" -#include "IntRect.h" - -#include <wtf/MathExtras.h> namespace WebCore { -TransformationMatrix::TransformationMatrix() - : m_transform(CGAffineTransformIdentity) -{ -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform = CGAffineTransformMake(narrowPrecisionToCGFloat(a), - narrowPrecisionToCGFloat(b), - narrowPrecisionToCGFloat(c), - narrowPrecisionToCGFloat(d), - narrowPrecisionToCGFloat(tx), - narrowPrecisionToCGFloat(ty)); -} - -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& t) - : m_transform(t) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform = CGAffineTransformMake(narrowPrecisionToCGFloat(a), - narrowPrecisionToCGFloat(b), - narrowPrecisionToCGFloat(c), - narrowPrecisionToCGFloat(d), - narrowPrecisionToCGFloat(tx), - narrowPrecisionToCGFloat(ty)); -} - -void TransformationMatrix::map(double x, double y, double *x2, double *y2) const -{ - CGPoint result = CGPointApplyAffineTransform(CGPointMake(narrowPrecisionToCGFloat(x), narrowPrecisionToCGFloat(y)), m_transform); - *x2 = result.x; - *y2 = result.y; -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ - return enclosingIntRect(CGRectApplyAffineTransform(CGRect(rect), m_transform)); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ - return FloatRect(CGRectApplyAffineTransform(CGRect(rect), m_transform)); -} - -bool TransformationMatrix::isIdentity() const -{ - return CGAffineTransformIsIdentity(m_transform); -} - -double TransformationMatrix::a() const -{ - return m_transform.a; -} - -void TransformationMatrix::setA(double a) -{ - m_transform.a = narrowPrecisionToCGFloat(a); -} - -double TransformationMatrix::b() const -{ - return m_transform.b; -} - -void TransformationMatrix::setB(double b) -{ - m_transform.b = narrowPrecisionToCGFloat(b); -} - -double TransformationMatrix::c() const -{ - return m_transform.c; -} - -void TransformationMatrix::setC(double c) -{ - m_transform.c = narrowPrecisionToCGFloat(c); -} - -double TransformationMatrix::d() const -{ - return m_transform.d; -} - -void TransformationMatrix::setD(double d) -{ - m_transform.d = narrowPrecisionToCGFloat(d); -} - -double TransformationMatrix::e() const -{ - return m_transform.tx; -} - -void TransformationMatrix::setE(double e) -{ - m_transform.tx = narrowPrecisionToCGFloat(e); -} - -double TransformationMatrix::f() const -{ - return m_transform.ty; -} - -void TransformationMatrix::setF(double f) -{ - m_transform.ty = narrowPrecisionToCGFloat(f); -} - -void TransformationMatrix::reset() -{ - m_transform = CGAffineTransformIdentity; -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform = CGAffineTransformScale(m_transform, narrowPrecisionToCGFloat(sx), narrowPrecisionToCGFloat(sy)); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform = CGAffineTransformRotate(m_transform, narrowPrecisionToCGFloat(deg2rad(d))); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform = CGAffineTransformTranslate(m_transform, narrowPrecisionToCGFloat(tx), narrowPrecisionToCGFloat(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - CGAffineTransform shear = CGAffineTransformMake(1.0f, narrowPrecisionToCGFloat(sy), narrowPrecisionToCGFloat(sx), 1.0f, 0.0f, 0.0f); - m_transform = CGAffineTransformConcat(shear, m_transform); - return *this; -} - -double TransformationMatrix::det() const -{ - return m_transform.a * m_transform.d - m_transform.b * m_transform.c; -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - if (isInvertible()) - return TransformationMatrix(CGAffineTransformInvert(m_transform)); - return TransformationMatrix(); -} - -TransformationMatrix::operator PlatformTransformationMatrix() const -{ - return m_transform; -} - -bool TransformationMatrix::operator== (const TransformationMatrix &m2) const -{ - return CGAffineTransformEqualToTransform(m_transform, CGAffineTransform(m2)); -} - -TransformationMatrix &TransformationMatrix::operator*= (const TransformationMatrix &m2) -{ - m_transform = CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); - return *this; -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &m2) +TransformationMatrix::operator CGAffineTransform() const { - return CGAffineTransformConcat(m_transform, CGAffineTransform(m2)); + return CGAffineTransformMake(narrowPrecisionToCGFloat(a()), + narrowPrecisionToCGFloat(b()), + narrowPrecisionToCGFloat(c()), + narrowPrecisionToCGFloat(d()), + narrowPrecisionToCGFloat(e()), + narrowPrecisionToCGFloat(f())); } } diff --git a/WebCore/platform/graphics/chromium/ColorChromium.cpp b/WebCore/platform/graphics/chromium/ColorChromium.cpp index 16ca17d..647169c 100644 --- a/WebCore/platform/graphics/chromium/ColorChromium.cpp +++ b/WebCore/platform/graphics/chromium/ColorChromium.cpp @@ -28,35 +28,15 @@ namespace WebCore { +#if !PLATFORM(DARWIN) +// On OS X, there's code to monitor changes in the focus color system setting. +// On Windows/Linux there is no equivalent system setting and therefore a static +// color is all we need. Color focusRingColor() { -// FIXME: This should be split up to ColorChromiumWin and ColorChromiumMac. -#if PLATFORM(DARWIN) - // To avoid the Mac Chromium build having to rebasline 500+ layout tests and - // continue to do this w/ new tests that get landed in WebKit, we want to - // run the layout tests w/ the same color that stock WebKit uses. - // - // TODO: For now we've hard coded the color that WebKit uses for layout - // tests. We need to revisit this and do either of the following: - // A. Fully honor the color from the UI, which means collecting the color - // (and change notifications) in the browser process, and messaging the - // color to the render process. - // B. Adding a "layout tests" flag, to control the orage vs. blue colors - // depending if we're running layout tests. - // To see the WebKit implementation of using the UI color and/or a flag for - // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. - // (Reality is we just need an api to override the focus color and both - // of the above are covered for what this file needs to provide, the - // two options would be details that happen in other places.) - - // From WebKit: - // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; - static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); - return oldAquaFocusRingColor; -#else static Color focusRingColor(229, 151, 0, 255); return focusRingColor; -#endif } +#endif } // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ColorChromiumMac.mm b/WebCore/platform/graphics/chromium/ColorChromiumMac.mm new file mode 100644 index 0000000..01dff7e --- /dev/null +++ b/WebCore/platform/graphics/chromium/ColorChromiumMac.mm @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Color.h" + +#import <AppKit/NSColor.h> +#import <wtf/Assertions.h> +#import <wtf/StdLibExtras.h> +#import <wtf/RetainPtr.h> + +namespace WebCore { + +Color focusRingColor() +{ + // To avoid the Mac Chromium build having to rebasline 500+ layout tests and + // continue to do this w/ new tests that get landed in WebKit, we want to + // run the layout tests w/ the same color that stock WebKit uses. + // + // FIXME: For now we've hard coded the color that WebKit uses for layout + // tests. We need to revisit this and do either of the following: + // A. Fully honor the color from the UI, which means collecting the color + // (and change notifications) in the browser process, and messaging the + // color to the render process. + // B. Adding a "layout tests" flag, to control the orage vs. blue colors + // depending if we're running layout tests. + // To see the WebKit implementation of using the UI color and/or a flag for + // layout tests see WebKit/WebCore/platform/graphics/mac/ColorMac.mm. + // (Reality is we just need an api to override the focus color and both + // of the above are covered for what this file needs to provide, the + // two options would be details that happen in other places.) + + // From WebKit: + // static RGBA32 oldAquaFocusRingColorRGBA = 0xFF7DADD9; + static Color oldAquaFocusRingColor(0x7D, 0xAD, 0xD9, 0xFF); + return oldAquaFocusRingColor; +} + +// createCGColor() and the functions it calls are verbatum copies of +// graphics/mac/ColorMac.mm. These are copied here so that we don't need to +// include ColorMac.mm in the Chromium build. +// FIXME: Check feasibility of using pure CG calls and unifying this copy with +// ColorMac.mm's under graphics/cg. + +NSColor* nsColor(const Color& color) +{ + unsigned c = color.rgb(); + switch (c) { + case 0: { + // Need this to avoid returning nil because cachedRGBAValues will default to 0. + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, clearColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:0.0f])); + return clearColor.get(); + } + case Color::black: { + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, blackColor, ([NSColor colorWithDeviceRed:0.0f green:0.0f blue:0.0f alpha:1.0f])); + return blackColor.get(); + } + case Color::white: { + DEFINE_STATIC_LOCAL(RetainPtr<NSColor>, whiteColor, ([NSColor colorWithDeviceRed:1.0f green:1.0f blue:1.0f alpha:1.0f])); + return whiteColor.get(); + } + default: { + const int cacheSize = 32; + static unsigned cachedRGBAValues[cacheSize]; + static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; + + for (int i = 0; i != cacheSize; ++i) + if (cachedRGBAValues[i] == c) + return cachedColors[i].get(); + + NSColor* result = [NSColor colorWithDeviceRed:color.red() / 255.0f + green:color.green() / 255.0f + blue:color.blue() / 255.0f + alpha:color.alpha() /255.0f]; + + static int cursor; + cachedRGBAValues[cursor] = c; + cachedColors[cursor] = result; + if (++cursor == cacheSize) + cursor = 0; + return result; + } + } +} + +static CGColorRef CGColorFromNSColor(NSColor* color) +{ + // This needs to always use device colorspace so it can de-calibrate the color for + // CGColor to possibly recalibrate it. + NSColor* deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; + CGFloat red = [deviceColor redComponent]; + CGFloat green = [deviceColor greenComponent]; + CGFloat blue = [deviceColor blueComponent]; + CGFloat alpha = [deviceColor alphaComponent]; + const CGFloat components[4] = { red, green, blue, alpha }; + static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); + CGColorRef cgColor = CGColorCreate(deviceRGBColorSpace, components); + return cgColor; +} + +CGColorRef createCGColor(const Color& c) +{ + // We could directly create a CGColor here, but that would + // skip any RGB caching the nsColor method does. A direct + // creation could be investigated for a possible performance win. + return CGColorFromNSColor(nsColor(c)); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp index 03583a0..129776e 100644 --- a/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -394,53 +394,18 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, cons family = panUniFonts[i]; data = getCachedFontPlatformData(font.fontDescription(), AtomicString(family, wcslen(family))); } - if (i < numFonts) // we found the font that covers this character ! + // When i-th font (0-base) in |panUniFonts| contains a character and + // we get out of the loop, |i| will be |i + 1|. That is, if only the + // last font in the array covers the character, |i| will be numFonts. + // So, we have to use '<=" rather than '<' to see if we found a font + // covering the character. + if (i <= numFonts) return getCachedFontData(data); return 0; } -const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) -{ - // Note that mapping to Courier is removed because - // because it's a bitmap font on Windows. - // Alias Courier -> Courier New - static AtomicString courier("Courier"), courierNew("Courier New"); - if (equalIgnoringCase(familyName, courier)) - return courierNew; - - // Alias Times <-> Times New Roman. - static AtomicString times("Times"), timesNewRoman("Times New Roman"); - if (equalIgnoringCase(familyName, times)) - return timesNewRoman; - if (equalIgnoringCase(familyName, timesNewRoman)) - return times; - - // Alias Helvetica <-> Arial - static AtomicString arial("Arial"), helvetica("Helvetica"); - if (equalIgnoringCase(familyName, helvetica)) - return arial; - if (equalIgnoringCase(familyName, arial)) - return helvetica; - - // We block bitmap fonts altogether so that we have to - // alias MS Sans Serif (bitmap font) -> Microsoft Sans Serif (truetype font) - static AtomicString msSans("MS Sans Serif"); - static AtomicString microsoftSans("Microsoft Sans Serif"); - if (equalIgnoringCase(familyName, msSans)) - return microsoftSans; - - // Alias MS Serif (bitmap) -> Times New Roman (truetype font). There's no - // 'Microsoft Sans Serif-equivalent' for Serif. - static AtomicString msSerif("MS Serif"); - if (equalIgnoringCase(familyName, msSerif)) - return timesNewRoman; - - // FIXME: should we map 'system' to something ('Tahoma') ? - return emptyAtom; -} - FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) { return 0; diff --git a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp index f187c55..89433e1 100644 --- a/WebCore/platform/graphics/chromium/FontCacheLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontCacheLinux.cpp @@ -91,16 +91,6 @@ const SimpleFontData* FontCache::getFontDataForCharacters(const Font& font, return ret; } -const AtomicString& FontCache::alternateFamilyName(const AtomicString& familyName) -{ - notImplemented(); - - // This is just to stop GCC emitting a warning about returning a reference - // to a temporary variable - static AtomicString a; - return a; -} - FontPlatformData* FontCache::getSimilarFontPlatformData(const Font& font) { return 0; diff --git a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp index 3cf18a6..1b71946 100644 --- a/WebCore/platform/graphics/chromium/FontChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -32,7 +32,6 @@ #include "config.h" #include "Font.h" -#include "TransformationMatrix.h" #include "ChromiumBridge.h" #include "FontFallbackList.h" #include "GlyphBuffer.h" @@ -40,6 +39,7 @@ #include "SimpleFontData.h" #include "SkiaFontWin.h" #include "SkiaUtils.h" +#include "TransparencyWin.h" #include "UniscribeHelperTextRun.h" #include "skia/ext/platform_canvas_win.h" @@ -49,122 +49,274 @@ namespace WebCore { -static bool windowsCanHandleTextDrawing(GraphicsContext* context) +namespace { + +bool canvasHasMultipleLayers(const SkCanvas* canvas) +{ + SkCanvas::LayerIter iter(const_cast<SkCanvas*>(canvas), false); + iter.next(); // There is always at least one layer. + return !iter.done(); // There is > 1 layer if the the iterator can stil advance. +} + +class TransparencyAwareFontPainter { +public: + TransparencyAwareFontPainter(GraphicsContext*, const FloatPoint&); + ~TransparencyAwareFontPainter(); + +protected: + // Called by our subclass' constructor to initialize GDI if necessary. This + // is a separate function so it can be called after the subclass finishes + // construction (it calls virtual functions). + void init(); + + virtual IntRect estimateTextBounds() = 0; + + // Use the context from the transparency helper when drawing with GDI. It + // may point to a temporary one. + GraphicsContext* m_graphicsContext; + PlatformGraphicsContext* m_platformContext; + + FloatPoint m_point; + + // Set when Windows can handle the type of drawing we're doing. + bool m_useGDI; + + // These members are valid only when m_useGDI is set. + HDC m_hdc; + TransparencyWin m_transparency; + +private: + // Call when we're using GDI mode to initialize the TransparencyWin to help + // us draw GDI text. + void initializeForGDI(); + + bool m_createdTransparencyLayer; // We created a layer to give the font some alpha. +}; + +TransparencyAwareFontPainter::TransparencyAwareFontPainter(GraphicsContext* context, + const FloatPoint& point) + : m_graphicsContext(context) + , m_platformContext(context->platformContext()) + , m_point(point) + , m_useGDI(windowsCanHandleTextDrawing(context)) + , m_hdc(0) + , m_createdTransparencyLayer(false) +{ +} + +void TransparencyAwareFontPainter::init() { - // Check for non-translation transforms. Sometimes zooms will look better in - // Skia, and sometimes better in Windows. The main problem is that zooming - // in using Skia will show you the hinted outlines for the smaller size, - // which look weird. All else being equal, it's better to use Windows' text - // drawing, so we don't check for zooms. - const TransformationMatrix& matrix = context->getCTM(); - if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. - return false; - - // Check for stroke effects. - if (context->platformContext()->getTextDrawingMode() != cTextFill) - return false; - - // Check for shadow effects. - if (context->platformContext()->getDrawLooper()) - return false; - - return true; + if (m_useGDI) + initializeForGDI(); } -// Skia equivalents to Windows text drawing functions. They -// will get the outlines from Windows and draw then using Skia using the given -// parameters in the paint arguments. This allows more complex effects and -// transforms to be drawn than Windows allows. -// -// These functions will be significantly slower than Windows GDI, and the text -// will look different (no ClearType), so use only when necessary. -// -// When you call a Skia* text drawing function, various glyph outlines will be -// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont -// when the font is destroyed so that the cache does not outlive the font (since -// the HFONTs are recycled). - -// Analog of the Windows GDI function DrawText, except using the given SkPaint -// attributes for the text. See above for more. -// -// Returns true of the text was drawn successfully. False indicates an error -// from Windows. -static bool skiaDrawText(HFONT hfont, - SkCanvas* canvas, - const SkPoint& point, - SkPaint* paint, - const WORD* glyphs, - const int* advances, - int numGlyphs) +void TransparencyAwareFontPainter::initializeForGDI() { - HDC dc = GetDC(0); - HGDIOBJ oldFont = SelectObject(dc, hfont); - - canvas->save(); - canvas->translate(point.fX, point.fY); - - for (int i = 0; i < numGlyphs; i++) { - const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); - if (!path) - return false; - canvas->drawPath(*path, *paint); - canvas->translate(advances[i], 0); + SkColor color = m_platformContext->effectiveFillColor(); + if (SkColorGetA(color) != 0xFF) { + // When the font has some transparency, apply it by creating a new + // transparency layer with that opacity applied. + m_createdTransparencyLayer = true; + m_graphicsContext->beginTransparencyLayer(SkColorGetA(color) / 255.0f); + // The color should be opaque now. + color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); } - canvas->restore(); + TransparencyWin::LayerMode layerMode; + IntRect layerRect; + if (m_platformContext->isDrawingToImageBuffer()) { + // Assume if we're drawing to an image buffer that the background + // is not opaque and we have to undo ClearType. We may want to + // enhance this to actually check, since it will often be opaque + // and we could do ClearType in that case. + layerMode = TransparencyWin::TextComposite; + layerRect = estimateTextBounds(); + + // The transparency helper requires that we draw text in black in + // this mode and it will apply the color. + m_transparency.setTextCompositeColor(color); + color = SkColorSetRGB(0, 0, 0); + } else if (canvasHasMultipleLayers(m_platformContext->canvas())) { + // When we're drawing a web page, we know the background is opaque, + // but if we're drawing to a layer, we still need extra work. + layerMode = TransparencyWin::OpaqueCompositeLayer; + layerRect = estimateTextBounds(); + } else { + // Common case of drawing onto the bottom layer of a web page: we + // know everything is opaque so don't need to do anything special. + layerMode = TransparencyWin::NoLayer; + } + m_transparency.init(m_graphicsContext, layerMode, TransparencyWin::KeepTransform, layerRect); - SelectObject(dc, oldFont); - ReleaseDC(0, dc); - return true; + // Set up the DC, using the one from the transparency helper. + m_hdc = m_transparency.platformContext()->canvas()->beginPlatformPaint(); + SetTextColor(m_hdc, skia::SkColorToCOLORREF(color)); + SetBkMode(m_hdc, TRANSPARENT); } -static bool paintSkiaText(PlatformContextSkia* platformContext, - HFONT hfont, - int numGlyphs, - const WORD* glyphs, - const int* advances, - const SkPoint& origin) +TransparencyAwareFontPainter::~TransparencyAwareFontPainter() { - int textMode = platformContext->getTextDrawingMode(); - - // Filling (if necessary). This is the common case. - SkPaint paint; - platformContext->setupPaintForFilling(&paint); - paint.setFlags(SkPaint::kAntiAlias_Flag); - bool didFill = false; - if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { - if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs)) - return false; - didFill = true; - } + if (!m_useGDI) + return; // Nothing to do. + m_transparency.composite(); + if (m_createdTransparencyLayer) + m_graphicsContext->endTransparencyLayer(); + m_platformContext->canvas()->endPlatformPaint(); +} - // Stroking on top (if necessary). - if ((textMode & WebCore::cTextStroke) - && platformContext->getStrokeStyle() != NoStroke - && platformContext->getStrokeThickness() > 0) { - - paint.reset(); - platformContext->setupPaintForStroking(&paint, 0, 0); - paint.setFlags(SkPaint::kAntiAlias_Flag); - if (didFill) { - // If there is a shadow and we filled above, there will already be - // a shadow. We don't want to draw it again or it will be too dark - // and it will go on top of the fill. - // - // Note that this isn't strictly correct, since the stroke could be - // very thick and the shadow wouldn't account for this. The "right" - // thing would be to draw to a new layer and then draw that layer - // with a shadow. But this is a lot of extra work for something - // that isn't normally an issue. - paint.setLooper(0)->safeUnref(); - } +// Specialization for simple GlyphBuffer painting. +class TransparencyAwareGlyphPainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareGlyphPainter(GraphicsContext*, + const SimpleFontData*, + const GlyphBuffer&, + int from, int numGlyphs, + const FloatPoint&); + ~TransparencyAwareGlyphPainter(); + + // Draws the partial string of glyphs, starting at |startAdvance| to the + // left of m_point. We express it this way so that if we're using the Skia + // drawing path we can use floating-point positioning, even though we have + // to use integer positioning in the GDI path. + bool drawGlyphs(int numGlyphs, const WORD* glyphs, const int* advances, int startAdvance) const; + + private: + virtual IntRect estimateTextBounds(); + + const SimpleFontData* m_font; + const GlyphBuffer& m_glyphBuffer; + int m_from; + int m_numGlyphs; + + // When m_useGdi is set, this stores the previous HFONT selected into the + // m_hdc so we can restore it. + HGDIOBJ m_oldFont; // For restoring the DC to its original state. +}; + +TransparencyAwareGlyphPainter::TransparencyAwareGlyphPainter( + GraphicsContext* context, + const SimpleFontData* font, + const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_glyphBuffer(glyphBuffer) + , m_from(from) + , m_numGlyphs(numGlyphs) + , m_oldFont(0) +{ + init(); + + m_oldFont = ::SelectObject(m_hdc, m_font->platformData().hfont()); +} - if (!skiaDrawText(hfont, platformContext->canvas(), origin, &paint, &glyphs[0], &advances[0], numGlyphs)) - return false; +TransparencyAwareGlyphPainter::~TransparencyAwareGlyphPainter() +{ + if (m_useGDI) + ::SelectObject(m_hdc, m_oldFont); +} + + +// Estimates the bounding box of the given text. This is copied from +// FontCGWin.cpp, it is possible, but a lot more work, to get the precide +// bounds. +IntRect TransparencyAwareGlyphPainter::estimateTextBounds() +{ + int totalWidth = 0; + for (int i = 0; i < m_numGlyphs; i++) + totalWidth += lroundf(m_glyphBuffer.advanceAt(m_from + i)); + + return IntRect(m_point.x() - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + totalWidth + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, + const WORD* glyphs, + const int* advances, + int startAdvance) const +{ + if (!m_useGDI) { + SkPoint origin = m_point; + origin.fX += startAdvance; + return paintSkiaText(m_graphicsContext, m_font->platformData().hfont(), + numGlyphs, glyphs, advances, 0, &origin); } - return true; + + // Windows' origin is the top-left of the bounding box, so we have + // to subtract off the font ascent to get it. + int x = lroundf(m_point.x() + startAdvance); + int y = lroundf(m_point.y() - m_font->ascent()); + return !!ExtTextOut(m_hdc, x, y, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), numGlyphs, &advances[0]); +} + + +class TransparencyAwareUniscribePainter : public TransparencyAwareFontPainter { + public: + TransparencyAwareUniscribePainter(GraphicsContext*, + const Font*, + const TextRun&, + int from, int to, + const FloatPoint&); + ~TransparencyAwareUniscribePainter(); + + // Uniscibe will draw directly into our buffer, so we need to expose our DC. + HDC hdc() const { return m_hdc; } + + private: + virtual IntRect estimateTextBounds(); + + const Font* m_font; + const TextRun& m_run; + int m_from; + int m_to; +}; + +TransparencyAwareUniscribePainter::TransparencyAwareUniscribePainter( + GraphicsContext* context, + const Font* font, + const TextRun& run, + int from, int to, + const FloatPoint& point) + : TransparencyAwareFontPainter(context, point) + , m_font(font) + , m_run(run) + , m_from(from) + , m_to(to) +{ + init(); } +TransparencyAwareUniscribePainter::~TransparencyAwareUniscribePainter() +{ +} + +IntRect TransparencyAwareUniscribePainter::estimateTextBounds() +{ + // This case really really sucks. There is no convenient way to estimate + // the bounding box. So we run Uniscribe twice. If we find this happens a + // lot, the way to fix it is to make the extra layer after the + // UniscribeHelper has measured the text. + IntPoint intPoint(lroundf(m_point.x()), + lroundf(m_point.y())); + + UniscribeHelperTextRun state(m_run, *m_font); + int left = lroundf(m_point.x()) + state.characterToX(m_from); + int right = lroundf(m_point.x()) + state.characterToX(m_to); + + // This algorithm for estimating how much extra space we need (the text may + // go outside the selection rect) is based roughly on + // TransparencyAwareGlyphPainter::estimateTextBounds above. + return IntRect(left - (m_font->ascent() + m_font->descent()) / 2, + m_point.y() - m_font->ascent() - m_font->lineGap(), + (right - left) + m_font->ascent() + m_font->descent(), + m_font->lineSpacing()); +} + +} // namespace + void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, @@ -172,51 +324,27 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, int numGlyphs, const FloatPoint& point) const { - PlatformGraphicsContext* context = graphicsContext->platformContext(); - - // Max buffer length passed to the underlying windows API. - const int kMaxBufferLength = 1024; - // Default size for the buffer. It should be enough for most of cases. - const int kDefaultBufferLength = 256; - - SkColor color = context->fillColor(); + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. - if (!alpha && context->getStrokeStyle() == NoStroke) + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) return; - // Set up our graphics context. - HDC hdc = context->canvas()->beginPlatformPaint(); - HGDIOBJ oldFont = SelectObject(hdc, font->platformData().hfont()); - - // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. - // Enforce non-transparent color. - color = SkColorSetRGB(SkColorGetR(color), SkColorGetG(color), SkColorGetB(color)); - SetTextColor(hdc, skia::SkColorToCOLORREF(color)); - SetBkMode(hdc, TRANSPARENT); - - // Windows needs the characters and the advances in nice contiguous - // buffers, which we build here. - Vector<WORD, kDefaultBufferLength> glyphs; - Vector<int, kDefaultBufferLength> advances; - - // Compute the coordinate. The 'origin' represents the baseline, so we need - // to move it up to the top of the bounding square. - int x = static_cast<int>(point.x()); - int lineTop = static_cast<int>(point.y()) - font->ascent(); - - bool canUseGDI = windowsCanHandleTextDrawing(graphicsContext); + TransparencyAwareGlyphPainter painter(graphicsContext, font, glyphBuffer, from, numGlyphs, point); // We draw the glyphs in chunks to avoid having to do a heap allocation for // the arrays of characters and advances. Since ExtTextOut is the // lowest-level text output function on Windows, there should be little // penalty for splitting up the text. On the other hand, the buffer cannot // be bigger than 4094 or the function will fail. - int glyphIndex = 0; + const int kMaxBufferLength = 256; + Vector<WORD, kMaxBufferLength> glyphs; + Vector<int, kMaxBufferLength> advances; + int glyphIndex = 0; // The starting glyph of the current chunk. + int curAdvance = 0; // How far from the left the current chunk is. while (glyphIndex < numGlyphs) { - // how many chars will be in this chunk? + // How many chars will be in this chunk? int curLen = std::min(kMaxBufferLength, numGlyphs - glyphIndex); - glyphs.resize(curLen); advances.resize(curLen); @@ -227,17 +355,10 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, curWidth += advances[i]; } + // Actually draw the glyphs (with retry on failure). bool success = false; for (int executions = 0; executions < 2; ++executions) { - if (canUseGDI) - success = !!ExtTextOut(hdc, x, lineTop, ETO_GLYPH_INDEX, 0, reinterpret_cast<const wchar_t*>(&glyphs[0]), curLen, &advances[0]); - else { - // Skia's text draing origin is the baseline, like WebKit, not - // the top, like Windows. - SkPoint origin = { x, point.y() }; - success = paintSkiaText(context, font->platformData().hfont(), numGlyphs, reinterpret_cast<const WORD*>(&glyphs[0]), &advances[0], origin); - } - + success = painter.drawGlyphs(curLen, &glyphs[0], &advances[0], curAdvance); if (!success && executions == 0) { // Ask the browser to load the font for us and retry. ChromiumBridge::ensureFontLoaded(font->platformData().hfont()); @@ -247,12 +368,8 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, } ASSERT(success); - - x += curWidth; + curAdvance += curWidth; } - - SelectObject(hdc, oldFont); - context->canvas()->endPlatformPaint(); } FloatRect Font::selectionRectForComplexText(const TextRun& run, @@ -283,13 +400,15 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, PlatformGraphicsContext* context = graphicsContext->platformContext(); UniscribeHelperTextRun state(run, *this); - SkColor color = context->fillColor(); + SkColor color = graphicsContext->platformContext()->effectiveFillColor(); unsigned char alpha = SkColorGetA(color); // Skip 100% transparent text; no need to draw anything. - if (!alpha) + if (!alpha && graphicsContext->platformContext()->getStrokeStyle() == NoStroke) return; - HDC hdc = context->canvas()->beginPlatformPaint(); + TransparencyAwareUniscribePainter painter(graphicsContext, this, run, from, to, point); + + HDC hdc = painter.hdc(); // TODO(maruel): http://b/700464 SetTextColor doesn't support transparency. // Enforce non-transparent color. @@ -299,7 +418,9 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, // Uniscribe counts the coordinates from the upper left, while WebKit uses // the baseline, so we have to subtract off the ascent. - state.draw(hdc, static_cast<int>(point.x()), static_cast<int>(point.y() - ascent()), from, to); + state.draw(graphicsContext, hdc, static_cast<int>(point.x()), + static_cast<int>(point.y() - ascent()), from, to); + context->canvas()->endPlatformPaint(); } diff --git a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp index 8f8df88..1e923ac 100644 --- a/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/chromium/FontCustomPlatformData.cpp @@ -116,7 +116,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b // Streams the concatenation of a header and font data. class EOTStream { public: - EOTStream(const Vector<UInt8, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) + EOTStream(const Vector<uint8_t, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) : m_eotHeader(eotHeader) , m_fontData(fontData) , m_overlayDst(overlayDst) @@ -130,7 +130,7 @@ public: size_t read(void* buffer, size_t count); private: - const Vector<UInt8, 512>& m_eotHeader; + const Vector<uint8_t, 512>& m_eotHeader; const SharedBuffer* m_fontData; size_t m_overlayDst; size_t m_overlaySrc; @@ -200,7 +200,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) // TTLoadEmbeddedFont works only with Embedded OpenType (.eot) data, // so we need to create an EOT header and prepend it to the font data. - Vector<UInt8, 512> eotHeader; + Vector<uint8_t, 512> eotHeader; size_t overlayDst; size_t overlaySrc; size_t overlayLength; diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index 7a3e614..2b7c562 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -49,14 +49,6 @@ namespace WebCore { void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { - SkCanvas* canvas = gc->platformContext()->canvas(); - SkPaint paint; - - gc->platformContext()->setupPaintCommon(&paint); - font->platformData().setupPaint(&paint); - paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - paint.setColor(gc->fillColor().rgb()); - SkASSERT(sizeof(GlyphBufferGlyph) == sizeof(uint16_t)); // compile-time assert const GlyphBufferGlyph* glyphs = glyphBuffer.glyphs(from); @@ -78,7 +70,39 @@ void Font::drawGlyphs(GraphicsContext* gc, const SimpleFontData* font, x += SkFloatToScalar(adv[i].width()); y += SkFloatToScalar(adv[i].height()); } - canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + + SkCanvas* canvas = gc->platformContext()->canvas(); + int textMode = gc->platformContext()->getTextDrawingMode(); + + // We draw text up to two times (once for fill, once for stroke). + if (textMode & cTextFill) { + SkPaint paint; + gc->platformContext()->setupPaintForFilling(&paint); + font->platformData().setupPaint(&paint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->fillColor().rgb()); + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } + + if ((textMode & cTextStroke) + && gc->platformContext()->getStrokeStyle() != NoStroke + && gc->platformContext()->getStrokeThickness() > 0) { + + SkPaint paint; + gc->platformContext()->setupPaintForStroking(&paint, 0, 0); + font->platformData().setupPaint(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setColor(gc->strokeColor().rgb()); + + if (textMode & cTextFill) { + // If we also filled, we don't want to draw shadows twice. + // See comment in FontChromiumWin.cpp::paintSkiaText() for more details. + paint.setLooper(0)->safeUnref(); + } + + canvas->drawPosText(glyphs, numGlyphs << 1, pos, paint); + } } void Font::drawComplexText(GraphicsContext* context, const TextRun& run, diff --git a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp index 86f96ee..7b7d197 100644 --- a/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -86,7 +86,7 @@ void FontPlatformData::setupPaint(SkPaint* paint) const { const float ts = m_textSize > 0 ? m_textSize : 12; - paint->setAntiAlias(false); + paint->setAntiAlias(true); paint->setSubpixelText(false); paint->setTextSize(SkFloatToScalar(ts)); paint->setTypeface(m_typeface); diff --git a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp index 4c5cf7b..31c5256 100644 --- a/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp +++ b/WebCore/platform/graphics/chromium/GlyphPageTreeNodeChromiumWin.cpp @@ -147,27 +147,17 @@ static bool fillBMPGlyphs(unsigned offset, // When this character should be a space, we ignore whatever the font // says and use a space. Otherwise, if fonts don't map one of these // space or zero width glyphs, we will get a box. - if (Font::treatAsSpace(c)) + if (Font::treatAsSpace(c)) { // Hard code the glyph indices for characters that should be // treated like spaces. glyph = initSpaceGlyph(dc, &spaceGlyph); - else if (Font::treatAsZeroWidthSpace(c) || c == 0x200B) { - // FIXME: change Font::treatAsZeroWidthSpace to use - // u_hasBinaryProperty, per jungshik's comment here: - // https://bugs.webkit.org/show_bug.cgi?id=20237#c6. - // Then the additional OR above won't be necessary. - glyph = initSpaceGlyph(dc, &spaceGlyph); - glyphFontData = fontData->zeroWidthFontData(); } else if (glyph == invalidGlyph) { // WebKit expects both the glyph index and FontData // pointer to be 0 if the glyph is not present glyph = 0; glyphFontData = 0; - } else { - if (SimpleFontData::isCJKCodePoint(c)) - glyphFontData = fontData->cjkWidthFontData(); + } else haveGlyphs = true; - } page->setGlyphDataForCharacter(offset + i, glyph, glyphFontData); } @@ -205,6 +195,7 @@ static bool fillNonBMPGlyphs(unsigned offset, fontData->m_font.scriptCache(), fontData->m_font.scriptFontProperties()); state.setInhibitLigate(true); + state.setDisableFontFallback(true); state.init(); for (unsigned i = 0; i < length; i++) { diff --git a/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h b/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h index 959147a..e8ba0ad 100644 --- a/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h +++ b/WebCore/platform/graphics/chromium/MediaPlayerPrivateChromium.h @@ -33,13 +33,13 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" namespace WebCore { -class MediaPlayerPrivate : public Noncopyable { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface { public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); IntSize naturalSize() const; @@ -74,13 +74,10 @@ public: unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>&); - static bool isAvailable(); - // Public methods to be called by WebMediaPlayer FrameView* frameView(); void networkStateChanged(); @@ -90,6 +87,12 @@ public: void repaint(); private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + MediaPlayer* m_player; void* m_data; }; diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp deleted file mode 100644 index 798ee32..0000000 --- a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "ThemeHelperChromiumWin.h" - -#include "FloatRect.h" -#include "GraphicsContext.h" - -namespace WebCore { - -ThemeHelperWin::ThemeHelperWin(GraphicsContext* context, const IntRect& rect) - : m_orgContext(context) - , m_orgMatrix(context->getCTM()) - , m_orgRect(rect) -{ - if (m_orgMatrix.b() != 0 || m_orgMatrix.c() != 0) { // Check for skew. - // Complicated effects, make a copy and draw the bitmap there. - m_type = COPY; - m_rect.setSize(rect.size()); - - m_newBuffer.set(ImageBuffer::create(rect.size(), false).release()); - - // Theme drawing messes with the transparency. - // FIXME: Ideally, we would leave this transparent, but I was - // having problems with button drawing, so we fill with white. Buttons - // looked good with transparent here and no fixing up of the alpha - // later, but text areas didn't. This makes text areas look good but - // gives buttons a white halo. Is there a way to fix this? I think - // buttons actually have antialised edges which is just not possible - // to handle on a transparent background given that it messes with the - // alpha channel. - FloatRect newContextRect(0, 0, rect.width(), rect.height()); - GraphicsContext* newContext = m_newBuffer->context(); - newContext->setFillColor(Color::white); - newContext->fillRect(newContextRect); - - return; - } - - if (m_orgMatrix.a() != 1.0 || m_orgMatrix.d() != 1.0) { // Check for scale. - // Only a scaling is applied. - m_type = SCALE; - - // Save the transformed coordinates to draw. - m_rect = m_orgMatrix.mapRect(rect); - - m_orgContext->save(); - m_orgContext->concatCTM(m_orgContext->getCTM().inverse()); - return; - } - - // Nothing interesting. - m_rect = rect; - m_type = ORIGINAL; -} - -ThemeHelperWin::~ThemeHelperWin() -{ - switch (m_type) { - case SCALE: - m_orgContext->restore(); - break; - case COPY: { - // Copy the duplicate bitmap with our control to the original canvas. - FloatRect destRect(m_orgRect); - m_newBuffer->context()->platformContext()->canvas()-> - getTopPlatformDevice().fixupAlphaBeforeCompositing(); - m_orgContext->drawImage(m_newBuffer->image(), destRect); - break; - } - case ORIGINAL: - break; - } -} - -} // namespace WebCore diff --git a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h b/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h deleted file mode 100644 index 1771fb4..0000000 --- a/WebCore/platform/graphics/chromium/ThemeHelperChromiumWin.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above - * copyright notice, this list of conditions and the following disclaimer - * in the documentation and/or other materials provided with the - * distribution. - * * Neither the name of Google Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef ThemeHelperWin_h -#define ThemeHelperWin_h - -#include "TransformationMatrix.h" -#include "ImageBuffer.h" -#include "IntRect.h" -#include <wtf/OwnPtr.h> - -namespace WebCore { - -class GraphicsContext; -class IntRect; - -// Helps drawing theme elements like buttons and scroll bars. This will handle -// translations and scalings that Windows might not, by either making Windows -// draw the appropriate sized control, or by rendering it into an off-screen -// context and transforming it ourselves. -class ThemeHelperWin { - enum Type { - // Use the original canvas with no changes. This is the normal mode. - ORIGINAL, - - // Use the original canvas but scale the rectangle of the control so - // that it will be the correct size, undoing any scale already on the - // canvas. This will have the effect of just drawing the control bigger - // or smaller and not actually expanding or contracting the pixels in - // it. This usually looks better. - SCALE, - - // Make a copy of the control and then transform it ourselves after - // Windows draws it. This allows us to get complex effects. - COPY, - }; - -public: - // Prepares drawing a control with the given rect to the given context. - ThemeHelperWin(GraphicsContext* context, const IntRect& rect); - ~ThemeHelperWin(); - - // Returns the context to draw the control into, which may be the original - // or the copy, depending on the mode. - GraphicsContext* context() - { - return m_newBuffer.get() ? m_newBuffer->context() : m_orgContext; - } - - // Returns the rectangle in which to draw into the canvas() by Windows. - const IntRect& rect() { return m_rect; } - -private: - Type m_type; - - // The original canvas to wrote to. Not owned by this class. - GraphicsContext* m_orgContext; - TransformationMatrix m_orgMatrix; - IntRect m_orgRect; - - // When m_type == COPY, this will be a new surface owned by this class that - // represents the copy. - OwnPtr<ImageBuffer> m_newBuffer; - - // The control rectangle in the coordinate space of canvas(). - IntRect m_rect; -}; - -} // namespace WebCore - -#endif // ThemeHelperWin_h diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp new file mode 100644 index 0000000..8c790af --- /dev/null +++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include <windows.h> + +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "PlatformContextSkia.h" +#include "SimpleFontData.h" +#include "TransformationMatrix.h" +#include "TransparencyWin.h" + +#include "SkColorPriv.h" +#include "skia/ext/platform_canvas.h" + +namespace WebCore { + +namespace { + +// The maximum size in pixels of the buffer we'll keep around for drawing text +// into. Buffers larger than this will be destroyed when we're done with them. +const int maxCachedBufferPixelSize = 65536; + +inline skia::PlatformCanvas* canvasForContext(const GraphicsContext& context) +{ + return context.platformContext()->canvas(); +} + +inline const SkBitmap& bitmapForContext(const GraphicsContext& context) +{ + return canvasForContext(context)->getTopPlatformDevice().accessBitmap(false); +} + +void compositeToCopy(const GraphicsContext& sourceLayers, + GraphicsContext& destContext, + const TransformationMatrix& matrix) +{ + // Make a list of all devices. The iterator goes top-down, and we want + // bottom-up. Note that each layer can also have an offset in canvas + // coordinates, which is the (x, y) position. + struct DeviceInfo { + DeviceInfo(SkDevice* d, int lx, int ly) + : device(d) + , x(lx) + , y(ly) {} + SkDevice* device; + int x; + int y; + }; + Vector<DeviceInfo> devices; + SkCanvas* sourceCanvas = canvasForContext(sourceLayers); + SkCanvas::LayerIter iter(sourceCanvas, false); + while (!iter.done()) { + devices.append(DeviceInfo(iter.device(), iter.x(), iter.y())); + iter.next(); + } + + // Create a temporary canvas for the compositing into the destination. + SkBitmap* destBmp = const_cast<SkBitmap*>(&bitmapForContext(destContext)); + SkCanvas destCanvas(*destBmp); + destCanvas.setMatrix(matrix); + + for (int i = devices.size() - 1; i >= 0; i--) { + const SkBitmap& srcBmp = devices[i].device->accessBitmap(false); + + SkRect destRect; + destRect.fLeft = devices[i].x; + destRect.fTop = devices[i].y; + destRect.fRight = destRect.fLeft + srcBmp.width(); + destRect.fBottom = destRect.fTop + srcBmp.height(); + + destCanvas.drawBitmapRect(srcBmp, 0, destRect); + } +} + +} // namespace + +// If either of these pointers is non-null, both must be valid and point to +// bitmaps of the same size. +class TransparencyWin::OwnedBuffers { +public: + OwnedBuffers(const IntSize& size, bool needReferenceBuffer) + { + m_destBitmap.adopt(ImageBuffer::create(size, false)); + + if (needReferenceBuffer) { + m_referenceBitmap.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height()); + m_referenceBitmap.allocPixels(); + m_referenceBitmap.eraseARGB(0, 0, 0, 0); + } + } + + ImageBuffer* destBitmap() { return m_destBitmap.get(); } + + // This bitmap will be empty if you don't specify needReferenceBuffer to the + // constructor. + SkBitmap* referenceBitmap() { return &m_referenceBitmap; } + + // Returns whether the current layer will fix a buffer of the given size. + bool canHandleSize(const IntSize& size) const + { + return m_destBitmap->size().width() >= size.width() && m_destBitmap->size().height() >= size.height(); + } + +private: + // The destination bitmap we're drawing into. + OwnPtr<ImageBuffer> m_destBitmap; + + // This could be an ImageBuffer but this is an optimization. Since this is + // only ever used as a reference, we don't need to make a full + // PlatformCanvas using Skia on Windows. Just allocating a regular SkBitmap + // is much faster since it's just a Malloc rather than a GDI call. + SkBitmap m_referenceBitmap; +}; + +TransparencyWin::OwnedBuffers* TransparencyWin::m_cachedBuffers = 0; + +TransparencyWin::TransparencyWin() + : m_destContext(0) + , m_orgTransform() + , m_layerMode(NoLayer) + , m_transformMode(KeepTransform) + , m_drawContext(0) + , m_savedOnDrawContext(false) + , m_layerBuffer(0) + , m_referenceBitmap(0) +{ +} + +TransparencyWin::~TransparencyWin() +{ + // This should be false, since calling composite() is mandatory. + ASSERT(!m_savedOnDrawContext); +} + +void TransparencyWin::composite() +{ + // Matches the save() in initializeNewTextContext (or the constructor for + // SCALE) to put the context back into the same state we found it. + if (m_savedOnDrawContext) { + m_drawContext->restore(); + m_savedOnDrawContext = false; + } + + switch (m_layerMode) { + case NoLayer: + break; + case OpaqueCompositeLayer: + case WhiteLayer: + compositeOpaqueComposite(); + break; + case TextComposite: + compositeTextComposite(); + break; + } +} + +void TransparencyWin::init(GraphicsContext* dest, + LayerMode layerMode, + TransformMode transformMode, + const IntRect& region) +{ + m_destContext = dest; + m_orgTransform = dest->getCTM(); + m_layerMode = layerMode; + m_transformMode = transformMode; + m_sourceRect = region; + + computeLayerSize(); + setupLayer(); + setupTransform(region); +} + +void TransparencyWin::computeLayerSize() +{ + if (m_transformMode == Untransform) { + // The meaning of the "transformed" source rect is a little ambigous + // here. The rest of the code doesn't care about it in the Untransform + // case since we're using our own happy coordinate system. So we set it + // to be the source rect since that matches how the code below actually + // uses the variable: to determine how to translate things to account + // for the offset of the layer. + m_transformedSourceRect = m_sourceRect; + m_layerSize = IntSize(m_sourceRect.width(), m_sourceRect.height()); + } else { + m_transformedSourceRect = m_orgTransform.mapRect(m_sourceRect); + m_layerSize = IntSize(m_transformedSourceRect.width(), m_transformedSourceRect.height()); + } +} + +void TransparencyWin::setupLayer() +{ + switch (m_layerMode) { + case NoLayer: + setupLayerForNoLayer(); + break; + case OpaqueCompositeLayer: + setupLayerForOpaqueCompositeLayer(); + break; + case TextComposite: + setupLayerForTextComposite(); + break; + case WhiteLayer: + setupLayerForWhiteLayer(); + break; + } +} + +void TransparencyWin::setupLayerForNoLayer() +{ + m_drawContext = m_destContext; // Draw to the source context. +} + +void TransparencyWin::setupLayerForOpaqueCompositeLayer() +{ + initializeNewContext(); + + TransformationMatrix mapping; + mapping.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); + if (m_transformMode == Untransform){ + // Compute the inverse mapping from the canvas space to the + // coordinate space of our bitmap. + mapping = m_orgTransform.inverse() * mapping; + } + compositeToCopy(*m_destContext, *m_drawContext, mapping); + + // Save the reference layer so we can tell what changed. + SkCanvas referenceCanvas(*m_referenceBitmap); + referenceCanvas.drawBitmap(bitmapForContext(*m_drawContext), 0, 0); + // Layer rect represents the part of the original layer. +} + +void TransparencyWin::setupLayerForTextComposite() +{ + ASSERT(m_transformMode == KeepTransform); + // Fall through to filling with white. + setupLayerForWhiteLayer(); +} + +void TransparencyWin::setupLayerForWhiteLayer() +{ + initializeNewContext(); + m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white); + // Layer rect represents the part of the original layer. +} + +void TransparencyWin::setupTransform(const IntRect& region) +{ + switch (m_transformMode) { + case KeepTransform: + setupTransformForKeepTransform(region); + break; + case Untransform: + setupTransformForUntransform(); + break; + case ScaleTransform: + setupTransformForScaleTransform(); + break; + } +} + +void TransparencyWin::setupTransformForKeepTransform(const IntRect& region) +{ + if (m_layerMode != NoLayer) { + // Need to save things since we're modifying the transform. + m_drawContext->save(); + m_savedOnDrawContext = true; + + // Account for the fact that the layer may be offset from the + // original. This only happens when we create a layer that has the + // same coordinate space as the parent. + TransformationMatrix xform; + xform.translate(-m_transformedSourceRect.x(), -m_transformedSourceRect.y()); + + // We're making a layer, so apply the old transform to the new one + // so it's maintained. We know the new layer has the identity + // transform now, we we can just multiply it. + xform = m_orgTransform * xform; + m_drawContext->concatCTM(xform); + } + m_drawRect = m_sourceRect; +} + +void TransparencyWin::setupTransformForUntransform() +{ + ASSERT(m_layerMode != NoLayer); + // We now have a new layer with the identity transform, which is the + // Untransformed space we'll use for drawing. + m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); +} + +void TransparencyWin::setupTransformForScaleTransform() +{ + if (m_layerMode == NoLayer) { + // Need to save things since we're modifying the layer. + m_drawContext->save(); + m_savedOnDrawContext = true; + + // Undo the transform on the current layer when we're re-using the + // current one. + m_drawContext->concatCTM(m_drawContext->getCTM().inverse()); + + // We're drawing to the original layer with just a different size. + m_drawRect = m_transformedSourceRect; + } else { + // Just go ahead and use the layer's coordinate space to draw into. + // It will have the scaled size, and an identity transform loaded. + m_drawRect = IntRect(IntPoint(0, 0), m_layerSize); + } +} + +void TransparencyWin::setTextCompositeColor(Color color) +{ + m_textCompositeColor = color; +} + +void TransparencyWin::initializeNewContext() +{ + int pixelSize = m_layerSize.width() * m_layerSize.height(); + if (pixelSize > maxCachedBufferPixelSize) { + // Create a 1-off buffer for drawing into. We only need the reference + // buffer if we're making an OpaqueCompositeLayer. + bool needReferenceBitmap = m_layerMode == OpaqueCompositeLayer; + m_ownedBuffers.set(new OwnedBuffers(m_layerSize, needReferenceBitmap)); + + m_layerBuffer = m_ownedBuffers->destBitmap(); + m_drawContext = m_layerBuffer->context(); + if (needReferenceBitmap) + m_referenceBitmap = m_ownedBuffers->referenceBitmap(); + return; + } + + if (m_cachedBuffers && m_cachedBuffers->canHandleSize(m_layerSize)) { + // We can re-use the existing buffer. We don't need to clear it since + // all layer modes will clear it in their initialization. + m_layerBuffer = m_cachedBuffers->destBitmap(); + m_drawContext = m_cachedBuffers->destBitmap()->context(); + bitmapForContext(*m_drawContext).eraseARGB(0, 0, 0, 0); + m_referenceBitmap = m_cachedBuffers->referenceBitmap(); + m_referenceBitmap->eraseARGB(0, 0, 0, 0); + return; + } + + // Create a new cached buffer. + if (m_cachedBuffers) + delete m_cachedBuffers; + m_cachedBuffers = new OwnedBuffers(m_layerSize, true); + + m_layerBuffer = m_cachedBuffers->destBitmap(); + m_drawContext = m_cachedBuffers->destBitmap()->context(); + m_referenceBitmap = m_cachedBuffers->referenceBitmap(); +} + +void TransparencyWin::compositeOpaqueComposite() +{ + SkCanvas* destCanvas = canvasForContext(*m_destContext); + destCanvas->save(); + + SkBitmap* bitmap = const_cast<SkBitmap*>( + &bitmapForContext(*m_layerBuffer->context())); + + // This function will be called for WhiteLayer as well, which we don't want + // to change. + if (m_layerMode == OpaqueCompositeLayer) { + // Fix up our bitmap, making it contain only the pixels which changed + // and transparent everywhere else. + SkAutoLockPixels sourceLock(*m_referenceBitmap); + SkAutoLockPixels lock(*bitmap); + for (int y = 0; y < bitmap->height(); y++) { + uint32_t* source = m_referenceBitmap->getAddr32(0, y); + uint32_t* dest = bitmap->getAddr32(0, y); + for (int x = 0; x < bitmap->width(); x++) { + // Clear out any pixels that were untouched. + if (dest[x] == source[x]) + dest[x] = 0; + else + dest[x] |= (0xFF << SK_A32_SHIFT); + } + } + } else + makeLayerOpaque(); + + SkRect destRect; + if (m_transformMode != Untransform) { + // We want to use Untransformed space. + // + // Note that we DON'T call m_layerBuffer->image() here. This actually + // makes a copy of the image, which is unnecessary and slow. Instead, we + // just draw the image from inside the destination context. + SkMatrix identity; + identity.reset(); + destCanvas->setMatrix(identity); + + destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom()); + } else + destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom()); + + SkPaint paint; + paint.setFilterBitmap(true); + paint.setAntiAlias(true); + + // Note that we need to specify the source layer subset, since the bitmap + // may have been cached and it could be larger than what we're using. + SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; + destCanvas->drawBitmapRect(*bitmap, &sourceRect, destRect, &paint); + destCanvas->restore(); +} + +void TransparencyWin::compositeTextComposite() +{ + const SkBitmap& bitmap = m_layerBuffer->context()->platformContext()->canvas()->getTopPlatformDevice().accessBitmap(true); + SkColor textColor = m_textCompositeColor.rgb(); + for (int y = 0; y < m_layerSize.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < m_layerSize.width(); x++) { + // The alpha is the average of the R, G, and B channels. + int alpha = (SkGetPackedR32(row[x]) + SkGetPackedG32(row[x]) + SkGetPackedB32(row[x])) / 3; + + // Apply that alpha to the text color and write the result. + row[x] = SkAlphaMulQ(textColor, SkAlpha255To256(255 - alpha)); + } + } + + // Now the layer has text with the proper color and opacity. + SkCanvas* destCanvas = canvasForContext(*m_destContext); + + // We want to use Untransformed space (see above) + SkMatrix identity; + identity.reset(); + destCanvas->setMatrix(identity); + SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() }; + + // Note that we need to specify the source layer subset, since the bitmap + // may have been cached and it could be larger than what we're using. + SkIRect sourceRect = { 0, 0, m_layerSize.width(), m_layerSize.height() }; + destCanvas->drawBitmapRect(bitmap, &sourceRect, destRect, 0); + destCanvas->restore(); +} + +void TransparencyWin::makeLayerOpaque() +{ + SkBitmap& bitmap = const_cast<SkBitmap&>(m_drawContext->platformContext()-> + canvas()->getTopPlatformDevice().accessBitmap(true)); + for (int y = 0; y < m_layerSize.height(); y++) { + uint32_t* row = bitmap.getAddr32(0, y); + for (int x = 0; x < m_layerSize.width(); x++) + row[x] |= 0xFF000000; + } +} + +} // namespace WebCore + diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.h b/WebCore/platform/graphics/chromium/TransparencyWin.h new file mode 100644 index 0000000..e1963b3 --- /dev/null +++ b/WebCore/platform/graphics/chromium/TransparencyWin.h @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TransparencyWin_h +#define TransparencyWin_h + +#include <windows.h> + +#include "ImageBuffer.h" +#include "Noncopyable.h" +#include "TransformationMatrix.h" +#include "wtf/OwnPtr.h" + +class SkBitmap; +class SkCanvas; + +namespace WebCore { + +class GraphicsContext; +class TransparencyWin_NoLayer_Test; +class TransparencyWin_WhiteLayer_Test; +class TransparencyWin_TextComposite_Test; +class TransparencyWin_OpaqueCompositeLayer_Test; + +// Helper class that abstracts away drawing ClearType text and Windows form +// controls either to the original context directly, or to an offscreen context +// that is composited later manually. This is to get around Windows' inability +// to handle the alpha channel, semitransparent text, and transformed form +// controls. +class TransparencyWin : public Noncopyable { +public: + enum LayerMode { + // No extra layer is created. Drawing will happen to the source. + // Valid only with KeepTransform and ScaleTransform. The region being + // drawn onto must be opaque, since the modified region will be forced + // to opaque when drawing is complete. + NoLayer, + + // Makes a temporary layer consisting of the composited layers below + // it. This result must be opaque. When complete, the result will be + // compared to the original, and the difference will be added to a thee + // destination layer. + // + // This mode only works if the lower layers are opque (normally the + // case for a web page) and layers are only drawn in the stack order, + // meaning you can never draw underneath a layer. + // + // This doesn't technically produce the correct answer in all cases. If + // you have an opaque base, a transparency layer, than a semitransparent + // drawing on top, the result will actually be blended in twice. But + // this isn't a very important case. This mode is used for form + // controls which are always opaque except for occationally some + // antialiasing. It means form control antialiasing will be too light in + // some cases, but only if you have extra layers. + OpaqueCompositeLayer, + + // Allows semitransparent text to be drawn on any background (even if it + // is itself semitransparent), but disables ClearType. + // + // It makes a trmporary layer filled with white. This is composited with + // the lower layer with a custom color applied to produce the result. + // The caller must draw the text in black, and set the desired final + // text color by calling setTextCompositeColor(). + // + // Only valid with KeepTransform, which is the only mode where drawing + // text in this fashion makes sense. + TextComposite, + + // Makes a temporary layer filled with white. When complete, the layer + // will be forced to be opqaue (since Windows may have messed up the + // alpha channel) and composited down. Any areas not drawn into will + // remain white. + // + // This is the mode of last resort. If the opacity of the final image + // is unknown and we can't do the text trick (since we know its color), + // then we have to live with potential white halos. This is used for + // form control drawing, for example. + WhiteLayer, + }; + + enum TransformMode { + // There are no changes to the transform. Use this when drawing + // horizontal text. The current transform must not have rotation. + KeepTransform, + + // Drawing happens in an Untransformed space, and then that bitmap is + // transformed according to the current context when it is copied down. + // Requires that a layer be created (layer mode is not NoLayer). + Untransform, + + // When the current transform only has a scaling factor applied and + // you're drawing form elements, use this parameter. This will unscale + // the coordinate space, so the OS will just draw the form controls + // larger or smaller depending on the destination size. + ScaleTransform, + }; + + // You MUST call init() below. + // |region| is expressed relative to the current transformation. + TransparencyWin(); + ~TransparencyWin(); + + // Initializes the members if you use the 0-argument constructor. Don't call + // this if you use the multiple-argument constructor. + void init(GraphicsContext* dest, + LayerMode layerMode, + TransformMode transformMode, + const IntRect& region); + + // Combines the source and destination bitmaps using the given mode. + void composite(); + + // Returns the context for drawing into, which may be the destination + // context, or a temporary one. + GraphicsContext* context() const { return m_drawContext; } + + PlatformGraphicsContext* platformContext() const { return m_drawContext->platformContext(); } + + // When the mode is TextComposite, this sets the color that the text will + // get. See the enum above for more. + void setTextCompositeColor(Color color); + + // Returns the input bounds translated into the destination space. This is + // not necessary for KeepTransform since the rectangle will be unchanged. + const IntRect& drawRect() { return m_drawRect; } + +private: + friend TransparencyWin_NoLayer_Test; + friend TransparencyWin_WhiteLayer_Test; + friend TransparencyWin_TextComposite_Test; + friend TransparencyWin_OpaqueCompositeLayer_Test; + + class OwnedBuffers; + + void computeLayerSize(); + + // Sets up a new layer, if any. setupLayer() will call the appopriate layer- + // specific helper. Must be called after computeLayerSize(); + void setupLayer(); + void setupLayerForNoLayer(); + void setupLayerForOpaqueCompositeLayer(); + void setupLayerForTextComposite(); + void setupLayerForWhiteLayer(); + + // Sets up the transformation on the newly created layer. setupTransform() + // will call the appropriate transform-specific helper. Must be called after + // setupLayer(). + void setupTransform(const IntRect& region); + void setupTransformForKeepTransform(const IntRect& region); + void setupTransformForUntransform(); + void setupTransformForScaleTransform(); + + void initializeNewContext(); + + void compositeOpaqueComposite(); + void compositeTextComposite(); + + // Fixes the alpha channel to make the region inside m_transformedRect + // opaque. + void makeLayerOpaque(); + + // The context our drawing will eventually end up in. + GraphicsContext* m_destContext; + + // The original transform from the destination context. + TransformationMatrix m_orgTransform; + + LayerMode m_layerMode; + TransformMode m_transformMode; + + // The rectangle we're drawing in the destination's coordinate space + IntRect m_sourceRect; + + // The source rectangle transformed into pixels in the final image. For + // Untransform this has no meaning, since the destination might not be a + // rectangle. + IntRect m_transformedSourceRect; + + // The size of the layer we created. If there's no layer, this is the size + // of the region we're using in the source. + IntSize m_layerSize; + + // The rectangle we're drawing to in the draw context's coordinate space. + // This will be the same as the source rectangle except for ScaleTransform + // where we create a new virtual coordinate space for the layer. + IntRect m_drawRect; + + // Points to the graphics context to draw text to, which will either be + // the original context or the copy, depending on our mode. + GraphicsContext* m_drawContext; + + // This flag is set when we call save() on the draw context during + // initialization. It allows us to avoid doing an extra save()/restore() + // when one is unnecessary. + bool m_savedOnDrawContext; + + // Used only when m_mode = TextComposite, this is the color that the text + // will end up being once we figure out the transparency. + Color m_textCompositeColor; + + // Layer we're drawing to. + ImageBuffer* m_layerBuffer; + + // When the layer type is OpaqueCompositeLayer, this will contain a copy + // of the original contents of the m_layerBuffer before Windows drew on it. + // It allows us to re-create what Windows did to the layer. It is an + // SkBitmap instead of an ImageBuffer because an SkBitmap is lighter-weight + // (ImageBuffers are also GDI surfaces, which we don't need here). + SkBitmap* m_referenceBitmap; + + // If the given size of bitmap can be cached, they will be stored here. Both + // the bitmap and the reference are guaranteed to be allocated if this + // member is non-null. + static OwnedBuffers* m_cachedBuffers; + + // If a buffer was too big to be cached, it will be created temporarily, and + // this member tracks its scope to make sure it gets deleted. Always use + // m_layerBuffer, which will either point to this object, or the statically + // cached one. Don't access directly. + OwnPtr<OwnedBuffers> m_ownedBuffers; +}; + +} // namespace WebCore + +#endif // TransaprencyWin_h diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.cpp b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp index caeb959..39b0847 100644 --- a/WebCore/platform/graphics/chromium/UniscribeHelper.cpp +++ b/WebCore/platform/graphics/chromium/UniscribeHelper.cpp @@ -34,6 +34,9 @@ #include <windows.h> #include "FontUtilsChromiumWin.h" +#include "PlatformContextSkia.h" +#include "SkiaFontWin.h" +#include "SkPoint.h" #include <wtf/Assertions.h> namespace WebCore { @@ -58,7 +61,7 @@ static bool containsMissingGlyphs(WORD *glyphs, SCRIPT_FONTPROPERTIES* properties) { for (int i = 0; i < length; ++i) { - if (glyphs[i] == properties->wgDefault + if (glyphs[i] == properties->wgDefault || (glyphs[i] == properties->wgInvalid && glyphs[i] != properties->wgBlank)) return true; @@ -112,6 +115,8 @@ UniscribeHelper::UniscribeHelper(const UChar* input, , m_spaceWidth(0) , m_wordSpacing(0) , m_ascent(0) + , m_disableFontFallback(false) + { m_logfont.lfFaceName[0] = 0; } @@ -285,11 +290,13 @@ int UniscribeHelper::xToCharacter(int x) const return 0; } -void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) +void UniscribeHelper::draw(GraphicsContext* graphicsContext, + HDC dc, int x, int y, int from, int to) { HGDIOBJ oldFont = 0; int curX = x; bool firstRun = true; + bool useWindowsDrawing = windowsCanHandleTextDrawing(graphicsContext); for (size_t screenIndex = 0; screenIndex < m_runs.size(); screenIndex++) { int itemIndex = m_screenOrder[screenIndex]; @@ -360,7 +367,7 @@ void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) // Actually draw the glyphs we found. int glyphCount = afterGlyph - fromGlyph; if (fromGlyph >= 0 && glyphCount > 0) { - // Account for the preceeding space we need to add to this run. We + // Account for the preceding space we need to add to this run. We // don't need to count for the following space because that will be // counted in advanceForItem below when we move to the next run. innerOffset += shaping.m_prePadding; @@ -377,30 +384,44 @@ void UniscribeHelper::draw(HDC dc, int x, int y, int from, int to) // Fonts with different ascents can be used to render different // runs. 'Across-runs' y-coordinate correction needs to be // adjusted for each font. - HRESULT hr = S_FALSE; + bool textOutOk = false; for (int executions = 0; executions < 2; ++executions) { - hr = ScriptTextOut(dc, shaping.m_scriptCache, - curX + innerOffset, - y - shaping.m_ascentOffset, - 0, 0, &item.a, 0, 0, - &shaping.m_glyphs[fromGlyph], - glyphCount, - &shaping.m_advance[fromGlyph], - justify, - &shaping.m_offsets[fromGlyph]); - if (S_OK != hr && 0 == executions) { - // If this ScriptTextOut is called from the renderer it - // might fail because the sandbox is preventing it from - // opening the font files. If we are running in the - // renderer, TryToPreloadFont is overridden to ask the - // browser to preload the font for us so we can access it. + if (useWindowsDrawing) { + HRESULT hr = ScriptTextOut(dc, shaping.m_scriptCache, + curX + innerOffset, + y - shaping.m_ascentOffset, + 0, 0, &item.a, 0, 0, + &shaping.m_glyphs[fromGlyph], + glyphCount, + &shaping.m_advance[fromGlyph], + justify, + &shaping.m_offsets[fromGlyph]); + ASSERT(S_OK == hr); + textOutOk = (hr == S_OK); + } else { + SkPoint origin; + origin.fX = curX + + innerOffset; + origin.fY = y + m_ascent - shaping.m_ascentOffset; + textOutOk = paintSkiaText(graphicsContext, + shaping.m_hfont, + glyphCount, + &shaping.m_glyphs[fromGlyph], + &shaping.m_advance[fromGlyph], + &shaping.m_offsets[fromGlyph], + &origin); + } + + if (!textOutOk && 0 == executions) { + // If TextOut is called from the renderer it might fail + // because the sandbox is preventing it from opening the + // font files. If we are running in the renderer, + // TryToPreloadFont is overridden to ask the browser to + // preload the font for us so we can access it. tryToPreloadFont(shaping.m_hfont); continue; } break; } - - ASSERT(S_OK == hr); } curX += advanceForItem(itemIndex); @@ -527,7 +548,10 @@ bool UniscribeHelper::shape(const UChar* input, HDC tempDC = 0; HGDIOBJ oldFont = 0; HRESULT hr; - bool lastFallbackTried = false; + // When used to fill up glyph pages for simple scripts in non-BMP, + // we don't want any font fallback in this class. The simple script + // font path can take care of font fallback. + bool lastFallbackTried = m_disableFontFallback; bool result; int generatedGlyphs = 0; @@ -557,7 +581,7 @@ bool UniscribeHelper::shape(const UChar* input, // PurifyMarkAsInitialized( // &shaping.m_glyphs[0], // sizeof(shaping.m_glyphs[0] * generatedGlyphs); - + ZeroMemory(&shaping.m_glyphs[0], sizeof(shaping.m_glyphs[0]) * shaping.m_glyphs.size()); #endif @@ -587,7 +611,8 @@ bool UniscribeHelper::shape(const UChar* input, tempDC = 0; } - if (nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { + if (!m_disableFontFallback && + nextWinFontData(&hfont, &scriptCache, &fontProperties, &ascent)) { // The primary font does not support this run. Try next font. // In case of web page rendering, they come from fonts specified in // CSS stylesheets. @@ -702,6 +727,13 @@ void UniscribeHelper::fillShapes() if (!shape(&m_input[startItem], itemLength, numGlyphs, m_runs[i], shaping)) continue; + // At the moment, the only time m_disableFontFallback is set is + // when we look up glyph indices for non-BMP code ranges. So, + // we can skip the glyph placement. When that becomes not the case + // any more, we have to add a new flag to control glyph placement. + if (m_disableFontFallback) + continue; + // Compute placements. Note that offsets is documented incorrectly // and is actually an array. @@ -779,9 +811,6 @@ void UniscribeHelper::adjustSpaceAdvances() int glyphIndex = shaping.m_logs[i]; int currentAdvance = shaping.m_advance[glyphIndex]; - // Don't give zero-width spaces a width. - if (!currentAdvance) - continue; // currentAdvance does not include additional letter-spacing, but // space_width does. Here we find out how off we are from the diff --git a/WebCore/platform/graphics/chromium/UniscribeHelper.h b/WebCore/platform/graphics/chromium/UniscribeHelper.h index d291105..ffd57db 100644 --- a/WebCore/platform/graphics/chromium/UniscribeHelper.h +++ b/WebCore/platform/graphics/chromium/UniscribeHelper.h @@ -44,6 +44,8 @@ class UniscribeTest_TooBig_Test; // A gunit test for UniscribeHelper. namespace WebCore { +class GraphicsContext; + #define UNISCRIBE_HELPER_STACK_RUNS 8 #define UNISCRIBE_HELPER_STACK_CHARS 32 @@ -145,6 +147,16 @@ public: m_ascent = ascent; } + // When set to true, this class is used only to look up glyph + // indices for a range of Unicode characters without glyph placement. + // By default, it's false. This should be set to true when this + // class is used for glyph index look-up for non-BMP characters + // in GlyphPageNodeChromiumWin.cpp. + void setDisableFontFallback(bool disableFontFallback) + { + m_disableFontFallback = true; + } + // You must call this after setting any options but before doing any // other calls like asking for widths or drawing. void init() @@ -177,7 +189,8 @@ public: // be pre-set. // // The y position is the upper left corner, NOT the baseline. - void draw(HDC, int x, int y, int from, int to); + void draw(GraphicsContext* graphicsContext, HDC dc, int x, int y, int from, + int to); // Returns the first glyph assigned to the character at the given offset. // This function is used to retrieve glyph information when Uniscribe is @@ -378,6 +391,7 @@ private: int m_letterSpacing; int m_spaceWidth; int m_wordSpacing; + bool m_disableFontFallback; // Uniscribe breaks the text into Runs. These are one length of text that is // in one script and one direction. This array is in reading order. diff --git a/WebCore/platform/graphics/gtk/FontPlatformData.h b/WebCore/platform/graphics/gtk/FontPlatformData.h index efa5dd5..20c52e5 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformData.h +++ b/WebCore/platform/graphics/gtk/FontPlatformData.h @@ -74,6 +74,7 @@ public: FontPlatformData(float size, bool bold, bool italic); FontPlatformData(cairo_font_face_t* fontFace, int size, bool bold, bool italic); + FontPlatformData(const FontPlatformData&); ~FontPlatformData(); @@ -95,6 +96,7 @@ public: } bool operator==(const FontPlatformData&) const; + FontPlatformData& operator=(const FontPlatformData&); bool isHashTableDeletedValue() const { #if defined(USE_FREETYPE) return m_pattern == hashTableDeletedFontValue(); diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp index 17d789b..68685e9 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataGtk.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2007 Holger Hans Peter Freyther + * Copyright (C) 2009 Igalia S.L. * All rights reserved. * * This library is free software; you can redistribute it and/or @@ -161,6 +162,45 @@ FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, int size, bool b m_scaledFont = cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options); } +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) +{ + // Check for self-assignment. + if (this == &other) + return *this; + + m_size = other.m_size; + m_syntheticBold = other.m_syntheticBold; + m_syntheticOblique = other.m_syntheticOblique; + + if (other.m_scaledFont) + cairo_scaled_font_reference(other.m_scaledFont); + if (m_scaledFont) + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = other.m_scaledFont; + + if (other.m_pattern) + FcPatternReference(other.m_pattern); + if (m_pattern) + FcPatternDestroy(m_pattern); + m_pattern = other.m_pattern; + + if (m_fallbacks) { + FcFontSetDestroy(m_fallbacks); + // This will be re-created on demand. + m_fallbacks = 0; + } + + return *this; +} + +FontPlatformData::FontPlatformData(const FontPlatformData& other) + : m_pattern(0) + , m_fallbacks(0) + , m_scaledFont(0) +{ + *this = other; +} + bool FontPlatformData::init() { static bool initialized = false; @@ -176,6 +216,20 @@ bool FontPlatformData::init() FontPlatformData::~FontPlatformData() { + if (m_pattern && ((FcPattern*)-1 != m_pattern)) { + FcPatternDestroy(m_pattern); + m_pattern = 0; + } + + if (m_fallbacks) { + FcFontSetDestroy(m_fallbacks); + m_fallbacks = 0; + } + + if (m_scaledFont) { + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = 0; + } } bool FontPlatformData::isFixedPitch() diff --git a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp index be3fd43..88bf427 100644 --- a/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp +++ b/WebCore/platform/graphics/gtk/FontPlatformDataPango.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2007, 2008 Alp Toker <alp@atoker.com> * Copyright (C) 2007 Holger Hans Peter Freyther * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * Copyright (C) 2009 Igalia S.L. * All rights reserved. * * This library is free software; you can redistribute it and/or @@ -46,8 +47,8 @@ namespace WebCore { - PangoFontMap* FontPlatformData::m_fontMap = 0; - GHashTable* FontPlatformData::m_hashTable = 0; +PangoFontMap* FontPlatformData::m_fontMap = 0; +GHashTable* FontPlatformData::m_hashTable = 0; FontPlatformData::FontPlatformData(const FontDescription& fontDescription, const AtomicString& familyName) : m_context(0) @@ -193,7 +194,20 @@ bool FontPlatformData::init() FontPlatformData::~FontPlatformData() { - // Destroy takes place in FontData::platformDestroy(). + if (m_font && m_font != reinterpret_cast<PangoFont*>(-1)) { + g_object_unref(m_font); + m_font = 0; + } + + if (m_context) { + g_object_unref(m_context); + m_context = 0; + } + + if (m_scaledFont) { + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = 0; + } } bool FontPlatformData::isFixedPitch() @@ -211,6 +225,45 @@ void FontPlatformData::setFont(cairo_t* cr) const cairo_set_scaled_font(cr, m_scaledFont); } +FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) +{ + // Check for self-assignment. + if (this == &other) + return *this; + + m_size = other.m_size; + m_syntheticBold = other.m_syntheticBold; + m_syntheticOblique = other.m_syntheticOblique; + + if (other.m_scaledFont) + cairo_scaled_font_reference(other.m_scaledFont); + if (m_scaledFont) + cairo_scaled_font_destroy(m_scaledFont); + m_scaledFont = other.m_scaledFont; + + if (other.m_font) + g_object_ref(other.m_font); + if (m_font) + g_object_unref(m_font); + m_font = other.m_font; + + if (other.m_context) + g_object_ref(other.m_context); + if (m_context) + g_object_unref(m_context); + m_context = other.m_context; + + return *this; +} + +FontPlatformData::FontPlatformData(const FontPlatformData& other) + : m_context(0) + , m_font(0) + , m_scaledFont(0) +{ + *this = other; +} + bool FontPlatformData::operator==(const FontPlatformData& other) const { if (m_font == other.m_font) diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp index 1f0cac6..8dd1333 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * @@ -101,6 +101,22 @@ gboolean mediaPlayerPrivateBufferingCallback(GstBus* bus, GstMessage* message, g return true; } +static void mediaPlayerPrivateRepaintCallback(WebKitVideoSink*, MediaPlayerPrivate* playerPrivate) +{ + playerPrivate->repaint(); +} + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_playBin(0) @@ -111,10 +127,10 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) , m_isEndReached(false) , m_volume(0.5f) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) , m_startedPlaying(false) , m_isStreaming(false) - , m_rect(IntRect()) + , m_size(IntSize()) , m_visible(true) { @@ -140,15 +156,15 @@ MediaPlayerPrivate::~MediaPlayerPrivate() } } -void MediaPlayerPrivate::load(String url) +void MediaPlayerPrivate::load(const String& url) { LOG_VERBOSE(Media, "Load %s", url.utf8().data()); if (m_networkState != MediaPlayer::Loading) { m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } @@ -175,24 +191,28 @@ void MediaPlayerPrivate::pause() m_startedPlaying = false; } -float MediaPlayerPrivate::duration() +float MediaPlayerPrivate::duration() const { if (!m_playBin) return 0.0; - GstFormat fmt = GST_FORMAT_TIME; - gint64 len = 0; - - if (gst_element_query_duration(m_playBin, &fmt, &len)) - LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(len)); - else - LOG_VERBOSE(Media, "Duration query failed "); + GstFormat timeFormat = GST_FORMAT_TIME; + gint64 timeLength = 0; - if ((GstClockTime)len == GST_CLOCK_TIME_NONE) { + // FIXME: We try to get the duration, but we do not trust the + // return value of the query function only; the problem we are + // trying to work-around here is that pipelines in stream mode may + // not be able to figure out the duration, but still return true! + // See https://bugs.webkit.org/show_bug.cgi?id=24639. + if (!gst_element_query_duration(m_playBin, &timeFormat, &timeLength) || timeLength <= 0) { + LOG_VERBOSE(Media, "Time duration query failed."); m_isStreaming = true; return numeric_limits<float>::infinity(); } - return (float) (len / 1000000000.0); + + LOG_VERBOSE(Media, "Duration: %" GST_TIME_FORMAT, GST_TIME_ARGS(timeLength)); + + return (float) (timeLength / 1000000000.0); // FIXME: handle 3.14.9.5 properly } @@ -288,7 +308,7 @@ bool MediaPlayerPrivate::seeking() const } // Returns the size of the video -IntSize MediaPlayerPrivate::naturalSize() +IntSize MediaPlayerPrivate::naturalSize() const { if (!hasVideo()) return IntSize(); @@ -302,7 +322,7 @@ IntSize MediaPlayerPrivate::naturalSize() return IntSize(x, y); } -bool MediaPlayerPrivate::hasVideo() +bool MediaPlayerPrivate::hasVideo() const { gint currentVideo = -1; if (m_playBin) @@ -355,17 +375,17 @@ int MediaPlayerPrivate::dataRate() const return 1; } -MediaPlayer::NetworkState MediaPlayerPrivate::networkState() +MediaPlayer::NetworkState MediaPlayerPrivate::networkState() const { return m_networkState; } -MediaPlayer::ReadyState MediaPlayerPrivate::readyState() +MediaPlayer::ReadyState MediaPlayerPrivate::readyState() const { return m_readyState; } -float MediaPlayerPrivate::maxTimeBuffered() +float MediaPlayerPrivate::maxTimeBuffered() const { notImplemented(); LOG_VERBOSE(Media, "maxTimeBuffered"); @@ -373,7 +393,7 @@ float MediaPlayerPrivate::maxTimeBuffered() return m_isStreaming ? 0 : maxTimeLoaded(); } -float MediaPlayerPrivate::maxTimeSeekable() +float MediaPlayerPrivate::maxTimeSeekable() const { // TODO LOG_VERBOSE(Media, "maxTimeSeekable"); @@ -383,7 +403,7 @@ float MediaPlayerPrivate::maxTimeSeekable() return maxTimeLoaded(); } -float MediaPlayerPrivate::maxTimeLoaded() +float MediaPlayerPrivate::maxTimeLoaded() const { // TODO LOG_VERBOSE(Media, "maxTimeLoaded"); @@ -391,7 +411,7 @@ float MediaPlayerPrivate::maxTimeLoaded() return duration(); } -unsigned MediaPlayerPrivate::bytesLoaded() +unsigned MediaPlayerPrivate::bytesLoaded() const { notImplemented(); LOG_VERBOSE(Media, "bytesLoaded"); @@ -404,14 +424,14 @@ unsigned MediaPlayerPrivate::bytesLoaded() return 1;//totalBytes() * maxTime / dur; } -bool MediaPlayerPrivate::totalBytesKnown() +bool MediaPlayerPrivate::totalBytesKnown() const { notImplemented(); LOG_VERBOSE(Media, "totalBytesKnown"); return totalBytes() > 0; } -unsigned MediaPlayerPrivate::totalBytes() +unsigned MediaPlayerPrivate::totalBytes() const { notImplemented(); LOG_VERBOSE(Media, "totalBytes"); @@ -455,12 +475,11 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(pending)); if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } else if (state == GST_STATE_PAUSED) { - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } - if (m_networkState < MediaPlayer::Loaded) - m_networkState = MediaPlayer::Loaded; + m_networkState = MediaPlayer::Loaded; g_object_get(m_playBin, "source", &m_source, NULL); if (!m_source) @@ -478,12 +497,11 @@ void MediaPlayerPrivate::updateStates() gst_element_state_get_name(state), gst_element_state_get_name(pending)); if (state == GST_STATE_READY) { - m_readyState = MediaPlayer::CanPlay; + m_readyState = MediaPlayer::HaveFutureData; } else if (state == GST_STATE_PAUSED) { - m_readyState = MediaPlayer::CanPlay; + m_readyState = MediaPlayer::HaveCurrentData; } - if (m_networkState < MediaPlayer::LoadedMetaData) - m_networkState = MediaPlayer::LoadedMetaData; + m_networkState = MediaPlayer::Loading; break; default: LOG_VERBOSE(Media, "Else : %d", ret); @@ -491,7 +509,7 @@ void MediaPlayerPrivate::updateStates() } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; if (m_networkState != oldNetworkState) { LOG_VERBOSE(Media, "Network State Changed from %u to %u", @@ -540,19 +558,19 @@ void MediaPlayerPrivate::didEnd() void MediaPlayerPrivate::loadingFailed() { - if (m_networkState != MediaPlayer::LoadFailed) { - m_networkState = MediaPlayer::LoadFailed; + if (m_networkState != MediaPlayer::NetworkError) { + m_networkState = MediaPlayer::NetworkError; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } } -void MediaPlayerPrivate::setRect(const IntRect& rect) +void MediaPlayerPrivate::setSize(const IntSize& size) { - m_rect = rect; + m_size = size; } void MediaPlayerPrivate::setVisible(bool visible) @@ -573,7 +591,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) if (!m_visible) return; - //TODO: m_rect vs rect? + //TODO: m_size vs rect? cairo_t* cr = context->platformContext(); cairo_save(cr); @@ -587,11 +605,18 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& rect) void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) { - // FIXME: do the real thing + // FIXME: query the engine to see what types are supported notImplemented(); types.add(String("video/x-theora+ogg")); } +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // FIXME: query the engine to see what types are supported + notImplemented(); + return type == "video/x-theora+ogg" ? (!codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + void MediaPlayerPrivate::createGSTPlayBin(String url) { ASSERT(!m_playBin); @@ -613,6 +638,8 @@ void MediaPlayerPrivate::createGSTPlayBin(String url) g_object_set(m_playBin, "audio-sink", audioSink, NULL); g_object_set(m_playBin, "video-sink", m_videoSink, NULL); + g_signal_connect(m_videoSink, "repaint-requested", G_CALLBACK(mediaPlayerPrivateRepaintCallback), this); + setVolume(m_volume); } diff --git a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h index 3f08bc0..628f0ac 100644 --- a/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h +++ b/WebCore/platform/graphics/gtk/MediaPlayerPrivateGStreamer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Collabora Ltd. All rights reserved. * Copyright (C) 2007 Alp Toker <alp@atoker.com> * @@ -24,7 +24,7 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" #include "Timer.h" #include <gtk/gtk.h> @@ -44,20 +44,21 @@ namespace WebCore { gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); - class MediaPlayerPrivate : Noncopyable + class MediaPlayerPrivate : public MediaPlayerPrivateInterface { friend gboolean mediaPlayerPrivateErrorCallback(GstBus* bus, GstMessage* message, gpointer data); friend gboolean mediaPlayerPrivateEOSCallback(GstBus* bus, GstMessage* message, gpointer data); friend gboolean mediaPlayerPrivateStateCallback(GstBus* bus, GstMessage* message, gpointer data); public: - MediaPlayerPrivate(MediaPlayer*); + + static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); - IntSize naturalSize(); - bool hasVideo(); + IntSize naturalSize() const; + bool hasVideo() const; - void load(String url); + void load(const String &url); void cancelLoad(); void play(); @@ -66,7 +67,7 @@ namespace WebCore { bool paused() const; bool seeking() const; - float duration(); + float duration() const; float currentTime() const; void seek(float); void setEndTime(float); @@ -77,17 +78,17 @@ namespace WebCore { int dataRate() const; - MediaPlayer::NetworkState networkState(); - MediaPlayer::ReadyState readyState(); + MediaPlayer::NetworkState networkState() const; + MediaPlayer::ReadyState readyState() const; - float maxTimeBuffered(); - float maxTimeSeekable(); - unsigned bytesLoaded(); - bool totalBytesKnown(); - unsigned totalBytes(); + float maxTimeBuffered() const; + float maxTimeSeekable() const; + unsigned bytesLoaded() const; + bool totalBytesKnown() const; + unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void loadStateChanged(); void rateChanged(); @@ -99,15 +100,20 @@ namespace WebCore { void repaint(); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>&); - static bool isAvailable() { return true; } private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable() { return true; } + void updateStates(); void cancelSeek(); void endPointTimerFired(Timer<MediaPlayerPrivate>*); - float maxTimeLoaded(); + float maxTimeLoaded() const; void startEndPointTimerIfNeeded(); void createGSTPlayBin(String url); @@ -124,8 +130,8 @@ namespace WebCore { MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; - bool m_isStreaming; - IntRect m_rect; + mutable bool m_isStreaming; + IntSize m_size; bool m_visible; cairo_surface_t* m_surface; }; diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp index d076cb6..4203a3c 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataGtk.cpp @@ -53,6 +53,13 @@ void SimpleFontData::platformInit() m_ascent = static_cast<int>(font_extents.ascent); m_descent = static_cast<int>(font_extents.descent); m_lineSpacing = static_cast<int>(font_extents.height); + // There seems to be some rounding error in cairo (or in how we + // use cairo) with some fonts, like DejaVu Sans Mono, which makes + // cairo report a height smaller than ascent + descent, which is + // wrong and confuses WebCore's layout system. Workaround this + // while we figure out what's going on. + if (m_lineSpacing < m_ascent + m_descent) + m_lineSpacing = m_ascent + m_descent; cairo_scaled_font_text_extents(m_font.m_scaledFont, "x", &text_extents); m_xHeight = text_extents.height; cairo_scaled_font_text_extents(m_font.m_scaledFont, " ", &text_extents); @@ -64,31 +71,13 @@ void SimpleFontData::platformDestroy() { delete m_smallCapsFontData; m_smallCapsFontData = 0; - - if (isCustomFont()) - return; - - if (m_font.m_pattern && ((FcPattern*)-1 != m_font.m_pattern)) { - FcPatternDestroy(m_font.m_pattern); - m_font.m_pattern = 0; - } - - if (m_font.m_fallbacks) { - FcFontSetDestroy(m_font.m_fallbacks); - m_font.m_fallbacks = 0; - } - - if (m_font.m_scaledFont) { - cairo_scaled_font_destroy(m_font.m_scaledFont); - m_font.m_scaledFont = 0; - } } SimpleFontData* SimpleFontData::smallCapsFontData(const FontDescription& fontDescription) const { if (!m_smallCapsFontData) { FontDescription desc = FontDescription(fontDescription); - desc.setSpecifiedSize(0.70f*fontDescription.computedSize()); + desc.setComputedSize(0.70f*fontDescription.computedSize()); const FontPlatformData* pdata = new FontPlatformData(desc, desc.family().family()); m_smallCapsFontData = new SimpleFontData(*pdata); } diff --git a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp index db8dd3b..e345a8c 100644 --- a/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp +++ b/WebCore/platform/graphics/gtk/SimpleFontDataPango.cpp @@ -52,6 +52,13 @@ void SimpleFontData::platformInit() m_ascent = static_cast<int>(font_extents.ascent); m_descent = static_cast<int>(font_extents.descent); m_lineSpacing = static_cast<int>(font_extents.height); + // There seems to be some rounding error in cairo (or in how we + // use cairo) with some fonts, like DejaVu Sans Mono, which makes + // cairo report a height smaller than ascent + descent, which is + // wrong and confuses WebCore's layout system. Workaround this + // while we figure out what's going on. + if (m_lineSpacing < m_ascent + m_descent) + m_lineSpacing = m_ascent + m_descent; cairo_scaled_font_text_extents(m_font.m_scaledFont, "x", &text_extents); m_xHeight = text_extents.height; cairo_scaled_font_text_extents(m_font.m_scaledFont, " ", &text_extents); @@ -61,24 +68,6 @@ void SimpleFontData::platformInit() void SimpleFontData::platformDestroy() { - if (!isCustomFont()) { - - if (m_font.m_font && m_font.m_font != reinterpret_cast<PangoFont*>(-1)) { - g_object_unref(m_font.m_font); - m_font.m_font = 0; - } - - if (m_font.m_context) { - g_object_unref (m_font.m_context); - m_font.m_context = 0; - } - - if (m_font.m_scaledFont) { - cairo_scaled_font_destroy(m_font.m_scaledFont); - m_font.m_scaledFont = 0; - } - } - delete m_smallCapsFontData; m_smallCapsFontData = 0; } diff --git a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp index 04df7ac..436841c 100644 --- a/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp +++ b/WebCore/platform/graphics/gtk/VideoSinkGStreamer.cpp @@ -46,10 +46,17 @@ static GstElementDetails webkit_video_sink_details = (gchar*) "Alp Toker <alp@atoker.com>"); enum { + REPAINT_REQUESTED, + LAST_SIGNAL +}; + +enum { PROP_0, PROP_SURFACE }; +static guint webkit_video_sink_signals[LAST_SIGNAL] = { 0, }; + struct _WebKitVideoSinkPrivate { cairo_surface_t* surface; GAsyncQueue* async_queue; @@ -95,11 +102,10 @@ webkit_video_sink_init(WebKitVideoSink* sink, WebKitVideoSinkClass* klass) static gboolean webkit_video_sink_idle_func(gpointer data) { - WebKitVideoSinkPrivate* priv; + WebKitVideoSink* sink = WEBKIT_VIDEO_SINK(data); + WebKitVideoSinkPrivate* priv = sink->priv; GstBuffer* buffer; - priv = (WebKitVideoSinkPrivate*)data; - if (!priv->async_queue) return FALSE; @@ -121,6 +127,8 @@ webkit_video_sink_idle_func(gpointer data) gst_buffer_unref(buffer); + g_signal_emit(sink, webkit_video_sink_signals[REPAINT_REQUESTED], 0); + return FALSE; } @@ -131,7 +139,7 @@ webkit_video_sink_render(GstBaseSink* bsink, GstBuffer* buffer) WebKitVideoSinkPrivate* priv = sink->priv; g_async_queue_push(priv->async_queue, gst_buffer_ref(buffer)); - g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, priv, NULL); + g_idle_add_full(G_PRIORITY_HIGH_IDLE, webkit_video_sink_idle_func, sink, NULL); return GST_FLOW_OK; } @@ -279,6 +287,15 @@ webkit_video_sink_class_init(WebKitVideoSinkClass* klass) gstbase_sink_class->stop = webkit_video_sink_stop; gstbase_sink_class->set_caps = webkit_video_sink_set_caps; + webkit_video_sink_signals[REPAINT_REQUESTED] = g_signal_new("repaint-requested", + G_TYPE_FROM_CLASS(klass), + (GSignalFlags)(G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION), + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + g_object_class_install_property( gobject_class, PROP_SURFACE, g_param_spec_pointer("surface", "surface", "Target cairo_surface_t*", diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index 9b0f770..1c4350c 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -27,6 +27,7 @@ #import "Color.h" #import "ColorMac.h" +#import <AppKit/AppKit.h> #import <wtf/Assertions.h> #import <wtf/StdLibExtras.h> #import <wtf/RetainPtr.h> @@ -110,7 +111,7 @@ static CGColorRef CGColorFromNSColor(NSColor* color) return cgColor; } -CGColorRef cgColor(const Color& c) +CGColorRef createCGColor(const Color& c) { // We could directly create a CGColor here, but that would // skip any RGB caching the nsColor method does. A direct diff --git a/WebCore/platform/graphics/mac/FontCacheMac.mm b/WebCore/platform/graphics/mac/FontCacheMac.mm index 26d84cc..2202459 100644 --- a/WebCore/platform/graphics/mac/FontCacheMac.mm +++ b/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -35,7 +35,8 @@ #import "FontPlatformData.h" #import "WebCoreSystemInterface.h" #import "WebFontCache.h" -#include <wtf/StdLibExtras.h> +#import <AppKit/AppKit.h> +#import <wtf/StdLibExtras.h> #ifdef BUILDING_ON_TIGER typedef int NSInteger; @@ -191,13 +192,11 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD actualTraits = [fontManager traitsOfFont:nsFont]; NSInteger actualWeight = [fontManager weightOfFont:nsFont]; - FontPlatformData* result = new FontPlatformData; + NSFont *platformFont = fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]; + bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); + bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); - // Use the correct font for print vs. screen. - result->setFont(fontDescription.usePrinterFont() ? [nsFont printerFont] : [nsFont screenFont]); - result->m_syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); - result->m_syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); - return result; + return new FontPlatformData(platformFont, syntheticBold, syntheticOblique); } } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp index 9aa4997..e40bbab 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -29,7 +29,8 @@ namespace WebCore { FontCustomPlatformData::~FontCustomPlatformData() { - ATSFontDeactivate(m_atsContainer, NULL, kATSOptionFlagsDefault); + if (m_atsContainer) + ATSFontDeactivate(m_atsContainer, NULL, kATSOptionFlagsDefault); CGFontRelease(m_cgFont); } @@ -42,8 +43,18 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) { ASSERT_ARG(buffer, buffer); - // Use ATS to activate the font. ATSFontContainerRef containerRef = 0; + ATSFontRef fontRef = 0; + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + RetainPtr<CFDataRef> bufferData(AdoptCF, buffer->createCFData()); + RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(bufferData.get())); + + CGFontRef cgFontRef = CGFontCreateWithDataProvider(dataProvider.get()); + if (!cgFontRef) + return 0; +#else + // Use ATS to activate the font. // The value "3" means that the font is private and can't be seen by anyone else. ATSFontActivateFromMemory((void*)buffer->data(), buffer->size(), 3, kATSFontFormatUnspecified, NULL, kATSOptionFlagsDefault, &containerRef); @@ -58,7 +69,6 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) return 0; } - ATSFontRef fontRef = 0; ATSFontFindFromContainer(containerRef, kATSOptionFlagsDefault, 1, &fontRef, NULL); if (!fontRef) { ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); @@ -77,6 +87,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) ATSFontDeactivate(containerRef, NULL, kATSOptionFlagsDefault); return 0; } +#endif // !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) return new FontCustomPlatformData(containerRef, fontRef, cgFontRef); } diff --git a/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/WebCore/platform/graphics/mac/FontCustomPlatformData.h index 1e73ae0..2c1222f 100644 --- a/WebCore/platform/graphics/mac/FontCustomPlatformData.h +++ b/WebCore/platform/graphics/mac/FontCustomPlatformData.h @@ -22,6 +22,7 @@ #define FontCustomPlatformData_h #include "FontRenderingMode.h" +#include <CoreFoundation/CFBase.h> #include <wtf/Noncopyable.h> typedef struct CGFont* CGFontRef; diff --git a/WebCore/platform/graphics/mac/FontMac.mm b/WebCore/platform/graphics/mac/FontMac.mm index bef18d0..dc86c4b 100644 --- a/WebCore/platform/graphics/mac/FontMac.mm +++ b/WebCore/platform/graphics/mac/FontMac.mm @@ -29,6 +29,7 @@ #import "SimpleFontData.h" #import "WebCoreSystemInterface.h" #import "WebCoreTextRenderer.h" +#import <AppKit/AppKit.h> #define SYNTHETIC_OBLIQUE_ANGLE 14 diff --git a/WebCore/platform/graphics/mac/FontMacATSUI.mm b/WebCore/platform/graphics/mac/FontMacATSUI.mm index 52493e7..3794149 100644 --- a/WebCore/platform/graphics/mac/FontMacATSUI.mm +++ b/WebCore/platform/graphics/mac/FontMacATSUI.mm @@ -30,6 +30,7 @@ #import "Logging.h" #import "ShapeArabic.h" #import "SimpleFontData.h" +#import <AppKit/NSGraphicsContext.h> #import <wtf/OwnArrayPtr.h> #define SYNTHETIC_OBLIQUE_ANGLE 14 diff --git a/WebCore/platform/graphics/mac/FontPlatformData.h b/WebCore/platform/graphics/mac/FontPlatformData.h index 40a2dbd..e911867 100644 --- a/WebCore/platform/graphics/mac/FontPlatformData.h +++ b/WebCore/platform/graphics/mac/FontPlatformData.h @@ -1,8 +1,8 @@ /* * This file is part of the internal font implementation. - * It should not be included by source files outside it. + * It should not be included by source files outside of it. * - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -33,7 +33,6 @@ class NSFont; #endif typedef struct CGFont* CGFontRef; -typedef UInt32 ATSUFontID; #ifndef BUILDING_ON_TIGER typedef const struct __CTFont* CTFontRef; #endif @@ -42,6 +41,8 @@ typedef const struct __CTFont* CTFontRef; #include <objc/objc-auto.h> #include <wtf/RetainPtr.h> +typedef UInt32 ATSUFontID; + namespace WebCore { #ifndef BUILDING_ON_TIGER @@ -61,10 +62,15 @@ struct FontPlatformData { { } - FontPlatformData(NSFont * = 0, bool syntheticBold = false, bool syntheticOblique = false); + FontPlatformData(NSFont *nsFont, bool syntheticBold = false, bool syntheticOblique = false); - FontPlatformData(CGFontRef f, ATSUFontID fontID, float s, bool b , bool o) - : m_syntheticBold(b), m_syntheticOblique(o), m_atsuFontID(fontID), m_size(s), m_font(0), m_cgFont(f) + FontPlatformData(CGFontRef cgFont, ATSUFontID fontID, float size, bool syntheticBold, bool syntheticOblique) + : m_syntheticBold(syntheticBold) + , m_syntheticOblique(syntheticOblique) + , m_atsuFontID(fontID) + , m_size(size) + , m_font(0) + , m_cgFont(cgFont) { } diff --git a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm index 7cd9ab6..83da4a9 100644 --- a/WebCore/platform/graphics/mac/FontPlatformDataMac.mm +++ b/WebCore/platform/graphics/mac/FontPlatformDataMac.mm @@ -1,7 +1,7 @@ /* * This file is part of the internal font implementation. * - * Copyright (C) 2006-7 Apple Computer, Inc. + * Copyright (C) 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,21 +24,24 @@ #import "FontPlatformData.h" #import "WebCoreSystemInterface.h" +#import <AppKit/NSFont.h> namespace WebCore { -FontPlatformData::FontPlatformData(NSFont *f, bool b , bool o) -: m_syntheticBold(b), m_syntheticOblique(o), m_font(f) +FontPlatformData::FontPlatformData(NSFont *nsFont, bool syntheticBold, bool syntheticOblique) + : m_syntheticBold(syntheticBold) + , m_syntheticOblique(syntheticOblique) + , m_font(nsFont) { - if (f) - CFRetain(f); - m_size = f ? [f pointSize] : 0.0f; + if (nsFont) + CFRetain(nsFont); + m_size = nsFont ? [nsFont pointSize] : 0.0f; #ifndef BUILDING_ON_TIGER - m_cgFont.adoptCF(CTFontCopyGraphicsFont(toCTFontRef(f), 0)); - m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(f), 0); + m_cgFont.adoptCF(CTFontCopyGraphicsFont(toCTFontRef(nsFont), 0)); + m_atsuFontID = CTFontGetPlatformFont(toCTFontRef(nsFont), 0); #else - m_cgFont = wkGetCGFontFromNSFont(f); - m_atsuFontID = wkGetNSFontATSUFontId(f); + m_cgFont = wkGetCGFontFromNSFont(nsFont); + m_atsuFontID = wkGetNSFontATSUFontId(nsFont); #endif } diff --git a/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/WebCore/platform/graphics/mac/GraphicsContextMac.mm index ae829e2..4e11602 100644 --- a/WebCore/platform/graphics/mac/GraphicsContextMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -27,10 +27,13 @@ #import "GraphicsContext.h" #import "../cg/GraphicsContextPlatformPrivateCG.h" +#import <AppKit/AppKit.h> #import <wtf/StdLibExtras.h> #import "WebCoreSystemInterface.h" +@class NSColor; + // FIXME: More of this should use CoreGraphics instead of AppKit. // FIXME: More of this should move into GraphicsContextCG.cpp. @@ -47,7 +50,7 @@ void GraphicsContext::drawFocusRing(const Color& color) int radius = (focusRingWidth() - 1) / 2; int offset = radius + focusRingOffset(); - CGColorRef colorRef = color.isValid() ? cgColor(color) : 0; + CGColorRef colorRef = color.isValid() ? createCGColor(color) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); const Vector<IntRect>& rects = focusRingRects(); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.h b/WebCore/platform/graphics/mac/GraphicsLayerCA.h new file mode 100644 index 0000000..3a692d3 --- /dev/null +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef GraphicsLayerCA_h +#define GraphicsLayerCA_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "GraphicsLayer.h" +#include <wtf/RetainPtr.h> + +@class WebAnimationDelegate; +@class WebLayer; + +namespace WebCore { + +class GraphicsLayerCA : public GraphicsLayer { +public: + + GraphicsLayerCA(GraphicsLayerClient*); + virtual ~GraphicsLayerCA(); + + virtual void setName(const String&); + + // for hosting this GraphicsLayer in a native layer hierarchy + virtual NativeLayer nativeLayer() const; + + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + + virtual void removeFromParent(); + + virtual void setPosition(const FloatPoint&); + virtual void setAnchorPoint(const FloatPoint3D&); + virtual void setSize(const FloatSize&); + + virtual void setTransform(const TransformationMatrix&); + + virtual void setChildrenTransform(const TransformationMatrix&); + + virtual void setPreserves3D(bool); + virtual void setMasksToBounds(bool); + virtual void setDrawsContent(bool); + + virtual void setBackgroundColor(const Color&, const Animation* anim = 0, double beginTime = 0); + virtual void clearBackgroundColor(); + + virtual void setContentsOpaque(bool); + virtual void setBackfaceVisibility(bool); + + // return true if we started an animation + virtual bool setOpacity(float, const Animation* anim = 0, double beginTime = 0); + + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + + virtual void suspendAnimations(); + virtual void resumeAnimations(); + + virtual bool animateTransform(const TransformValueList&, const IntSize&, const Animation*, double beginTime, bool isTransition); + virtual bool animateFloat(AnimatedPropertyID, const FloatValueList&, const Animation*, double beginTime); + + virtual void setContentsToImage(Image*); + virtual void setContentsToVideo(PlatformLayer*); + virtual void clearContents(); + + virtual void updateContentsRect(); + + virtual PlatformLayer* platformLayer() const; + +#ifndef NDEBUG + virtual void setDebugBackgroundColor(const Color&); + virtual void setDebugBorder(const Color&, float borderWidth); + virtual void setZPosition(float); +#endif + +private: + WebLayer* primaryLayer() const { return m_transformLayer.get() ? m_transformLayer.get() : m_layer.get(); } + WebLayer* hostLayerForSublayers() const; + WebLayer* layerForSuperlayer() const; + + WebLayer* animatedLayer(AnimatedPropertyID property) const + { + return (property == AnimatedPropertyBackgroundColor) ? m_contentsLayer.get() : primaryLayer(); + } + + void setBasicAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation*, double time); + void setKeyframeAnimation(AnimatedPropertyID, TransformOperation::OperationType, short index, void* keys, void* values, void* timingFunctions, bool isTransition, const Animation*, double time); + + virtual void removeAnimation(int index, bool reset); + + bool requiresTiledLayer(const FloatSize&) const; + void swapFromOrToTiledLayer(bool useTiledLayer); + + void setHasContentsLayer(bool); + void setContentsLayer(WebLayer*); + void setContentsLayerFlipped(bool); + + RetainPtr<WebLayer> m_layer; + RetainPtr<WebLayer> m_transformLayer; + RetainPtr<WebLayer> m_contentsLayer; + + RetainPtr<WebAnimationDelegate> m_animationDelegate; + + bool m_contentLayerForImageOrVideo; +}; + +} // namespace WebCore + + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // GraphicsLayerCA_h diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm new file mode 100644 index 0000000..f3f2d7f --- /dev/null +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -0,0 +1,1540 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "GraphicsLayerCA.h" + +#import "Animation.h" +#import "BlockExceptions.h" +#import "CString.h" +#import "FloatConversion.h" +#import "FloatRect.h" +#import "Image.h" +#import "PlatformString.h" +#import <QuartzCore/QuartzCore.h> +#import "RotateTransformOperation.h" +#import "ScaleTransformOperation.h" +#import "SystemTime.h" +#import "TranslateTransformOperation.h" +#import "WebLayer.h" +#import "WebTiledLayer.h" +#import <wtf/CurrentTime.h> +#import <wtf/UnusedParam.h> + +using namespace std; + +#define HAVE_MODERN_QUARTZCORE (!defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD)) + +namespace WebCore { + +// The threshold width or height above which a tiled layer will be used. This should be +// large enough to avoid tiled layers for most GraphicsLayers, but less than the OpenGL +// texture size limit on all supported hardware. +static const int cMaxPixelDimension = 2000; + +// The width and height of a single tile in a tiled layer. Should be large enough to +// avoid lots of small tiles (and therefore lots of drawing callbacks), but small enough +// to keep the overall tile cost low. +static const int cTiledLayerTileSize = 512; + +// If we send a duration of 0 to CA, then it will use the default duration +// of 250ms. So send a very small value instead. +static const float cAnimationAlmostZeroDuration = 1e-3f; + +// CACurrentMediaTime() is a time since boot. These methods convert between that and +// WebCore time, which is system time (UTC). +static CFTimeInterval currentTimeToMediaTime(double t) +{ + return CACurrentMediaTime() + t - WTF::currentTime(); +} + +static double mediaTimeToCurrentTime(CFTimeInterval t) +{ + return WTF::currentTime() + t - CACurrentMediaTime(); +} + +} // namespace WebCore + +static NSString* const WebAnimationCSSPropertyKey = @"GraphicsLayerCA_property"; + +@interface WebAnimationDelegate : NSObject { + WebCore::GraphicsLayerCA* m_graphicsLayer; +} + +- (void)animationDidStart:(CAAnimation *)anim; +- (WebCore::GraphicsLayerCA*)graphicsLayer; +- (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer; + +@end + +@implementation WebAnimationDelegate + +- (void)animationDidStart:(CAAnimation *)animation +{ + if (!m_graphicsLayer) + return; + + double startTime = WebCore::mediaTimeToCurrentTime([animation beginTime]); + m_graphicsLayer->client()->notifyAnimationStarted(m_graphicsLayer, startTime); +} + +- (WebCore::GraphicsLayerCA*)graphicsLayer +{ + return m_graphicsLayer; +} + +- (void)setLayer:(WebCore::GraphicsLayerCA*)graphicsLayer +{ + m_graphicsLayer = graphicsLayer; +} + +@end + +namespace WebCore { + +static inline void copyTransform(CATransform3D& toT3D, const TransformationMatrix& t) +{ + toT3D.m11 = narrowPrecisionToFloat(t.m11()); + toT3D.m12 = narrowPrecisionToFloat(t.m12()); + toT3D.m13 = narrowPrecisionToFloat(t.m13()); + toT3D.m14 = narrowPrecisionToFloat(t.m14()); + toT3D.m21 = narrowPrecisionToFloat(t.m21()); + toT3D.m22 = narrowPrecisionToFloat(t.m22()); + toT3D.m23 = narrowPrecisionToFloat(t.m23()); + toT3D.m24 = narrowPrecisionToFloat(t.m24()); + toT3D.m31 = narrowPrecisionToFloat(t.m31()); + toT3D.m32 = narrowPrecisionToFloat(t.m32()); + toT3D.m33 = narrowPrecisionToFloat(t.m33()); + toT3D.m34 = narrowPrecisionToFloat(t.m34()); + toT3D.m41 = narrowPrecisionToFloat(t.m41()); + toT3D.m42 = narrowPrecisionToFloat(t.m42()); + toT3D.m43 = narrowPrecisionToFloat(t.m43()); + toT3D.m44 = narrowPrecisionToFloat(t.m44()); +} + +static NSValue* getTransformFunctionValue(const GraphicsLayer::TransformValue& transformValue, size_t index, const IntSize& size, TransformOperation::OperationType transformType) +{ + TransformOperation* op = (index >= transformValue.value()->operations().size()) ? 0 : transformValue.value()->operations()[index].get(); + + switch (transformType) { + case TransformOperation::ROTATE: + case TransformOperation::ROTATE_X: + case TransformOperation::ROTATE_Y: + return [NSNumber numberWithDouble:op ? deg2rad(static_cast<RotateTransformOperation*>(op)->angle()) : 0]; + case TransformOperation::SCALE_X: + return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->x() : 0]; + case TransformOperation::SCALE_Y: + return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->y() : 0]; + case TransformOperation::SCALE_Z: + return [NSNumber numberWithDouble:op ? static_cast<ScaleTransformOperation*>(op)->z() : 0]; + case TransformOperation::TRANSLATE_X: + return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->x(size) : 0]; + case TransformOperation::TRANSLATE_Y: + return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->y(size) : 0]; + case TransformOperation::TRANSLATE_Z: + return [NSNumber numberWithDouble:op ? static_cast<TranslateTransformOperation*>(op)->z(size) : 0]; + case TransformOperation::SCALE: + case TransformOperation::TRANSLATE: + case TransformOperation::SKEW_X: + case TransformOperation::SKEW_Y: + case TransformOperation::SKEW: + case TransformOperation::MATRIX: + case TransformOperation::SCALE_3D: + case TransformOperation::TRANSLATE_3D: + case TransformOperation::ROTATE_3D: + case TransformOperation::MATRIX_3D: + case TransformOperation::PERSPECTIVE: + case TransformOperation::IDENTITY: + case TransformOperation::NONE: { + TransformationMatrix t; + if (op) + op->apply(t, size); + CATransform3D cat; + copyTransform(cat, t); + return [NSValue valueWithCATransform3D:cat]; + } + } + + return 0; +} + +#if HAVE_MODERN_QUARTZCORE +static NSString* getValueFunctionNameForTransformOperation(TransformOperation::OperationType transformType) +{ + // Use literal strings to avoid link-time dependency on those symbols. + switch (transformType) { + case TransformOperation::ROTATE_X: + return @"rotateX"; // kCAValueFunctionRotateX; + case TransformOperation::ROTATE_Y: + return @"rotateY"; // kCAValueFunctionRotateY; + case TransformOperation::ROTATE: + return @"rotateZ"; // kCAValueFunctionRotateZ; + case TransformOperation::SCALE_X: + return @"scaleX"; // kCAValueFunctionScaleX; + case TransformOperation::SCALE_Y: + return @"scaleY"; // kCAValueFunctionScaleY; + case TransformOperation::SCALE_Z: + return @"scaleZ"; // kCAValueFunctionScaleZ; + case TransformOperation::TRANSLATE_X: + return @"translateX"; // kCAValueFunctionTranslateX; + case TransformOperation::TRANSLATE_Y: + return @"translateY"; // kCAValueFunctionTranslateY; + case TransformOperation::TRANSLATE_Z: + return @"translateZ"; // kCAValueFunctionTranslateZ; + default: + return nil; + } +} +#endif + +static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction& timingFunction) +{ + switch (timingFunction.type()) { + case LinearTimingFunction: + return [CAMediaTimingFunction functionWithName:@"linear"]; + case CubicBezierTimingFunction: + return [CAMediaTimingFunction functionWithControlPoints:static_cast<float>(timingFunction.x1()) :static_cast<float>(timingFunction.y1()) + :static_cast<float>(timingFunction.x2()) :static_cast<float>(timingFunction.y2())]; + } + return 0; +} + +#ifndef NDEBUG +static void setLayerBorderColor(PlatformLayer* layer, const Color& color) +{ + CGColorRef borderColor = createCGColor(color); + [layer setBorderColor:borderColor]; + CGColorRelease(borderColor); +} + +static void clearBorderColor(PlatformLayer* layer) +{ + [layer setBorderColor:nil]; +} +#endif + +static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) +{ + CGColorRef bgColor = createCGColor(color); + [layer setBackgroundColor:bgColor]; + CGColorRelease(bgColor); +} + +static void clearLayerBackgroundColor(PlatformLayer* layer) +{ + [layer setBackgroundColor:0]; +} + +static CALayer* getPresentationLayer(CALayer* layer) +{ + CALayer* presLayer = [layer presentationLayer]; + if (!presLayer) + presLayer = layer; + + return presLayer; +} + +static bool caValueFunctionSupported() +{ + static bool sHaveValueFunction = [CAPropertyAnimation instancesRespondToSelector:@selector(setValueFunction:)]; + return sHaveValueFunction; +} + +static bool forceSoftwareAnimation() +{ + static bool forceSoftwareAnimation = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreForceSoftwareAnimation"]; + return forceSoftwareAnimation; +} + +bool GraphicsLayer::graphicsContextsFlipped() +{ + return true; +} + +#ifndef NDEBUG +bool GraphicsLayer::showDebugBorders() +{ + static bool showDebugBorders = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerBorders"]; + return showDebugBorders; +} + +bool GraphicsLayer::showRepaintCounter() +{ + static bool showRepaintCounter = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebCoreLayerRepaintCounter"]; + return showRepaintCounter; +} +#endif + +static NSDictionary* nullActionsDictionary() +{ + NSNull* nullValue = [NSNull null]; + NSDictionary* actions = [NSDictionary dictionaryWithObjectsAndKeys: + nullValue, @"anchorPoint", + nullValue, @"bounds", + nullValue, @"contents", + nullValue, @"contentsRect", + nullValue, @"opacity", + nullValue, @"position", + nullValue, @"shadowColor", + nullValue, @"sublayerTransform", + nullValue, @"sublayers", + nullValue, @"transform", +#ifndef NDEBUG + nullValue, @"zPosition", +#endif + nil]; + return actions; +} + +GraphicsLayer* GraphicsLayer::createGraphicsLayer(GraphicsLayerClient* client) +{ + return new GraphicsLayerCA(client); +} + +GraphicsLayerCA::GraphicsLayerCA(GraphicsLayerClient* client) +: GraphicsLayer(client) +, m_contentLayerForImageOrVideo(false) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + m_layer.adoptNS([[WebLayer alloc] init]); + [m_layer.get() setLayerOwner:this]; + +#ifndef NDEBUG + updateDebugIndicators(); +#endif + + m_animationDelegate.adoptNS([[WebAnimationDelegate alloc] init]); + [m_animationDelegate.get() setLayer:this]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +GraphicsLayerCA::~GraphicsLayerCA() +{ + // Remove a inner layer if there is one. + clearContents(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Clean up the WK layer. + if (m_layer) { + WebLayer* layer = m_layer.get(); + [layer setLayerOwner:nil]; + [layer removeFromSuperlayer]; + } + + if (m_transformLayer) + [m_transformLayer.get() removeFromSuperlayer]; + + // animationDidStart: can fire after this, so we need to clear out the layer on the delegate. + [m_animationDelegate.get() setLayer:0]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setName(const String& name) +{ + String longName = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + name; + GraphicsLayer::setName(longName); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setName:name]; + END_BLOCK_OBJC_EXCEPTIONS +} + +NativeLayer GraphicsLayerCA::nativeLayer() const +{ + return m_layer.get(); +} + +void GraphicsLayerCA::addChild(GraphicsLayer* childLayer) +{ + GraphicsLayer::addChild(childLayer); + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + BEGIN_BLOCK_OBJC_EXCEPTIONS + [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::addChildAtIndex(GraphicsLayer* childLayer, int index) +{ + GraphicsLayer::addChildAtIndex(childLayer, index); + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + BEGIN_BLOCK_OBJC_EXCEPTIONS + [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() atIndex:index]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::addChildBelow(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + // FIXME: share code with base class + ASSERT(childLayer != this); + childLayer->removeFromParent(); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i, childLayer); + found = true; + break; + } + } + childLayer->setParent(this); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(sibling); + if (found) + [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() below:siblingLayerCA->layerForSuperlayer()]; + else { + m_children.append(childLayer); + [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::addChildAbove(GraphicsLayer* childLayer, GraphicsLayer* sibling) +{ + // FIXME: share code with base class + ASSERT(childLayer != this); + childLayer->removeFromParent(); + + unsigned i; + bool found = false; + for (i = 0; i < m_children.size(); i++) { + if (sibling == m_children[i]) { + m_children.insert(i+1, childLayer); + found = true; + break; + } + } + childLayer->setParent(this); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + GraphicsLayerCA* childLayerCA = static_cast<GraphicsLayerCA*>(childLayer); + GraphicsLayerCA* siblingLayerCA = static_cast<GraphicsLayerCA*>(sibling); + if (found) { + [hostLayerForSublayers() insertSublayer:childLayerCA->layerForSuperlayer() above:siblingLayerCA->layerForSuperlayer()]; + } else { + m_children.append(childLayer); + [hostLayerForSublayers() addSublayer:childLayerCA->layerForSuperlayer()]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +bool GraphicsLayerCA::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + // FIXME: share code with base class + ASSERT(!newChild->parent()); + + bool found = false; + for (unsigned i = 0; i < m_children.size(); i++) { + if (oldChild == m_children[i]) { + m_children[i] = newChild; + found = true; + break; + } + } + + if (found) { + oldChild->setParent(0); + + newChild->removeFromParent(); + newChild->setParent(this); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + GraphicsLayerCA* oldChildCA = static_cast<GraphicsLayerCA*>(oldChild); + GraphicsLayerCA* newChildCA = static_cast<GraphicsLayerCA*>(newChild); + [hostLayerForSublayers() replaceSublayer:oldChildCA->layerForSuperlayer() with:newChildCA->layerForSuperlayer()]; + END_BLOCK_OBJC_EXCEPTIONS + return true; + } + return false; +} + +void GraphicsLayerCA::removeFromParent() +{ + GraphicsLayer::removeFromParent(); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [layerForSuperlayer() removeFromSuperlayer]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setPosition(const FloatPoint& point) +{ + // Don't short-circuit here, because position and anchor point are inter-dependent. + GraphicsLayer::setPosition(point); + + // Position is offset on the layer by the layer anchor point. + CGPoint posPoint = CGPointMake(m_position.x() + m_anchorPoint.x() * m_size.width(), + m_position.y() + m_anchorPoint.y() * m_size.height()); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setPosition:posPoint]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setAnchorPoint(const FloatPoint3D& point) +{ + // Don't short-circuit here, because position and anchor point are inter-dependent. + bool zChanged = (point.z() != m_anchorPoint.z()); + GraphicsLayer::setAnchorPoint(point); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + // set the value on the layer to the new transform. + [primaryLayer() setAnchorPoint:FloatPoint(point.x(), point.y())]; + + if (zChanged) { +#if HAVE_MODERN_QUARTZCORE + [primaryLayer() setAnchorPointZ:m_anchorPoint.z()]; +#endif + } + + // Position depends on anchor point, so update it now. + setPosition(m_position); + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setSize(const FloatSize& size) +{ + GraphicsLayer::setSize(size); + + CGRect rect = CGRectMake(0.0f, + 0.0f, + m_size.width(), + m_size.height()); + + CGPoint centerPoint = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (m_transformLayer) { + [m_transformLayer.get() setBounds:rect]; + + // the anchor of the contents layer is always at 0.5, 0.5, so the position + // is center-relative + [m_layer.get() setPosition:centerPoint]; + } + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + [m_layer.get() setBounds:rect]; + + // Note that we don't resize m_contentsLayer. It's up the caller to do that. + + END_BLOCK_OBJC_EXCEPTIONS + + // if we've changed the bounds, we need to recalculate the position + // of the layer, taking anchor point into account + setPosition(m_position); +} + +void GraphicsLayerCA::setTransform(const TransformationMatrix& t) +{ + GraphicsLayer::setTransform(t); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + CATransform3D transform; + copyTransform(transform, t); + [primaryLayer() setTransform:transform]; + END_BLOCK_OBJC_EXCEPTIONS + + // Remove any old transition entries for transform. + removeAllAnimationsForProperty(AnimatedPropertyWebkitTransform); + + // Even if we don't have a transition in the list, the layer may still have one. + // This happens when we are setting the final transform value after an animation or + // transition has ended. In removeAnimation we toss the entry from the list but don't + // remove it from the list. That way we aren't in danger of displaying a stale transform + // in the time between removing the animation and setting the new unanimated value. We + // can't do this in removeAnimation because we don't know the new transform value there. + String keyPath = propertyIdToString(AnimatedPropertyWebkitTransform); + CALayer* layer = animatedLayer(AnimatedPropertyWebkitTransform); + + for (int i = 0; ; ++i) { + String animName = keyPath + "_" + String::number(i); + if (![layer animationForKey: animName]) + break; + [layer removeAnimationForKey:animName]; + } +} + +void GraphicsLayerCA::setChildrenTransform(const TransformationMatrix& t) +{ + if (t == m_childrenTransform) + return; + + GraphicsLayer::setChildrenTransform(t); + + CATransform3D transform; + copyTransform(transform, t); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + // Set the value on the layer to the new transform. + [primaryLayer() setSublayerTransform:transform]; + END_BLOCK_OBJC_EXCEPTIONS +} + +static void moveAnimation(AnimatedPropertyID property, CALayer* fromLayer, CALayer* toLayer) +{ + String keyPath = GraphicsLayer::propertyIdToString(property); + for (short index = 0; ; ++index) { + String animName = keyPath + "_" + String::number(index); + CAAnimation* anim = [fromLayer animationForKey:animName]; + if (!anim) + break; + + [anim retain]; + [fromLayer removeAnimationForKey:animName]; + [toLayer addAnimation:anim forKey:animName]; + [anim release]; + } +} + +static void moveSublayers(CALayer* fromLayer, CALayer* toLayer) +{ + NSArray* sublayersCopy = [[fromLayer sublayers] copy]; // Avoid mutation while enumerating, and keep the sublayers alive. + NSEnumerator* childrenEnumerator = [sublayersCopy objectEnumerator]; + + CALayer* layer; + while ((layer = [childrenEnumerator nextObject]) != nil) { + [layer removeFromSuperlayer]; + [toLayer addSublayer:layer]; + } + [sublayersCopy release]; +} + +void GraphicsLayerCA::setPreserves3D(bool preserves3D) +{ + GraphicsLayer::setPreserves3D(preserves3D); + + CGPoint point = CGPointMake(m_size.width() / 2.0f, m_size.height() / 2.0f); + CGPoint centerPoint = CGPointMake(0.5f, 0.5f); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + Class transformLayerClass = NSClassFromString(@"CATransformLayer"); + if (preserves3D && !m_transformLayer && transformLayerClass) { + // Create the transform layer. + m_transformLayer.adoptNS([[transformLayerClass alloc] init]); + + // Turn off default animations. + [m_transformLayer.get() setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + +#ifndef NDEBUG + [m_transformLayer.get() setName:[NSString stringWithFormat:@"Transform Layer CATransformLayer(%p) GraphicsLayer(%p)", m_transformLayer.get(), this]]; +#endif + // Copy the position from this layer. + [m_transformLayer.get() setBounds:[m_layer.get() bounds]]; + [m_transformLayer.get() setPosition:[m_layer.get() position]]; + [m_transformLayer.get() setAnchorPoint:[m_layer.get() anchorPoint]]; +#if HAVE_MODERN_QUARTZCORE + [m_transformLayer.get() setAnchorPointZ:[m_layer.get() anchorPointZ]]; +#endif + [m_transformLayer.get() setContentsRect:[m_layer.get() contentsRect]]; +#ifndef NDEBUG + [m_transformLayer.get() setZPosition:[m_layer.get() zPosition]]; +#endif + + // The contents layer is positioned at (0,0) relative to the transformLayer. + [m_layer.get() setPosition:point]; + [m_layer.get() setAnchorPoint:centerPoint]; +#ifndef NDEBUG + [m_layer.get() setZPosition:0.0f]; +#endif + + // Transfer the transform over. + [m_transformLayer.get() setTransform:[m_layer.get() transform]]; + [m_layer.get() setTransform:CATransform3DIdentity]; + + // Transfer the opacity from the old layer to the transform layer. + [m_transformLayer.get() setOpacity:m_opacity]; + [m_layer.get() setOpacity:1]; + + // Move this layer to be a child of the transform layer. + [[m_layer.get() superlayer] replaceSublayer:m_layer.get() with:m_transformLayer.get()]; + [m_transformLayer.get() addSublayer:m_layer.get()]; + + moveAnimation(AnimatedPropertyWebkitTransform, m_layer.get(), m_transformLayer.get()); + moveSublayers(m_layer.get(), m_transformLayer.get()); + + } else if (!preserves3D && m_transformLayer) { + // Relace the transformLayer in the parent with this layer. + [m_layer.get() removeFromSuperlayer]; + [[m_transformLayer.get() superlayer] replaceSublayer:m_transformLayer.get() with:m_layer.get()]; + + moveAnimation(AnimatedPropertyWebkitTransform, m_transformLayer.get(), m_layer.get()); + moveSublayers(m_transformLayer.get(), m_layer.get()); + + // Reset the layer position and transform. + [m_layer.get() setPosition:[m_transformLayer.get() position]]; + [m_layer.get() setAnchorPoint:[m_transformLayer.get() anchorPoint]]; +#if HAVE_MODERN_QUARTZCORE + [m_layer.get() setAnchorPointZ:[m_transformLayer.get() anchorPointZ]]; +#endif + [m_layer.get() setContentsRect:[m_transformLayer.get() contentsRect]]; + [m_layer.get() setTransform:[m_transformLayer.get() transform]]; + [m_layer.get() setOpacity:[m_transformLayer.get() opacity]]; +#ifndef NDEBUG + [m_layer.get() setZPosition:[m_transformLayer.get() zPosition]]; +#endif + + // Release the transform layer. + m_transformLayer = 0; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setMasksToBounds(bool masksToBounds) +{ + if (masksToBounds == m_masksToBounds) + return; + + GraphicsLayer::setMasksToBounds(masksToBounds); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setMasksToBounds:masksToBounds]; + END_BLOCK_OBJC_EXCEPTIONS + +#ifndef NDEBUG + updateDebugIndicators(); +#endif +} + +void GraphicsLayerCA::setDrawsContent(bool drawsContent) +{ + if (drawsContent != m_drawsContent) { + GraphicsLayer::setDrawsContent(drawsContent); + + bool needTiledLayer = requiresTiledLayer(m_size); + if (needTiledLayer != m_usingTiledLayer) + swapFromOrToTiledLayer(needTiledLayer); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + // Clobber any existing content. If necessary, CA will create backing store on the next display. + [m_layer.get() setContents:nil]; + +#ifndef NDEBUG + updateDebugIndicators(); +#endif + END_BLOCK_OBJC_EXCEPTIONS + } +} + +void GraphicsLayerCA::setBackgroundColor(const Color& color, const Animation* transition, double beginTime) +{ + GraphicsLayer::setBackgroundColor(color, transition, beginTime); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + setHasContentsLayer(true); + + if (transition && !transition->isEmptyOrZeroDuration()) { + CALayer* presLayer = [m_contentsLayer.get() presentationLayer]; + // If we don't have a presentationLayer, just use the CALayer + if (!presLayer) + presLayer = m_contentsLayer.get(); + + // Get the current value of the background color from the layer + CGColorRef fromBackgroundColor = [presLayer backgroundColor]; + + CGColorRef bgColor = createCGColor(color); + setBasicAnimation(AnimatedPropertyBackgroundColor, TransformOperation::NONE, 0, fromBackgroundColor, bgColor, true, transition, beginTime); + CGColorRelease(bgColor); + } else { + removeAllAnimationsForProperty(AnimatedPropertyBackgroundColor); + setHasContentsLayer(true); + setLayerBackgroundColor(m_contentsLayer.get(), m_backgroundColor); + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::clearBackgroundColor() +{ + if (!m_contentLayerForImageOrVideo) + setHasContentsLayer(false); + else + clearLayerBackgroundColor(m_contentsLayer.get()); +} + +void GraphicsLayerCA::setContentsOpaque(bool opaque) +{ + GraphicsLayer::setContentsOpaque(opaque); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setOpaque:m_contentsOpaque]; + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setBackfaceVisibility(bool visible) +{ + if (m_backfaceVisibility == visible) + return; + + GraphicsLayer::setBackfaceVisibility(visible); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_layer.get() setDoubleSided:visible]; + END_BLOCK_OBJC_EXCEPTIONS +} + +bool GraphicsLayerCA::setOpacity(float opacity, const Animation* transition, double beginTime) +{ + if (forceSoftwareAnimation()) + return false; + + float clampedOpacity = max(0.0f, min(opacity, 1.0f)); + + bool opacitiesDiffer = (m_opacity != clampedOpacity); + + GraphicsLayer::setOpacity(clampedOpacity, transition, beginTime); + + int animIndex = findAnimationEntry(AnimatedPropertyOpacity, 0); + + // If we don't have a transition just set the opacity + if (!transition || transition->isEmptyOrZeroDuration()) { + // Three cases: + // 1) no existing animation or transition: just set opacity + // 2) existing transition: clear it and set opacity + // 3) existing animation: just return + // + if (animIndex < 0 || m_animations[animIndex].isTransition()) { + BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setOpacity:opacity]; + if (animIndex >= 0) { + removeAllAnimationsForProperty(AnimatedPropertyOpacity); + animIndex = -1; + } else { + String keyPath = propertyIdToString(AnimatedPropertyOpacity); + + // FIXME: using hardcoded '0' here. We should be clearing all animations. For now there is only 1. + String animName = keyPath + "_0"; + [animatedLayer(AnimatedPropertyOpacity) removeAnimationForKey:animName]; + } + END_BLOCK_OBJC_EXCEPTIONS + } else { + // We have an animation, so don't set the opacity directly. + return false; + } + } else { + // At this point, we know we have a transition. But if it is the same and + // the opacity value has not changed, don't do anything. + if (!opacitiesDiffer && + ((animIndex == -1) || + (m_animations[animIndex].isTransition() && *(m_animations[animIndex].animation()) == *transition))) { + return false; + } + } + + // If an animation is running, ignore this transition, but still save the value. + if (animIndex >= 0 && !m_animations[animIndex].isTransition()) + return false; + + bool didAnimate = false; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + NSNumber* fromOpacityValue = nil; + NSNumber* toOpacityValue = [NSNumber numberWithFloat:opacity]; + + if (transition && !transition->isEmptyOrZeroDuration()) { + CALayer* presLayer = getPresentationLayer(primaryLayer()); + float fromOpacity = [presLayer opacity]; + fromOpacityValue = [NSNumber numberWithFloat:fromOpacity]; + setBasicAnimation(AnimatedPropertyOpacity, TransformOperation::NONE, 0, fromOpacityValue, toOpacityValue, true, transition, beginTime); + didAnimate = true; + } + + END_BLOCK_OBJC_EXCEPTIONS + + return didAnimate; +} + +void GraphicsLayerCA::setNeedsDisplay() +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (drawsContent()) + [m_layer.get() setNeedsDisplay]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setNeedsDisplayInRect(const FloatRect& rect) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (drawsContent()) + [m_layer.get() setNeedsDisplayInRect:rect]; + + END_BLOCK_OBJC_EXCEPTIONS +} + + +bool GraphicsLayerCA::animateTransform(const TransformValueList& valueList, const IntSize& size, const Animation* anim, double beginTime, bool isTransition) +{ + if (forceSoftwareAnimation() || !anim || anim->isEmptyOrZeroDuration() || valueList.size() < 2) + return false; + + TransformValueList::FunctionList functionList; + bool isValid, hasBigRotation; + valueList.makeFunctionList(functionList, isValid, hasBigRotation); + + // We need to fall back to software animation if we don't have setValueFunction:, and + // we would need to animate each incoming transform function separately. This is the + // case if we have a rotation >= 180 or we have more than one transform function. + if ((hasBigRotation || functionList.size() > 1) && !caValueFunctionSupported()) + return false; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // Rules for animation: + // + // 1) If functionList is empty or we don't have a big rotation, we do a matrix animation. We could + // use component animation for lists without a big rotation, but there is no need to, and this + // is more efficient. + // + // 2) Otherwise we do a component hardware animation. + bool isMatrixAnimation = !isValid || !hasBigRotation; + + // Set transform to identity since we are animating components and we need the base + // to be the identity transform. + TransformationMatrix t; + CATransform3D toT3D; + copyTransform(toT3D, t); + [primaryLayer() setTransform:toT3D]; + + bool isKeyframe = valueList.size() > 2; + + // Iterate through the transform functions, sending an animation for each one. + for (int functionIndex = 0; ; ++functionIndex) { + if (functionIndex >= static_cast<int>(functionList.size()) && !isMatrixAnimation) + break; + + TransformOperation::OperationType opType = isMatrixAnimation ? TransformOperation::MATRIX_3D : functionList[functionIndex]; + + if (isKeyframe) { + NSMutableArray* timesArray = [[NSMutableArray alloc] init]; + NSMutableArray* valArray = [[NSMutableArray alloc] init]; + NSMutableArray* tfArray = [[NSMutableArray alloc] init]; + + // Iterate through the keyframes, building arrays for the animation. + for (Vector<TransformValue>::const_iterator it = valueList.values().begin(); it != valueList.values().end(); ++it) { + const TransformValue& curValue = (*it); + + // fill in the key time and timing function + [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; + + const TimingFunction* tf = 0; + if (curValue.timingFunction()) + tf = curValue.timingFunction(); + else if (anim->isTimingFunctionSet()) + tf = &anim->timingFunction(); + + CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction(LinearTimingFunction)); + [tfArray addObject:timingFunction]; + + // fill in the function + if (isMatrixAnimation) { + TransformationMatrix t; + curValue.value()->apply(size, t); + CATransform3D cat; + copyTransform(cat, t); + [valArray addObject:[NSValue valueWithCATransform3D:cat]]; + } else + [valArray addObject:getTransformFunctionValue(curValue, functionIndex, size, opType)]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [tfArray removeLastObject]; + + setKeyframeAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, timesArray, valArray, tfArray, isTransition, anim, beginTime); + + [timesArray release]; + [valArray release]; + [tfArray release]; + } else { + // Is a transition + id fromValue, toValue; + + if (isMatrixAnimation) { + TransformationMatrix fromt, tot; + valueList.at(0).value()->apply(size, fromt); + valueList.at(1).value()->apply(size, tot); + + CATransform3D cat; + copyTransform(cat, fromt); + fromValue = [NSValue valueWithCATransform3D:cat]; + copyTransform(cat, tot); + toValue = [NSValue valueWithCATransform3D:cat]; + } else { + fromValue = getTransformFunctionValue(valueList.at(0), functionIndex, size, opType); + toValue = getTransformFunctionValue(valueList.at(1), functionIndex, size, opType); + } + + setBasicAnimation(AnimatedPropertyWebkitTransform, opType, functionIndex, fromValue, toValue, isTransition, anim, beginTime); + } + + if (isMatrixAnimation) + break; + } + + END_BLOCK_OBJC_EXCEPTIONS + return true; +} + +bool GraphicsLayerCA::animateFloat(AnimatedPropertyID property, const FloatValueList& valueList, const Animation* animation, double beginTime) +{ + if (forceSoftwareAnimation() || valueList.size() < 2) + return false; + + // if there is already is an animation for this property and it hasn't changed, ignore it. + int i = findAnimationEntry(property, 0); + if (i >= 0 && *m_animations[i].animation() == *animation) { + m_animations[i].setIsCurrent(); + return false; + } + + if (valueList.size() == 2) { + float fromVal = valueList.at(0).value(); + float toVal = valueList.at(1).value(); + if (isnan(toVal) && isnan(fromVal)) + return false; + + // initialize the property to 0 + [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; + setBasicAnimation(property, TransformOperation::NONE, 0, isnan(fromVal) ? nil : [NSNumber numberWithFloat:fromVal], isnan(toVal) ? nil : [NSNumber numberWithFloat:toVal], false, animation, beginTime); + return true; + } + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + NSMutableArray* timesArray = [[NSMutableArray alloc] init]; + NSMutableArray* valArray = [[NSMutableArray alloc] init]; + NSMutableArray* tfArray = [[NSMutableArray alloc] init]; + + for (unsigned i = 0; i < valueList.values().size(); ++i) { + const FloatValue& curValue = valueList.values()[i]; + [timesArray addObject:[NSNumber numberWithFloat:curValue.key()]]; + [valArray addObject:[NSNumber numberWithFloat:curValue.value()]]; + + const TimingFunction* tf = 0; + if (curValue.timingFunction()) + tf = curValue.timingFunction(); + else if (animation->isTimingFunctionSet()) + tf = &animation->timingFunction(); + + CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + [tfArray addObject:timingFunction]; + } + + // We toss the last tfArray value because it has to one shorter than the others. + [tfArray removeLastObject]; + + // Initialize the property to 0. + [animatedLayer(property) setValue:0 forKeyPath:propertyIdToString(property)]; + // Then set the animation. + setKeyframeAnimation(property, TransformOperation::NONE, 0, timesArray, valArray, tfArray, false, animation, beginTime); + + [timesArray release]; + [valArray release]; + [tfArray release]; + + END_BLOCK_OBJC_EXCEPTIONS + return true; +} + +void GraphicsLayerCA::setContentsToImage(Image* image) +{ + if (image) { + setHasContentsLayer(true); + + // FIXME: is image flipping really a property of the graphics context? + bool needToFlip = GraphicsLayer::graphicsContextsFlipped(); + CGPoint anchorPoint = needToFlip ? CGPointMake(0.0f, 1.0f) : CGPointZero; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + { + CGImageRef theImage = image->nativeImageForCurrentFrame(); + // FIXME: maybe only do trilinear if the image is being scaled down, + // but then what if the layer size changes? +#if HAVE_MODERN_QUARTZCORE + [m_contentsLayer.get() setMinificationFilter:kCAFilterTrilinear]; +#endif + if (needToFlip) { + CATransform3D flipper = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; + [m_contentsLayer.get() setTransform:flipper]; + } + + [m_contentsLayer.get() setAnchorPoint:anchorPoint]; + [m_contentsLayer.get() setContents:(id)theImage]; + } + END_BLOCK_OBJC_EXCEPTIONS + } else + setHasContentsLayer(false); + + m_contentLayerForImageOrVideo = (image != 0); +} + +void GraphicsLayerCA::setContentsToVideo(PlatformLayer* videoLayer) +{ + setContentsLayer(videoLayer); + m_contentLayerForImageOrVideo = (videoLayer != 0); +} + +void GraphicsLayerCA::clearContents() +{ + if (m_contentLayerForImageOrVideo) { + setHasContentsLayer(false); + m_contentLayerForImageOrVideo = false; + } +} + +void GraphicsLayerCA::updateContentsRect() +{ + if (m_client && m_contentsLayer) { + IntRect contentRect = m_client->contentsBox(this); + + CGPoint point = CGPointMake(contentRect.x(), + contentRect.y()); + CGRect rect = CGRectMake(0.0f, + 0.0f, + contentRect.width(), + contentRect.height()); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [m_contentsLayer.get() setPosition:point]; + [m_contentsLayer.get() setBounds:rect]; + END_BLOCK_OBJC_EXCEPTIONS + } +} + +void GraphicsLayerCA::setBasicAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* fromVal, void* toVal, bool isTransition, const Animation* transition, double beginTime) +{ + ASSERT(fromVal || toVal); + + WebLayer* layer = animatedLayer(property); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // add an entry for this animation + addAnimationEntry(property, index, isTransition, transition); + + String keyPath = propertyIdToString(property); + String animName = keyPath + "_" + String::number(index); + + CABasicAnimation* basicAnim = [CABasicAnimation animationWithKeyPath:keyPath]; + + double duration = transition->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; + + float repeatCount = transition->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (transition->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; + + [basicAnim setDuration:duration]; + [basicAnim setRepeatCount:repeatCount]; + [basicAnim setAutoreverses:transition->direction()]; + [basicAnim setRemovedOnCompletion:NO]; + + // Note that currently transform is the only property which has animations + // with an index > 0. + [basicAnim setAdditive:property == AnimatedPropertyWebkitTransform]; + [basicAnim setFillMode:@"extended"]; +#if HAVE_MODERN_QUARTZCORE + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) + [basicAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#else + UNUSED_PARAM(operationType); +#endif + + // Set the delegate (and property value). + int prop = isTransition ? property : AnimatedPropertyInvalid; + [basicAnim setValue:[NSNumber numberWithInt:prop] forKey:WebAnimationCSSPropertyKey]; + [basicAnim setDelegate:m_animationDelegate.get()]; + + NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; + [basicAnim setBeginTime:bt]; + + if (fromVal) + [basicAnim setFromValue:reinterpret_cast<id>(fromVal)]; + if (toVal) + [basicAnim setToValue:reinterpret_cast<id>(toVal)]; + + const TimingFunction* tf = 0; + if (transition->isTimingFunctionSet()) + tf = &transition->timingFunction(); + + CAMediaTimingFunction* timingFunction = getCAMediaTimingFunction(tf ? *tf : TimingFunction()); + [basicAnim setTimingFunction:timingFunction]; + + // Send over the animation. + [layer removeAnimationForKey:animName]; + [layer addAnimation:basicAnim forKey:animName]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setKeyframeAnimation(AnimatedPropertyID property, TransformOperation::OperationType operationType, short index, void* keys, void* values, void* timingFunctions, + bool isTransition, const Animation* anim, double beginTime) +{ + PlatformLayer* layer = animatedLayer(property); + + // Add an entry for this animation (which may change beginTime). + addAnimationEntry(property, index, isTransition, anim); + + String keyPath = propertyIdToString(property); + String animName = keyPath + "_" + String::number(index); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + CAKeyframeAnimation* keyframeAnim = [CAKeyframeAnimation animationWithKeyPath:keyPath]; + + double duration = anim->duration(); + if (duration <= 0) + duration = cAnimationAlmostZeroDuration; + + float repeatCount = anim->iterationCount(); + if (repeatCount == Animation::IterationCountInfinite) + repeatCount = FLT_MAX; + else if (anim->direction() == Animation::AnimationDirectionAlternate) + repeatCount /= 2; + + [keyframeAnim setDuration:duration]; + [keyframeAnim setRepeatCount:repeatCount]; + [keyframeAnim setAutoreverses:anim->direction()]; + [keyframeAnim setRemovedOnCompletion:NO]; + + // The first animation is non-additive, all the rest are additive. + // Note that currently transform is the only property which has animations + // with an index > 0. + [keyframeAnim setAdditive:(property == AnimatedPropertyWebkitTransform) ? YES : NO]; + [keyframeAnim setFillMode:@"extended"]; +#if HAVE_MODERN_QUARTZCORE + if (NSString* valueFunctionName = getValueFunctionNameForTransformOperation(operationType)) + [keyframeAnim setValueFunction:[CAValueFunction functionWithName:valueFunctionName]]; +#else + UNUSED_PARAM(operationType); +#endif + + [keyframeAnim setKeyTimes:reinterpret_cast<id>(keys)]; + [keyframeAnim setValues:reinterpret_cast<id>(values)]; + + // Set the delegate (and property value). + int prop = isTransition ? property : AnimatedPropertyInvalid; + [keyframeAnim setValue:[NSNumber numberWithInt: prop] forKey:WebAnimationCSSPropertyKey]; + [keyframeAnim setDelegate:m_animationDelegate.get()]; + + NSTimeInterval bt = beginTime ? [layer convertTime:currentTimeToMediaTime(beginTime) fromLayer:nil] : 0; + [keyframeAnim setBeginTime:bt]; + + // Set the timing functions, if any. + if (timingFunctions != nil) + [keyframeAnim setTimingFunctions:(id)timingFunctions]; + + // Send over the animation. + [layer removeAnimationForKey:animName]; + [layer addAnimation:keyframeAnim forKey:animName]; + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::suspendAnimations() +{ + double t = currentTimeToMediaTime(currentTime()); + [primaryLayer() setSpeed:0]; + [primaryLayer() setTimeOffset:t]; +} + +void GraphicsLayerCA::resumeAnimations() +{ + [primaryLayer() setSpeed:1]; + [primaryLayer() setTimeOffset:0]; +} + +void GraphicsLayerCA::removeAnimation(int index, bool reset) +{ + ASSERT(index >= 0); + + AnimatedPropertyID property = m_animations[index].property(); + + // Set the value of the property and remove the animation. + String keyPath = propertyIdToString(property); + String animName = keyPath + "_" + String::number(m_animations[index].index()); + CALayer* layer = animatedLayer(property); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + // If we are not resetting, it means we are pausing. So we need to get the current presentation + // value into the property before we remove the animation. + if (!reset) { + // Put the current value into the property. + CALayer* presLayer = [layer presentationLayer]; + if (presLayer) + [layer setValue:[presLayer valueForKeyPath:keyPath] forKeyPath:keyPath]; + + // Make sure the saved values accurately reflect the value in the layer. + id val = [layer valueForKeyPath:keyPath]; + switch (property) { + case AnimatedPropertyWebkitTransform: + // FIXME: needs comment explaining why the m_transform is not obtained from the layer + break; + case AnimatedPropertyBackgroundColor: + m_backgroundColor = Color(reinterpret_cast<CGColorRef>(val)); + break; + case AnimatedPropertyOpacity: + m_opacity = [val floatValue]; + break; + case AnimatedPropertyInvalid: + ASSERT_NOT_REACHED(); + break; + } + } + + // If we have reached the end of an animation, we don't want to actually remove the + // animation from the CALayer. At some point we will be setting the property to its + // unanimated value and at that point we will remove the animation. That will avoid + // any flashing between the time the animation is removed and the property is set. + if (!reset || m_animations[index].isTransition()) + [layer removeAnimationForKey:animName]; + + END_BLOCK_OBJC_EXCEPTIONS + + // Remove the animation entry. + m_animations.remove(index); +} + +PlatformLayer* GraphicsLayerCA::hostLayerForSublayers() const +{ + return m_transformLayer ? m_transformLayer.get() : m_layer.get(); +} + +PlatformLayer* GraphicsLayerCA::layerForSuperlayer() const +{ + if (m_transformLayer) + return m_transformLayer.get(); + + return m_layer.get(); +} + +PlatformLayer* GraphicsLayerCA::platformLayer() const +{ + return primaryLayer(); +} + +#ifndef NDEBUG +void GraphicsLayerCA::setDebugBackgroundColor(const Color& color) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (color.isValid()) + setLayerBackgroundColor(m_layer.get(), color); + else + clearLayerBackgroundColor(m_layer.get()); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setDebugBorder(const Color& color, float borderWidth) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (color.isValid()) { + setLayerBorderColor(m_layer.get(), color); + [m_layer.get() setBorderWidth:borderWidth]; + } else { + clearBorderColor(m_layer.get()); + [m_layer.get() setBorderWidth:0]; + } + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setZPosition(float position) +{ + GraphicsLayer::setZPosition(position); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + [primaryLayer() setZPosition:position]; + END_BLOCK_OBJC_EXCEPTIONS +} +#endif + +bool GraphicsLayerCA::requiresTiledLayer(const FloatSize& size) const +{ + if (!m_drawsContent) + return false; + + // FIXME: catch zero-size height or width here (or earlier)? + return size.width() > cMaxPixelDimension || size.height() > cMaxPixelDimension; +} + +void GraphicsLayerCA::swapFromOrToTiledLayer(bool userTiledLayer) +{ + if (userTiledLayer == m_usingTiledLayer) + return; + + CGSize tileSize = CGSizeMake(cTiledLayerTileSize, cTiledLayerTileSize); + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + RetainPtr<CALayer> oldLayer = m_layer.get(); + + Class layerClass = userTiledLayer ? [WebTiledLayer self] : [WebLayer self]; + m_layer.adoptNS([[layerClass alloc] init]); + + if (userTiledLayer) { + WebTiledLayer* tiledLayer = (WebTiledLayer*)m_layer.get(); + [tiledLayer setTileSize:tileSize]; + [tiledLayer setLevelsOfDetail:1]; + [tiledLayer setLevelsOfDetailBias:0]; + + if (GraphicsLayer::graphicsContextsFlipped()) + [tiledLayer setContentsGravity:@"bottomLeft"]; + else + [tiledLayer setContentsGravity:@"topLeft"]; + } + + [m_layer.get() setLayerOwner:this]; + [m_layer.get() setSublayers:[oldLayer.get() sublayers]]; + + [[oldLayer.get() superlayer] replaceSublayer:oldLayer.get() with:m_layer.get()]; + + [m_layer.get() setBounds:[oldLayer.get() bounds]]; + [m_layer.get() setPosition:[oldLayer.get() position]]; + [m_layer.get() setAnchorPoint:[oldLayer.get() anchorPoint]]; + [m_layer.get() setOpaque:[oldLayer.get() isOpaque]]; + [m_layer.get() setOpacity:[oldLayer.get() opacity]]; + [m_layer.get() setTransform:[oldLayer.get() transform]]; + [m_layer.get() setSublayerTransform:[oldLayer.get() sublayerTransform]]; + [m_layer.get() setDoubleSided:[oldLayer.get() isDoubleSided]]; +#ifndef NDEBUG + [m_layer.get() setZPosition:[oldLayer.get() zPosition]]; +#endif + +#ifndef NDEBUG + String name = String::format("CALayer(%p) GraphicsLayer(%p) ", m_layer.get(), this) + m_name; + [m_layer.get() setName:name]; +#endif + + // move over animations + moveAnimation(AnimatedPropertyWebkitTransform, oldLayer.get(), m_layer.get()); + moveAnimation(AnimatedPropertyOpacity, oldLayer.get(), m_layer.get()); + moveAnimation(AnimatedPropertyBackgroundColor, oldLayer.get(), m_layer.get()); + + // need to tell new layer to draw itself + setNeedsDisplay(); + + END_BLOCK_OBJC_EXCEPTIONS + + m_usingTiledLayer = userTiledLayer; + +#ifndef NDEBUG + updateDebugIndicators(); +#endif +} + +void GraphicsLayerCA::setHasContentsLayer(bool hasLayer) +{ + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (hasLayer && !m_contentsLayer) { + // create the inner layer + WebLayer* contentsLayer = [WebLayer layer]; +#ifndef NDEBUG + [contentsLayer setName:@"Contents Layer"]; +#endif + setContentsLayer(contentsLayer); + + } else if (!hasLayer && m_contentsLayer) + setContentsLayer(0); + + END_BLOCK_OBJC_EXCEPTIONS +} + +void GraphicsLayerCA::setContentsLayer(WebLayer* contentsLayer) +{ + if (contentsLayer == m_contentsLayer) + return; + + BEGIN_BLOCK_OBJC_EXCEPTIONS + + if (m_contentsLayer) { + [m_contentsLayer.get() removeFromSuperlayer]; + m_contentsLayer = 0; + } + + if (contentsLayer) { + // Turn off implicit animations on the inner layer. + [contentsLayer setStyle:[NSDictionary dictionaryWithObject:nullActionsDictionary() forKey:@"actions"]]; + + m_contentsLayer.adoptNS([contentsLayer retain]); + [m_contentsLayer.get() setAnchorPoint:CGPointZero]; + [m_layer.get() addSublayer:m_contentsLayer.get()]; + + updateContentsRect(); + + // Set contents to nil if the layer does not draw its own content. + if (m_client && !drawsContent()) + [m_layer.get() setContents:nil]; + +#ifndef NDEBUG + if (showDebugBorders()) { + setLayerBorderColor(m_contentsLayer.get(), Color(0, 0, 128, 180)); + [m_contentsLayer.get() setBorderWidth:1.0f]; + } +#endif + } +#ifndef NDEBUG + updateDebugIndicators(); +#endif + + END_BLOCK_OBJC_EXCEPTIONS +} + +} // namespace WebCore + + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h index 3f18ab4..677c31a 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * 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 @@ -28,7 +28,7 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" #include "Timer.h" #include <wtf/RetainPtr.h> @@ -52,11 +52,28 @@ class WebCoreMovieObserver; namespace WebCore { -class MediaPlayerPrivate : Noncopyable { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface { public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); + ~MediaPlayerPrivate(); - + + void repaint(); + void loadStateChanged(); + void rateChanged(); + void sizeChanged(); + void timeChanged(); + void didEnd(); + +private: + MediaPlayerPrivate(MediaPlayer*); + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + IntSize naturalSize() const; bool hasVideo() const; @@ -72,11 +89,12 @@ public: float duration() const; float currentTime() const; void seek(float time); - void setEndTime(float time); void setRate(float); void setVolume(float); - + + void setEndTime(float time); + int dataRate() const; MediaPlayer::NetworkState networkState() const { return m_networkState; } @@ -89,21 +107,10 @@ public: unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect& r); - - void loadStateChanged(); - void rateChanged(); - void sizeChanged(); - void timeChanged(); - void didEnd(); + void setSize(const IntSize&); - void repaint(); void paint(GraphicsContext*, const IntRect&); - - static void getSupportedTypes(HashSet<String>& types); - static bool isAvailable(); - -private: + void createQTMovie(const String& url); void setUpVideoRendering(); void tearDownVideoRendering(); @@ -117,10 +124,10 @@ private: void doSeek(); void cancelSeek(); void seekTimerFired(Timer<MediaPlayerPrivate>*); - void endPointTimerFired(Timer<MediaPlayerPrivate>*); float maxTimeLoaded() const; - void startEndPointTimerIfNeeded(); - void disableUnsupportedTracks(unsigned& enabledTrackCount); + void disableUnsupportedTracks(); + + bool metaDataAvailable() const { return m_qtMovie && m_readyState >= MediaPlayer::HaveMetadata; } MediaPlayer* m_player; RetainPtr<QTMovie> m_qtMovie; @@ -128,14 +135,15 @@ private: RetainPtr<QTVideoRendererWebKitOnly> m_qtVideoRenderer; RetainPtr<WebCoreMovieObserver> m_objcObserver; float m_seekTo; - float m_endTime; Timer<MediaPlayerPrivate> m_seekTimer; - Timer<MediaPlayerPrivate> m_endPointTimer; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; bool m_startedPlaying; bool m_isStreaming; bool m_visible; + IntRect m_rect; + unsigned m_enabledTrackCount; + float m_duration; #if DRAW_FRAME_RATE int m_frameCountWhilePlaying; double m_timeStartedPlaying; diff --git a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index a33c8d2..74a9ff9 100644 --- a/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * 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 @@ -69,6 +69,7 @@ SOFT_LINK_CLASS(QTKit, QTMovieView) SOFT_LINK_POINTER(QTKit, QTMediaTypeAttribute, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeBase, NSString *) +SOFT_LINK_POINTER(QTKit, QTMediaTypeMPEG, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeSound, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeText, NSString *) SOFT_LINK_POINTER(QTKit, QTMediaTypeVideo, NSString *) @@ -95,6 +96,7 @@ SOFT_LINK_POINTER(QTKit, QTVideoRendererWebKitOnlyNewImageAvailableNotification, #define QTMediaTypeAttribute getQTMediaTypeAttribute() #define QTMediaTypeBase getQTMediaTypeBase() +#define QTMediaTypeMPEG getQTMediaTypeMPEG() #define QTMediaTypeSound getQTMediaTypeSound() #define QTMediaTypeText getQTMediaTypeText() #define QTMediaTypeVideo getQTMediaTypeVideo() @@ -155,24 +157,35 @@ using namespace std; namespace WebCore { -static const float endPointTimerInterval = 0.020f; - #ifdef BUILDING_ON_TIGER static const long minimumQuickTimeVersion = 0x07300000; // 7.3 #endif + +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_objcObserver(AdoptNS, [[WebCoreMovieObserver alloc] initWithCallback:this]) , m_seekTo(-1) - , m_endTime(numeric_limits<float>::infinity()) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) - , m_endPointTimer(this, &MediaPlayerPrivate::endPointTimerFired) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) , m_startedPlaying(false) , m_isStreaming(false) , m_visible(false) + , m_rect() + , m_enabledTrackCount(0) + , m_duration(-1.0f) #if DRAW_FRAME_RATE , m_frameCountWhilePlaying(0) , m_timeStartedPlaying(0) @@ -270,7 +283,7 @@ void MediaPlayerPrivate::createQTMovieView() detachQTMovieView(); static bool addedCustomMethods = false; - if (!addedCustomMethods) { + if (!m_player->inMediaDocument() && !addedCustomMethods) { Class QTMovieContentViewClass = NSClassFromString(@"QTMovieContentView"); ASSERT(QTMovieContentViewClass); @@ -281,9 +294,12 @@ void MediaPlayerPrivate::createQTMovieView() addedCustomMethods = true; } + // delay callbacks as we *will* get notifications during setup + [m_objcObserver.get() setDelayCallbacks:YES]; + m_qtMovieView.adoptNS([[QTMovieView alloc] init]); - setRect(m_player->rect()); - NSView* parentView = m_player->m_frameView->documentView(); + setSize(m_player->size()); + NSView* parentView = m_player->frameView()->documentView(); [parentView addSubview:m_qtMovieView.get()]; #ifdef BUILDING_ON_TIGER // setDelegate: isn't a public call in Tiger, so use performSelector to keep the compiler happy @@ -303,6 +319,8 @@ void MediaPlayerPrivate::createQTMovieView() // Note that we expect mainThreadSetNeedsDisplay to be invoked only when synchronous drawing is requested. if (!m_player->inMediaDocument()) wkQTMovieViewSetDrawSynchronously(m_qtMovieView.get(), YES); + + [m_objcObserver.get() setDelayCallbacks:NO]; } void MediaPlayerPrivate::detachQTMovieView() @@ -356,7 +374,7 @@ void MediaPlayerPrivate::destroyQTVideoRenderer() void MediaPlayerPrivate::setUpVideoRendering() { - if (!m_player->m_frameView || !m_qtMovie) + if (!m_player->frameView() || !m_qtMovie) return; if (m_player->inMediaDocument() || !QTVideoRendererClass() ) @@ -375,7 +393,7 @@ void MediaPlayerPrivate::tearDownVideoRendering() QTTime MediaPlayerPrivate::createQTTime(float time) const { - if (!m_qtMovie) + if (!metaDataAvailable()) return QTMakeTime(0, 600); long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue]; return QTMakeTime(time * timeScale, timeScale); @@ -387,12 +405,11 @@ void MediaPlayerPrivate::load(const String& url) m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } cancelSeek(); - m_endPointTimer.stop(); [m_objcObserver.get() setDelayCallbacks:YES]; @@ -404,7 +421,7 @@ void MediaPlayerPrivate::load(const String& url) void MediaPlayerPrivate::play() { - if (!m_qtMovie) + if (!metaDataAvailable()) return; m_startedPlaying = true; #if DRAW_FRAME_RATE @@ -413,12 +430,11 @@ void MediaPlayerPrivate::play() [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() setRate:m_player->rate()]; [m_objcObserver.get() setDelayCallbacks:NO]; - startEndPointTimerIfNeeded(); } void MediaPlayerPrivate::pause() { - if (!m_qtMovie) + if (!metaDataAvailable()) return; m_startedPlaying = false; #if DRAW_FRAME_RATE @@ -427,12 +443,11 @@ void MediaPlayerPrivate::pause() [m_objcObserver.get() setDelayCallbacks:YES]; [m_qtMovie.get() stop]; [m_objcObserver.get() setDelayCallbacks:NO]; - m_endPointTimer.stop(); } float MediaPlayerPrivate::duration() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; QTTime time = [m_qtMovie.get() duration]; if (time.flags == kQTTimeIsIndefinite) @@ -442,22 +457,22 @@ float MediaPlayerPrivate::duration() const float MediaPlayerPrivate::currentTime() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; QTTime time = [m_qtMovie.get() currentTime]; - return min(static_cast<float>(time.timeValue) / time.timeScale, m_endTime); + return static_cast<float>(time.timeValue) / time.timeScale; } void MediaPlayerPrivate::seek(float time) { cancelSeek(); - if (!m_qtMovie) + if (!metaDataAvailable()) return; if (time > duration()) time = duration(); - + m_seekTo = time; if (maxTimeLoaded() >= m_seekTo) doSeek(); @@ -475,7 +490,7 @@ void MediaPlayerPrivate::doSeek() [m_qtMovie.get() setCurrentTime:qttime]; float timeAfterSeek = currentTime(); // restore playback only if not at end, othewise QTMovie will loop - if (timeAfterSeek < duration() && timeAfterSeek < m_endTime) + if (oldRate && timeAfterSeek < duration()) [m_qtMovie.get() setRate:oldRate]; cancelSeek(); [m_objcObserver.get() setDelayCallbacks:NO]; @@ -489,7 +504,7 @@ void MediaPlayerPrivate::cancelSeek() void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) { - if (!m_qtMovie || !seeking() || currentTime() == m_seekTo) { + if (!metaDataAvailable()|| !seeking() || currentTime() == m_seekTo) { cancelSeek(); updateStates(); m_player->timeChanged(); @@ -508,67 +523,48 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) } } -void MediaPlayerPrivate::setEndTime(float time) +void MediaPlayerPrivate::setEndTime(float) { - m_endTime = time; - startEndPointTimerIfNeeded(); -} - -void MediaPlayerPrivate::startEndPointTimerIfNeeded() -{ - if (m_endTime < duration() && m_startedPlaying && !m_endPointTimer.isActive()) - m_endPointTimer.startRepeating(endPointTimerInterval); -} - -void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*) -{ - float time = currentTime(); - - // just do end for now - if (time >= m_endTime) { - pause(); - didEnd(); - } } bool MediaPlayerPrivate::paused() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return true; return [m_qtMovie.get() rate] == 0; } bool MediaPlayerPrivate::seeking() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return false; return m_seekTo >= 0; } IntSize MediaPlayerPrivate::naturalSize() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return IntSize(); return IntSize([[m_qtMovie.get() attributeForKey:QTMovieNaturalSizeAttribute] sizeValue]); } bool MediaPlayerPrivate::hasVideo() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return false; return [[m_qtMovie.get() attributeForKey:QTMovieHasVideoAttribute] boolValue]; } void MediaPlayerPrivate::setVolume(float volume) { - if (!m_qtMovie) + if (!metaDataAvailable()) return; [m_qtMovie.get() setVolume:volume]; } void MediaPlayerPrivate::setRate(float rate) { - if (!m_qtMovie) + if (!metaDataAvailable()) return; if (!paused()) [m_qtMovie.get() setRate:rate]; @@ -576,7 +572,7 @@ void MediaPlayerPrivate::setRate(float rate) int MediaPlayerPrivate::dataRate() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; return wkQTMovieDataRate(m_qtMovie.get()); } @@ -596,7 +592,7 @@ float MediaPlayerPrivate::maxTimeSeekable() const float MediaPlayerPrivate::maxTimeLoaded() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; return wkQTMovieMaxTimeLoaded(m_qtMovie.get()); } @@ -616,7 +612,7 @@ bool MediaPlayerPrivate::totalBytesKnown() const unsigned MediaPlayerPrivate::totalBytes() const { - if (!m_qtMovie) + if (!metaDataAvailable()) return 0; return [[m_qtMovie.get() attributeForKey:QTMovieDataSizeAttribute] intValue]; } @@ -639,52 +635,66 @@ void MediaPlayerPrivate::updateStates() MediaPlayer::ReadyState oldReadyState = m_readyState; long loadState = m_qtMovie ? [[m_qtMovie.get() attributeForKey:QTMovieLoadStateAttribute] longValue] : static_cast<long>(QTMovieLoadStateError); - - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { - unsigned enabledTrackCount; - disableUnsupportedTracks(enabledTrackCount); - // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> - if (!enabledTrackCount) + + if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata && !m_player->inMediaDocument()) { + disableUnsupportedTracks(); + if (!m_enabledTrackCount) loadState = QTMovieLoadStateError; } - // "Loaded" is reserved for fully buffered movies, never the case when streaming if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { - if (m_networkState < MediaPlayer::Loaded) - m_networkState = MediaPlayer::Loaded; - m_readyState = MediaPlayer::CanPlayThrough; + // "Loaded" is reserved for fully buffered movies, never the case when streaming + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; } else if (loadState >= QTMovieLoadStatePlaythroughOK) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveFutureData; + m_networkState = MediaPlayer::Loading; } else if (loadState >= QTMovieLoadStatePlayable) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> - m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::CanPlay : MediaPlayer::DataUnavailable; + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; + m_networkState = MediaPlayer::Loading; } else if (loadState >= QTMovieLoadStateLoaded) { - if (m_networkState < MediaPlayer::LoadedMetaData) - m_networkState = MediaPlayer::LoadedMetaData; - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveMetadata; + m_networkState = MediaPlayer::Loading; } else if (loadState > QTMovieLoadStateError) { - if (m_networkState < MediaPlayer::Loading) - m_networkState = MediaPlayer::Loading; - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; + m_networkState = MediaPlayer::Loading; } else { - m_networkState = MediaPlayer::LoadFailed; - m_readyState = MediaPlayer::DataUnavailable; + float loaded = maxTimeLoaded(); + + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; - + m_readyState = MediaPlayer::HaveNothing; + if (m_networkState != oldNetworkState) m_player->networkStateChanged(); if (m_readyState != oldReadyState) m_player->readyStateChanged(); - if (loadState >= QTMovieLoadStateLoaded && oldNetworkState < MediaPlayer::LoadedMetaData && m_player->visible()) + if (loadState >= QTMovieLoadStateLoaded && oldReadyState < MediaPlayer::HaveMetadata && m_player->visible()) setUpVideoRendering(); + + if (loadState >= QTMovieLoadStateLoaded) { + float dur = duration(); + if (dur != m_duration) { + if (m_duration != -1.0f) + m_player->durationChanged(); + m_duration = dur; + } + } } void MediaPlayerPrivate::loadStateChanged() @@ -695,10 +705,12 @@ void MediaPlayerPrivate::loadStateChanged() void MediaPlayerPrivate::rateChanged() { updateStates(); + m_player->rateChanged(); } void MediaPlayerPrivate::sizeChanged() { + m_player->sizeChanged(); } void MediaPlayerPrivate::timeChanged() @@ -709,7 +721,6 @@ void MediaPlayerPrivate::timeChanged() void MediaPlayerPrivate::didEnd() { - m_endPointTimer.stop(); m_startedPlaying = false; #if DRAW_FRAME_RATE m_timeStoppedPlaying = [NSDate timeIntervalSinceReferenceDate]; @@ -718,18 +729,19 @@ void MediaPlayerPrivate::didEnd() m_player->timeChanged(); } -void MediaPlayerPrivate::setRect(const IntRect& r) +void MediaPlayerPrivate::setSize(const IntSize& size) { if (!m_qtMovieView) return; + m_rect.setSize(size); if (m_player->inMediaDocument()) // We need the QTMovieView to be placed in the proper location for document mode. - [m_qtMovieView.get() setFrame:r]; + [m_qtMovieView.get() setFrame:m_rect]; else { // We don't really need the QTMovieView in any specific location so let's just get it out of the way // where it won't intercept events or try to bring up the context menu. - IntRect farAwayButCorrectSize(r); + IntRect farAwayButCorrectSize(m_rect); farAwayButCorrectSize.move(-1000000, -1000000); [m_qtMovieView.get() setFrame:farAwayButCorrectSize]; } @@ -740,7 +752,7 @@ void MediaPlayerPrivate::setVisible(bool b) if (m_visible != b) { m_visible = b; if (b) { - if (m_networkState >= MediaPlayer::LoadedMetaData) + if (m_readyState >= MediaPlayer::HaveMetadata) setUpVideoRendering(); } else tearDownVideoRendering(); @@ -789,13 +801,19 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) [NSGraphicsContext setCurrentContext:newContext]; [(id<WebKitVideoRenderingDetails>)qtVideoRenderer drawInRect:paintRect]; [NSGraphicsContext restoreGraphicsState]; - } else + } else { + if (m_player->inMediaDocument() && r != m_rect) { + // the QTMovieView needs to be placed in the proper location for document mode + m_rect = r; + [view setFrame:m_rect]; + } [view displayRectIgnoringOpacity:paintRect inContext:newContext]; + } #if DRAW_FRAME_RATE // Draw the frame rate only after having played more than 10 frames. if (m_frameCountWhilePlaying > 10) { - Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL; Document* document = frame ? frame->document() : NULL; RenderObject* renderer = document ? document->renderer() : NULL; RenderStyle* styleToUse = renderer ? renderer->style() : NULL; @@ -821,22 +839,42 @@ void MediaPlayerPrivate::paint(GraphicsContext* context, const IntRect& r) [m_objcObserver.get() setDelayCallbacks:NO]; } -void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +static HashSet<String> mimeTypeCache() { - NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes]; - int count = [fileTypes count]; - for (int n = 0; n < count; n++) { - CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]); - RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL)); - if (!uti) - continue; - RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType)); - if (!mime) - continue; - types.add(mime.get()); + DEFINE_STATIC_LOCAL(HashSet<String>, cache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + NSArray* fileTypes = [QTMovie movieFileTypes:QTIncludeCommonTypes]; + int count = [fileTypes count]; + for (int n = 0; n < count; n++) { + CFStringRef ext = reinterpret_cast<CFStringRef>([fileTypes objectAtIndex:n]); + RetainPtr<CFStringRef> uti(AdoptCF, UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext, NULL)); + if (!uti) + continue; + RetainPtr<CFStringRef> mime(AdoptCF, UTTypeCopyPreferredTagWithClass(uti.get(), kUTTagClassMIMEType)); + if (!mime) + continue; + cache.add(mime.get()); + } + typeListInitialized = true; } -} + return cache; +} + +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +{ + types = mimeTypeCache(); +} + +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type yet + return mimeTypeCache().contains(type) ? (codecs && !codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + bool MediaPlayerPrivate::isAvailable() { #ifdef BUILDING_ON_TIGER @@ -858,10 +896,10 @@ bool MediaPlayerPrivate::isAvailable() #endif } -void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) +void MediaPlayerPrivate::disableUnsupportedTracks() { if (!m_qtMovie) { - enabledTrackCount = 0; + m_enabledTrackCount = 0; return; } @@ -872,6 +910,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) allowedTrackTypes->add(QTMediaTypeSound); allowedTrackTypes->add(QTMediaTypeText); allowedTrackTypes->add(QTMediaTypeBase); + allowedTrackTypes->add(QTMediaTypeMPEG); allowedTrackTypes->add("clcp"); allowedTrackTypes->add("sbtl"); } @@ -879,7 +918,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) NSArray *tracks = [m_qtMovie.get() tracks]; unsigned trackCount = [tracks count]; - enabledTrackCount = trackCount; + m_enabledTrackCount = trackCount; for (unsigned trackIndex = 0; trackIndex < trackCount; trackIndex++) { // Grab the track at the current index. If there isn't one there, then // we can move onto the next one. @@ -907,7 +946,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) if (!allowedTrackTypes->contains(mediaType)) { // If this track type is not allowed, then we need to disable it. [track setEnabled:NO]; - --enabledTrackCount; + --m_enabledTrackCount; } // Disable chapter tracks. These are most likely to lead to trouble, as @@ -939,7 +978,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) // Disable the evil, evil track. [chapterTrack setEnabled:NO]; - --enabledTrackCount; + --m_enabledTrackCount; } } @@ -947,7 +986,7 @@ void MediaPlayerPrivate::disableUnsupportedTracks(unsigned& enabledTrackCount) @implementation WebCoreMovieObserver -- (id)initWithCallback:(MediaPlayerPrivate *)callback +- (id)initWithCallback:(MediaPlayerPrivate*)callback { m_callback = callback; return [super init]; diff --git a/WebCore/platform/graphics/mac/MediaPlayerProxy.h b/WebCore/platform/graphics/mac/MediaPlayerProxy.h new file mode 100644 index 0000000..6060484 --- /dev/null +++ b/WebCore/platform/graphics/mac/MediaPlayerProxy.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaPlayerProxy_h +#define MediaPlayerProxy_h + +#ifdef __OBJC__ +@class WebMediaPlayerProxy; +#else +class WebMediaPlayerProxy; +#endif + +enum MediaPlayerProxyNotificationType { + + MediaPlayerNotificationMediaValidated = 1, + MediaPlayerNotificationMediaFailedToValidate, + + MediaPlayerNotificationStartUsingNetwork, + MediaPlayerNotificationStopUsingNetwork, + + MediaPlayerNotificationEnteredFullScreen, + MediaPlayerNotificationExitedFullScreen, + + MediaPlayerNotificationReadyForInspection, + MediaPlayerNotificationReadyForPlayback, + MediaPlayerNotificationDidPlayToTheEnd, + + MediaPlayerNotificationPlaybackFailed, + + MediaPlayerNotificationStreamLikelyToKeepUp, + MediaPlayerNotificationStreamUnlikelyToKeepUp, + MediaPlayerNotificationStreamBufferFull, + MediaPlayerNotificationStreamRanDry, + MediaPlayerNotificationFileLoaded, + + MediaPlayerNotificationSizeDidChange, + MediaPlayerNotificationVolumeDidChange, + MediaPlayerNotificationMutedDidChange, + MediaPlayerNotificationTimeJumped, + + MediaPlayerNotificationPlayPauseButtonPressed, +}; + +#ifdef __OBJC__ +@interface NSObject (WebMediaPlayerProxy) + +- (int)_interfaceVersion; + +- (void)_disconnect; + +- (void)_load:(NSURL *)url; +- (void)_cancelLoad; + +- (void)_setPoster:(NSURL *)url; + +- (void)_play; +- (void)_pause; + +- (NSSize)_naturalSize; + +- (BOOL)_hasVideo; +- (BOOL)_hasAudio; + +- (NSTimeInterval)_duration; + +- (double)_currentTime; +- (void)_setCurrentTime:(double)time; +- (BOOL)_seeking; + +- (void)_setEndTime:(double)time; + +- (float)_rate; +- (void)_setRate:(float)rate; + +- (float)_volume; +- (void)_setVolume:(float)newVolume; + +- (BOOL)_muted; +- (void)_setMuted:(BOOL)muted; + +- (float)_maxTimeBuffered; +- (float)_maxTimeSeekable; +- (NSArray *)_bufferedTimeRanges; + +- (int)_dataRate; + +- (BOOL)_totalBytesKnown; +- (unsigned)_totalBytes; +- (unsigned)_bytesLoaded; + +- (NSArray *)_mimeTypes; + +@end +#endif + +#endif diff --git a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 30dbf97..a3c10fa 100644 --- a/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -38,6 +38,7 @@ #import "FontDescription.h" #import "SharedBuffer.h" #import "WebCoreSystemInterface.h" +#import <AppKit/AppKit.h> #import <ApplicationServices/ApplicationServices.h> #import <float.h> #import <unicode/uchar.h> diff --git a/WebCore/platform/graphics/mac/WebLayer.h b/WebCore/platform/graphics/mac/WebLayer.h new file mode 100644 index 0000000..b8b46ed --- /dev/null +++ b/WebCore/platform/graphics/mac/WebLayer.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebLayer_h +#define WebLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import <QuartzCore/QuartzCore.h> + +namespace WebCore { + class GraphicsLayer; +} + +// Category implemented by WebLayer and WebTiledLayer. +@interface CALayer(WebLayerAdditions) + +- (void)setLayerOwner:(WebCore::GraphicsLayer*)layer; +- (WebCore::GraphicsLayer*)layerOwner; + +@end + +@interface WebLayer : CALayer +{ + WebCore::GraphicsLayer* m_layerOwner; +} + +// Class method allows us to share implementation across TiledLayerMac and WebLayer ++ (void)drawContents:(WebCore::GraphicsLayer*)layerContents ofLayer:(CALayer*)layer intoContext:(CGContextRef)context; + +@end + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebLayer_h diff --git a/WebCore/platform/graphics/mac/WebLayer.mm b/WebCore/platform/graphics/mac/WebLayer.mm new file mode 100644 index 0000000..267b5bc --- /dev/null +++ b/WebCore/platform/graphics/mac/WebLayer.mm @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebLayer.h" + +#import "GraphicsContext.h" +#import "GraphicsLayer.h" +#import <QuartzCore/QuartzCore.h> +#import "WebCoreTextRenderer.h" +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebLayer + ++ (void)drawContents:(WebCore::GraphicsLayer*)layerContents ofLayer:(CALayer*)layer intoContext:(CGContextRef)context +{ + UNUSED_PARAM(layer); + CGContextSaveGState(context); + + if (layerContents && layerContents->client()) { + [NSGraphicsContext saveGraphicsState]; + + // Set up an NSGraphicsContext for the context, so that parts of AppKit that rely on + // the current NSGraphicsContext (e.g. NSCell drawing) get the right one. + NSGraphicsContext* layerContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]; + [NSGraphicsContext setCurrentContext:layerContext]; + + GraphicsContext graphicsContext(context); + + // It's important to get the clip from the context, because it may be significantly + // smaller than the layer bounds (e.g. tiled layers) + CGRect clipBounds = CGContextGetClipBoundingBox(context); + IntRect clip(enclosingIntRect(clipBounds)); + layerContents->paintGraphicsLayerContents(graphicsContext, clip); + + [NSGraphicsContext restoreGraphicsState]; + } +#ifndef NDEBUG + else { + ASSERT_NOT_REACHED(); + + // FIXME: ideally we'd avoid calling -setNeedsDisplay on a layer that is a plain color, + // so CA never makes backing store for it (which is what -setNeedsDisplay will do above). + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 1.0f); + CGRect aBounds = [layer bounds]; + CGContextFillRect(context, aBounds); + } +#endif + + CGContextRestoreGState(context); + +#ifndef NDEBUG + if (layerContents && layerContents->showRepaintCounter()) { + bool isTiledLayer = [layer isKindOfClass:[CATiledLayer class]]; + + char text[16]; // that's a lot of repaints + snprintf(text, sizeof(text), "%d", layerContents->incrementRepaintCount()); + + CGAffineTransform a = CGContextGetCTM(context); + + CGContextSaveGState(context); + if (isTiledLayer) + CGContextSetRGBFillColor(context, 0.0f, 1.0f, 0.0f, 0.8f); + else + CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 0.8f); + + CGRect aBounds = [layer bounds]; + + aBounds.size.width = 10 + 12 * strlen(text); + aBounds.size.height = 25; + CGContextFillRect(context, aBounds); + + CGContextSetRGBFillColor(context, 0.0f, 0.0f, 0.0f, 1.0f); + + CGContextSetTextMatrix(context, CGAffineTransformMakeScale(1.0f, -1.0f)); + CGContextSelectFont(context, "Helvetica", 25, kCGEncodingMacRoman); + CGContextShowTextAtPoint(context, aBounds.origin.x + 3.0f, aBounds.origin.y + 20.0f, text, strlen(text)); + + CGContextRestoreGState(context); + } +#endif +} + +// Disable default animations +- (id<CAAction>)actionForKey:(NSString *)key +{ + UNUSED_PARAM(key); + return nil; +} + +// Implement this so presentationLayer can get our custom attributes +- (id)initWithLayer:(id)layer +{ + if ((self = [super initWithLayer:layer])) + m_layerOwner = [(WebLayer*)layer layerOwner]; + + return self; +} + +- (void)setNeedsDisplay +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) + [super setNeedsDisplay]; +} + +- (void)setNeedsDisplayInRect:(CGRect)dirtyRect +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { + [super setNeedsDisplayInRect:dirtyRect]; + +#ifndef NDEBUG + if (m_layerOwner->showRepaintCounter()) { + CGRect bounds = [self bounds]; + [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + } +#endif + } +} + +- (void)drawInContext:(CGContextRef)context +{ + [WebLayer drawContents:m_layerOwner ofLayer:self intoContext:context]; +} + +@end // implementation WebLayer + +#pragma mark - + +@implementation WebLayer(WebLayerAdditions) + +- (void)setLayerOwner:(GraphicsLayer*)aLayer +{ + m_layerOwner = aLayer; +} + +- (GraphicsLayer*)layerOwner +{ + return m_layerOwner; +} + +@end + +#pragma mark - + +#ifndef NDEBUG + +@implementation CALayer(ExtendedDescription) + +- (NSString*)_descriptionWithPrefix:(NSString*)inPrefix +{ + CGRect aBounds = [self bounds]; + CGPoint aPos = [self position]; + CATransform3D t = [self transform]; + + NSString* selfString = [NSString stringWithFormat:@"%@<%@ 0x%08x> \"%@\" bounds(%.1f, %.1f, %.1f, %.1f) pos(%.1f, %.1f), sublayers=%d masking=%d", + inPrefix, + [self class], + self, + [self name], + aBounds.origin.x, aBounds.origin.y, aBounds.size.width, aBounds.size.height, + aPos.x, aPos.y, + [[self sublayers] count], + [self masksToBounds]]; + + NSMutableString* curDesc = [NSMutableString stringWithString:selfString]; + + if ([[self sublayers] count] > 0) + [curDesc appendString:@"\n"]; + + NSString* sublayerPrefix = [inPrefix stringByAppendingString:@"\t"]; + + NSEnumerator* sublayersEnum = [[self sublayers] objectEnumerator]; + CALayer* curLayer; + while ((curLayer = [sublayersEnum nextObject])) + [curDesc appendString:[curLayer _descriptionWithPrefix:sublayerPrefix]]; + + if ([[self sublayers] count] == 0) + [curDesc appendString:@"\n"]; + + return curDesc; +} + +- (NSString*)extendedDescription +{ + return [self _descriptionWithPrefix:@""]; +} + +@end // implementation WebLayer(ExtendedDescription) + +#endif // NDEBUG + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.h b/WebCore/platform/graphics/mac/WebTiledLayer.h new file mode 100644 index 0000000..1c9144d --- /dev/null +++ b/WebCore/platform/graphics/mac/WebTiledLayer.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef WebTiledLayer_h +#define WebTiledLayer_h + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebLayer.h" + +@interface WebTiledLayer : CATiledLayer +{ + WebCore::GraphicsLayer* m_layerOwner; +} + +// implements WebLayerAdditions + +@end + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WebTiledLayer_h + diff --git a/WebCore/platform/graphics/mac/WebTiledLayer.mm b/WebCore/platform/graphics/mac/WebTiledLayer.mm new file mode 100644 index 0000000..1dd00ba --- /dev/null +++ b/WebCore/platform/graphics/mac/WebTiledLayer.mm @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if USE(ACCELERATED_COMPOSITING) + +#import "WebTiledLayer.h" + +#import "GraphicsContext.h" +#import "GraphicsLayer.h" +#import <wtf/UnusedParam.h> + +using namespace WebCore; + +@implementation WebTiledLayer + +// Set a zero duration for the fade in of tiles ++ (CFTimeInterval)fadeDuration +{ + return 0; +} + +// Make sure that tiles are drawn on the main thread ++ (BOOL)shouldDrawOnMainThread +{ + return YES; +} + +// Disable default animations +- (id<CAAction>)actionForKey:(NSString *)key +{ + UNUSED_PARAM(key); + return nil; +} + +// Implement this so presentationLayer can get our custom attributes +- (id)initWithLayer:(id)layer +{ + if ((self = [super initWithLayer:layer])) + m_layerOwner = [(WebLayer*)layer layerOwner]; + + return self; +} + +- (void)setNeedsDisplay +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) + [super setNeedsDisplay]; +} + +- (void)setNeedsDisplayInRect:(CGRect)dirtyRect +{ + if (m_layerOwner && m_layerOwner->client() && m_layerOwner->drawsContent()) { + [super setNeedsDisplayInRect:dirtyRect]; + +#ifndef NDEBUG + if (m_layerOwner->showRepaintCounter()) { + CGRect bounds = [self bounds]; + [super setNeedsDisplayInRect:CGRectMake(bounds.origin.x, bounds.origin.y, 46, 25)]; + } +#endif + } +} + +- (void)drawInContext:(CGContextRef)ctx +{ + [WebLayer drawContents:m_layerOwner ofLayer:self intoContext:ctx]; +} + +@end // implementation WebTiledLayer + +#pragma mark - + +@implementation WebTiledLayer(LayerMacAdditions) + +- (void)setLayerOwner:(GraphicsLayer*)aLayer +{ + m_layerOwner = aLayer; +} + +- (GraphicsLayer*)layerOwner +{ + return m_layerOwner; +} + +@end + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.cpp b/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp index 1951320..16c3c00 100644 --- a/WebCore/platform/graphics/win/OpenTypeUtilities.cpp +++ b/WebCore/platform/graphics/opentype/OpenTypeUtilities.cpp @@ -49,9 +49,9 @@ struct EOTPrefix { unsigned fontDataSize; unsigned version; unsigned flags; - UInt8 fontPANOSE[10]; - UInt8 charset; - UInt8 italic; + uint8_t fontPANOSE[10]; + uint8_t charset; + uint8_t italic; unsigned weight; unsigned short fsType; unsigned short magicNumber; @@ -69,6 +69,15 @@ struct TableDirectoryEntry { BigEndianULong length; }; +#if !PLATFORM(CG) +// Fixed type is not defined on non-CG platforms. |version| in sfntHeader +// and headTable and |fontRevision| in headTable are of Fixed, but they're +// not actually refered to anywhere. Therefore, we just have to match +// the size (4 bytes). For the definition of Fixed type, see +// http://developer.apple.com/documentation/mac/Legacy/GXEnvironment/GXEnvironment-356.html#HEADING356-6. +typedef int32_t Fixed; +#endif + struct sfntHeader { Fixed version; BigEndianUShort numTables; @@ -95,9 +104,9 @@ struct OS2Table { BigEndianUShort strikeoutSize; BigEndianUShort strikeoutPosition; BigEndianUShort familyClass; - UInt8 panose[10]; + uint8_t panose[10]; BigEndianULong unicodeRange[4]; - UInt8 vendID[4]; + uint8_t vendID[4]; BigEndianUShort fsSelection; BigEndianUShort firstCharIndex; BigEndianUShort lastCharIndex; @@ -152,7 +161,7 @@ struct nameTable { #pragma pack() -static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, const BigEndianUShort* string, unsigned short length) +static void appendBigEndianStringToEOTHeader(Vector<uint8_t, 512>& eotHeader, const BigEndianUShort* string, unsigned short length) { size_t size = eotHeader.size(); eotHeader.resize(size + length + 2 * sizeof(unsigned short)); @@ -165,7 +174,7 @@ static void appendBigEndianStringToEOTHeader(Vector<UInt8, 512>& eotHeader, cons dst[i] = 0; } -bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) +bool getEOTHeader(SharedBuffer* fontData, Vector<uint8_t, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength) { overlayDst = 0; overlaySrc = 0; @@ -311,7 +320,7 @@ bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& appendBigEndianStringToEOTHeader(eotHeader, fullName, fullNameLength); unsigned short padding = 0; - eotHeader.append(reinterpret_cast<UInt8*>(&padding), sizeof(padding)); + eotHeader.append(reinterpret_cast<uint8_t*>(&padding), sizeof(padding)); prefix->eotSize = eotHeader.size() + fontData->size(); diff --git a/WebCore/platform/graphics/win/OpenTypeUtilities.h b/WebCore/platform/graphics/opentype/OpenTypeUtilities.h index ab35551..a67ffc7 100644 --- a/WebCore/platform/graphics/win/OpenTypeUtilities.h +++ b/WebCore/platform/graphics/opentype/OpenTypeUtilities.h @@ -33,7 +33,7 @@ namespace WebCore { class SharedBuffer; -bool getEOTHeader(SharedBuffer* fontData, Vector<UInt8, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); +bool getEOTHeader(SharedBuffer* fontData, Vector<uint8_t, 512>& eotHeader, size_t& overlayDst, size_t& overlaySrc, size_t& overlayLength); HANDLE renameAndActivateFont(SharedBuffer*, const String&); } // namespace WebCore diff --git a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp index ea51fe8..f0dd3ea 100644 --- a/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp +++ b/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -61,12 +61,14 @@ FontPlatformData::FontPlatformData(const QFont& font, bool bold) { } +#if ENABLE(SVG_FONTS) FontPlatformData::FontPlatformData(float size, bool bold, bool oblique) : m_size(size) , m_bold(bold) , m_oblique(oblique) { } +#endif FontPlatformData::FontPlatformData() : m_size(0.0f) diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index deeea99..9ed5915 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -1,6 +1,7 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) Copyright (C) 2008 Holger Hans Peter Freyther + Copyright (C) 2009 Dirk Schulze <krit@webkit.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -24,11 +25,18 @@ #include "FontFallbackList.h" #include "FontSelector.h" +#include "Gradient.h" #include "GraphicsContext.h" -#include <QTextLayout> -#include <QPainter> -#include <QFontMetrics> +#include "Pattern.h" +#include "TransformationMatrix.h" + +#include <QBrush> #include <QFontInfo> +#include <QFontMetrics> +#include <QPainter> +#include <QPainterPath> +#include <QPen> +#include <QTextLayout> #include <qalgorithms.h> #include <qdebug.h> @@ -37,20 +45,27 @@ #if QT_VERSION >= 0x040400 namespace WebCore { -static QString qstring(const TextRun& run) +static const QString qstring(const TextRun& run) +{ + //We don't detach + return QString::fromRawData((const QChar *)run.characters(), run.length()); +} + +static const QString fixSpacing(const QString &string) { - QString string((QChar *)run.characters(), run.length()); - QChar *uc = string.data(); + //Only detach if we're actually changing something + QString possiblyDetached = string; for (int i = 0; i < string.length(); ++i) { - if (Font::treatAsSpace(uc[i].unicode())) - uc[i] = 0x20; - else if (Font::treatAsZeroWidthSpace(uc[i].unicode())) - uc[i] = 0x200c; + const QChar c = string.at(i); + if (c.unicode() != 0x20 && Font::treatAsSpace(c.unicode())) { + possiblyDetached[i] = 0x20; //detach + } else if (c.unicode() != 0x200c && Font::treatAsZeroWidthSpace(c.unicode())) { + possiblyDetached[i] = 0x200c; //detach + } } - return string; + return possiblyDetached; } - static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) { int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; @@ -72,10 +87,32 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float to = run.length(); QPainter *p = ctx->platformContext(); - Color color = ctx->fillColor(); - p->setPen(QColor(color)); - QString string = qstring(run); + if (ctx->textDrawingMode() & cTextFill) { + if (ctx->fillGradient()) { + QBrush brush(*ctx->fillGradient()->platformGradient()); + brush.setTransform(ctx->fillGradient()->gradientSpaceTransform()); + p->setPen(QPen(brush, 0)); + } else if (ctx->fillPattern()) { + TransformationMatrix affine; + p->setPen(QPen(QBrush(ctx->fillPattern()->createPlatformPattern(affine)), 0)); + } else + p->setPen(QColor(ctx->fillColor())); + } + + if (ctx->textDrawingMode() & cTextStroke) { + if (ctx->strokeGradient()) { + QBrush brush(*ctx->strokeGradient()->platformGradient()); + brush.setTransform(ctx->strokeGradient()->gradientSpaceTransform()); + p->setPen(QPen(brush, ctx->strokeThickness())); + } else if (ctx->strokePattern()) { + TransformationMatrix affine; + p->setPen(QPen(QBrush(ctx->strokePattern()->createPlatformPattern(affine)), ctx->strokeThickness())); + } else + p->setPen(QPen(QColor(ctx->strokeColor()), ctx->strokeThickness())); + } + + const QString string = fixSpacing(qstring(run)); // text shadow IntSize shadowSize; @@ -137,14 +174,20 @@ void Font::drawComplexText(GraphicsContext* ctx, const TextRun& run, const Float p->drawText(pt, string, flags, run.padding()); p->restore(); } - p->drawText(pt, string, flags, run.padding()); + if (ctx->textDrawingMode() & cTextStroke) { + QPainterPath path; + path.addText(pt, font(), string); + p->strokePath(path, p->pen()); + } + if (ctx->textDrawingMode() & cTextFill) + p->drawText(pt, string, flags, run.padding()); } float Font::floatWidthForComplexText(const TextRun& run) const { if (!run.length()) return 0; - QString string = qstring(run); + const QString string = fixSpacing(qstring(run)); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); int w = int(line.naturalTextWidth()); @@ -157,7 +200,7 @@ float Font::floatWidthForComplexText(const TextRun& run) const int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool includePartialGlyphs) const { - QString string = qstring(run); + const QString string = fixSpacing(qstring(run)); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); return line.xToCursor(position); @@ -165,7 +208,7 @@ int Font::offsetForPositionForComplexText(const TextRun& run, int position, bool FloatRect Font::selectionRectForComplexText(const TextRun& run, const IntPoint& pt, int h, int from, int to) const { - QString string = qstring(run); + const QString string = fixSpacing(qstring(run)); QTextLayout layout(string, font()); QTextLine line = setupLayout(&layout, run); diff --git a/WebCore/platform/graphics/qt/GradientQt.cpp b/WebCore/platform/graphics/qt/GradientQt.cpp index a0edf8d..1e71f58 100644 --- a/WebCore/platform/graphics/qt/GradientQt.cpp +++ b/WebCore/platform/graphics/qt/GradientQt.cpp @@ -28,9 +28,10 @@ #include "Gradient.h" #include "CSSParser.h" -#include "NotImplemented.h" +#include "GraphicsContext.h" #include <QGradient> +#include <QPainter> namespace WebCore { @@ -66,12 +67,24 @@ QGradient* Gradient::platformGradient() ++stopIterator; } + switch(m_spreadMethod) { + case SpreadMethodPad: + m_gradient->setSpread(QGradient::PadSpread); + break; + case SpreadMethodReflect: + m_gradient->setSpread(QGradient::ReflectSpread); + break; + case SpreadMethodRepeat: + m_gradient->setSpread(QGradient::RepeatSpread); + break; + } + return m_gradient; } void Gradient::fill(GraphicsContext* context, const FloatRect& rect) { - notImplemented(); + context->platformContext()->fillRect(rect, *platformGradient()); } } //namespace diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 2e7cdcb..ccf4b06 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -51,6 +51,7 @@ #include "Pen.h" #include "NotImplemented.h" +#include <QBrush> #include <QDebug> #include <QGradient> #include <QPainter> @@ -152,22 +153,6 @@ static Qt::PenStyle toQPenStyle(StrokeStyle style) return Qt::NoPen; } -static inline QGradient applySpreadMethod(QGradient gradient, GradientSpreadMethod spreadMethod) -{ - switch (spreadMethod) { - case SpreadMethodPad: - gradient.setSpread(QGradient::PadSpread); - break; - case SpreadMethodReflect: - gradient.setSpread(QGradient::ReflectSpread); - break; - case SpreadMethodRepeat: - gradient.setSpread(QGradient::RepeatSpread); - break; - } - return gradient; -} - struct TransparencyLayer { TransparencyLayer(const QPainter* p, const QRect &rect) @@ -282,7 +267,11 @@ PlatformGraphicsContext* GraphicsContext::platformContext() const TransformationMatrix GraphicsContext::getCTM() const { - return platformContext()->combinedMatrix(); + QTransform matrix(platformContext()->combinedTransform()); + return TransformationMatrix(matrix.m11(), matrix.m12(), 0, matrix.m13(), + matrix.m21(), matrix.m22(), 0, matrix.m23(), + 0, 0, 1, 0, + matrix.m31(), matrix.m32(), 0, matrix.m33()); } void GraphicsContext::savePlatformState() @@ -295,7 +284,7 @@ void GraphicsContext::restorePlatformState() m_data->p()->restore(); if (!m_data->currentPath.isEmpty() && m_common->state.pathTransform.isInvertible()) { - QMatrix matrix = m_common->state.pathTransform; + QTransform matrix = m_common->state.pathTransform; m_data->currentPath = m_data->currentPath * matrix; } } @@ -458,13 +447,21 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) if (paintingDisabled()) return; + StrokeStyle style = strokeStyle(); + Color color = strokeColor(); + if (style == NoStroke || !color.alpha()) + return; + + float width = strokeThickness(); + FloatPoint p1 = point1; FloatPoint p2 = point2; + bool isVerticalLine = (p1.x() == p2.x()); QPainter *p = m_data->p(); const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); p->setRenderHint(QPainter::Antialiasing, m_data->antiAliasingForRectsAndLines); - adjustLineToPixelBoundaries(p1, p2, strokeThickness(), strokeStyle()); + adjustLineToPixelBoundaries(p1, p2, width, style); IntSize shadowSize; int shadowBlur; @@ -477,8 +474,76 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) p->restore(); } + int patWidth = 0; + switch (style) { + case NoStroke: + case SolidStroke: + break; + case DottedStroke: + patWidth = (int)width; + break; + case DashedStroke: + patWidth = 3 * (int)width; + break; + } + + if (patWidth) { + p->save(); + + // Do a rect fill of our endpoints. This ensures we always have the + // appearance of being a border. We then draw the actual dotted/dashed line. + if (isVerticalLine) { + p->fillRect(FloatRect(p1.x() - width / 2, p1.y() - width, width, width), QColor(color)); + p->fillRect(FloatRect(p2.x() - width / 2, p2.y(), width, width), QColor(color)); + } else { + p->fillRect(FloatRect(p1.x() - width, p1.y() - width / 2, width, width), QColor(color)); + p->fillRect(FloatRect(p2.x(), p2.y() - width / 2, width, width), QColor(color)); + } + + // Example: 80 pixels with a width of 30 pixels. + // Remainder is 20. The maximum pixels of line we could paint + // will be 50 pixels. + int distance = (isVerticalLine ? (point2.y() - point1.y()) : (point2.x() - point1.x())) - 2*(int)width; + int remainder = distance % patWidth; + int coverage = distance - remainder; + int numSegments = coverage / patWidth; + + float patternOffset = 0.0f; + // Special case 1px dotted borders for speed. + if (patWidth == 1) + patternOffset = 1.0f; + else { + bool evenNumberOfSegments = numSegments % 2 == 0; + if (remainder) + evenNumberOfSegments = !evenNumberOfSegments; + if (evenNumberOfSegments) { + if (remainder) { + patternOffset += patWidth - remainder; + patternOffset += remainder / 2; + } else + patternOffset = patWidth / 2; + } else { + if (remainder) + patternOffset = (patWidth - remainder)/2; + } + } + + QVector<qreal> dashes; + dashes << qreal(patWidth) / width << qreal(patWidth) / width; + + QPen pen = p->pen(); + pen.setWidthF(width); + pen.setCapStyle(Qt::FlatCap); + pen.setDashPattern(dashes); + pen.setDashOffset(patternOffset / width); + p->setPen(pen); + } + p->drawLine(p1, p2); + if (patWidth) + p->restore(); + p->setRenderHint(QPainter::Antialiasing, antiAlias); } @@ -553,9 +618,9 @@ void GraphicsContext::fillPath() break; } case GradientColorSpace: - QGradient* gradient = m_common->state.fillGradient->platformGradient(); - *gradient = applySpreadMethod(*gradient, spreadMethod()); - p->fillPath(path, QBrush(*gradient)); + QBrush brush(*m_common->state.fillGradient->platformGradient()); + brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform()); + p->fillPath(path, brush); break; } m_data->currentPath = QPainterPath(); @@ -583,9 +648,9 @@ void GraphicsContext::strokePath() break; } case GradientColorSpace: { - QGradient* gradient = m_common->state.strokeGradient->platformGradient(); - *gradient = applySpreadMethod(*gradient, spreadMethod()); - pen.setBrush(QBrush(*gradient)); + QBrush brush(*m_common->state.strokeGradient->platformGradient()); + brush.setTransform(m_common->state.strokeGradient->gradientSpaceTransform()); + pen.setBrush(brush); p->setPen(pen); p->strokePath(path, pen); break; @@ -612,7 +677,9 @@ void GraphicsContext::fillRect(const FloatRect& rect) break; } case GradientColorSpace: - p->fillRect(rect, QBrush(*(m_common->state.fillGradient.get()->platformGradient()))); + QBrush brush(*m_common->state.fillGradient->platformGradient()); + brush.setTransform(m_common->state.fillGradient->gradientSpaceTransform()); + p->fillRect(rect, brush); break; } m_data->currentPath = QPainterPath(); @@ -663,10 +730,7 @@ void GraphicsContext::clip(const FloatRect& rect) if (paintingDisabled()) return; - QPainter *p = m_data->p(); - if (p->clipRegion().isEmpty()) - p->setClipRect(rect); - else p->setClipRect(rect, Qt::IntersectClip); + m_data->p()->setClipRect(rect, Qt::IntersectClip); } void GraphicsContext::clipPath(WindRule clipRule) @@ -818,7 +882,7 @@ void GraphicsContext::clearRect(const FloatRect& rect) QPainter::CompositionMode currentCompositionMode = p->compositionMode(); if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) p->setCompositionMode(QPainter::CompositionMode_Source); - p->eraseRect(rect); + p->fillRect(rect, Qt::transparent); if (p->paintEngine()->hasFeature(QPaintEngine::PorterDuff)) p->setCompositionMode(currentCompositionMode); } @@ -939,7 +1003,7 @@ void GraphicsContext::translate(float x, float y) m_data->p()->translate(x, y); if (!m_data->currentPath.isEmpty()) { - QMatrix matrix; + QTransform matrix; m_data->currentPath = m_data->currentPath * matrix.translate(-x, -y); m_common->state.pathTransform.translate(x, y); } @@ -961,7 +1025,7 @@ void GraphicsContext::rotate(float radians) m_data->p()->rotate(180/M_PI*radians); if (!m_data->currentPath.isEmpty()) { - QMatrix matrix; + QTransform matrix; m_data->currentPath = m_data->currentPath * matrix.rotate(-180/M_PI*radians); m_common->state.pathTransform.rotate(radians); } @@ -975,9 +1039,9 @@ void GraphicsContext::scale(const FloatSize& s) m_data->p()->scale(s.width(), s.height()); if (!m_data->currentPath.isEmpty()) { - QMatrix matrix; + QTransform matrix; m_data->currentPath = m_data->currentPath * matrix.scale(1 / s.width(), 1 / s.height()); - m_common->state.pathTransform.scale(s.width(), s.height()); + m_common->state.pathTransform.scaleNonUniform(s.width(), s.height()); } } @@ -1041,12 +1105,12 @@ void GraphicsContext::concatCTM(const TransformationMatrix& transform) if (paintingDisabled()) return; - m_data->p()->setMatrix(transform, true); + m_data->p()->setWorldTransform(transform, true); // Transformations to the context shouldn't transform the currentPath. // We have to undo every change made to the context from the currentPath to avoid wrong drawings. if (!m_data->currentPath.isEmpty() && transform.isInvertible()) { - QMatrix matrix = transform.inverse(); + QTransform matrix = transform.inverse(); m_data->currentPath = m_data->currentPath * matrix; m_common->state.pathTransform.multiply(transform); } diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index d4ab59f..d748305 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> * Copyright (C) 2008 Holger Hans Peter Freyther + * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,10 +32,11 @@ #include "GraphicsContext.h" #include "ImageData.h" #include "MIMETypeRegistry.h" -#include "NotImplemented.h" #include "StillImageQt.h" #include <QBuffer> +#include <QColor> +#include <QImage> #include <QImageWriter> #include <QPainter> #include <QPixmap> @@ -79,15 +81,108 @@ Image* ImageBuffer::image() const return m_image.get(); } -PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect&) const +PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const { - notImplemented(); - return 0; + PassRefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); + unsigned char* data = result->data()->data()->data(); + + if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || (rect.y() + rect.height()) > m_size.height()) + memset(data, 0, result->data()->length()); + + int originx = rect.x(); + int destx = 0; + if (originx < 0) { + destx = -originx; + originx = 0; + } + int endx = rect.x() + rect.width(); + if (endx > m_size.width()) + endx = m_size.width(); + int numColumns = endx - originx; + + int originy = rect.y(); + int desty = 0; + if (originy < 0) { + desty = -originy; + originy = 0; + } + int endy = rect.y() + rect.height(); + if (endy > m_size.height()) + endy = m_size.height(); + int numRows = endy - originy; + + QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + ASSERT(!image.isNull()); + + unsigned destBytesPerRow = 4 * rect.width(); + unsigned char* destRows = data + desty * destBytesPerRow + destx * 4; + for (int y = 0; y < numRows; ++y) { + for (int x = 0; x < numColumns; x++) { + QRgb value = image.pixel(x + originx, y + originy); + int basex = x * 4; + + destRows[basex] = qRed(value); + destRows[basex + 1] = qGreen(value); + destRows[basex + 2] = qBlue(value); + destRows[basex + 3] = qAlpha(value); + } + destRows += destBytesPerRow; + } + + return result; } -void ImageBuffer::putImageData(ImageData*, const IntRect&, const IntPoint&) +void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) { - notImplemented(); + ASSERT(sourceRect.width() > 0); + ASSERT(sourceRect.height() > 0); + + int originx = sourceRect.x(); + int destx = destPoint.x() + sourceRect.x(); + ASSERT(destx >= 0); + ASSERT(destx < m_size.width()); + ASSERT(originx >= 0); + ASSERT(originx <= sourceRect.right()); + + int endx = destPoint.x() + sourceRect.right(); + ASSERT(endx <= m_size.width()); + + int numColumns = endx - destx; + + int originy = sourceRect.y(); + int desty = destPoint.y() + sourceRect.y(); + ASSERT(desty >= 0); + ASSERT(desty < m_size.height()); + ASSERT(originy >= 0); + ASSERT(originy <= sourceRect.bottom()); + + int endy = destPoint.y() + sourceRect.bottom(); + ASSERT(endy <= m_size.height()); + int numRows = endy - desty; + + unsigned srcBytesPerRow = 4 * source->width(); + + bool isPainting = m_data.m_painter->isActive(); + if (isPainting) + m_data.m_painter->end(); + + QImage image = m_data.m_pixmap.toImage().convertToFormat(QImage::Format_ARGB32); + + unsigned char* srcRows = source->data()->data()->data() + originy * srcBytesPerRow + originx * 4; + for (int y = 0; y < numRows; ++y) { + quint32* scanLine = reinterpret_cast<quint32*>(image.scanLine(y + desty)); + for (int x = 0; x < numColumns; x++) { + int basex = x * 4; + scanLine[x + destx] = reinterpret_cast<quint32*>(srcRows + basex)[0]; + } + + srcRows += srcBytesPerRow; + } + + m_data.m_pixmap = QPixmap::fromImage(image); + + if (isPainting) + m_data.m_painter->begin(&m_data.m_pixmap); } // We get a mimeType here but QImageWriter does not support mimetypes but diff --git a/WebCore/platform/graphics/qt/ImageQt.cpp b/WebCore/platform/graphics/qt/ImageQt.cpp index 99062f9..3bc67ae 100644 --- a/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/WebCore/platform/graphics/qt/ImageQt.cpp @@ -2,6 +2,7 @@ * Copyright (C) 2006 Dirk Mueller <mueller@kde.org> * Copyright (C) 2006 Zack Rusin <zack@kde.org> * Copyright (C) 2006 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ * * All rights reserved. * @@ -96,7 +97,26 @@ PassRefPtr<Image> Image::loadPlatformResource(const char* name) void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) { - notImplemented(); + QPixmap* framePixmap = nativeImageForCurrentFrame(); + if (!framePixmap) // If it's too early we won't have an image yet. + return; + + QPixmap pixmap = *framePixmap; + QRect tr = QRectF(tileRect).toRect(); + if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) { + pixmap = pixmap.copy(tr); + } + + QBrush b(pixmap); + b.setTransform(patternTransform); + ctxt->save(); + ctxt->setCompositeOperation(op); + QPainter* p = ctxt->platformContext(); + if (!pixmap.hasAlpha() && p->compositionMode() == QPainter::CompositionMode_SourceOver) + p->setCompositionMode(QPainter::CompositionMode_Source); + p->setBrushOrigin(phase); + p->fillRect(destRect, b); + ctxt->restore(); } void BitmapImage::initPlatformData() @@ -131,6 +151,9 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, QPainter* painter(ctxt->platformContext()); + if (!image->hasAlpha() && painter->compositionMode() == QPainter::CompositionMode_SourceOver) + painter->setCompositionMode(QPainter::CompositionMode_Source); + // Test using example site at // http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html painter->drawPixmap(dst, *image, src); @@ -138,33 +161,20 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, ctxt->restore(); } -void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const TransformationMatrix& patternTransform, - const FloatPoint& phase, CompositeOperator op, const FloatRect& destRect) +void BitmapImage::checkForSolidColor() { - QPixmap* framePixmap = nativeImageForCurrentFrame(); - if (!framePixmap) // If it's too early we won't have an image yet. - return; + m_isSolidColor = false; + m_checkedForSolidColor = true; - QPixmap pixmap = *framePixmap; - QRect tr = QRectF(tileRect).toRect(); - if (tr.x() || tr.y() || tr.width() != pixmap.width() || tr.height() != pixmap.height()) { - pixmap = pixmap.copy(tr); - } + if (frameCount() > 1) + return; - QBrush b(pixmap); - b.setMatrix(patternTransform); - ctxt->save(); - ctxt->setCompositeOperation(op); - QPainter* p = ctxt->platformContext(); - p->setBrushOrigin(phase); - p->fillRect(destRect, b); - ctxt->restore(); -} + QPixmap* framePixmap = frameAtIndex(0); + if (!framePixmap || framePixmap->width() != 1 || framePixmap->height() != 1) + return; -void BitmapImage::checkForSolidColor() -{ - // FIXME: It's easy to implement this optimization. Just need to check the RGBA32 buffer to see if it is 1x1. - m_isSolidColor = false; + m_isSolidColor = true; + m_solidColor = QColor::fromRgba(framePixmap->toImage().pixel(0, 0)); } } diff --git a/WebCore/platform/graphics/qt/ImageSourceQt.cpp b/WebCore/platform/graphics/qt/ImageSourceQt.cpp index d62acc3..84de443 100644 --- a/WebCore/platform/graphics/qt/ImageSourceQt.cpp +++ b/WebCore/platform/graphics/qt/ImageSourceQt.cpp @@ -163,7 +163,7 @@ void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* delete m_decoder; m_decoder = 0; if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } } diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index b1a48fb..c80d73b 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -76,7 +77,7 @@ namespace WebCore { MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) , m_mediaObject(new MediaObject()) , m_videoWidget(new VideoWidget(0)) , m_audioOutput(new AudioOutput()) @@ -110,6 +111,18 @@ MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) connect(m_mediaObject, SIGNAL(totalTimeChanged(qint64)), this, SLOT(totalTimeChanged(qint64))); } +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + + MediaPlayerPrivate::~MediaPlayerPrivate() { LOG(Media, "MediaPlayerPrivatePhonon::dtor deleting videowidget"); @@ -131,6 +144,13 @@ void MediaPlayerPrivate::getSupportedTypes(HashSet<String>&) notImplemented(); } +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // FIXME: do the real thing + notImplemented(); + return MediaPlayer::IsNotSupported; +} + bool MediaPlayerPrivate::hasVideo() const { bool hasVideo = m_mediaObject->hasVideo(); @@ -138,7 +158,7 @@ bool MediaPlayerPrivate::hasVideo() const return hasVideo; } -void MediaPlayerPrivate::load(String url) +void MediaPlayerPrivate::load(const String& url) { LOG(Media, "MediaPlayerPrivatePhonon::load(\"%s\")", url.utf8().data()); @@ -149,8 +169,8 @@ void MediaPlayerPrivate::load(String url) } // And we don't have any data yet - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } @@ -205,7 +225,7 @@ bool MediaPlayerPrivate::seeking() const float MediaPlayerPrivate::duration() const { - if (m_networkState < MediaPlayer::LoadedMetaData) + if (m_readyState < MediaPlayer::HaveMetadata) return 0.0f; float duration = m_mediaObject->totalTime() / 1000.0f; @@ -309,18 +329,19 @@ void MediaPlayerPrivate::updateStates() Phonon::State phononState = m_mediaObject->state(); if (phononState == Phonon::StoppedState) { - if (oldNetworkState < MediaPlayer::LoadedMetaData) { - m_networkState = MediaPlayer::LoadedMetaData; - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState < MediaPlayer::HaveMetadata) { + m_networkState = MediaPlayer::Loading; // FIXME: should this be MediaPlayer::Idle? + m_readyState = MediaPlayer::HaveMetadata; m_mediaObject->pause(); } } else if (phononState == Phonon::PausedState) { m_networkState = MediaPlayer::Loaded; - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } else if (phononState == Phonon::ErrorState) { if (!m_mediaObject || m_mediaObject->errorType() == Phonon::FatalError) { - m_networkState = MediaPlayer::LoadFailed; - m_readyState = MediaPlayer::DataUnavailable; + // FIXME: is it possile to differentiate between different types of errors + m_networkState = MediaPlayer::NetworkError; + m_readyState = MediaPlayer::HaveNothing; cancelLoad(); } else { m_mediaObject->pause(); @@ -328,7 +349,7 @@ void MediaPlayerPrivate::updateStates() } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; if (m_networkState != oldNetworkState) { const QMetaObject* metaObj = this->metaObject(); @@ -357,19 +378,18 @@ void MediaPlayerPrivate::setVisible(bool visible) m_videoWidget->setVisible(m_isVisible); } -void MediaPlayerPrivate::setRect(const IntRect& newRect) +void MediaPlayerPrivate::setSize(const IntSize& newSize) { if (!m_videoWidget) return; - LOG(Media, "MediaPlayerPrivatePhonon::setRect(%d,%d %dx%d)", - newRect.x(), newRect.y(), - newRect.width(), newRect.height()); + LOG(Media, "MediaPlayerPrivatePhonon::setSize(%d,%d)", + newSize.width(), newSize.height()); QRect currentRect = m_videoWidget->rect(); - if (newRect.width() != currentRect.width() || newRect.height() != currentRect.height()) - m_videoWidget->resize(newRect.width(), newRect.height()); + if (newSize.width() != currentRect.width() || newSize.height() != currentRect.height()) + m_videoWidget->resize(newSize.width(), newSize.height()); } IntSize MediaPlayerPrivate::naturalSize() const @@ -380,7 +400,7 @@ IntSize MediaPlayerPrivate::naturalSize() const return IntSize(); } - if (m_networkState < MediaPlayer::LoadedMetaData) { + if (m_readyState < MediaPlayer::HaveMetadata) { LOG(Media, "MediaPlayerPrivatePhonon::naturalSize() -> %dx%d", 0, 0); return IntSize(); diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h index 1b20a84..9572d61 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.h @@ -1,5 +1,6 @@ /* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Apple Inc. All rights reserved. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -20,8 +21,7 @@ #ifndef MediaPlayerPrivatePhonon_h #define MediaPlayerPrivatePhonon_h -#include "MediaPlayer.h" -#include <wtf/Noncopyable.h> +#include "MediaPlayerPrivate.h" #include <QObject> #include <phononnamespace.h> @@ -40,31 +40,33 @@ QT_END_NAMESPACE namespace WebCore { - class MediaPlayerPrivate : public QObject, Noncopyable { + class MediaPlayerPrivate : public QObject, public MediaPlayerPrivateInterface { Q_OBJECT public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); ~MediaPlayerPrivate(); // These enums are used for debugging Q_ENUMS(ReadyState NetworkState PhononState) enum ReadyState { - DataUnavailable, - CanShowCurrentFrame, - CanPlay, - CanPlayThrough + HaveNothing, + HaveMetadata, + HaveCurrentData, + HaveFutureData, + HaveEnoughData }; enum NetworkState { - Empty, - LoadFailed, - Loading, - LoadedMetaData, - LoadedFirstFrame, - Loaded + Empty, + Idle, + Loading, + Loaded, + FormatError, + NetworkError, + DecodeError }; enum PhononState { @@ -79,7 +81,7 @@ namespace WebCore { IntSize naturalSize() const; bool hasVideo() const; - void load(String url); + void load(const String &url); void cancelLoad(); void play(); @@ -109,11 +111,9 @@ namespace WebCore { unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>&); - static bool isAvailable() { return true; } protected: bool eventFilter(QObject*, QEvent*); @@ -130,6 +130,13 @@ namespace WebCore { void totalTimeChanged(qint64); private: + MediaPlayerPrivate(MediaPlayer*); + static MediaPlayerPrivateInterface* create(MediaPlayer* player); + + static void getSupportedTypes(HashSet<String>&); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable() { return true; } + void updateStates(); MediaPlayer* m_player; diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index bd0192c..a8a3ea2 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -1,6 +1,7 @@ /* - * Copyright (C) 2006 Zack Rusin <zack@kde.org> - * 2006 Rob Buis <buis@kde.org> + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * 2006 Rob Buis <buis@kde.org> + * 2009 Dirk Schulze <krit@webkit.org> * * All rights reserved. * @@ -36,7 +37,7 @@ #include "PlatformString.h" #include "StrokeStyleApplier.h" #include <QPainterPath> -#include <QMatrix> +#include <QTransform> #include <QString> #define _USE_MATH_DEFINES @@ -108,7 +109,7 @@ bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) void Path::translate(const FloatSize& size) { - QMatrix matrix; + QTransform matrix; matrix.translate(size.width(), size.height()); *m_path = (*m_path) * matrix; } @@ -161,9 +162,72 @@ void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius) { - //FIXME: busted - qWarning("arcTo is busted"); - m_path->arcTo(p1.x(), p1.y(), p2.x(), p2.y(), radius, 90); + FloatPoint p0(m_path->currentPosition()); + + if ((p1.x() == p0.x() && p1.y() == p0.y()) || (p1.x() == p2.x() && p1.y() == p2.y()) || radius == 0.f) { + m_path->lineTo(p1); + return; + } + + FloatPoint p1p0((p0.x() - p1.x()),(p0.y() - p1.y())); + FloatPoint p1p2((p2.x() - p1.x()),(p2.y() - p1.y())); + float p1p0_length = sqrtf(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y()); + float p1p2_length = sqrtf(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y()); + + double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length); + // all points on a line logic + if (cos_phi == -1) { + m_path->lineTo(p1); + return; + } + if (cos_phi == 1) { + // add infinite far away point + unsigned int max_length = 65535; + double factor_max = max_length / p1p0_length; + FloatPoint ep((p0.x() + factor_max * p1p0.x()), (p0.y() + factor_max * p1p0.y())); + m_path->lineTo(ep); + return; + } + + float tangent = radius / tan(acos(cos_phi) / 2); + float factor_p1p0 = tangent / p1p0_length; + FloatPoint t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y())); + + FloatPoint orth_p1p0(p1p0.y(), -p1p0.x()); + float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y()); + float factor_ra = radius / orth_p1p0_length; + + // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0 + double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length); + if (cos_alpha < 0.f) + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + + FloatPoint p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y())); + + // calculate angles for addArc + orth_p1p0 = FloatPoint(-orth_p1p0.x(), -orth_p1p0.y()); + float sa = acos(orth_p1p0.x() / orth_p1p0_length); + if (orth_p1p0.y() < 0.f) + sa = 2 * piDouble - sa; + + // anticlockwise logic + bool anticlockwise = false; + + float factor_p1p2 = tangent / p1p2_length; + FloatPoint t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y())); + FloatPoint orth_p1p2((t_p1p2.x() - p.x()),(t_p1p2.y() - p.y())); + float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y()); + float ea = acos(orth_p1p2.x() / orth_p1p2_length); + if (orth_p1p2.y() < 0) + ea = 2 * piDouble - ea; + if ((sa > ea) && ((sa - ea) < piDouble)) + anticlockwise = true; + if ((sa < ea) && ((ea - sa) > piDouble)) + anticlockwise = true; + + m_path->lineTo(t_p1p0); + + addArc(p, radius, sa, ea, anticlockwise); } void Path::closeSubpath() @@ -316,7 +380,7 @@ void Path::apply(void* info, PathApplierFunction function) const void Path::transform(const TransformationMatrix& transform) { if (m_path) { - QMatrix mat = transform; + QTransform mat = transform; QPainterPath temp = mat.map(*m_path); delete m_path; m_path = new QPainterPath(temp); diff --git a/WebCore/platform/graphics/qt/PatternQt.cpp b/WebCore/platform/graphics/qt/PatternQt.cpp index 5b76841..b261613 100644 --- a/WebCore/platform/graphics/qt/PatternQt.cpp +++ b/WebCore/platform/graphics/qt/PatternQt.cpp @@ -31,14 +31,15 @@ namespace WebCore { -QBrush Pattern::createPlatformPattern(const TransformationMatrix& transform) const +QBrush Pattern::createPlatformPattern(const TransformationMatrix&) const { QPixmap* pixmap = tileImage()->nativeImageForCurrentFrame(); if (!pixmap) return QBrush(); + // Qt merges patter space and user space itself QBrush brush(*pixmap); - brush.setMatrix(transform); + brush.setTransform(m_patternSpaceTransformation); return brush; } diff --git a/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp index 47abd17..15f0cc5 100644 --- a/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp +++ b/WebCore/platform/graphics/qt/TransformationMatrixQt.cpp @@ -31,170 +31,9 @@ namespace WebCore { -TransformationMatrix::TransformationMatrix() - : m_transform() -{ -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double tx, double ty) - : m_transform(a, b, c, d, tx, ty) -{ -} - -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) - : m_transform(matrix) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double tx, double ty) -{ - m_transform.setMatrix(a, b, c, d, tx, ty); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - qreal tx2, ty2; - m_transform.map(qreal(x), qreal(y), &tx2, &ty2); - *x2 = tx2; - *y2 = ty2; -} - -IntRect TransformationMatrix::mapRect(const IntRect& rect) const -{ - return m_transform.mapRect(rect); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect& rect) const -{ - return m_transform.mapRect(rect); -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -double TransformationMatrix::a() const -{ - return m_transform.m11(); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.setMatrix(a, b(), c(), d(), e(), f()); -} - -double TransformationMatrix::b() const -{ - return m_transform.m12(); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.setMatrix(a(), b, c(), d(), e(), f()); -} - -double TransformationMatrix::c() const -{ - return m_transform.m21(); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.setMatrix(a(), b(), c, d(), e(), f()); -} - -double TransformationMatrix::d() const -{ - return m_transform.m22(); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.setMatrix(a(), b(), c(), d, e(), f()); -} - -double TransformationMatrix::e() const -{ - return m_transform.dx(); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.setMatrix(a(), b(), c(), d(), e, f()); -} - -double TransformationMatrix::f() const -{ - return m_transform.dy(); -} - -void TransformationMatrix::setF(double f) -{ - m_transform.setMatrix(a(), b(), c(), d(), e(), f); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -TransformationMatrix& TransformationMatrix::scale(double sx, double sy) -{ - m_transform.scale(sx, sy); - return *this; -} - -TransformationMatrix& TransformationMatrix::rotate(double d) -{ - m_transform.rotate(d); - return *this; -} - -TransformationMatrix& TransformationMatrix::translate(double tx, double ty) -{ - m_transform.translate(tx, ty); - return *this; -} - -TransformationMatrix& TransformationMatrix::shear(double sx, double sy) -{ - m_transform.shear(sx, sy); - return *this; -} - -double TransformationMatrix::det() const -{ - return m_transform.det(); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - if(!isInvertible()) - return TransformationMatrix(); - - return m_transform.inverted(); -} - -TransformationMatrix::operator QMatrix() const -{ - return m_transform; -} - -bool TransformationMatrix::operator==(const TransformationMatrix& other) const -{ - return m_transform == other.m_transform; -} - -TransformationMatrix& TransformationMatrix::operator*=(const TransformationMatrix& other) -{ - m_transform *= other.m_transform; - return *this; -} - -TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& other) -{ - return m_transform * other.m_transform; +TransformationMatrix::operator QTransform() const +{ + return QTransform(m11(), m12(), m14(), m21(), m22(), m24(), m41(), m42(), m44()); } } diff --git a/WebCore/platform/graphics/skia/GradientSkia.cpp b/WebCore/platform/graphics/skia/GradientSkia.cpp index eff7c66..2d2000c 100644 --- a/WebCore/platform/graphics/skia/GradientSkia.cpp +++ b/WebCore/platform/graphics/skia/GradientSkia.cpp @@ -136,6 +136,19 @@ SkShader* Gradient::platformGradient() fillStops(m_stops.data(), m_stops.size(), pos, colors); + SkShader::TileMode tile = SkShader::kClamp_TileMode; + switch (m_spreadMethod) { + case SpreadMethodReflect: + tile = SkShader::kMirror_TileMode; + break; + case SpreadMethodRepeat: + tile = SkShader::kRepeat_TileMode; + break; + case SpreadMethodPad: + tile = SkShader::kClamp_TileMode; + break; + } + if (m_radial) { // FIXME: CSS radial Gradients allow an offset focal point (the // "start circle"), but skia doesn't seem to support that, so this just @@ -145,13 +158,16 @@ SkShader* Gradient::platformGradient() // description of the expected behavior. m_gradient = SkGradientShader::CreateRadial(m_p1, WebCoreFloatToSkScalar(m_r1), colors, pos, - static_cast<int>(countUsed), SkShader::kClamp_TileMode); + static_cast<int>(countUsed), tile); } else { SkPoint pts[2] = { m_p0, m_p1 }; m_gradient = SkGradientShader::CreateLinear(pts, colors, pos, - static_cast<int>(countUsed), SkShader::kClamp_TileMode); + static_cast<int>(countUsed), tile); } + SkMatrix matrix = m_gradientSpaceTransformation; + m_gradient->setLocalMatrix(matrix); + return m_gradient; } diff --git a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index e6c7783..376fa4b 100644 --- a/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -36,6 +36,7 @@ #include "Color.h" #include "FloatRect.h" #include "Gradient.h" +#include "ImageBuffer.h" #include "IntRect.h" #include "NativeImageSkia.h" #include "NotImplemented.h" @@ -274,11 +275,6 @@ void GraphicsContext::endTransparencyLayer() { if (paintingDisabled()) return; - -#if PLATFORM(WIN_OS) - platformContext()->canvas()->getTopPlatformDevice(). - fixupAlphaBeforeCompositing(); -#endif platformContext()->canvas()->restore(); } @@ -406,8 +402,7 @@ void GraphicsContext::clipPath(WindRule clipRule) if (paintingDisabled()) return; - const SkPath* oldPath = platformContext()->currentPath(); - SkPath path(*oldPath); + SkPath path = platformContext()->currentPathInLocalCoordinates(); path.setFillType(clipRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); platformContext()->canvas()->clipPath(path); } @@ -418,8 +413,9 @@ void GraphicsContext::clipToImageBuffer(const FloatRect& rect, if (paintingDisabled()) return; - // FIXME: This is needed for image masking and complex text fills. - notImplemented(); +#if defined(__linux__) || PLATFORM(WIN_OS) + platformContext()->beginLayerClippedToImage(rect, imageBuffer); +#endif } void GraphicsContext::concatCTM(const TransformationMatrix& xform) @@ -645,6 +641,9 @@ void GraphicsContext::drawLineForText(const IntPoint& pt, if (paintingDisabled()) return; + if (width <= 0) + return; + int thickness = SkMax32(static_cast<int>(strokeThickness()), 1); SkRect r; r.fLeft = SkIntToScalar(pt.x()); @@ -653,7 +652,9 @@ void GraphicsContext::drawLineForText(const IntPoint& pt, r.fBottom = r.fTop + SkIntToScalar(thickness); SkPaint paint; - paint.setColor(strokeColor().rgb()); + platformContext()->setupPaintForFilling(&paint); + // Text lines are drawn using the stroke color. + paint.setColor(platformContext()->effectiveStrokeColor()); platformContext()->canvas()->drawRect(r, paint); } @@ -664,9 +665,10 @@ void GraphicsContext::drawRect(const IntRect& rect) return; SkRect r = rect; - if (!isRectSkiaSafe(getCTM(), r)) + if (!isRectSkiaSafe(getCTM(), r)) { // See the fillRect below. ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } platformContext()->drawRect(r); } @@ -676,7 +678,7 @@ void GraphicsContext::fillPath() if (paintingDisabled()) return; - const SkPath& path = *platformContext()->currentPath(); + SkPath path = platformContext()->currentPathInLocalCoordinates(); if (!isPathSkiaSafe(getCTM(), path)) return; @@ -686,7 +688,7 @@ void GraphicsContext::fillPath() if (colorSpace == SolidColorSpace && !fillColor().alpha()) return; - platformContext()->setFillRule(state.fillRule == RULE_EVENODD ? + path.setFillType(state.fillRule == RULE_EVENODD ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType); SkPaint paint; @@ -708,9 +710,10 @@ void GraphicsContext::fillRect(const FloatRect& rect) return; SkRect r = rect; - if (!isRectSkiaSafe(getCTM(), r)) + if (!isRectSkiaSafe(getCTM(), r)) { // See the other version of fillRect below. ClipRectToCanvas(*platformContext()->canvas(), r, &r); + } const GraphicsContextState& state = m_common->state; ColorSpace colorSpace = state.fillColorSpace; @@ -775,6 +778,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, // See fillRect(). ClipRectToCanvas(*platformContext()->canvas(), r, &r); + if (topLeft.width() + topRight.width() > rect.width() + || bottomLeft.width() + bottomRight.width() > rect.width() + || topLeft.height() + bottomLeft.height() > rect.height() + || topRight.height() + bottomRight.height() > rect.height()) { + // Not all the radii fit, return a rect. This matches the behavior of + // Path::createRoundedRectangle. Without this we attempt to draw a round + // shadow for a square box. + fillRect(rect, color); + return; + } + SkPath path; addCornerArc(&path, r, topRight, 270); addCornerArc(&path, r, bottomRight, 0); @@ -784,12 +798,17 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, SkPaint paint; platformContext()->setupPaintForFilling(&paint); platformContext()->canvas()->drawPath(path, paint); - return fillRect(rect, color); } TransformationMatrix GraphicsContext::getCTM() const { - return platformContext()->canvas()->getTotalMatrix(); + const SkMatrix& m = platformContext()->canvas()->getTotalMatrix(); + return TransformationMatrix(SkScalarToDouble(m.getScaleX()), // a + SkScalarToDouble(m.getSkewY()), // b + SkScalarToDouble(m.getSkewX()), // c + SkScalarToDouble(m.getScaleY()), // d + SkScalarToDouble(m.getTranslateX()), // e + SkScalarToDouble(m.getTranslateY())); // f } FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& rect) @@ -1050,7 +1069,7 @@ void GraphicsContext::strokePath() if (paintingDisabled()) return; - const SkPath& path = *platformContext()->currentPath(); + SkPath path = platformContext()->currentPathInLocalCoordinates(); if (!isPathSkiaSafe(getCTM(), path)) return; diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index fdfcb85..5e90491 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -31,12 +31,14 @@ #include "config.h" #include "ImageBuffer.h" +#include "Base64.h" #include "BitmapImage.h" #include "BitmapImageSingleFrameSkia.h" #include "GraphicsContext.h" #include "ImageData.h" #include "NotImplemented.h" #include "PlatformContextSkia.h" +#include "PNGImageEncoder.h" #include "SkiaUtils.h" using namespace std; @@ -63,6 +65,9 @@ ImageBuffer::ImageBuffer(const IntSize& size, bool grayScale, bool& success) m_data.m_platformContext.setCanvas(&m_data.m_canvas); m_context.set(new GraphicsContext(&m_data.m_platformContext)); +#if PLATFORM(WIN_OS) + m_context->platformContext()->setDrawingToImageBuffer(true); +#endif // Make the background transparent. It would be nice if this wasn't // required, but the canvas is currently filled with the magic transparency @@ -101,7 +106,7 @@ PassRefPtr<ImageData> ImageBuffer::getImageData(const IntRect& rect) const ASSERT(context()); RefPtr<ImageData> result = ImageData::create(rect.width(), rect.height()); - unsigned char* data = result->data()->data(); + unsigned char* data = result->data()->data()->data(); if (rect.x() < 0 || rect.y() < 0 || (rect.x() + rect.width()) > m_size.width() || @@ -188,7 +193,7 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, unsigned srcBytesPerRow = 4 * source->width(); - const unsigned char* srcRow = source->data()->data() + originY * srcBytesPerRow + originX * 4; + const unsigned char* srcRow = source->data()->data()->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { uint32_t* destRow = bitmap.getAddr32(destX, destY + y); @@ -203,8 +208,18 @@ void ImageBuffer::putImageData(ImageData* source, const IntRect& sourceRect, String ImageBuffer::toDataURL(const String&) const { - notImplemented(); - return String(); + // Encode the image into a vector. + Vector<unsigned char> pngEncodedData; + PNGImageEncoder::encode(*context()->platformContext()->bitmap(), &pngEncodedData); + + // Convert it into base64. + Vector<char> base64EncodedData; + base64Encode(*reinterpret_cast<Vector<char>*>(&pngEncodedData), base64EncodedData); + // Append with a \0 so that it's a valid string. + base64EncodedData.append('\0'); + + // And the resulting string. + return String::format("data:image/png;base64,%s", base64EncodedData.data()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp index 1123fe9..d7f2830 100644 --- a/WebCore/platform/graphics/skia/ImageSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -225,6 +225,7 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag { SkPaint paint; paint.setPorterDuffXfermode(compOp); + paint.setFilterBitmap(true); skia::PlatformCanvas* canvas = platformContext->canvas(); @@ -233,7 +234,6 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag SkScalarToFloat(destRect.width()), SkScalarToFloat(destRect.height())); if (resampling == RESAMPLE_AWESOME) { - paint.setFilterBitmap(false); drawResampledBitmap(*canvas, paint, bitmap, srcRect, destRect); } else { // No resampling necessary, we can just draw the bitmap. We want to @@ -241,7 +241,6 @@ static void paintSkBitmap(PlatformContextSkia* platformContext, const NativeImag // is something interesting going on with the matrix (like a rotation). // Note: for serialization, we will want to subset the bitmap first so // we don't send extra pixels. - paint.setFilterBitmap(resampling == RESAMPLE_LINEAR); canvas->drawBitmapRect(bitmap, &srcRect, destRect, &paint); } } @@ -401,6 +400,7 @@ void BitmapImage::invalidatePlatformData() void BitmapImage::checkForSolidColor() { + m_checkedForSolidColor = true; } void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, @@ -427,7 +427,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dstRect, paintSkBitmap(ctxt->platformContext(), *bm, enclosingIntRect(normSrcRect), - enclosingIntRect(normDstRect), + normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); } @@ -447,7 +447,7 @@ void BitmapImageSingleFrameSkia::draw(GraphicsContext* ctxt, paintSkBitmap(ctxt->platformContext(), m_nativeImage, enclosingIntRect(normSrcRect), - enclosingIntRect(normDstRect), + normDstRect, WebCoreCompositeToSkiaComposite(compositeOp)); } diff --git a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp index f77620b..b5f7e1d 100644 --- a/WebCore/platform/graphics/skia/ImageSourceSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSourceSkia.cpp @@ -100,16 +100,16 @@ ImageSource::~ImageSource() void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* data, bool allDataReceived) { - // TODO(darin): Figure out what to do with the |data| and |allDataReceived| params. - - if (destroyAll) { - delete m_decoder; - m_decoder = 0; + if (!destroyAll) { + if (m_decoder) + m_decoder->clearFrameBufferCache(clearBeforeFrame); return; } - if (m_decoder) - m_decoder->clearFrameBufferCache(clearBeforeFrame); + delete m_decoder; + m_decoder = 0; + if (data) + setData(data, allDataReceived); } bool ImageSource::initialized() const diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp index ca99322..2700da8 100644 --- a/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -274,7 +274,7 @@ static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) SkPaint paint; context->platformContext()->setupPaintForStroking(&paint, 0, 0); SkPath boundingPath; - paint.getFillPath(context->platformContext()->currentPath(), &boundingPath); + paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); SkRect r; boundingPath.computeBounds(&r, SkPath::kExact_BoundsType); return r; diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index 60dbbe0..6c633f2 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -31,6 +31,7 @@ #include "config.h" #include "GraphicsContext.h" +#include "ImageBuffer.h" #include "NativeImageSkia.h" #include "PlatformContextSkia.h" #include "SkiaUtils.h" @@ -45,10 +46,6 @@ #include <wtf/MathExtras.h> -#if defined(__linux__) -#include "GdkSkia.h" -#endif - // State ----------------------------------------------------------------------- // Encapsulates the additional painting state information we store for each @@ -86,6 +83,13 @@ struct PlatformContextSkia::State { // color to produce a new output color. SkColor applyAlpha(SkColor) const; +#if defined(__linux__) || PLATFORM(WIN_OS) + // If non-empty, the current State is clipped to this image. + SkBitmap m_imageBufferClip; + // If m_imageBufferClip is non-empty, this is the region the image is clipped to. + WebCore::FloatRect m_clip; +#endif + private: // Not supported. void operator=(const State&); @@ -113,9 +117,28 @@ PlatformContextSkia::State::State() } PlatformContextSkia::State::State(const State& other) + : m_alpha(other.m_alpha) + , m_porterDuffMode(other.m_porterDuffMode) + , m_gradient(other.m_gradient) + , m_pattern(other.m_pattern) + , m_useAntialiasing(other.m_useAntialiasing) + , m_looper(other.m_looper) + , m_fillColor(other.m_fillColor) + , m_strokeStyle(other.m_strokeStyle) + , m_strokeColor(other.m_strokeColor) + , m_strokeThickness(other.m_strokeThickness) + , m_dashRatio(other.m_dashRatio) + , m_miterLimit(other.m_miterLimit) + , m_lineCap(other.m_lineCap) + , m_lineJoin(other.m_lineJoin) + , m_dash(other.m_dash) + , m_textDrawingMode(other.m_textDrawingMode) +#if defined(__linux__) || PLATFORM(WIN_OS) + , m_imageBufferClip(other.m_imageBufferClip) + , m_clip(other.m_clip) +#endif { - memcpy(this, &other, sizeof(State)); - + // Up the ref count of these. saveRef does nothing if 'this' is NULL. m_looper->safeRef(); m_dash->safeRef(); m_gradient->safeRef(); @@ -148,22 +171,16 @@ SkColor PlatformContextSkia::State::applyAlpha(SkColor c) const PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) , m_stateStack(sizeof(State)) +#if PLATFORM(WIN_OS) + , m_drawingToImageBuffer(false) +#endif { m_stateStack.append(State()); m_state = &m_stateStack.last(); -#if defined(OS_LINUX) - m_gdkskia = m_canvas ? gdk_skia_new(m_canvas) : 0; -#endif } PlatformContextSkia::~PlatformContextSkia() { -#if defined(OS_LINUX) - if (m_gdkskia) { - g_object_unref(m_gdkskia); - m_gdkskia = 0; - } -#endif } void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) @@ -171,17 +188,72 @@ void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) m_canvas = canvas; } +#if PLATFORM(WIN_OS) +void PlatformContextSkia::setDrawingToImageBuffer(bool value) +{ + m_drawingToImageBuffer = value; +} + +bool PlatformContextSkia::isDrawingToImageBuffer() const +{ + return m_drawingToImageBuffer; +} +#endif + void PlatformContextSkia::save() { m_stateStack.append(*m_state); m_state = &m_stateStack.last(); +#if defined(__linux__) || PLATFORM(WIN_OS) + // The clip image only needs to be applied once. Reset the image so that we + // don't attempt to clip multiple times. + m_state->m_imageBufferClip.reset(); +#endif + // Save our native canvas. canvas()->save(); } +#if defined(__linux__) || PLATFORM(WIN_OS) +void PlatformContextSkia::beginLayerClippedToImage(const WebCore::FloatRect& rect, + const WebCore::ImageBuffer* imageBuffer) +{ + // Skia doesn't support clipping to an image, so we create a layer. The next + // time restore is invoked the layer and |imageBuffer| are combined to + // create the resulting image. + m_state->m_clip = rect; + SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), + SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) }; + + canvas()->saveLayerAlpha(&bounds, 255, + static_cast<SkCanvas::SaveFlags>(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag)); + // Copy off the image as |imageBuffer| may be deleted before restore is invoked. + const SkBitmap* bitmap = imageBuffer->context()->platformContext()->bitmap(); + if (!bitmap->pixelRef()) { + // The bitmap owns it's pixels. This happens when we've allocated the + // pixels in some way and assigned them directly to the bitmap (as + // happens when we allocate a DIB). In this case the assignment operator + // does not copy the pixels, rather the copied bitmap ends up + // referencing the same pixels. As the pixels may not live as long as we + // need it to, we copy the image. + bitmap->copyTo(&m_state->m_imageBufferClip, SkBitmap::kARGB_8888_Config); + } else { + // If there is a pixel ref, we can safely use the assignment operator. + m_state->m_imageBufferClip = *bitmap; + } +} +#endif + void PlatformContextSkia::restore() { +#if defined(__linux__) || PLATFORM(WIN_OS) + if (!m_state->m_imageBufferClip.empty()) { + applyClipFromImage(m_state->m_clip, m_state->m_imageBufferClip); + canvas()->restore(); + } +#endif + m_stateStack.removeLast(); m_state = &m_stateStack.last(); @@ -200,12 +272,21 @@ void PlatformContextSkia::drawRect(SkRect rect) if (m_state->m_strokeStyle != WebCore::NoStroke && (m_state->m_strokeColor & 0xFF000000)) { - if (fillcolorNotTransparent) { - // This call is expensive so don't call it unnecessarily. - paint.reset(); - } - setupPaintForStroking(&paint, &rect, 0); - canvas()->drawRect(rect, paint); + // We do a fill of four rects to simulate the stroke of a border. + SkColor oldFillColor = m_state->m_fillColor; + if (oldFillColor != m_state->m_strokeColor) + setFillColor(m_state->m_strokeColor); + setupPaintForFilling(&paint); + SkRect topBorder = { rect.fLeft, rect.fTop, rect.width(), 1 }; + canvas()->drawRect(topBorder, paint); + SkRect bottomBorder = { rect.fLeft, rect.fBottom - 1, rect.width(), 1 }; + canvas()->drawRect(bottomBorder, paint); + SkRect leftBorder = { rect.fLeft, rect.fTop + 1, 1, rect.height() - 2 }; + canvas()->drawRect(leftBorder, paint); + SkRect rightBorder = { rect.fRight - 1, rect.fTop + 1, 1, rect.height() - 2 }; + canvas()->drawRect(rightBorder, paint); + if (oldFillColor != m_state->m_strokeColor) + setFillColor(oldFillColor); } } @@ -239,10 +320,6 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i setupPaintCommon(paint); float width = m_state->m_strokeThickness; - // This allows dashing and dotting to work properly for hairline strokes. - if (width == 0) - width = 1; - paint->setColor(m_state->applyAlpha(m_state->m_strokeColor)); paint->setStyle(SkPaint::kStroke_Style); paint->setStrokeWidth(SkFloatToScalar(width)); @@ -250,9 +327,6 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i paint->setStrokeJoin(m_state->m_lineJoin); paint->setStrokeMiter(SkFloatToScalar(m_state->m_miterLimit)); - if (rect != 0 && (static_cast<int>(roundf(width)) & 1)) - rect->inset(-SK_ScalarHalf, -SK_ScalarHalf); - if (m_state->m_dash) paint->setPathEffect(m_state->m_dash); else { @@ -267,7 +341,8 @@ float PlatformContextSkia::setupPaintForStroking(SkPaint* paint, SkRect* rect, i SkScalar dashLength; if (length) { // Determine about how many dashes or dots we should have. - int numDashes = length / roundf(width); + float roundedWidth = roundf(width); + int numDashes = roundedWidth ? (length / roundedWidth) : length; if (!(numDashes & 1)) numDashes++; // Make it odd so we end on a dash/dot. // Use the number of dashes to determine the length of a @@ -366,9 +441,14 @@ void PlatformContextSkia::setUseAntialiasing(bool enable) m_state->m_useAntialiasing = enable; } -SkColor PlatformContextSkia::fillColor() const +SkColor PlatformContextSkia::effectiveFillColor() const { - return m_state->m_fillColor; + return m_state->applyAlpha(m_state->m_fillColor); +} + +SkColor PlatformContextSkia::effectiveStrokeColor() const +{ + return m_state->applyAlpha(m_state->m_strokeColor); } void PlatformContextSkia::beginPath() @@ -378,7 +458,17 @@ void PlatformContextSkia::beginPath() void PlatformContextSkia::addPath(const SkPath& path) { - m_path.addPath(path); + m_path.addPath(path, m_canvas->getTotalMatrix()); +} + +SkPath PlatformContextSkia::currentPathInLocalCoordinates() const +{ + SkPath localPath = m_path; + const SkMatrix& matrix = m_canvas->getTotalMatrix(); + SkMatrix inverseMatrix; + matrix.invert(&inverseMatrix); + localPath.transform(inverseMatrix); + return localPath; } void PlatformContextSkia::setFillRule(SkPath::FillType fr) @@ -425,3 +515,14 @@ bool PlatformContextSkia::isPrinting() { return m_canvas->getTopPlatformDevice().IsVectorial(); } + +#if defined(__linux__) || PLATFORM(WIN_OS) +void PlatformContextSkia::applyClipFromImage(const WebCore::FloatRect& rect, const SkBitmap& imageBuffer) +{ + // NOTE: this assumes the image mask contains opaque black for the portions that are to be shown, as such we + // only look at the alpha when compositing. I'm not 100% sure this is what WebKit expects for image clipping. + SkPaint paint; + paint.setPorterDuffXfermode(SkPorterDuff::kDstIn_Mode); + m_canvas->drawBitmap(imageBuffer, SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), &paint); +} +#endif diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index 78e9a19..8850a6a 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -43,8 +43,6 @@ #include <wtf/Vector.h> -typedef struct _GdkDrawable GdkSkia; - // This class holds the platform-specific state for GraphicsContext. We put // most of our Skia wrappers on this class. In theory, a lot of this stuff could // be moved to GraphicsContext directly, except that some code external to this @@ -73,9 +71,28 @@ public: // to the constructor. void setCanvas(skia::PlatformCanvas*); +#if PLATFORM(WIN_OS) + // If false we're rendering to a GraphicsContext for a web page, if false + // we're not (as is the case when rendering to a canvas object). + // If this is true the contents have not been marked up with the magic + // color and all text drawing needs to go to a layer so that the alpha is + // correctly updated. + void setDrawingToImageBuffer(bool); + bool isDrawingToImageBuffer() const; +#endif + void save(); void restore(); + // Begins a layer that is clipped to the image |imageBuffer| at the location + // |rect|. This layer is implicitly restored when the next restore is + // invoked. + // NOTE: |imageBuffer| may be deleted before the |restore| is invoked. +#if defined(__linux__) || PLATFORM(WIN_OS) + void beginLayerClippedToImage(const WebCore::FloatRect&, + const WebCore::ImageBuffer*); +#endif + // Sets up the common flags on a paint for antialiasing, effects, etc. // This is implicitly called by setupPaintFill and setupPaintStroke, but // you may wish to call it directly sometimes if you don't want that other @@ -116,9 +133,15 @@ public: void beginPath(); void addPath(const SkPath&); - const SkPath* currentPath() const { return &m_path; } + SkPath currentPathInLocalCoordinates() const; + + // Returns the fill color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveFillColor() const; - SkColor fillColor() const; + // Returns the stroke color. The returned color has it's alpha adjusted + // by the current alpha. + SkColor effectiveStrokeColor() const; skia::PlatformCanvas* canvas() { return m_canvas; } @@ -142,12 +165,13 @@ public: // possible quality. bool isPrinting(); -#if defined(__linux__) - // FIXME: should be camelCase. - GdkSkia* gdk_skia() const { return m_gdkskia; } +private: +#if defined(__linux__) || PLATFORM(WIN_OS) + // Used when restoring and the state has an image clip. Only shows the pixels in + // m_canvas that are also in imageBuffer. + void applyClipFromImage(const WebCore::FloatRect&, const SkBitmap&); #endif -private: // Defines drawing style. struct State; @@ -161,12 +185,11 @@ private: // mStateStack.back(). State* m_state; - // Current path. + // Current path in global coordinates. SkPath m_path; -#if defined(__linux__) - // A pointer to a GDK Drawable wrapping of this Skia canvas - GdkSkia* m_gdkskia; +#if PLATFORM(WIN_OS) + bool m_drawingToImageBuffer; #endif }; diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.cpp b/WebCore/platform/graphics/skia/SkiaFontWin.cpp index 6e79a7e..d0cd4c5 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.cpp +++ b/WebCore/platform/graphics/skia/SkiaFontWin.cpp @@ -31,8 +31,13 @@ #include "config.h" #include "SkiaFontWin.h" +#include "PlatformContextSkia.h" +#include "Gradient.h" +#include "Pattern.h" #include "SkCanvas.h" #include "SkPaint.h" +#include "SkShader.h" +#include "TransformationMatrix.h" #include <wtf/ListHashSet.h> #include <wtf/Vector.h> @@ -162,10 +167,10 @@ static bool getPathForGlyph(HDC dc, WORD glyph, SkPath* path) addPolyCurveToPath(polyCurve, path); curPoly += sizeof(WORD) * 2 + sizeof(POINTFX) * polyCurve->cpfx; } + path->close(); curGlyph += polyHeader->cb; } - path->close(); return true; } @@ -215,4 +220,152 @@ void SkiaWinOutlineCache::removePathsForFont(HFONT hfont) deleteOutline(outlineCache.find(*i)); } +bool windowsCanHandleTextDrawing(GraphicsContext* context) +{ + // Check for non-translation transforms. Sometimes zooms will look better in + // Skia, and sometimes better in Windows. The main problem is that zooming + // in using Skia will show you the hinted outlines for the smaller size, + // which look weird. All else being equal, it's better to use Windows' text + // drawing, so we don't check for zooms. + const TransformationMatrix& matrix = context->getCTM(); + if (matrix.b() != 0 || matrix.c() != 0) // Check for skew. + return false; + + // Check for stroke effects. + if (context->platformContext()->getTextDrawingMode() != cTextFill) + return false; + + // Check for gradients. + if (context->fillGradient() || context->strokeGradient()) + return false; + + // Check for patterns. + if (context->fillPattern() || context->strokePattern()) + return false; + + // Check for shadow effects. + if (context->platformContext()->getDrawLooper()) + return false; + + return true; +} + +// Draws the given text string using skia. Note that gradient or +// pattern may be NULL, in which case a solid colour is used. +static bool skiaDrawText(HFONT hfont, + HDC dc, + SkCanvas* canvas, + const SkPoint& point, + SkPaint* paint, + const TransformationMatrix& transformationMatrix, + Gradient* gradient, + Pattern* pattern, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + int numGlyphs) +{ + SkShader* shader = NULL; + if (gradient) + shader = gradient->platformGradient(); + else if (pattern) + shader = pattern->createPlatformPattern(transformationMatrix); + + paint->setShader(shader); + float x = point.fX, y = point.fY; + + for (int i = 0; i < numGlyphs; i++) { + const SkPath* path = SkiaWinOutlineCache::lookupOrCreatePathForGlyph(dc, hfont, glyphs[i]); + if (!path) + return false; + + float offsetX = 0.0f, offsetY = 0.0f; + if (offsets && (offsets[i].du != 0 || offsets[i].dv != 0)) { + offsetX = offsets[i].du; + offsetY = offsets[i].dv; + } + + SkPath newPath; + newPath.addPath(*path, x + offsetX, y + offsetY); + canvas->drawPath(newPath, *paint); + + x += advances[i]; + } + + return true; +} + +bool paintSkiaText(GraphicsContext* context, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin) +{ + HDC dc = GetDC(0); + HGDIOBJ oldFont = SelectObject(dc, hfont); + + PlatformContextSkia* platformContext = context->platformContext(); + int textMode = platformContext->getTextDrawingMode(); + + // Filling (if necessary). This is the common case. + SkPaint paint; + platformContext->setupPaintForFilling(&paint); + paint.setFlags(SkPaint::kAntiAlias_Flag); + bool didFill = false; + + if ((textMode & cTextFill) && SkColorGetA(paint.getColor())) { + Gradient* fillGradient = 0; + Pattern* fillPattern = 0; + if (context->fillColorSpace() == GradientColorSpace) + fillGradient = context->fillGradient(); + else if (context->fillColorSpace() == PatternColorSpace) + fillPattern = context->fillPattern(); + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + context->getCTM(), fillGradient, fillPattern, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + didFill = true; + } + + // Stroking on top (if necessary). + if ((textMode & WebCore::cTextStroke) + && platformContext->getStrokeStyle() != NoStroke + && platformContext->getStrokeThickness() > 0) { + + paint.reset(); + platformContext->setupPaintForStroking(&paint, 0, 0); + paint.setFlags(SkPaint::kAntiAlias_Flag); + if (didFill) { + // If there is a shadow and we filled above, there will already be + // a shadow. We don't want to draw it again or it will be too dark + // and it will go on top of the fill. + // + // Note that this isn't strictly correct, since the stroke could be + // very thick and the shadow wouldn't account for this. The "right" + // thing would be to draw to a new layer and then draw that layer + // with a shadow. But this is a lot of extra work for something + // that isn't normally an issue. + paint.setLooper(0)->safeUnref(); + } + + Gradient* strokeGradient = 0; + Pattern* strokePattern = 0; + if (context->strokeColorSpace() == GradientColorSpace) + strokeGradient = context->strokeGradient(); + else if (context->strokeColorSpace() == PatternColorSpace) + strokePattern = context->strokePattern(); + if (!skiaDrawText(hfont, dc, platformContext->canvas(), *origin, &paint, + context->getCTM(), strokeGradient, strokePattern, + &glyphs[0], &advances[0], &offsets[0], numGlyphs)) + return false; + } + + SelectObject(dc, oldFont); + ReleaseDC(0, dc); + + return true; +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/SkiaFontWin.h b/WebCore/platform/graphics/skia/SkiaFontWin.h index 2adab39..0e0c953 100644 --- a/WebCore/platform/graphics/skia/SkiaFontWin.h +++ b/WebCore/platform/graphics/skia/SkiaFontWin.h @@ -32,8 +32,12 @@ #define SkiaWinOutlineCache_h #include <windows.h> +#include <usp10.h> +class GraphicsContext; class SkPath; +class SkPoint; +class PlatformContextSkia; namespace WebCore { @@ -49,6 +53,37 @@ private: SkiaWinOutlineCache(); }; +// The functions below are used for more complex font drawing (effects such as +// stroking and more complex transforms) than Windows supports directly. Since +// Windows drawing is faster you should use windowsCanHandleTextDrawing first to +// check if using Skia is required at all. +// Note that the text will look different (no ClearType) so this should only be +// used when necessary. +// +// When you call a Skia* text drawing function, various glyph outlines will be +// cached. As a result, you should call SkiaWinOutlineCache::removePathsForFont +// when the font is destroyed so that the cache does not outlive the font (since +// the HFONTs are recycled). +// +// Remember that Skia's text drawing origin is the baseline, like WebKit, not +// the top, like Windows. + +// Returns true if advanced font rendering is recommended. +bool windowsCanHandleTextDrawing(GraphicsContext* context); + +// Note that the offsets parameter is optional. If not NULL it represents a +// per glyph offset (such as returned by ScriptPlace Windows API function). +// +// Returns true of the text was drawn successfully. False indicates an error +// from Windows. +bool paintSkiaText(GraphicsContext* graphicsContext, + HFONT hfont, + int numGlyphs, + const WORD* glyphs, + const int* advances, + const GOFFSET* offsets, + const SkPoint* origin); + } // namespace WebCore #endif // SkiaWinOutlineCache_h diff --git a/WebCore/platform/graphics/skia/SkiaUtils.cpp b/WebCore/platform/graphics/skia/SkiaUtils.cpp index 6d9ffe2..55cba37 100644 --- a/WebCore/platform/graphics/skia/SkiaUtils.cpp +++ b/WebCore/platform/graphics/skia/SkiaUtils.cpp @@ -38,6 +38,7 @@ #include "SkColorPriv.h" #include "SkMatrix.h" #include "SkRegion.h" +#include "SkUnPreMultiply.h" namespace WebCore { @@ -74,29 +75,12 @@ SkPorterDuff::Mode WebCoreCompositeToSkiaComposite(CompositeOperator op) return SkPorterDuff::kSrcOver_Mode; // fall-back } -static U8CPU InvScaleByte(U8CPU component, uint32_t scale) -{ - SkASSERT(component == (uint8_t)component); - return (component * scale + 0x8000) >> 16; -} - -SkColor SkPMColorToColor(SkPMColor pm) -{ - if (0 == pm) - return 0; - - unsigned a = SkGetPackedA32(pm); - uint32_t scale = (255 << 16) / a; - - return SkColorSetARGB(a, - InvScaleByte(SkGetPackedR32(pm), scale), - InvScaleByte(SkGetPackedG32(pm), scale), - InvScaleByte(SkGetPackedB32(pm), scale)); -} - Color SkPMColorToWebCoreColor(SkPMColor pm) { - return SkPMColorToColor(pm); + SkColor c = SkUnPreMultiply::PMColorToColor(pm); + // need the cast to find the right constructor + return WebCore::Color((int)SkColorGetR(c), (int)SkColorGetG(c), + (int)SkColorGetB(c), (int)SkColorGetA(c)); } void IntersectRectAndRegion(const SkRegion& region, const SkRect& srcRect, SkRect* destRect) { @@ -151,7 +135,12 @@ bool SkPathContainsPoint(SkPath* originalPath, const FloatPoint& point, SkPath:: int scale = 1; SkRect bounds; +#if PLATFORM(SGL) + // this is the API from skia/trunk + bounds = originalPath->getBounds(); +#else originalPath->computeBounds(&bounds, SkPath::kFast_BoundsType); +#endif // We can immediately return false if the point is outside the bounding rect if (!bounds.contains(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()))) diff --git a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp index 1e2a194..2d0f9f8 100644 --- a/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp +++ b/WebCore/platform/graphics/skia/TransformationMatrixSkia.cpp @@ -30,193 +30,28 @@ #include "config.h" #include "TransformationMatrix.h" -#include "FloatRect.h" -#include "IntRect.h" - #include "SkiaUtils.h" namespace WebCore { -TransformationMatrix::TransformationMatrix() -{ - m_transform.reset(); -} - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) -{ - setMatrix(a, b, c, d, e, f); -} - -TransformationMatrix::TransformationMatrix(const SkMatrix& matrix) - : m_transform(matrix) -{ -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) -{ - m_transform.reset(); - - m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); - m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); - m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); - - m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); - m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); - m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); -} - -void TransformationMatrix::map(double x, double y, double* x2, double* y2) const -{ - SkPoint src, dst; - src.set(WebCoreDoubleToSkScalar(x), WebCoreDoubleToSkScalar(y)); - m_transform.mapPoints(&dst, &src, 1); - - *x2 = SkScalarToDouble(dst.fX); - *y2 = SkScalarToDouble(dst.fY); -} - -IntRect TransformationMatrix::mapRect(const IntRect& src) const -{ - SkRect dst; - m_transform.mapRect(&dst, src); - return enclosingIntRect(dst); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect& src) const -{ - SkRect dst; - m_transform.mapRect(&dst, src); - return dst; -} - -bool TransformationMatrix::isIdentity() const -{ - return m_transform.isIdentity(); -} - -void TransformationMatrix::reset() -{ - m_transform.reset(); -} - -TransformationMatrix &TransformationMatrix::scale(double sx, double sy) -{ - m_transform.preScale(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); - return *this; -} - -TransformationMatrix &TransformationMatrix::rotate(double d) -{ - m_transform.preRotate(WebCoreDoubleToSkScalar(d), 0, 0); - return *this; -} - -TransformationMatrix &TransformationMatrix::translate(double tx, double ty) -{ - m_transform.preTranslate(WebCoreDoubleToSkScalar(tx), WebCoreDoubleToSkScalar(ty)); - return *this; -} - -TransformationMatrix &TransformationMatrix::shear(double sx, double sy) -{ - m_transform.preSkew(WebCoreDoubleToSkScalar(sx), WebCoreDoubleToSkScalar(sy), 0, 0); - return *this; -} - -double TransformationMatrix::det() const -{ - return SkScalarToDouble(m_transform.getScaleX()) * SkScalarToDouble(m_transform.getScaleY()) - - SkScalarToDouble(m_transform.getSkewY()) * SkScalarToDouble(m_transform.getSkewX()); -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - TransformationMatrix inverse; - m_transform.invert(&inverse.m_transform); - return inverse; -} - TransformationMatrix::operator SkMatrix() const { - return m_transform; -} - -bool TransformationMatrix::operator==(const TransformationMatrix& m2) const -{ - return m_transform == m2.m_transform; -} + SkMatrix result; -TransformationMatrix &TransformationMatrix::operator*=(const TransformationMatrix& m2) -{ - m_transform.setConcat(m2.m_transform, m_transform); - return *this; -} + result.setScaleX(WebCoreDoubleToSkScalar(a())); + result.setSkewX(WebCoreDoubleToSkScalar(c())); + result.setTranslateX(WebCoreDoubleToSkScalar(e())); -TransformationMatrix TransformationMatrix::operator*(const TransformationMatrix& m2) -{ - TransformationMatrix cat; - cat.m_transform.setConcat(m2.m_transform, m_transform); - return cat; -} + result.setScaleY(WebCoreDoubleToSkScalar(d())); + result.setSkewY(WebCoreDoubleToSkScalar(b())); + result.setTranslateY(WebCoreDoubleToSkScalar(f())); -double TransformationMatrix::a() const -{ - return SkScalarToDouble(m_transform.getScaleX()); -} - -void TransformationMatrix::setA(double a) -{ - m_transform.setScaleX(WebCoreDoubleToSkScalar(a)); -} - -double TransformationMatrix::b() const -{ - return SkScalarToDouble(m_transform.getSkewY()); -} - -void TransformationMatrix::setB(double b) -{ - m_transform.setSkewY(WebCoreDoubleToSkScalar(b)); -} + // FIXME: Set perspective properly. + result.setPerspX(0); + result.setPerspY(0); + result.set(SkMatrix::kMPersp2, SK_Scalar1); -double TransformationMatrix::c() const -{ - return SkScalarToDouble(m_transform.getSkewX()); -} - -void TransformationMatrix::setC(double c) -{ - m_transform.setSkewX(WebCoreDoubleToSkScalar(c)); -} - -double TransformationMatrix::d() const -{ - return SkScalarToDouble(m_transform.getScaleY()); -} - -void TransformationMatrix::setD(double d) -{ - m_transform.setScaleY(WebCoreDoubleToSkScalar(d)); -} - -double TransformationMatrix::e() const -{ - return SkScalarToDouble(m_transform.getTranslateX()); -} - -void TransformationMatrix::setE(double e) -{ - m_transform.setTranslateX(WebCoreDoubleToSkScalar(e)); -} - -double TransformationMatrix::f() const -{ - return SkScalarToDouble(m_transform.getTranslateY()); -} - -void TransformationMatrix::setF(double f) -{ - m_transform.setTranslateY(WebCoreDoubleToSkScalar(f)); + return result; } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp new file mode 100644 index 0000000..ab3413b --- /dev/null +++ b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "Matrix3DTransformOperation.h" + +#include <algorithm> + +using namespace std; + +namespace WebCore { + +PassRefPtr<TransformOperation> Matrix3DTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + // Convert the TransformOperations into matrices + IntSize size; + TransformationMatrix fromT; + TransformationMatrix toT; + if (from) + from->apply(fromT, size); + + apply(toT, size); + + if (blendToIdentity) + swap(fromT, toT); + + toT.blend(fromT, progress); + return Matrix3DTransformOperation::create(toT); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h new file mode 100644 index 0000000..7430dbc --- /dev/null +++ b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef Matrix3DTransformOperation_h +#define Matrix3DTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class Matrix3DTransformOperation : public TransformOperation { +public: + static PassRefPtr<Matrix3DTransformOperation> create(const TransformationMatrix& matrix) + { + return adoptRef(new Matrix3DTransformOperation(matrix)); + } + +private: + virtual bool isIdentity() const { return m_matrix.isIdentity(); } + + virtual OperationType getOperationType() const { return MATRIX_3D; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == MATRIX_3D; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const Matrix3DTransformOperation* m = static_cast<const Matrix3DTransformOperation*>(&o); + return m_matrix == m->m_matrix; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + transform.multLeft(TransformationMatrix(m_matrix)); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + Matrix3DTransformOperation(const TransformationMatrix& mat) + { + m_matrix = mat; + } + + TransformationMatrix m_matrix; +}; + +} // namespace WebCore + +#endif // Matrix3DTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp index 153d96d..4934fa6 100644 --- a/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.cpp @@ -24,6 +24,8 @@ #include <algorithm> +using namespace std; + namespace WebCore { PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) @@ -41,7 +43,7 @@ PassRefPtr<TransformOperation> MatrixTransformOperation::blend(const TransformOp } if (blendToIdentity) - std::swap(fromT, toT); + swap(fromT, toT); toT.blend(fromT, progress); return MatrixTransformOperation::create(toT.a(), toT.b(), toT.c(), toT.d(), toT.e(), toT.f()); diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h index d272229..ee47a11 100644 --- a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h @@ -26,6 +26,7 @@ #define MatrixTransformOperation_h #include "TransformOperation.h" +#include "TransformationMatrix.h" namespace WebCore { @@ -36,8 +37,14 @@ public: return adoptRef(new MatrixTransformOperation(a, b, c, d, e, f)); } + static PassRefPtr<MatrixTransformOperation> create(const TransformationMatrix& t) + { + return adoptRef(new MatrixTransformOperation(t)); + } + private: virtual bool isIdentity() const { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; } + virtual OperationType getOperationType() const { return MATRIX; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == MATRIX; } @@ -53,7 +60,7 @@ private: virtual bool apply(TransformationMatrix& transform, const IntSize&) const { TransformationMatrix matrix(m_a, m_b, m_c, m_d, m_e, m_f); - transform = matrix * transform; + transform.multLeft(TransformationMatrix(matrix)); return false; } @@ -68,6 +75,16 @@ private: , m_f(f) { } + + MatrixTransformOperation(const TransformationMatrix& t) + : m_a(t.a()) + , m_b(t.b()) + , m_c(t.c()) + , m_d(t.d()) + , m_e(t.e()) + , m_f(t.f()) + { + } double m_a; double m_b; diff --git a/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp new file mode 100644 index 0000000..9fd03a1 --- /dev/null +++ b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PerspectiveTransformOperation.h" + +#include <algorithm> + +using namespace std; + +namespace WebCore { + +PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) +{ + if (from && !from->isSameType(*this)) + return this; + + if (blendToIdentity) + return PerspectiveTransformOperation::create(m_p + (1. - m_p) * progress); + + const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from); + double fromP = fromOp ? fromOp->m_p : 0; + double toP = m_p; + + TransformationMatrix fromT; + TransformationMatrix toT; + fromT.applyPerspective(fromP); + toT.applyPerspective(toP); + toT.blend(fromT, progress); + TransformationMatrix::DecomposedType decomp; + toT.decompose(decomp); + + return PerspectiveTransformOperation::create(decomp.perspectiveZ ? -1.0 / decomp.perspectiveZ : 0.0); +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h new file mode 100644 index 0000000..a665f3e --- /dev/null +++ b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2009 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PerspectiveTransformOperation_h +#define PerspectiveTransformOperation_h + +#include "TransformOperation.h" + +namespace WebCore { + +class PerspectiveTransformOperation : public TransformOperation { +public: + static PassRefPtr<PerspectiveTransformOperation> create(double p) + { + return adoptRef(new PerspectiveTransformOperation(p)); + } + +private: + virtual bool isIdentity() const { return m_p == 0; } + virtual OperationType getOperationType() const { return PERSPECTIVE; } + virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == PERSPECTIVE; } + + virtual bool operator==(const TransformOperation& o) const + { + if (!isSameType(o)) + return false; + const PerspectiveTransformOperation* p = static_cast<const PerspectiveTransformOperation*>(&o); + return m_p == p->m_p; + } + + virtual bool apply(TransformationMatrix& transform, const IntSize&) const + { + transform.applyPerspective(m_p); + return false; + } + + virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); + + PerspectiveTransformOperation(double p) + : m_p(p) + { + } + + double m_p; +}; + +} // namespace WebCore + +#endif // PerspectiveTransformOperation_h diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp b/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp index 4887cee..919d174 100644 --- a/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp @@ -22,6 +22,11 @@ #include "config.h" #include "RotateTransformOperation.h" +#include <algorithm> +#include <wtf/MathExtras.h> + +using namespace std; + namespace WebCore { PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOperation* from, double progress, bool blendToIdentity) @@ -30,11 +35,61 @@ PassRefPtr<TransformOperation> RotateTransformOperation::blend(const TransformOp return this; if (blendToIdentity) - return RotateTransformOperation::create(m_angle - m_angle * progress, m_type); + return RotateTransformOperation::create(m_x, m_y, m_z, m_angle - m_angle * progress, m_type); const RotateTransformOperation* fromOp = static_cast<const RotateTransformOperation*>(from); - double fromAngle = fromOp ? fromOp->m_angle : 0; - return RotateTransformOperation::create(fromAngle + (m_angle - fromAngle) * progress, m_type); + + // Optimize for single axis rotation + if (!fromOp || (fromOp->m_x == 0 && fromOp->m_y == 0 && fromOp->m_z == 1) || + (fromOp->m_x == 0 && fromOp->m_y == 1 && fromOp->m_z == 0) || + (fromOp->m_x == 1 && fromOp->m_y == 0 && fromOp->m_z == 0)) { + double fromAngle = fromOp ? fromOp->m_angle : 0; + return RotateTransformOperation::create(fromOp ? fromOp->m_x : m_x, + fromOp ? fromOp->m_y : m_y, + fromOp ? fromOp->m_z : m_z, + fromAngle + (m_angle - fromAngle) * progress, m_type); + } + + const RotateTransformOperation* toOp = this; + + // Create the 2 rotation matrices + TransformationMatrix fromT; + TransformationMatrix toT; + fromT.rotate3d((float)(fromOp ? fromOp->m_x : 0), + (float)(fromOp ? fromOp->m_y : 0), + (float)(fromOp ? fromOp->m_z : 1), + (float)(fromOp ? fromOp->m_angle : 0)); + + toT.rotate3d((float)(toOp ? toOp->m_x : 0), + (float)(toOp ? toOp->m_y : 0), + (float)(toOp ? toOp->m_z : 1), + (float)(toOp ? toOp->m_angle : 0)); + + // Blend them + toT.blend(fromT, progress); + + // Extract the result as a quaternion + TransformationMatrix::DecomposedType decomp; + toT.decompose(decomp); + + // Convert that to Axis/Angle form + double x = -decomp.quaternionX; + double y = -decomp.quaternionY; + double z = -decomp.quaternionZ; + double length = sqrt(x * x + y * y + z * z); + double angle = 0; + + if (length > 0.00001) { + x /= length; + y /= length; + z /= length; + angle = rad2deg(acos(decomp.quaternionW) * 2); + } else { + x = 0; + y = 0; + z = 1; + } + return RotateTransformOperation::create(x, y, z, angle, ROTATE_3D); } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.h b/WebCore/platform/graphics/transforms/RotateTransformOperation.h index adc6c4c..699ea43 100644 --- a/WebCore/platform/graphics/transforms/RotateTransformOperation.h +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.h @@ -33,10 +33,19 @@ class RotateTransformOperation : public TransformOperation { public: static PassRefPtr<RotateTransformOperation> create(double angle, OperationType type) { - return adoptRef(new RotateTransformOperation(angle, type)); + return adoptRef(new RotateTransformOperation(0, 0, 1, angle, type)); } + static PassRefPtr<RotateTransformOperation> create(double x, double y, double z, double angle, OperationType type) + { + return adoptRef(new RotateTransformOperation(x, y, z, angle, type)); + } + + double angle() const { return m_angle; } + +private: virtual bool isIdentity() const { return m_angle == 0; } + virtual OperationType getOperationType() const { return m_type; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } @@ -45,26 +54,30 @@ public: if (!isSameType(o)) return false; const RotateTransformOperation* r = static_cast<const RotateTransformOperation*>(&o); - return m_angle == r->m_angle; + return m_x == r->m_x && m_y == r->m_y && m_z == r->m_z && m_angle == r->m_angle; } virtual bool apply(TransformationMatrix& transform, const IntSize& /*borderBoxSize*/) const { - transform.rotate(m_angle); + transform.rotate3d(m_x, m_y, m_z, m_angle); return false; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); - double angle() const { return m_angle; } - -private: - RotateTransformOperation(double angle, OperationType type) - : m_angle(angle) + RotateTransformOperation(double x, double y, double z, double angle, OperationType type) + : m_x(x) + , m_y(y) + , m_z(z) + , m_angle(angle) , m_type(type) { + ASSERT(type == ROTATE_X || type == ROTATE_Y || type == ROTATE_Z || type == ROTATE_3D); } + double m_x; + double m_y; + double m_z; double m_angle; OperationType m_type; }; diff --git a/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp b/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp index 49a8fd8..45d119c 100644 --- a/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/ScaleTransformOperation.cpp @@ -30,12 +30,17 @@ PassRefPtr<TransformOperation> ScaleTransformOperation::blend(const TransformOpe return this; if (blendToIdentity) - return ScaleTransformOperation::create(m_x + (1. - m_x) * progress, m_y + (1. - m_y) * progress, m_type); + return ScaleTransformOperation::create(m_x + (1. - m_x) * progress, + m_y + (1. - m_y) * progress, + m_z + (1. - m_z) * progress, m_type); const ScaleTransformOperation* fromOp = static_cast<const ScaleTransformOperation*>(from); double fromX = fromOp ? fromOp->m_x : 1.; double fromY = fromOp ? fromOp->m_y : 1.; - return ScaleTransformOperation::create(fromX + (m_x - fromX) * progress, fromY + (m_y - fromY) * progress, m_type); + double fromZ = fromOp ? fromOp->m_z : 1.; + return ScaleTransformOperation::create(fromX + (m_x - fromX) * progress, + fromY + (m_y - fromY) * progress, + fromZ + (m_z - fromZ) * progress, m_type); } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/ScaleTransformOperation.h b/WebCore/platform/graphics/transforms/ScaleTransformOperation.h index 289f8a1..a87bb3b 100644 --- a/WebCore/platform/graphics/transforms/ScaleTransformOperation.h +++ b/WebCore/platform/graphics/transforms/ScaleTransformOperation.h @@ -33,11 +33,21 @@ class ScaleTransformOperation : public TransformOperation { public: static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, OperationType type) { - return adoptRef(new ScaleTransformOperation(sx, sy, type)); + return adoptRef(new ScaleTransformOperation(sx, sy, 1, type)); } + static PassRefPtr<ScaleTransformOperation> create(double sx, double sy, double sz, OperationType type) + { + return adoptRef(new ScaleTransformOperation(sx, sy, sz, type)); + } + + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } + private: - virtual bool isIdentity() const { return m_x == 1 && m_y == 1; } + virtual bool isIdentity() const { return m_x == 1 && m_y == 1 && m_z == 1; } + virtual OperationType getOperationType() const { return m_type; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } @@ -46,26 +56,29 @@ private: if (!isSameType(o)) return false; const ScaleTransformOperation* s = static_cast<const ScaleTransformOperation*>(&o); - return m_x == s->m_x && m_y == s->m_y; + return m_x == s->m_x && m_y == s->m_y && m_z == s->m_z; } virtual bool apply(TransformationMatrix& transform, const IntSize&) const { - transform.scale(m_x, m_y); + transform.scale3d(m_x, m_y, m_z); return false; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); - ScaleTransformOperation(double sx, double sy, OperationType type) + ScaleTransformOperation(double sx, double sy, double sz, OperationType type) : m_x(sx) , m_y(sy) + , m_z(sz) , m_type(type) { + ASSERT(type == SCALE_X || type == SCALE_Y || type == SCALE_Z || type == SCALE || type == SCALE_3D); } double m_x; double m_y; + double m_z; OperationType m_type; }; diff --git a/WebCore/platform/graphics/transforms/TransformOperation.h b/WebCore/platform/graphics/transforms/TransformOperation.h index 65a0def..c610c4b 100644 --- a/WebCore/platform/graphics/transforms/TransformOperation.h +++ b/WebCore/platform/graphics/transforms/TransformOperation.h @@ -39,9 +39,16 @@ public: enum OperationType { SCALE_X, SCALE_Y, SCALE, TRANSLATE_X, TRANSLATE_Y, TRANSLATE, - ROTATE, + ROTATE, + ROTATE_Z = ROTATE, SKEW_X, SKEW_Y, SKEW, - MATRIX, IDENTITY, NONE + MATRIX, + SCALE_Z, SCALE_3D, + TRANSLATE_Z, TRANSLATE_3D, + ROTATE_X, ROTATE_Y, ROTATE_3D, + MATRIX_3D, + PERSPECTIVE, + IDENTITY, NONE }; virtual ~TransformOperation() { } @@ -51,12 +58,27 @@ public: virtual bool isIdentity() const = 0; + // Return true if the borderBoxSize was used in the computation, false otherwise. virtual bool apply(TransformationMatrix&, const IntSize& borderBoxSize) const = 0; virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false) = 0; virtual OperationType getOperationType() const = 0; virtual bool isSameType(const TransformOperation&) const { return false; } + + bool is3DOperation() const + { + OperationType opType = getOperationType(); + return opType == SCALE_Z || + opType == SCALE_3D || + opType == TRANSLATE_Z || + opType == TRANSLATE_3D || + opType == ROTATE_X || + opType == ROTATE_Y || + opType == ROTATE_3D || + opType == MATRIX_3D || + opType == PERSPECTIVE; + } }; } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TransformOperations.h b/WebCore/platform/graphics/transforms/TransformOperations.h index f929417..11605e8 100644 --- a/WebCore/platform/graphics/transforms/TransformOperations.h +++ b/WebCore/platform/graphics/transforms/TransformOperations.h @@ -47,6 +47,16 @@ public: m_operations[i]->apply(t, sz); } + // Return true if any of the operation types are 3D operation types (even if the + // values describe affine transforms) + bool has3DOperation() const + { + for (unsigned i = 0; i < m_operations.size(); ++i) + if (m_operations[i]->is3DOperation()) + return true; + return false; + } + Vector<RefPtr<TransformOperation> >& operations() { return m_operations; } const Vector<RefPtr<TransformOperation> >& operations() const { return m_operations; } diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.cpp b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp index b48d572..63a7b8e 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.cpp +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "TransformationMatrix.h" +#include "FloatPoint3D.h" #include "FloatRect.h" #include "FloatQuad.h" #include "IntRect.h" @@ -34,76 +35,467 @@ namespace WebCore { -static void affineTransformDecompose(const TransformationMatrix& matrix, double sr[9]) +// +// Supporting Math Functions +// +// This is a set of function from various places (attributed inline) to do things like +// inversion and decomposition of a 4x4 matrix. They are used throughout the code +// + +// +// Adapted from Matrix Inversion by Richard Carling, Graphics Gems <http://tog.acm.org/GraphicsGems/index.html>. + +// EULA: The Graphics Gems code is copyright-protected. In other words, you cannot claim the text of the code +// as your own and resell it. Using the code is permitted in any program, product, or library, non-commercial +// or commercial. Giving credit is not required, though is a nice gesture. The code comes as-is, and if there +// are any flaws or problems with any Gems code, nobody involved with Gems - authors, editors, publishers, or +// webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes +// with no guarantee. + +typedef double Vector4[4]; +typedef double Vector3[3]; + +const double SMALL_NUMBER = 1.e-8; + +// inverse(original_matrix, inverse_matrix) +// +// calculate the inverse of a 4x4 matrix +// +// -1 +// A = ___1__ adjoint A +// det A + +// double = determinant2x2(double a, double b, double c, double d) +// +// calculate the determinant of a 2x2 matrix. + +static double determinant2x2(double a, double b, double c, double d) { - TransformationMatrix m(matrix); + return a * d - b * c; +} - // Compute scaling factors - double sx = sqrt(m.a() * m.a() + m.b() * m.b()); - double sy = sqrt(m.c() * m.c() + m.d() * m.d()); +// double = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3) +// +// Calculate the determinant of a 3x3 matrix +// in the form +// +// | a1, b1, c1 | +// | a2, b2, c2 | +// | a3, b3, c3 | - /* Compute cross product of transformed unit vectors. If negative, - one axis was flipped. */ +static double determinant3x3(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3) +{ + return a1 * determinant2x2(b2, b3, c2, c3) + - b1 * determinant2x2(a2, a3, c2, c3) + + c1 * determinant2x2(a2, a3, b2, b3); +} - if (m.a() * m.d() - m.c() * m.b() < 0.0) { - // Flip axis with minimum unit vector dot product +// double = determinant4x4(matrix) +// +// calculate the determinant of a 4x4 matrix. - if (m.a() < m.d()) - sx = -sx; - else - sy = -sy; - } +static double determinant4x4(const TransformationMatrix::Matrix4& m) +{ + // Assign to individual variable names to aid selecting + // correct elements + + double a1 = m[0][0]; + double b1 = m[0][1]; + double c1 = m[0][2]; + double d1 = m[0][3]; + + double a2 = m[1][0]; + double b2 = m[1][1]; + double c2 = m[1][2]; + double d2 = m[1][3]; + + double a3 = m[2][0]; + double b3 = m[2][1]; + double c3 = m[2][2]; + double d3 = m[2][3]; + + double a4 = m[3][0]; + double b4 = m[3][1]; + double c4 = m[3][2]; + double d4 = m[3][3]; + + return a1 * determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4) + - b1 * determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4) + + c1 * determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4) + - d1 * determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); +} + +// adjoint( original_matrix, inverse_matrix ) +// +// calculate the adjoint of a 4x4 matrix +// +// Let a denote the minor determinant of matrix A obtained by +// ij +// +// deleting the ith row and jth column from A. +// +// i+j +// Let b = (-1) a +// ij ji +// +// The matrix B = (b ) is the adjoint of A +// ij + +static void adjoint(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) +{ + // Assign to individual variable names to aid + // selecting correct values + double a1 = matrix[0][0]; + double b1 = matrix[0][1]; + double c1 = matrix[0][2]; + double d1 = matrix[0][3]; - // Remove scale from matrix + double a2 = matrix[1][0]; + double b2 = matrix[1][1]; + double c2 = matrix[1][2]; + double d2 = matrix[1][3]; - m.scale(1.0 / sx, 1.0 / sy); + double a3 = matrix[2][0]; + double b3 = matrix[2][1]; + double c3 = matrix[2][2]; + double d3 = matrix[2][3]; - // Compute rotation + double a4 = matrix[3][0]; + double b4 = matrix[3][1]; + double c4 = matrix[3][2]; + double d4 = matrix[3][3]; - double angle = atan2(m.b(), m.a()); + // Row column labeling reversed since we transpose rows & columns + result[0][0] = determinant3x3(b2, b3, b4, c2, c3, c4, d2, d3, d4); + result[1][0] = - determinant3x3(a2, a3, a4, c2, c3, c4, d2, d3, d4); + result[2][0] = determinant3x3(a2, a3, a4, b2, b3, b4, d2, d3, d4); + result[3][0] = - determinant3x3(a2, a3, a4, b2, b3, b4, c2, c3, c4); + + result[0][1] = - determinant3x3(b1, b3, b4, c1, c3, c4, d1, d3, d4); + result[1][1] = determinant3x3(a1, a3, a4, c1, c3, c4, d1, d3, d4); + result[2][1] = - determinant3x3(a1, a3, a4, b1, b3, b4, d1, d3, d4); + result[3][1] = determinant3x3(a1, a3, a4, b1, b3, b4, c1, c3, c4); + + result[0][2] = determinant3x3(b1, b2, b4, c1, c2, c4, d1, d2, d4); + result[1][2] = - determinant3x3(a1, a2, a4, c1, c2, c4, d1, d2, d4); + result[2][2] = determinant3x3(a1, a2, a4, b1, b2, b4, d1, d2, d4); + result[3][2] = - determinant3x3(a1, a2, a4, b1, b2, b4, c1, c2, c4); + + result[0][3] = - determinant3x3(b1, b2, b3, c1, c2, c3, d1, d2, d3); + result[1][3] = determinant3x3(a1, a2, a3, c1, c2, c3, d1, d2, d3); + result[2][3] = - determinant3x3(a1, a2, a3, b1, b2, b3, d1, d2, d3); + result[3][3] = determinant3x3(a1, a2, a3, b1, b2, b3, c1, c2, c3); +} + +// Returns false if the matrix is not invertible +static bool inverse(const TransformationMatrix::Matrix4& matrix, TransformationMatrix::Matrix4& result) +{ + // Calculate the adjoint matrix + adjoint(matrix, result); - // Remove rotation from matrix + // Calculate the 4x4 determinant + // If the determinant is zero, + // then the inverse matrix is not unique. + double det = determinant4x4(matrix); - m.rotate(rad2deg(-angle)); + if (fabs(det) < SMALL_NUMBER) + return false; - // Return results + // Scale the adjoint matrix to get the inverse - sr[0] = sx; sr[1] = sy; sr[2] = angle; - sr[3] = m.a(); sr[4] = m.b(); - sr[5] = m.c(); sr[6] = m.d(); - sr[7] = m.e(); sr[8] = m.f(); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + result[i][j] = result[i][j] / det; + + return true; } -static void affineTransformCompose(TransformationMatrix& m, const double sr[9]) +// End of code adapted from Matrix Inversion by Richard Carling + +// Perform a decomposition on the passed matrix, return false if unsuccessful +// From Graphics Gems: unmatrix.c + +// Transpose rotation portion of matrix a, return b +static void transposeMatrix4(const TransformationMatrix::Matrix4& a, TransformationMatrix::Matrix4& b) { - m.setA(sr[3]); - m.setB(sr[4]); - m.setC(sr[5]); - m.setD(sr[6]); - m.setE(sr[7]); - m.setF(sr[8]); - m.rotate(rad2deg(sr[2])); - m.scale(sr[0], sr[1]); + for (int i = 0; i < 4; i++) + for (int j = 0; j < 4; j++) + b[i][j] = a[j][i]; } -bool TransformationMatrix::isInvertible() const +// Multiply a homogeneous point by a matrix and return the transformed point +static void v4MulPointByMatrix(const Vector4 p, const TransformationMatrix::Matrix4& m, Vector4 result) { - return det() != 0.0; + result[0] = (p[0] * m[0][0]) + (p[1] * m[1][0]) + + (p[2] * m[2][0]) + (p[3] * m[3][0]); + result[1] = (p[0] * m[0][1]) + (p[1] * m[1][1]) + + (p[2] * m[2][1]) + (p[3] * m[3][1]); + result[2] = (p[0] * m[0][2]) + (p[1] * m[1][2]) + + (p[2] * m[2][2]) + (p[3] * m[3][2]); + result[3] = (p[0] * m[0][3]) + (p[1] * m[1][3]) + + (p[2] * m[2][3]) + (p[3] * m[3][3]); } -TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix& other) +static double v3Length(Vector3 a) { - return (*this) *= other; + return sqrt((a[0] * a[0]) + (a[1] * a[1]) + (a[2] * a[2])); } -TransformationMatrix& TransformationMatrix::scale(double s) +static void v3Scale(Vector3 v, double desiredLength) { - return scale(s, s); + double len = v3Length(v); + if (len != 0) { + double l = desiredLength / len; + v[0] *= l; + v[1] *= l; + v[2] *= l; + } } -TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) +static double v3Dot(const Vector3 a, const Vector3 b) { - return scale(sx, sy); + return (a[0] * b[0]) + (a[1] * b[1]) + (a[2] * b[2]); +} + +// Make a linear combination of two vectors and return the result. +// result = (a * ascl) + (b * bscl) +static void v3Combine(const Vector3 a, const Vector3 b, Vector3 result, double ascl, double bscl) +{ + result[0] = (ascl * a[0]) + (bscl * b[0]); + result[1] = (ascl * a[1]) + (bscl * b[1]); + result[2] = (ascl * a[2]) + (bscl * b[2]); +} + +// Return the cross product result = a cross b */ +static void v3Cross(const Vector3 a, const Vector3 b, Vector3 result) +{ + result[0] = (a[1] * b[2]) - (a[2] * b[1]); + result[1] = (a[2] * b[0]) - (a[0] * b[2]); + result[2] = (a[0] * b[1]) - (a[1] * b[0]); +} + +static bool decompose(const TransformationMatrix::Matrix4& mat, TransformationMatrix::DecomposedType& result) +{ + TransformationMatrix::Matrix4 localMatrix; + memcpy(localMatrix, mat, sizeof(TransformationMatrix::Matrix4)); + + // Normalize the matrix. + if (localMatrix[3][3] == 0) + return false; + + int i, j; + for (i = 0; i < 4; i++) + for (j = 0; j < 4; j++) + localMatrix[i][j] /= localMatrix[3][3]; + + // perspectiveMatrix is used to solve for perspective, but it also provides + // an easy way to test for singularity of the upper 3x3 component. + TransformationMatrix::Matrix4 perspectiveMatrix; + memcpy(perspectiveMatrix, localMatrix, sizeof(TransformationMatrix::Matrix4)); + for (i = 0; i < 3; i++) + perspectiveMatrix[i][3] = 0; + perspectiveMatrix[3][3] = 1; + + if (determinant4x4(perspectiveMatrix) == 0) + return false; + + // First, isolate perspective. This is the messiest. + if (localMatrix[0][3] != 0 || localMatrix[1][3] != 0 || localMatrix[2][3] != 0) { + // rightHandSide is the right hand side of the equation. + Vector4 rightHandSide; + rightHandSide[0] = localMatrix[0][3]; + rightHandSide[1] = localMatrix[1][3]; + rightHandSide[2] = localMatrix[2][3]; + rightHandSide[3] = localMatrix[3][3]; + + // Solve the equation by inverting perspectiveMatrix and multiplying + // rightHandSide by the inverse. (This is the easiest way, not + // necessarily the best.) + TransformationMatrix::Matrix4 inversePerspectiveMatrix, transposedInversePerspectiveMatrix; + inverse(perspectiveMatrix, inversePerspectiveMatrix); + transposeMatrix4(inversePerspectiveMatrix, transposedInversePerspectiveMatrix); + + Vector4 perspectivePoint; + v4MulPointByMatrix(rightHandSide, transposedInversePerspectiveMatrix, perspectivePoint); + + result.perspectiveX = perspectivePoint[0]; + result.perspectiveY = perspectivePoint[1]; + result.perspectiveZ = perspectivePoint[2]; + result.perspectiveW = perspectivePoint[3]; + + // Clear the perspective partition + localMatrix[0][3] = localMatrix[1][3] = localMatrix[2][3] = 0; + localMatrix[3][3] = 1; + } else { + // No perspective. + result.perspectiveX = result.perspectiveY = result.perspectiveZ = 0; + result.perspectiveW = 1; + } + + // Next take care of translation (easy). + result.translateX = localMatrix[3][0]; + localMatrix[3][0] = 0; + result.translateY = localMatrix[3][1]; + localMatrix[3][1] = 0; + result.translateZ = localMatrix[3][2]; + localMatrix[3][2] = 0; + + // Vector4 type and functions need to be added to the common set. + Vector3 row[3], pdum3; + + // Now get scale and shear. + for (i = 0; i < 3; i++) { + row[i][0] = localMatrix[i][0]; + row[i][1] = localMatrix[i][1]; + row[i][2] = localMatrix[i][2]; + } + + // Compute X scale factor and normalize first row. + result.scaleX = v3Length(row[0]); + v3Scale(row[0], 1.0); + + // Compute XY shear factor and make 2nd row orthogonal to 1st. + result.skewXY = v3Dot(row[0], row[1]); + v3Combine(row[1], row[0], row[1], 1.0, -result.skewXY); + + // Now, compute Y scale and normalize 2nd row. + result.scaleY = v3Length(row[1]); + v3Scale(row[1], 1.0); + result.skewXY /= result.scaleY; + + // Compute XZ and YZ shears, orthogonalize 3rd row. + result.skewXZ = v3Dot(row[0], row[2]); + v3Combine(row[2], row[0], row[2], 1.0, -result.skewXZ); + result.skewYZ = v3Dot(row[1], row[2]); + v3Combine(row[2], row[1], row[2], 1.0, -result.skewYZ); + + // Next, get Z scale and normalize 3rd row. + result.scaleZ = v3Length(row[2]); + v3Scale(row[2], 1.0); + result.skewXZ /= result.scaleZ; + result.skewYZ /= result.scaleZ; + + // At this point, the matrix (in rows[]) is orthonormal. + // Check for a coordinate system flip. If the determinant + // is -1, then negate the matrix and the scaling factors. + v3Cross(row[1], row[2], pdum3); + if (v3Dot(row[0], pdum3) < 0) { + for (i = 0; i < 3; i++) { + result.scaleX *= -1; + row[i][0] *= -1; + row[i][1] *= -1; + row[i][2] *= -1; + } + } + + // Now, get the rotations out, as described in the gem. + + // FIXME - Add the ability to return either quaternions (which are + // easier to recompose with) or Euler angles (rx, ry, rz), which + // are easier for authors to deal with. The latter will only be useful + // when we fix https://bugs.webkit.org/show_bug.cgi?id=23799, so I + // will leave the Euler angle code here for now. + + // ret.rotateY = asin(-row[0][2]); + // if (cos(ret.rotateY) != 0) { + // ret.rotateX = atan2(row[1][2], row[2][2]); + // ret.rotateZ = atan2(row[0][1], row[0][0]); + // } else { + // ret.rotateX = atan2(-row[2][0], row[1][1]); + // ret.rotateZ = 0; + // } + + double s, t, x, y, z, w; + + t = row[0][0] + row[1][1] + row[2][2] + 1.0; + + if (t > 1e-4) { + s = 0.5 / sqrt(t); + w = 0.25 / s; + x = (row[2][1] - row[1][2]) * s; + y = (row[0][2] - row[2][0]) * s; + z = (row[1][0] - row[0][1]) * s; + } else if (row[0][0] > row[1][1] && row[0][0] > row[2][2]) { + s = sqrt (1.0 + row[0][0] - row[1][1] - row[2][2]) * 2.0; // S=4*qx + x = 0.25 * s; + y = (row[0][1] + row[1][0]) / s; + z = (row[0][2] + row[2][0]) / s; + w = (row[2][1] - row[1][2]) / s; + } else if (row[1][1] > row[2][2]) { + s = sqrt (1.0 + row[1][1] - row[0][0] - row[2][2]) * 2.0; // S=4*qy + x = (row[0][1] + row[1][0]) / s; + y = 0.25 * s; + z = (row[1][2] + row[2][1]) / s; + w = (row[0][2] - row[2][0]) / s; + } else { + s = sqrt(1.0 + row[2][2] - row[0][0] - row[1][1]) * 2.0; // S=4*qz + x = (row[0][2] + row[2][0]) / s; + y = (row[1][2] + row[2][1]) / s; + z = 0.25 * s; + w = (row[1][0] - row[0][1]) / s; + } + + result.quaternionX = x; + result.quaternionY = y; + result.quaternionZ = z; + result.quaternionW = w; + + return true; +} + +// Perform a spherical linear interpolation between the two +// passed quaternions with 0 <= t <= 1 +static void slerp(double qa[4], const double qb[4], double t) +{ + double ax, ay, az, aw; + double bx, by, bz, bw; + double cx, cy, cz, cw; + double angle; + double th, invth, scale, invscale; + + ax = qa[0]; ay = qa[1]; az = qa[2]; aw = qa[3]; + bx = qb[0]; by = qb[1]; bz = qb[2]; bw = qb[3]; + + angle = ax * bx + ay * by + az * bz + aw * bw; + + if (angle < 0.0) { + ax = -ax; ay = -ay; + az = -az; aw = -aw; + angle = -angle; + } + + if (angle + 1.0 > .05) { + if (1.0 - angle >= .05) { + th = acos (angle); + invth = 1.0 / sin (th); + scale = sin (th * (1.0 - t)) * invth; + invscale = sin (th * t) * invth; + } else { + scale = 1.0 - t; + invscale = t; + } + } else { + bx = -ay; + by = ax; + bz = -aw; + bw = az; + scale = sin(piDouble * (.5 - t)); + invscale = sin (piDouble * t); + } + + cx = ax * scale + bx * invscale; + cy = ay * scale + by * invscale; + cz = az * scale + bz * invscale; + cw = aw * scale + bw * invscale; + + qa[0] = cx; qa[1] = cy; qa[2] = cz; qa[3] = cw; +} + +// End of Supporting Math Functions + +TransformationMatrix& TransformationMatrix::scale(double s) +{ + return scaleNonUniform(s, s); } TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) @@ -113,93 +505,588 @@ TransformationMatrix& TransformationMatrix::rotateFromVector(double x, double y) TransformationMatrix& TransformationMatrix::flipX() { - return scale(-1.0f, 1.0f); + return scaleNonUniform(-1.0f, 1.0f); } TransformationMatrix& TransformationMatrix::flipY() { - return scale(1.0f, -1.0f); + return scaleNonUniform(1.0f, -1.0f); } -TransformationMatrix& TransformationMatrix::skew(double angleX, double angleY) +FloatPoint TransformationMatrix::projectPoint(const FloatPoint& p) const { - return shear(tan(deg2rad(angleX)), tan(deg2rad(angleY))); + // This is basically raytracing. We have a point in the destination + // plane with z=0, and we cast a ray parallel to the z-axis from that + // point to find the z-position at which it intersects the z=0 plane + // with the transform applied. Once we have that point we apply the + // inverse transform to find the corresponding point in the source + // space. + // + // Given a plane with normal Pn, and a ray starting at point R0 and + // with direction defined by the vector Rd, we can find the + // intersection point as a distance d from R0 in units of Rd by: + // + // d = -dot (Pn', R0) / dot (Pn', Rd) + + double x = p.x(); + double y = p.y(); + double z = -(m13() * x + m23() * y + m43()) / m33(); + + double outX = x * m11() + y * m21() + z * m31() + m41(); + double outY = x * m12() + y * m22() + z * m32() + m42(); + + double w = x * m14() + y * m24() + z * m34() + m44(); + if (w != 1 && w != 0) { + outX /= w; + outY /= w; + } + + return FloatPoint(static_cast<float>(outX), static_cast<float>(outY)); } -TransformationMatrix& TransformationMatrix::skewX(double angle) +FloatQuad TransformationMatrix::projectQuad(const FloatQuad& q) const { - return shear(tan(deg2rad(angle)), 0.0f); + FloatQuad projectedQuad; + projectedQuad.setP1(projectPoint(q.p1())); + projectedQuad.setP2(projectPoint(q.p2())); + projectedQuad.setP3(projectPoint(q.p3())); + projectedQuad.setP4(projectPoint(q.p4())); + return projectedQuad; } -TransformationMatrix& TransformationMatrix::skewY(double angle) +FloatPoint TransformationMatrix::mapPoint(const FloatPoint& p) const { - return shear(0.0f, tan(deg2rad(angle))); + double x, y; + multVecMatrix(p.x(), p.y(), x, y); + return FloatPoint(static_cast<float>(x), static_cast<float>(y)); } -TransformationMatrix makeMapBetweenRects(const FloatRect& source, const FloatRect& dest) +FloatPoint3D TransformationMatrix::mapPoint(const FloatPoint3D& p) const { - TransformationMatrix transform; - transform.translate(dest.x() - source.x(), dest.y() - source.y()); - transform.scale(dest.width() / source.width(), dest.height() / source.height()); - return transform; + double x, y, z; + multVecMatrix(p.x(), p.y(), p.z(), x, y, z); + return FloatPoint3D(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z)); } IntPoint TransformationMatrix::mapPoint(const IntPoint& point) const { - double x2, y2; - map(point.x(), point.y(), &x2, &y2); + double x, y; + multVecMatrix(point.x(), point.y(), x, y); // Round the point. - return IntPoint(lround(x2), lround(y2)); + return IntPoint(lround(x), lround(y)); +} + +IntRect TransformationMatrix::mapRect(const IntRect &rect) const +{ + return enclosingIntRect(mapRect(FloatRect(rect))); } -FloatPoint TransformationMatrix::mapPoint(const FloatPoint& point) const +FloatRect TransformationMatrix::mapRect(const FloatRect& r) const { - double x2, y2; - map(point.x(), point.y(), &x2, &y2); + FloatQuad resultQuad = mapQuad(FloatQuad(r)); + return resultQuad.boundingBox(); +} - return FloatPoint(static_cast<float>(x2), static_cast<float>(y2)); +FloatQuad TransformationMatrix::mapQuad(const FloatQuad& q) const +{ + FloatQuad result; + result.setP1(mapPoint(q.p1())); + result.setP2(mapPoint(q.p2())); + result.setP3(mapPoint(q.p3())); + result.setP4(mapPoint(q.p4())); + return result; } -FloatQuad TransformationMatrix::mapQuad(const FloatQuad& quad) const +TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy) { - // FIXME: avoid 4 seperate library calls. Point mapping really needs - // to be platform-independent code. - return FloatQuad(mapPoint(quad.p1()), - mapPoint(quad.p2()), - mapPoint(quad.p3()), - mapPoint(quad.p4())); + TransformationMatrix mat; + mat.m_matrix[0][0] = sx; + mat.m_matrix[1][1] = sy; + + multLeft(mat); + return *this; } -void TransformationMatrix::blend(const TransformationMatrix& from, double progress) +TransformationMatrix& TransformationMatrix::scale3d(double sx, double sy, double sz) +{ + TransformationMatrix mat; + mat.m_matrix[0][0] = sx; + mat.m_matrix[1][1] = sy; + mat.m_matrix[2][2] = sz; + + multLeft(mat); + return *this; +} + +TransformationMatrix& TransformationMatrix::rotate3d(double x, double y, double z, double angle) +{ + // angles are in degrees. Switch to radians + angle = deg2rad(angle); + + angle /= 2.0f; + double sinA = sin(angle); + double cosA = cos(angle); + double sinA2 = sinA * sinA; + + // normalize + double length = sqrt(x * x + y * y + z * z); + if (length == 0) { + // bad vector, just use something reasonable + x = 0; + y = 0; + z = 1; + } else if (length != 1) { + x /= length; + y /= length; + z /= length; + } + + TransformationMatrix mat; + + // optimize case where axis is along major axis + if (x == 1.0f && y == 0.0f && z == 0.0f) { + mat.m_matrix[0][0] = 1.0f; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 2.0f * sinA * cosA; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = -2.0f * sinA * cosA; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } else if (x == 0.0f && y == 1.0f && z == 0.0f) { + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = -2.0f * sinA * cosA; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 2.0f * sinA * cosA; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } else if (x == 0.0f && y == 0.0f && z == 1.0f) { + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 2.0f * sinA * cosA; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = -2.0f * sinA * cosA; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } else { + double x2 = x*x; + double y2 = y*y; + double z2 = z*z; + + mat.m_matrix[0][0] = 1.0f - 2.0f * (y2 + z2) * sinA2; + mat.m_matrix[0][1] = 2.0f * (x * y * sinA2 + z * sinA * cosA); + mat.m_matrix[0][2] = 2.0f * (x * z * sinA2 - y * sinA * cosA); + mat.m_matrix[1][0] = 2.0f * (y * x * sinA2 - z * sinA * cosA); + mat.m_matrix[1][1] = 1.0f - 2.0f * (z2 + x2) * sinA2; + mat.m_matrix[1][2] = 2.0f * (y * z * sinA2 + x * sinA * cosA); + mat.m_matrix[2][0] = 2.0f * (z * x * sinA2 + y * sinA * cosA); + mat.m_matrix[2][1] = 2.0f * (z * y * sinA2 - x * sinA * cosA); + mat.m_matrix[2][2] = 1.0f - 2.0f * (x2 + y2) * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + } + multLeft(mat); + return *this; +} + +TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, double rz) +{ + // angles are in degrees. Switch to radians + rx = deg2rad(rx); + ry = deg2rad(ry); + rz = deg2rad(rz); + + TransformationMatrix mat; + + rz /= 2.0f; + double sinA = sin(rz); + double cosA = cos(rz); + double sinA2 = sinA * sinA; + + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 2.0f * sinA * cosA; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = -2.0f * sinA * cosA; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + + TransformationMatrix rmat(mat); + + ry /= 2.0f; + sinA = sin(ry); + cosA = cos(ry); + sinA2 = sinA * sinA; + + mat.m_matrix[0][0] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = -2.0f * sinA * cosA; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f; + mat.m_matrix[1][2] = 0.0f; + mat.m_matrix[2][0] = 2.0f * sinA * cosA; + mat.m_matrix[2][1] = 0.0f; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + + rmat.multLeft(mat); + + rx /= 2.0f; + sinA = sin(rx); + cosA = cos(rx); + sinA2 = sinA * sinA; + + mat.m_matrix[0][0] = 1.0f; + mat.m_matrix[0][1] = 0.0f; + mat.m_matrix[0][2] = 0.0f; + mat.m_matrix[1][0] = 0.0f; + mat.m_matrix[1][1] = 1.0f - 2.0f * sinA2; + mat.m_matrix[1][2] = 2.0f * sinA * cosA; + mat.m_matrix[2][0] = 0.0f; + mat.m_matrix[2][1] = -2.0f * sinA * cosA; + mat.m_matrix[2][2] = 1.0f - 2.0f * sinA2; + mat.m_matrix[0][3] = mat.m_matrix[1][3] = mat.m_matrix[2][3] = 0.0f; + mat.m_matrix[3][0] = mat.m_matrix[3][1] = mat.m_matrix[3][2] = 0.0f; + mat.m_matrix[3][3] = 1.0f; + + rmat.multLeft(mat); + + multLeft(rmat); + return *this; +} + +TransformationMatrix& TransformationMatrix::translate(double tx, double ty) +{ +#ifdef ANDROID_FASTER_MATRIX + m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0]; + m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1]; + m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2]; + m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3]; +#else + // FIXME: optimize to avoid matrix copy + TransformationMatrix mat; + mat.m_matrix[3][0] = tx; + mat.m_matrix[3][1] = ty; + + multLeft(mat); +#endif + return *this; +} + +TransformationMatrix& TransformationMatrix::translate3d(double tx, double ty, double tz) { - double srA[9], srB[9]; +#ifdef ANDROID_FASTER_MATRIX + m_matrix[3][0] += tx * m_matrix[0][0] + ty * m_matrix[1][0] + tz * m_matrix[2][0]; + m_matrix[3][1] += tx * m_matrix[0][1] + ty * m_matrix[1][1] + tz * m_matrix[2][1]; + m_matrix[3][2] += tx * m_matrix[0][2] + ty * m_matrix[1][2] + tz * m_matrix[2][2]; + m_matrix[3][3] += tx * m_matrix[0][3] + ty * m_matrix[1][3] + tz * m_matrix[2][3]; +#else + // FIXME: optimize to avoid matrix copy + TransformationMatrix mat; + mat.m_matrix[3][0] = tx; + mat.m_matrix[3][1] = ty; + mat.m_matrix[3][2] = tz; - affineTransformDecompose(from, srA); - affineTransformDecompose(*this, srB); + multLeft(mat); +#endif + return *this; +} - // If x-axis of one is flipped, and y-axis of the other, convert to an unflipped rotation. - if ((srA[0] < 0.0 && srB[1] < 0.0) || (srA[1] < 0.0 && srB[0] < 0.0)) { - srA[0] = -srA[0]; - srA[1] = -srA[1]; - srA[2] += srA[2] < 0 ? piDouble : -piDouble; +TransformationMatrix& TransformationMatrix::translateRight(double tx, double ty) +{ + if (tx != 0) { + m_matrix[0][0] += m_matrix[0][3] * tx; + m_matrix[1][0] += m_matrix[1][3] * tx; + m_matrix[2][0] += m_matrix[2][3] * tx; + m_matrix[3][0] += m_matrix[3][3] * tx; } - // Don't rotate the long way around. - srA[2] = fmod(srA[2], 2.0 * piDouble); - srB[2] = fmod(srB[2], 2.0 * piDouble); + if (ty != 0) { + m_matrix[0][1] += m_matrix[0][3] * ty; + m_matrix[1][1] += m_matrix[1][3] * ty; + m_matrix[2][1] += m_matrix[2][3] * ty; + m_matrix[3][1] += m_matrix[3][3] * ty; + } - if (fabs(srA[2] - srB[2]) > piDouble) { - if (srA[2] > srB[2]) - srA[2] -= piDouble * 2.0; - else - srB[2] -= piDouble * 2.0; + return *this; +} + +TransformationMatrix& TransformationMatrix::translateRight3d(double tx, double ty, double tz) +{ + translateRight(tx, ty); + if (tz != 0) { + m_matrix[0][2] += m_matrix[0][3] * tz; + m_matrix[1][2] += m_matrix[1][3] * tz; + m_matrix[2][2] += m_matrix[2][3] * tz; + m_matrix[3][2] += m_matrix[3][3] * tz; } - for (int i = 0; i < 9; i++) - srA[i] = srA[i] + progress * (srB[i] - srA[i]); + return *this; +} + +TransformationMatrix& TransformationMatrix::skew(double sx, double sy) +{ + // angles are in degrees. Switch to radians + sx = deg2rad(sx); + sy = deg2rad(sy); + + TransformationMatrix mat; + mat.m_matrix[0][1] = tan(sy); // note that the y shear goes in the first row + mat.m_matrix[1][0] = tan(sx); // and the x shear in the second row + + multLeft(mat); + return *this; +} - affineTransformCompose(*this, srA); +TransformationMatrix& TransformationMatrix::applyPerspective(double p) +{ + TransformationMatrix mat; + if (p != 0) + mat.m_matrix[2][3] = -1/p; + + multLeft(mat); + return *this; +} + +// +// *this = mat * *this +// +TransformationMatrix& TransformationMatrix::multLeft(const TransformationMatrix& mat) +{ + Matrix4 tmp; + + tmp[0][0] = (mat.m_matrix[0][0] * m_matrix[0][0] + mat.m_matrix[0][1] * m_matrix[1][0] + + mat.m_matrix[0][2] * m_matrix[2][0] + mat.m_matrix[0][3] * m_matrix[3][0]); + tmp[0][1] = (mat.m_matrix[0][0] * m_matrix[0][1] + mat.m_matrix[0][1] * m_matrix[1][1] + + mat.m_matrix[0][2] * m_matrix[2][1] + mat.m_matrix[0][3] * m_matrix[3][1]); + tmp[0][2] = (mat.m_matrix[0][0] * m_matrix[0][2] + mat.m_matrix[0][1] * m_matrix[1][2] + + mat.m_matrix[0][2] * m_matrix[2][2] + mat.m_matrix[0][3] * m_matrix[3][2]); + tmp[0][3] = (mat.m_matrix[0][0] * m_matrix[0][3] + mat.m_matrix[0][1] * m_matrix[1][3] + + mat.m_matrix[0][2] * m_matrix[2][3] + mat.m_matrix[0][3] * m_matrix[3][3]); + + tmp[1][0] = (mat.m_matrix[1][0] * m_matrix[0][0] + mat.m_matrix[1][1] * m_matrix[1][0] + + mat.m_matrix[1][2] * m_matrix[2][0] + mat.m_matrix[1][3] * m_matrix[3][0]); + tmp[1][1] = (mat.m_matrix[1][0] * m_matrix[0][1] + mat.m_matrix[1][1] * m_matrix[1][1] + + mat.m_matrix[1][2] * m_matrix[2][1] + mat.m_matrix[1][3] * m_matrix[3][1]); + tmp[1][2] = (mat.m_matrix[1][0] * m_matrix[0][2] + mat.m_matrix[1][1] * m_matrix[1][2] + + mat.m_matrix[1][2] * m_matrix[2][2] + mat.m_matrix[1][3] * m_matrix[3][2]); + tmp[1][3] = (mat.m_matrix[1][0] * m_matrix[0][3] + mat.m_matrix[1][1] * m_matrix[1][3] + + mat.m_matrix[1][2] * m_matrix[2][3] + mat.m_matrix[1][3] * m_matrix[3][3]); + + tmp[2][0] = (mat.m_matrix[2][0] * m_matrix[0][0] + mat.m_matrix[2][1] * m_matrix[1][0] + + mat.m_matrix[2][2] * m_matrix[2][0] + mat.m_matrix[2][3] * m_matrix[3][0]); + tmp[2][1] = (mat.m_matrix[2][0] * m_matrix[0][1] + mat.m_matrix[2][1] * m_matrix[1][1] + + mat.m_matrix[2][2] * m_matrix[2][1] + mat.m_matrix[2][3] * m_matrix[3][1]); + tmp[2][2] = (mat.m_matrix[2][0] * m_matrix[0][2] + mat.m_matrix[2][1] * m_matrix[1][2] + + mat.m_matrix[2][2] * m_matrix[2][2] + mat.m_matrix[2][3] * m_matrix[3][2]); + tmp[2][3] = (mat.m_matrix[2][0] * m_matrix[0][3] + mat.m_matrix[2][1] * m_matrix[1][3] + + mat.m_matrix[2][2] * m_matrix[2][3] + mat.m_matrix[2][3] * m_matrix[3][3]); + + tmp[3][0] = (mat.m_matrix[3][0] * m_matrix[0][0] + mat.m_matrix[3][1] * m_matrix[1][0] + + mat.m_matrix[3][2] * m_matrix[2][0] + mat.m_matrix[3][3] * m_matrix[3][0]); + tmp[3][1] = (mat.m_matrix[3][0] * m_matrix[0][1] + mat.m_matrix[3][1] * m_matrix[1][1] + + mat.m_matrix[3][2] * m_matrix[2][1] + mat.m_matrix[3][3] * m_matrix[3][1]); + tmp[3][2] = (mat.m_matrix[3][0] * m_matrix[0][2] + mat.m_matrix[3][1] * m_matrix[1][2] + + mat.m_matrix[3][2] * m_matrix[2][2] + mat.m_matrix[3][3] * m_matrix[3][2]); + tmp[3][3] = (mat.m_matrix[3][0] * m_matrix[0][3] + mat.m_matrix[3][1] * m_matrix[1][3] + + mat.m_matrix[3][2] * m_matrix[2][3] + mat.m_matrix[3][3] * m_matrix[3][3]); + + setMatrix(tmp); + return *this; +} + +void TransformationMatrix::multVecMatrix(double x, double y, double& resultX, double& resultY) const +{ + resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0]; + resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1]; + double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3]; + if (w != 1 && w != 0) { + resultX /= w; + resultY /= w; + } +} + +void TransformationMatrix::multVecMatrix(double x, double y, double z, double& resultX, double& resultY, double& resultZ) const +{ + resultX = m_matrix[3][0] + x * m_matrix[0][0] + y * m_matrix[1][0] + z * m_matrix[2][0]; + resultY = m_matrix[3][1] + x * m_matrix[0][1] + y * m_matrix[1][1] + z * m_matrix[2][1]; + resultZ = m_matrix[3][2] + x * m_matrix[0][2] + y * m_matrix[1][2] + z * m_matrix[2][2]; + double w = m_matrix[3][3] + x * m_matrix[0][3] + y * m_matrix[1][3] + z * m_matrix[2][3]; + if (w != 1 && w != 0) { + resultX /= w; + resultY /= w; + resultZ /= w; + } +} + +bool TransformationMatrix::isInvertible() const +{ + double det = WebCore::determinant4x4(m_matrix); + + if (fabs(det) < SMALL_NUMBER) + return false; + + return true; +} + +TransformationMatrix TransformationMatrix::inverse() const +{ + TransformationMatrix invMat; + + bool inverted = WebCore::inverse(m_matrix, invMat.m_matrix); + if (!inverted) + return TransformationMatrix(); + + return invMat; +} + +void TransformationMatrix::makeAffine() +{ + m_matrix[0][2] = 0; + m_matrix[0][3] = 0; + + m_matrix[1][2] = 0; + m_matrix[1][3] = 0; + + m_matrix[2][0] = 0; + m_matrix[2][1] = 0; + m_matrix[2][2] = 1; + m_matrix[2][3] = 0; + + m_matrix[3][2] = 0; + m_matrix[3][3] = 1; +} + +static inline void blendFloat(double& from, double to, double progress) +{ + if (from != to) + from = from + (to - from) * progress; +} + +void TransformationMatrix::blend(const TransformationMatrix& from, double progress) +{ + if (from.isIdentity() && isIdentity()) + return; + + // decompose + DecomposedType fromDecomp; + DecomposedType toDecomp; + from.decompose(fromDecomp); + decompose(toDecomp); + + // interpolate + blendFloat(fromDecomp.scaleX, toDecomp.scaleX, progress); + blendFloat(fromDecomp.scaleY, toDecomp.scaleY, progress); + blendFloat(fromDecomp.scaleZ, toDecomp.scaleZ, progress); + blendFloat(fromDecomp.skewXY, toDecomp.skewXY, progress); + blendFloat(fromDecomp.skewXZ, toDecomp.skewXZ, progress); + blendFloat(fromDecomp.skewYZ, toDecomp.skewYZ, progress); + blendFloat(fromDecomp.translateX, toDecomp.translateX, progress); + blendFloat(fromDecomp.translateY, toDecomp.translateY, progress); + blendFloat(fromDecomp.translateZ, toDecomp.translateZ, progress); + blendFloat(fromDecomp.perspectiveX, toDecomp.perspectiveX, progress); + blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress); + blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress); + blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress); + + slerp(&fromDecomp.quaternionX, &toDecomp.quaternionX, progress); + + // recompose + recompose(fromDecomp); +} + +bool TransformationMatrix::decompose(DecomposedType& decomp) const +{ + if (isIdentity()) { + memset(&decomp, 0, sizeof(decomp)); + decomp.perspectiveW = 1; + decomp.scaleX = 1; + decomp.scaleY = 1; + decomp.scaleZ = 1; + } + + if (!WebCore::decompose(m_matrix, decomp)) + return false; + return true; +} + +void TransformationMatrix::recompose(const DecomposedType& decomp) +{ + makeIdentity(); + + // first apply perspective + m_matrix[0][3] = (float) decomp.perspectiveX; + m_matrix[1][3] = (float) decomp.perspectiveY; + m_matrix[2][3] = (float) decomp.perspectiveZ; + m_matrix[3][3] = (float) decomp.perspectiveW; + + // now translate + translate3d((float) decomp.translateX, (float) decomp.translateY, (float) decomp.translateZ); + + // apply rotation + double xx = decomp.quaternionX * decomp.quaternionX; + double xy = decomp.quaternionX * decomp.quaternionY; + double xz = decomp.quaternionX * decomp.quaternionZ; + double xw = decomp.quaternionX * decomp.quaternionW; + double yy = decomp.quaternionY * decomp.quaternionY; + double yz = decomp.quaternionY * decomp.quaternionZ; + double yw = decomp.quaternionY * decomp.quaternionW; + double zz = decomp.quaternionZ * decomp.quaternionZ; + double zw = decomp.quaternionZ * decomp.quaternionW; + + // Construct a composite rotation matrix from the quaternion values + TransformationMatrix rotationMatrix(1 - 2 * (yy + zz), 2 * (xy - zw), 2 * (xz + yw), 0, + 2 * (xy + zw), 1 - 2 * (xx + zz), 2 * (yz - xw), 0, + 2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0, + 0, 0, 0, 1); + + multLeft(rotationMatrix); + + // now apply skew + if (decomp.skewYZ) { + TransformationMatrix tmp; + tmp.setM32((float) decomp.skewYZ); + multLeft(tmp); + } + + if (decomp.skewXZ) { + TransformationMatrix tmp; + tmp.setM31((float) decomp.skewXZ); + multLeft(tmp); + } + + if (decomp.skewXY) { + TransformationMatrix tmp; + tmp.setM21((float) decomp.skewXY); + multLeft(tmp); + } + + // finally, apply scale + scale3d((float) decomp.scaleX, (float) decomp.scaleY, (float) decomp.scaleZ); } } diff --git a/WebCore/platform/graphics/transforms/TransformationMatrix.h b/WebCore/platform/graphics/transforms/TransformationMatrix.h index db121d1..62e4eb8 100644 --- a/WebCore/platform/graphics/transforms/TransformationMatrix.h +++ b/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -28,112 +28,293 @@ #if PLATFORM(CG) #include <CoreGraphics/CGAffineTransform.h> -typedef CGAffineTransform PlatformTransformationMatrix; -#elif PLATFORM(QT) -#include <QMatrix> -typedef QMatrix PlatformTransformationMatrix; #elif PLATFORM(CAIRO) #include <cairo.h> -typedef cairo_matrix_t PlatformTransformationMatrix; +#elif PLATFORM(QT) +#include <QTransform> #elif PLATFORM(SKIA) || PLATFORM(SGL) -#include "SkMatrix.h" -typedef SkMatrix PlatformTransformationMatrix; +#include <SkMatrix.h> #elif PLATFORM(WX) && USE(WXGC) -#include <wx/defs.h> #include <wx/graphics.h> -typedef wxGraphicsMatrix PlatformTransformationMatrix; #endif +#include <string.h> //for memcpy + namespace WebCore { class IntPoint; class IntRect; class FloatPoint; +class FloatPoint3D; class FloatRect; class FloatQuad; class TransformationMatrix { public: - TransformationMatrix(); - TransformationMatrix(double a, double b, double c, double d, double e, double f); -#if !PLATFORM(WX) || USE(WXGC) - TransformationMatrix(const PlatformTransformationMatrix&); -#endif + typedef double Matrix4[4][4]; - void setMatrix(double a, double b, double c, double d, double e, double f); - void map(double x, double y, double *x2, double *y2) const; + TransformationMatrix() { makeIdentity(); } + TransformationMatrix(const TransformationMatrix& t) { *this = t; } + TransformationMatrix(double a, double b, double c, double d, double e, double f) { setMatrix(a, b, c, d, e, f); } + TransformationMatrix(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double m41, double m42, double m43, double m44) + { + setMatrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44); + } - // Rounds the mapped point to the nearest integer value. - IntPoint mapPoint(const IntPoint&) const; + void setMatrix(double a, double b, double c, double d, double e, double f) + { + m_matrix[0][0] = a; m_matrix[0][1] = b; m_matrix[0][2] = 0; m_matrix[0][3] = 0; + m_matrix[1][0] = c; m_matrix[1][1] = d; m_matrix[1][2] = 0; m_matrix[1][3] = 0; + m_matrix[2][0] = 0; m_matrix[2][1] = 0; m_matrix[2][2] = 1; m_matrix[2][3] = 0; + m_matrix[3][0] = e; m_matrix[3][1] = f; m_matrix[3][2] = 0; m_matrix[3][3] = 1; + } + + void setMatrix(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double m41, double m42, double m43, double m44) + { + m_matrix[0][0] = m11; m_matrix[0][1] = m12; m_matrix[0][2] = m13; m_matrix[0][3] = m14; + m_matrix[1][0] = m21; m_matrix[1][1] = m22; m_matrix[1][2] = m23; m_matrix[1][3] = m24; + m_matrix[2][0] = m31; m_matrix[2][1] = m32; m_matrix[2][2] = m33; m_matrix[2][3] = m34; + m_matrix[3][0] = m41; m_matrix[3][1] = m42; m_matrix[3][2] = m43; m_matrix[3][3] = m44; + } + + TransformationMatrix& operator =(const TransformationMatrix &t) + { + setMatrix(t.m_matrix); + return *this; + } + + TransformationMatrix& makeIdentity() + { + setMatrix(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1); + return *this; + } + + bool isIdentity() const + { + return m_matrix[0][0] == 1 && m_matrix[0][1] == 0 && m_matrix[0][2] == 0 && m_matrix[0][3] == 0 && + m_matrix[1][0] == 0 && m_matrix[1][1] == 1 && m_matrix[1][2] == 0 && m_matrix[1][3] == 0 && + m_matrix[2][0] == 0 && m_matrix[2][1] == 0 && m_matrix[2][2] == 1 && m_matrix[2][3] == 0 && + m_matrix[3][0] == 0 && m_matrix[3][1] == 0 && m_matrix[3][2] == 0 && m_matrix[3][3] == 1; + } + + // This form preserves the double math from input to output + void map(double x, double y, double& x2, double& y2) const { multVecMatrix(x, y, x2, y2); } + // Map a 3D point through the transform, returning a 3D point. + FloatPoint3D mapPoint(const FloatPoint3D&) const; + + // Map a 2D point through the transform, returning a 2D point. + // Note that this ignores the z component, effectively projecting the point into the z=0 plane. FloatPoint mapPoint(const FloatPoint&) const; + // Like the version above, except that it rounds the mapped point to the nearest integer value. + IntPoint mapPoint(const IntPoint&) const; + + // If the matrix has 3D components, the z component of the result is + // dropped, effectively projecting the rect into the z=0 plane + FloatRect mapRect(const FloatRect&) const; + // Rounds the resulting mapped rectangle out. This is helpful for bounding // box computations but may not be what is wanted in other contexts. IntRect mapRect(const IntRect&) const; - FloatRect mapRect(const FloatRect&) const; - + // If the matrix has 3D components, the z component of the result is + // dropped, effectively projecting the quad into the z=0 plane FloatQuad mapQuad(const FloatQuad&) const; - bool isIdentity() const; + // Map a point on the z=0 plane into a point on + // the plane with with the transform applied, by extending + // a ray perpendicular to the source plane and computing + // the local x,y position of the point where that ray intersects + // with the destination plane. + FloatPoint projectPoint(const FloatPoint&) const; + // Projects the four corners of the quad + FloatQuad projectQuad(const FloatQuad&) const; - double a() const; - void setA(double a); + double m11() const { return m_matrix[0][0]; } + void setM11(double f) { m_matrix[0][0] = f; } + double m12() const { return m_matrix[0][1]; } + void setM12(double f) { m_matrix[0][1] = f; } + double m13() const { return m_matrix[0][2]; } + void setM13(double f) { m_matrix[0][2] = f; } + double m14() const { return m_matrix[0][3]; } + void setM14(double f) { m_matrix[0][3] = f; } + double m21() const { return m_matrix[1][0]; } + void setM21(double f) { m_matrix[1][0] = f; } + double m22() const { return m_matrix[1][1]; } + void setM22(double f) { m_matrix[1][1] = f; } + double m23() const { return m_matrix[1][2]; } + void setM23(double f) { m_matrix[1][2] = f; } + double m24() const { return m_matrix[1][3]; } + void setM24(double f) { m_matrix[1][3] = f; } + double m31() const { return m_matrix[2][0]; } + void setM31(double f) { m_matrix[2][0] = f; } + double m32() const { return m_matrix[2][1]; } + void setM32(double f) { m_matrix[2][1] = f; } + double m33() const { return m_matrix[2][2]; } + void setM33(double f) { m_matrix[2][2] = f; } + double m34() const { return m_matrix[2][3]; } + void setM34(double f) { m_matrix[2][3] = f; } + double m41() const { return m_matrix[3][0]; } + void setM41(double f) { m_matrix[3][0] = f; } + double m42() const { return m_matrix[3][1]; } + void setM42(double f) { m_matrix[3][1] = f; } + double m43() const { return m_matrix[3][2]; } + void setM43(double f) { m_matrix[3][2] = f; } + double m44() const { return m_matrix[3][3]; } + void setM44(double f) { m_matrix[3][3] = f; } + + double a() const { return m_matrix[0][0]; } + void setA(double a) { m_matrix[0][0] = a; } - double b() const; - void setB(double b); + double b() const { return m_matrix[0][1]; } + void setB(double b) { m_matrix[0][1] = b; } - double c() const; - void setC(double c); + double c() const { return m_matrix[1][0]; } + void setC(double c) { m_matrix[1][0] = c; } - double d() const; - void setD(double d); + double d() const { return m_matrix[1][1]; } + void setD(double d) { m_matrix[1][1] = d; } - double e() const; - void setE(double e); + double e() const { return m_matrix[3][0]; } + void setE(double e) { m_matrix[3][0] = e; } - double f() const; - void setF(double f); + double f() const { return m_matrix[3][1]; } + void setF(double f) { m_matrix[3][1] = f; } - void reset(); + // this = this * mat + TransformationMatrix& multiply(const TransformationMatrix& t) { return *this *= t; } - TransformationMatrix& multiply(const TransformationMatrix&); + // this = mat * this + TransformationMatrix& multLeft(const TransformationMatrix& mat); + TransformationMatrix& scale(double); - TransformationMatrix& scale(double sx, double sy); TransformationMatrix& scaleNonUniform(double sx, double sy); - TransformationMatrix& rotate(double d); + TransformationMatrix& scale3d(double sx, double sy, double sz); + + TransformationMatrix& rotate(double d) { return rotate3d(0, 0, d); } TransformationMatrix& rotateFromVector(double x, double y); + TransformationMatrix& rotate3d(double rx, double ry, double rz); + + // The vector (x,y,z) is normalized if it's not already. A vector of + // (0,0,0) uses a vector of (0,0,1). + TransformationMatrix& rotate3d(double x, double y, double z, double angle); + TransformationMatrix& translate(double tx, double ty); - TransformationMatrix& shear(double sx, double sy); + TransformationMatrix& translate3d(double tx, double ty, double tz); + + // translation added with a post-multiply + TransformationMatrix& translateRight(double tx, double ty); + TransformationMatrix& translateRight3d(double tx, double ty, double tz); + TransformationMatrix& flipX(); TransformationMatrix& flipY(); TransformationMatrix& skew(double angleX, double angleY); - TransformationMatrix& skewX(double angle); - TransformationMatrix& skewY(double angle); + TransformationMatrix& skewX(double angle) { return skew(angle, 0); } + TransformationMatrix& skewY(double angle) { return skew(0, angle); } + + TransformationMatrix& applyPerspective(double p); + bool hasPerspective() const { return m_matrix[2][3] != 0.0f; } - double det() const; bool isInvertible() const; + + // This method returns the identity matrix if it is not invertible. + // Use isInvertible() before calling this if you need to know. TransformationMatrix inverse() const; + // decompose the matrix into its component parts + typedef struct { + double scaleX, scaleY, scaleZ; + double skewXY, skewXZ, skewYZ; + double quaternionX, quaternionY, quaternionZ, quaternionW; + double translateX, translateY, translateZ; + double perspectiveX, perspectiveY, perspectiveZ, perspectiveW; + } DecomposedType; + + bool decompose(DecomposedType& decomp) const; + void recompose(const DecomposedType& decomp); + void blend(const TransformationMatrix& from, double progress); -#if !PLATFORM(WX) || USE(WXGC) - operator PlatformTransformationMatrix() const; -#endif + bool isAffine() const + { + return (m13() == 0 && m14() == 0 && m23() == 0 && m24() == 0 && + m31() == 0 && m32() == 0 && m33() == 1 && m34() == 0 && m43() == 0 && m44() == 1); + } + + // Throw away the non-affine parts of the matrix (lossy!) + void makeAffine(); + + bool operator==(const TransformationMatrix& m2) const + { + return (m_matrix[0][0] == m2.m_matrix[0][0] && + m_matrix[0][1] == m2.m_matrix[0][1] && + m_matrix[0][2] == m2.m_matrix[0][2] && + m_matrix[0][3] == m2.m_matrix[0][3] && + m_matrix[1][0] == m2.m_matrix[1][0] && + m_matrix[1][1] == m2.m_matrix[1][1] && + m_matrix[1][2] == m2.m_matrix[1][2] && + m_matrix[1][3] == m2.m_matrix[1][3] && + m_matrix[2][0] == m2.m_matrix[2][0] && + m_matrix[2][1] == m2.m_matrix[2][1] && + m_matrix[2][2] == m2.m_matrix[2][2] && + m_matrix[2][3] == m2.m_matrix[2][3] && + m_matrix[3][0] == m2.m_matrix[3][0] && + m_matrix[3][1] == m2.m_matrix[3][1] && + m_matrix[3][2] == m2.m_matrix[3][2] && + m_matrix[3][3] == m2.m_matrix[3][3]); + } - bool operator==(const TransformationMatrix&) const; bool operator!=(const TransformationMatrix& other) const { return !(*this == other); } - TransformationMatrix& operator*=(const TransformationMatrix&); - TransformationMatrix operator*(const TransformationMatrix&); + + // *this = *this * t (i.e., a multRight) + TransformationMatrix& operator*=(const TransformationMatrix& t) + { + *this = *this * t; + return *this; + } + + // result = *this * t (i.e., a multRight) + TransformationMatrix operator*(const TransformationMatrix& t) + { + TransformationMatrix result = t; + result.multLeft(*this); + return result; + } -private: -#if !PLATFORM(WX) || USE(WXGC) - PlatformTransformationMatrix m_transform; +#if PLATFORM(CG) + operator CGAffineTransform() const; +#elif PLATFORM(CAIRO) + operator cairo_matrix_t() const; +#elif PLATFORM(QT) + operator QTransform() const; +#elif PLATFORM(SKIA) || PLATFORM(SGL) + operator SkMatrix() const; +#elif PLATFORM(WX) && USE(WXGC) + operator wxGraphicsMatrix() const; #endif -}; -TransformationMatrix makeMapBetweenRects(const FloatRect& source, const FloatRect& dest); +private: + // multiply passed 2D point by matrix (assume z=0) + void multVecMatrix(double x, double y, double& dstX, double& dstY) const; + + // multiply passed 3D point by matrix + void multVecMatrix(double x, double y, double z, double& dstX, double& dstY, double& dstZ) const; + + void setMatrix(const Matrix4 m) + { + if (m && m != m_matrix) + memcpy(m_matrix, m, sizeof(Matrix4)); + } + + Matrix4 m_matrix; +}; } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp b/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp index 47471c4..a8ad131 100644 --- a/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.cpp @@ -30,12 +30,15 @@ PassRefPtr<TransformOperation> TranslateTransformOperation::blend(const Transfor return this; if (blendToIdentity) - return TranslateTransformOperation::create(Length(m_x.type()).blend(m_x, progress), Length(m_y.type()).blend(m_y, progress), m_type); + return TranslateTransformOperation::create(Length(m_x.type()).blend(m_x, progress), + Length(m_y.type()).blend(m_y, progress), + Length(m_z.type()).blend(m_z, progress), m_type); const TranslateTransformOperation* fromOp = static_cast<const TranslateTransformOperation*>(from); Length fromX = fromOp ? fromOp->m_x : Length(m_x.type()); Length fromY = fromOp ? fromOp->m_y : Length(m_y.type()); - return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_type); + Length fromZ = fromOp ? fromOp->m_z : Length(m_z.type()); + return TranslateTransformOperation::create(m_x.blend(fromX, progress), m_y.blend(fromY, progress), m_z.blend(fromZ, progress), m_type); } } // namespace WebCore diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h index 61ccbcc..a66cc3d 100644 --- a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h @@ -34,10 +34,21 @@ class TranslateTransformOperation : public TransformOperation { public: static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, OperationType type) { - return adoptRef(new TranslateTransformOperation(tx, ty, type)); + return adoptRef(new TranslateTransformOperation(tx, ty, Length(0, Fixed), type)); } - virtual bool isIdentity() const { return m_x.calcFloatValue(1) == 0 && m_y.calcFloatValue(1) == 0; } + static PassRefPtr<TranslateTransformOperation> create(const Length& tx, const Length& ty, const Length& tz, OperationType type) + { + return adoptRef(new TranslateTransformOperation(tx, ty, tz, type)); + } + + double x(const IntSize& borderBoxSize) const { return m_x.calcFloatValue(borderBoxSize.width()); } + double y(const IntSize& borderBoxSize) const { return m_y.calcFloatValue(borderBoxSize.height()); } + double z(const IntSize&) const { return m_z.calcFloatValue(1); } + +private: + virtual bool isIdentity() const { return m_x.calcFloatValue(1) == 0 && m_y.calcFloatValue(1) == 0 && m_z.calcFloatValue(1) == 0; } + virtual OperationType getOperationType() const { return m_type; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == m_type; } @@ -46,27 +57,29 @@ public: if (!isSameType(o)) return false; const TranslateTransformOperation* t = static_cast<const TranslateTransformOperation*>(&o); - return m_x == t->m_x && m_y == t->m_y; + return m_x == t->m_x && m_y == t->m_y && m_z == t->m_z; } virtual bool apply(TransformationMatrix& transform, const IntSize& borderBoxSize) const { - transform.translate(m_x.calcFloatValue(borderBoxSize.width()), m_y.calcFloatValue(borderBoxSize.height())); + transform.translate3d(x(borderBoxSize), y(borderBoxSize), z(borderBoxSize)); return m_x.type() == Percent || m_y.type() == Percent; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); -private: - TranslateTransformOperation(const Length& tx, const Length& ty, OperationType type) + TranslateTransformOperation(const Length& tx, const Length& ty, const Length& tz, OperationType type) : m_x(tx) , m_y(ty) + , m_z(tz) , m_type(type) { + ASSERT(type == TRANSLATE_X || type == TRANSLATE_Y || type == TRANSLATE_Z || type == TRANSLATE || type == TRANSLATE_3D); } Length m_x; Length m_y; + Length m_z; OperationType m_type; }; diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp index 9ca95f3..feeb2ae 100644 --- a/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -223,8 +223,6 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData ExtTextOut(hdc, 0, 0, ETO_GLYPH_INDEX, 0, reinterpret_cast<const WCHAR*>(glyphBuffer.glyphs(from)), numGlyphs, gdiAdvances.data()); } } else { - RetainPtr<CGMutablePathRef> path(AdoptCF, CGPathCreateMutable()); - XFORM xform; GetWorldTransform(hdc, &xform); TransformationMatrix hdcTransform(xform.eM11, xform.eM21, xform.eM12, xform.eM22, xform.eDx, xform.eDy); @@ -233,16 +231,8 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData initialGlyphTransform = CGAffineTransformConcat(initialGlyphTransform, CGAffineTransformMake(1, 0, tanf(syntheticObliqueAngle * piFloat / 180.0f), 1, 0, 0)); initialGlyphTransform.tx = 0; initialGlyphTransform.ty = 0; - CGAffineTransform glyphTranslation = CGAffineTransformIdentity; - - for (unsigned i = 0; i < numGlyphs; ++i) { - RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); - CGAffineTransform glyphTransform = CGAffineTransformConcat(initialGlyphTransform, glyphTranslation); - CGPathAddPath(path.get(), &glyphTransform, glyphPath.get()); - glyphTranslation = CGAffineTransformTranslate(glyphTranslation, gdiAdvances[i], 0); - } - CGContextRef cgContext = graphicsContext->platformContext(); + CGContextSaveGState(cgContext); BOOL fontSmoothingEnabled = false; @@ -252,26 +242,36 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData CGContextScaleCTM(cgContext, 1.0, -1.0); CGContextTranslateCTM(cgContext, point.x() + glyphBuffer.offsetAt(from).width(), -(point.y() + glyphBuffer.offsetAt(from).height())); - if (drawingMode & cTextFill) { - CGContextAddPath(cgContext, path.get()); - CGContextFillPath(cgContext); - if (font->m_syntheticBoldOffset) { - CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); - CGContextAddPath(cgContext, path.get()); + for (unsigned i = 0; i < numGlyphs; ++i) { + RetainPtr<CGPathRef> glyphPath(AdoptCF, createPathForGlyph(hdc, glyphBuffer.glyphAt(from + i))); + CGContextSaveGState(cgContext); + CGContextConcatCTM(cgContext, initialGlyphTransform); + + if (drawingMode & cTextFill) { + CGContextAddPath(cgContext, glyphPath.get()); CGContextFillPath(cgContext); - CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + if (font->m_syntheticBoldOffset) { + CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); + CGContextAddPath(cgContext, glyphPath.get()); + CGContextFillPath(cgContext); + CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + } } - } - if (drawingMode & cTextStroke) { - CGContextAddPath(cgContext, path.get()); - CGContextStrokePath(cgContext); - if (font->m_syntheticBoldOffset) { - CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); - CGContextAddPath(cgContext, path.get()); + if (drawingMode & cTextStroke) { + CGContextAddPath(cgContext, glyphPath.get()); CGContextStrokePath(cgContext); - CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + if (font->m_syntheticBoldOffset) { + CGContextTranslateCTM(cgContext, font->m_syntheticBoldOffset, 0); + CGContextAddPath(cgContext, glyphPath.get()); + CGContextStrokePath(cgContext); + CGContextTranslateCTM(cgContext, -font->m_syntheticBoldOffset, 0); + } } + + CGContextRestoreGState(cgContext); + CGContextTranslateCTM(cgContext, gdiAdvances[i], 0); } + CGContextRestoreGState(cgContext); } @@ -298,8 +298,7 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo bool shouldUseFontSmoothing = WebCoreShouldUseFontSmoothing(); if (font->platformData().useGDI()) { - static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); - if (!canUsePlatformNativeGlyphs || !shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { + if (!shouldUseFontSmoothing || (graphicsContext->textDrawingMode() & cTextStroke)) { drawGDIGlyphs(graphicsContext, font, glyphBuffer, from, numGlyphs, point); return; } diff --git a/WebCore/platform/graphics/win/FontCacheWin.cpp b/WebCore/platform/graphics/win/FontCacheWin.cpp index 9acc5a0..887bf79 100644 --- a/WebCore/platform/graphics/win/FontCacheWin.cpp +++ b/WebCore/platform/graphics/win/FontCacheWin.cpp @@ -422,7 +422,24 @@ static HFONT createGDIFont(const AtomicString& family, LONG desiredWeight, bool matchData.m_chosen.lfQuality = DEFAULT_QUALITY; matchData.m_chosen.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; - return CreateFontIndirect(&matchData.m_chosen); + HFONT result = CreateFontIndirect(&matchData.m_chosen); + if (!result) + return 0; + + HDC dc = GetDC(0); + SaveDC(dc); + SelectObject(dc, result); + WCHAR actualName[LF_FACESIZE]; + GetTextFace(dc, LF_FACESIZE, actualName); + RestoreDC(dc, -1); + ReleaseDC(0, dc); + + if (wcsicmp(matchData.m_chosen.lfFaceName, actualName)) { + DeleteObject(result); + result = 0; + } + + return result; } struct TraitsInFamilyProcData { diff --git a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index ba8afe7..1ac3359 100644 --- a/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -65,10 +65,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b ASSERT(m_fontReference); ASSERT(T2embedLibrary()); - static bool canUsePlatformNativeGlyphs = wkCanUsePlatformNativeGlyphs(); - LOGFONT _logFont; - - LOGFONT& logFont = canUsePlatformNativeGlyphs ? *static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))) : _logFont; + LOGFONT& logFont = *static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))); if (m_name.isNull()) TTGetNewFontName(&m_fontReference, logFont.lfFaceName, LF_FACESIZE, 0, 0); else @@ -90,8 +87,7 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b logFont.lfWeight = bold ? 700 : 400; HFONT hfont = CreateFontIndirect(&logFont); - if (canUsePlatformNativeGlyphs) - wkSetFontPlatformInfo(m_cgFont, &logFont, free); + wkSetFontPlatformInfo(m_cgFont, &logFont, free); return FontPlatformData(hfont, m_cgFont, size, bold, italic, renderingMode == AlternateRenderingMode); } @@ -120,7 +116,7 @@ size_t getBytesWithOffset(void *info, void* buffer, size_t offset, size_t count) // Streams the concatenation of a header and font data. class EOTStream { public: - EOTStream(const Vector<UInt8, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) + EOTStream(const Vector<uint8_t, 512>& eotHeader, const SharedBuffer* fontData, size_t overlayDst, size_t overlaySrc, size_t overlayLength) : m_eotHeader(eotHeader) , m_fontData(fontData) , m_overlayDst(overlayDst) @@ -134,7 +130,7 @@ public: size_t read(void* buffer, size_t count); private: - const Vector<UInt8, 512>& m_eotHeader; + const Vector<uint8_t, 512>& m_eotHeader; const SharedBuffer* m_fontData; size_t m_overlayDst; size_t m_overlaySrc; @@ -210,7 +206,7 @@ FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) // TTLoadEmbeddedFont works only with Embedded OpenType (.eot) data, so we need to create an EOT header // and prepend it to the font data. - Vector<UInt8, 512> eotHeader; + Vector<uint8_t, 512> eotHeader; size_t overlayDst; size_t overlaySrc; size_t overlayLength; diff --git a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp index c7e59ab..59f7e5c 100644 --- a/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp +++ b/WebCore/platform/graphics/win/FontPlatformDataCGWin.cpp @@ -120,7 +120,7 @@ void FontPlatformData::platformDataInit(HFONT font, float size, HDC hdc, WCHAR* ASSERT(m_cgFont); } } - if (m_useGDI && wkCanUsePlatformNativeGlyphs()) { + if (m_useGDI) { LOGFONT* logfont = static_cast<LOGFONT*>(malloc(sizeof(LOGFONT))); GetObject(font, sizeof(*logfont), logfont); wkSetFontPlatformInfo(m_cgFont.get(), logfont, free); diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index dccbe6c..da5b503 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -76,8 +76,6 @@ GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) } } -bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } - // FIXME: Is it possible to merge getWindowsContext and createWindowsBitmap into a single API // suitable for all clients? HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) @@ -173,16 +171,6 @@ void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, boo m_data->restore(); } -void GraphicsContext::setShouldIncludeChildWindows(bool include) -{ - m_data->m_shouldIncludeChildWindows = include; -} - -bool GraphicsContext::shouldIncludeChildWindows() const -{ - return m_data->m_shouldIncludeChildWindows; -} - GraphicsContext::WindowsBitmap::WindowsBitmap(HDC hdc, IntSize size) : m_hdc(0) , m_size(size) @@ -265,7 +253,7 @@ void GraphicsContext::drawFocusRing(const Color& color) float radius = (focusRingWidth() - 1) / 2.0f; int offset = radius + focusRingOffset(); - CGColorRef colorRef = color.isValid() ? cgColor(color) : 0; + CGColorRef colorRef = color.isValid() ? createCGColor(color) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); const Vector<IntRect>& rects = focusRingRects(); diff --git a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp index 892d24a..1980d18 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCairoWin.cpp @@ -87,8 +87,6 @@ HDC GraphicsContext::getWindowsContext(const IntRect& dstRect, bool supportAlpha return hdc; } -bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } - void GraphicsContext::releaseWindowsContext(HDC hdc, const IntRect& dstRect, bool supportAlphaBlend, bool mayCreateBitmap) { // FIXME: We aren't really doing anything with the 'mayCreateBitmap' flag. This needs diff --git a/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/WebCore/platform/graphics/win/GraphicsContextWin.cpp index b3ebcb0..e4c5b04 100644 --- a/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -43,6 +43,18 @@ namespace WebCore { class SVGResourceImage; +bool GraphicsContext::inTransparencyLayer() const { return m_data->m_transparencyCount; } + +void GraphicsContext::setShouldIncludeChildWindows(bool include) +{ + m_data->m_shouldIncludeChildWindows = include; +} + +bool GraphicsContext::shouldIncludeChildWindows() const +{ + return m_data->m_shouldIncludeChildWindows; +} + void GraphicsContextPlatformPrivate::save() { if (!m_hdc) diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 1b09e1f..c293f49 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -32,7 +32,10 @@ #include "KURL.h" #include "QTMovieWin.h" #include "ScrollView.h" +#include "StringHash.h" +#include <wtf/HashSet.h> #include <wtf/MathExtras.h> +#include <wtf/StdLibExtras.h> #if DRAW_FRAME_RATE #include "Font.h" @@ -48,16 +51,25 @@ using namespace std; namespace WebCore { -static const double endPointTimerInterval = 0.020; - +MediaPlayerPrivateInterface* MediaPlayerPrivate::create(MediaPlayer* player) +{ + return new MediaPlayerPrivate(player); +} + +void MediaPlayerPrivate::registerMediaEngine(MediaEngineRegistrar registrar) +{ + if (isAvailable()) + registrar(create, getSupportedTypes, supportsType); +} + MediaPlayerPrivate::MediaPlayerPrivate(MediaPlayer* player) : m_player(player) , m_seekTo(-1) , m_endTime(numeric_limits<float>::infinity()) , m_seekTimer(this, &MediaPlayerPrivate::seekTimerFired) - , m_endPointTimer(this, &MediaPlayerPrivate::endPointTimerFired) , m_networkState(MediaPlayer::Empty) - , m_readyState(MediaPlayer::DataUnavailable) + , m_readyState(MediaPlayer::HaveNothing) + , m_enabledTrackCount(0) , m_startedPlaying(false) , m_isStreaming(false) #if DRAW_FRAME_RATE @@ -75,7 +87,8 @@ MediaPlayerPrivate::~MediaPlayerPrivate() void MediaPlayerPrivate::load(const String& url) { if (!QTMovieWin::initializeQuickTime()) { - m_networkState = MediaPlayer::LoadFailed; + // FIXME: is this the right error to return? + m_networkState = MediaPlayer::DecodeError; m_player->networkStateChanged(); return; } @@ -84,17 +97,16 @@ void MediaPlayerPrivate::load(const String& url) m_networkState = MediaPlayer::Loading; m_player->networkStateChanged(); } - if (m_readyState != MediaPlayer::DataUnavailable) { - m_readyState = MediaPlayer::DataUnavailable; + if (m_readyState != MediaPlayer::HaveNothing) { + m_readyState = MediaPlayer::HaveNothing; m_player->readyStateChanged(); } cancelSeek(); - m_endPointTimer.stop(); m_qtMovie.set(new QTMovieWin(this)); m_qtMovie->load(url.characters(), url.length()); - m_qtMovie->setVolume(m_player->m_volume); - m_qtMovie->setVisible(m_player->m_visible); + m_qtMovie->setVolume(m_player->volume()); + m_qtMovie->setVisible(m_player->visible()); } void MediaPlayerPrivate::play() @@ -107,7 +119,6 @@ void MediaPlayerPrivate::play() #endif m_qtMovie->play(); - startEndPointTimerIfNeeded(); } void MediaPlayerPrivate::pause() @@ -119,7 +130,6 @@ void MediaPlayerPrivate::pause() m_timeStoppedPlaying = GetTickCount(); #endif m_qtMovie->pause(); - m_endPointTimer.stop(); } float MediaPlayerPrivate::duration() const @@ -156,11 +166,12 @@ void MediaPlayerPrivate::seek(float time) void MediaPlayerPrivate::doSeek() { float oldRate = m_qtMovie->rate(); - m_qtMovie->setRate(0); + if (oldRate) + m_qtMovie->setRate(0); m_qtMovie->setCurrentTime(m_seekTo); float timeAfterSeek = currentTime(); // restore playback only if not at end, othewise QTMovie will loop - if (timeAfterSeek < duration() && timeAfterSeek < m_endTime) + if (oldRate && timeAfterSeek < duration() && timeAfterSeek < m_endTime) m_qtMovie->setRate(oldRate); cancelSeek(); } @@ -195,22 +206,6 @@ void MediaPlayerPrivate::seekTimerFired(Timer<MediaPlayerPrivate>*) void MediaPlayerPrivate::setEndTime(float time) { m_endTime = time; - startEndPointTimerIfNeeded(); -} - -void MediaPlayerPrivate::startEndPointTimerIfNeeded() -{ - if (m_endTime < duration() && m_startedPlaying && !m_endPointTimer.isActive()) - m_endPointTimer.startRepeating(endPointTimerInterval); -} - -void MediaPlayerPrivate::endPointTimerFired(Timer<MediaPlayerPrivate>*) -{ - float time = currentTime(); - if (time >= m_endTime) { - pause(); - didEnd(); - } } bool MediaPlayerPrivate::paused() const @@ -325,42 +320,44 @@ void MediaPlayerPrivate::updateStates() long loadState = m_qtMovie ? m_qtMovie->loadState() : QTMovieLoadStateError; - if (loadState >= QTMovieLoadStateLoaded && m_networkState < MediaPlayer::LoadedMetaData && !m_player->inMediaDocument()) { - unsigned enabledTrackCount; - m_qtMovie->disableUnsupportedTracks(enabledTrackCount); - // FIXME: We should differentiate between load errors and decode errors <rdar://problem/5605692> - if (!enabledTrackCount) + if (loadState >= QTMovieLoadStateLoaded && m_readyState < MediaPlayer::HaveMetadata && !m_player->inMediaDocument()) { + m_qtMovie->disableUnsupportedTracks(m_enabledTrackCount); + if (!m_enabledTrackCount) loadState = QTMovieLoadStateError; } // "Loaded" is reserved for fully buffered movies, never the case when streaming if (loadState >= QTMovieLoadStateComplete && !m_isStreaming) { - if (m_networkState < MediaPlayer::Loaded) - m_networkState = MediaPlayer::Loaded; - m_readyState = MediaPlayer::CanPlayThrough; + m_networkState = MediaPlayer::Loaded; + m_readyState = MediaPlayer::HaveEnoughData; } else if (loadState >= QTMovieLoadStatePlaythroughOK) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; - m_readyState = MediaPlayer::CanPlayThrough; + m_readyState = MediaPlayer::HaveEnoughData; } else if (loadState >= QTMovieLoadStatePlayable) { - if (m_networkState < MediaPlayer::LoadedFirstFrame && !seeking()) - m_networkState = MediaPlayer::LoadedFirstFrame; - m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::CanPlay : MediaPlayer::DataUnavailable; + // FIXME: This might not work correctly in streaming case, <rdar://problem/5693967> + m_readyState = currentTime() < maxTimeLoaded() ? MediaPlayer::HaveFutureData : MediaPlayer::HaveCurrentData; } else if (loadState >= QTMovieLoadStateLoaded) { - if (m_networkState < MediaPlayer::LoadedMetaData) - m_networkState = MediaPlayer::LoadedMetaData; - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveMetadata; } else if (loadState > QTMovieLoadStateError) { - if (m_networkState < MediaPlayer::Loading) - m_networkState = MediaPlayer::Loading; - m_readyState = MediaPlayer::DataUnavailable; + m_networkState = MediaPlayer::Loading; + m_readyState = MediaPlayer::HaveNothing; } else { - m_networkState = MediaPlayer::LoadFailed; - m_readyState = MediaPlayer::DataUnavailable; + float loaded = maxTimeLoaded(); + if (!loaded) + m_readyState = MediaPlayer::HaveNothing; + + if (!m_enabledTrackCount) + m_networkState = MediaPlayer::FormatError; + else { + // FIXME: We should differentiate between load/network errors and decode errors <rdar://problem/5605692> + if (loaded > 0) + m_networkState = MediaPlayer::DecodeError; + else + m_readyState = MediaPlayer::HaveNothing; + } } if (seeking()) - m_readyState = MediaPlayer::DataUnavailable; + m_readyState = MediaPlayer::HaveNothing; if (m_networkState != oldNetworkState) m_player->networkStateChanged(); @@ -371,7 +368,6 @@ void MediaPlayerPrivate::updateStates() void MediaPlayerPrivate::didEnd() { - m_endPointTimer.stop(); m_startedPlaying = false; #if DRAW_FRAME_RATE m_timeStoppedPlaying = GetTickCount(); @@ -380,10 +376,10 @@ void MediaPlayerPrivate::didEnd() m_player->timeChanged(); } -void MediaPlayerPrivate::setRect(const IntRect& r) +void MediaPlayerPrivate::setSize(const IntSize& size) { if (m_qtMovie) - m_qtMovie->setSize(r.width(), r.height()); + m_qtMovie->setSize(size.width(), size.height()); } void MediaPlayerPrivate::setVisible(bool b) @@ -403,7 +399,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) #if DRAW_FRAME_RATE if (m_frameCountWhilePlaying > 10) { - Frame* frame = m_player->m_frameView ? m_player->m_frameView->frame() : NULL; + Frame* frame = m_player->frameView() ? m_player->frameView()->frame() : NULL; Document* document = frame ? frame->document() : NULL; RenderObject* renderer = document ? document->renderer() : NULL; RenderStyle* styleToUse = renderer ? renderer->style() : NULL; @@ -427,16 +423,30 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) #endif } -void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +static HashSet<String> mimeTypeCache() { - unsigned count = QTMovieWin::countSupportedTypes(); - for (unsigned n = 0; n < count; n++) { - const UChar* character; - unsigned len; - QTMovieWin::getSupportedType(n, character, len); - if (len) - types.add(String(character, len)); + DEFINE_STATIC_LOCAL(HashSet<String>, typeCache, ()); + static bool typeListInitialized = false; + + if (!typeListInitialized) { + unsigned count = QTMovieWin::countSupportedTypes(); + for (unsigned n = 0; n < count; n++) { + const UChar* character; + unsigned len; + QTMovieWin::getSupportedType(n, character, len); + if (len) + typeCache.add(String(character, len)); + } + + typeListInitialized = true; } + + return typeCache; +} + +void MediaPlayerPrivate::getSupportedTypes(HashSet<String>& types) +{ + types = mimeTypeCache(); } bool MediaPlayerPrivate::isAvailable() @@ -444,6 +454,13 @@ bool MediaPlayerPrivate::isAvailable() return QTMovieWin::initializeQuickTime(); } +MediaPlayer::SupportsType MediaPlayerPrivate::supportsType(const String& type, const String& codecs) +{ + // only return "IsSupported" if there is no codecs parameter for now as there is no way to ask QT if it supports an + // extended MIME type + return mimeTypeCache().contains(type) ? (!codecs.isEmpty() ? MediaPlayer::MayBeSupported : MediaPlayer::IsSupported) : MediaPlayer::IsNotSupported; +} + void MediaPlayerPrivate::movieEnded(QTMovieWin* movie) { ASSERT(m_qtMovie.get() == movie); diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h index c4c893c..63aa62b 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * 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 @@ -28,7 +28,7 @@ #if ENABLE(VIDEO) -#include "MediaPlayer.h" +#include "MediaPlayerPrivate.h" #include "Timer.h" #include <QTMovieWin.h> #include <wtf/OwnPtr.h> @@ -44,9 +44,10 @@ class IntSize; class IntRect; class String; -class MediaPlayerPrivate : QTMovieWinClient, Noncopyable { +class MediaPlayerPrivate : public MediaPlayerPrivateInterface, public QTMovieWinClient { public: - MediaPlayerPrivate(MediaPlayer*); + static void registerMediaEngine(MediaEngineRegistrar); + ~MediaPlayerPrivate(); IntSize naturalSize() const; @@ -81,38 +82,42 @@ public: unsigned totalBytes() const; void setVisible(bool); - void setRect(const IntRect&); + void setSize(const IntSize&); void loadStateChanged(); void didEnd(); void paint(GraphicsContext*, const IntRect&); - static void getSupportedTypes(HashSet<String>& types); - static bool isAvailable(); private: + MediaPlayerPrivate(MediaPlayer*); + void updateStates(); void doSeek(); void cancelSeek(); void seekTimerFired(Timer<MediaPlayerPrivate>*); - void endPointTimerFired(Timer<MediaPlayerPrivate>*); float maxTimeLoaded() const; - void startEndPointTimerIfNeeded(); virtual void movieEnded(QTMovieWin*); virtual void movieLoadStateChanged(QTMovieWin*); virtual void movieTimeChanged(QTMovieWin*); virtual void movieNewImageAvailable(QTMovieWin*); - + + // engine support + static MediaPlayerPrivateInterface* create(MediaPlayer*); + static void getSupportedTypes(HashSet<String>& types); + static MediaPlayer::SupportsType supportsType(const String& type, const String& codecs); + static bool isAvailable(); + MediaPlayer* m_player; OwnPtr<QTMovieWin> m_qtMovie; float m_seekTo; float m_endTime; Timer<MediaPlayerPrivate> m_seekTimer; - Timer<MediaPlayerPrivate> m_endPointTimer; MediaPlayer::NetworkState m_networkState; MediaPlayer::ReadyState m_readyState; + unsigned m_enabledTrackCount; bool m_startedPlaying; bool m_isStreaming; #if DRAW_FRAME_RATE diff --git a/WebCore/platform/graphics/win/QTMovieWin.cpp b/WebCore/platform/graphics/win/QTMovieWin.cpp index 32aecd3..3f23698 100644 --- a/WebCore/platform/graphics/win/QTMovieWin.cpp +++ b/WebCore/platform/graphics/win/QTMovieWin.cpp @@ -658,6 +658,12 @@ void QTMovieWin::disableUnsupportedTracks(unsigned& enabledTrackCount) continue; if (!allowedTrackTypes->contains(mediaType)) { + + // Different mpeg variants import as different track types so check for the "mpeg + // characteristic" instead of hard coding the (current) list of mpeg media types. + if (GetMovieIndTrackType(m_private->m_movie, 1, 'mpeg', movieTrackCharacteristic | movieTrackEnabledOnly)) + continue; + SetTrackEnabled(currentTrack, false); --enabledTrackCount; } diff --git a/WebCore/platform/graphics/win/QTMovieWinTimer.cpp b/WebCore/platform/graphics/win/QTMovieWinTimer.cpp index d0aa3e6..f6103ea 100644 --- a/WebCore/platform/graphics/win/QTMovieWinTimer.cpp +++ b/WebCore/platform/graphics/win/QTMovieWinTimer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2009 Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -51,6 +51,9 @@ static LRESULT CALLBACK TimerWindowWndProc(HWND hWnd, UINT message, WPARAM wPara processingCustomTimerMessage = true; sharedTimerFiredFunction(); processingCustomTimerMessage = false; + } else if (message == WM_TIMER && wParam == timerID) { + stopSharedTimer(); + sharedTimerFiredFunction(); } else return DefWindowProc(hWnd, message, wParam, lParam); return 0; @@ -79,11 +82,6 @@ void setSharedTimerFiredFunction(void (*f)()) sharedTimerFiredFunction = f; } -static void CALLBACK timerFired(HWND, UINT, UINT_PTR, DWORD) -{ - sharedTimerFiredFunction(); -} - void setSharedTimerFireDelay(double interval) { ASSERT(sharedTimerFiredFunction); @@ -99,29 +97,27 @@ void setSharedTimerFireDelay(double interval) intervalInMS = (unsigned)interval; } - if (timerID) { - KillTimer(0, timerID); - timerID = 0; - } + stopSharedTimer(); + initializeOffScreenTimerWindow(); // We don't allow nested PostMessages, since the custom messages will effectively starve // painting and user input. (Win32 has a tri-level queue with application messages > // user input > WM_PAINT/WM_TIMER.) // In addition, if the queue contains input events that have been there since the last call to // GetQueueStatus, PeekMessage or GetMessage we favor timers. - if (intervalInMS < USER_TIMER_MINIMUM && processingCustomTimerMessage && - !LOWORD(::GetQueueStatus(QS_ALLINPUT))) { + if (intervalInMS < USER_TIMER_MINIMUM + && !processingCustomTimerMessage + && !LOWORD(::GetQueueStatus(QS_ALLINPUT))) { // Windows SetTimer does not allow timeouts smaller than 10ms (USER_TIMER_MINIMUM) - initializeOffScreenTimerWindow(); PostMessage(timerWindowHandle, timerFiredMessage, 0, 0); } else - timerID = SetTimer(0, 0, intervalInMS, timerFired); + timerID = SetTimer(timerWindowHandle, timerFiredMessage, intervalInMS, 0); } void stopSharedTimer() { if (timerID) { - KillTimer(0, timerID); + KillTimer(timerWindowHandle, timerID); timerID = 0; } } diff --git a/WebCore/platform/graphics/wx/FontCacheWx.cpp b/WebCore/platform/graphics/wx/FontCacheWx.cpp index db107e4..db107e4 100755..100644 --- a/WebCore/platform/graphics/wx/FontCacheWx.cpp +++ b/WebCore/platform/graphics/wx/FontCacheWx.cpp diff --git a/WebCore/platform/graphics/wx/FontPlatformData.h b/WebCore/platform/graphics/wx/FontPlatformData.h index d2394dc..c77968a 100644 --- a/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/WebCore/platform/graphics/wx/FontPlatformData.h @@ -30,41 +30,59 @@ #define FontPlatformData_H #include "FontDescription.h" -#include "CString.h" #include "AtomicString.h" +#include "CString.h" #include "StringImpl.h" +#include <wtf/RefPtr.h> #include <wx/defs.h> #include <wx/font.h> namespace WebCore { +class FontHolder: public WTF::RefCounted<FontHolder> +{ +public: + FontHolder() + : m_font(0) + {} + + FontHolder(wxFont* font) + : m_font(font) + {} + + wxFont* font() { return m_font; } + +private: + wxFont* m_font; +}; + class FontPlatformData { public: enum FontState { UNINITIALIZED, DELETED, VALID }; FontPlatformData(WTF::HashTableDeletedValueType) - : m_fontState(DELETED) + : m_fontState(DELETED), + m_font(0) { } ~FontPlatformData(); - FontPlatformData(wxFont f) - : m_font(f) - , m_fontState(VALID) + FontPlatformData(const FontDescription&, const AtomicString&); + FontPlatformData(float size, bool bold, bool italic) + : m_fontState(UNINITIALIZED) + , m_font(0) { - m_fontHash = computeHash(); } - FontPlatformData(const FontDescription&, const AtomicString&); - FontPlatformData() : m_fontState(UNINITIALIZED) + , m_font(0) { } - wxFont font() const { - return m_font; + wxFont* font() const { + return m_font->font(); } unsigned hash() const { @@ -78,32 +96,26 @@ public: } } + unsigned computeHash() const; + bool operator==(const FontPlatformData& other) const { - if (m_fontState == VALID) - return other.m_fontState == VALID && m_font.IsOk() && other.m_font.IsOk() && m_font.IsSameAs(other.m_font); + if (m_font && m_fontState == VALID && other.m_fontState == VALID && other.m_font) { + wxFont* thisFont = m_font->font(); + wxFont* otherFont = other.m_font->font(); + return thisFont->IsOk() && otherFont->IsOk() && thisFont->IsSameAs(*otherFont); + } else return m_fontState == other.m_fontState; } bool isHashTableDeletedValue() const { return m_fontState == DELETED; } - unsigned computeHash() const { - ASSERT(m_font.IsOk()); - - // make a hash that is unique for this font, but not globally unique - that is, - // a font whose properties are equal should generate the same hash - uintptr_t hashCodes[6] = { m_font.GetPointSize(), m_font.GetFamily(), m_font.GetStyle(), - m_font.GetWeight(), m_font.GetUnderlined(), - StringImpl::computeHash(m_font.GetFaceName().mb_str(wxConvUTF8)) }; - - return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); - } + private: - wxFont m_font; - FontState m_fontState; - unsigned m_fontHash; + WTF::RefPtr<FontHolder> m_font; + FontState m_fontState; }; } diff --git a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index 7162eab..ce5e40e 100755..100644 --- a/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -58,6 +58,9 @@ static wxFontWeight fontWeightToWxFontWeight(FontWeight weight) { if (weight >= FontWeight600) return wxFONTWEIGHT_BOLD; + + if (weight <= FontWeight300) + return wxFONTWEIGHT_LIGHT; return wxFONTWEIGHT_NORMAL; } @@ -78,30 +81,45 @@ FontPlatformData::FontPlatformData(const FontDescription& desc, const AtomicStri // this is a moot issue on Linux and Mac as they only accept the point argument. So, // we use the pixel size constructor on Windows, but we use point size on Linux and Mac. #if __WXMSW__ - m_font = wxFont( wxSize(0, -desc.computedPixelSize()), + m_font = new FontHolder(new wxFont( wxSize(0, -desc.computedPixelSize()), fontFamilyToWxFontFamily(desc.genericFamily()), italicToWxFontStyle(desc.italic()), fontWeightToWxFontWeight(desc.weight()), false, family.string() - ); + ) + ); #else - m_font = wxFont( desc.computedPixelSize(), + m_font = new FontHolder(new wxFont( desc.computedPixelSize(), fontFamilyToWxFontFamily(desc.genericFamily()), italicToWxFontStyle(desc.italic()), fontWeightToWxFontWeight(desc.weight()), false, family.string() - ); + ) + ); #endif m_fontState = VALID; - m_fontHash = computeHash(); } - + +unsigned FontPlatformData::computeHash() const { + wxFont* thisFont = m_font->font(); + ASSERT(thisFont && thisFont->IsOk()); + + // make a hash that is unique for this font, but not globally unique - that is, + // a font whose properties are equal should generate the same hash + uintptr_t hashCodes[6] = { thisFont->GetPointSize(), thisFont->GetFamily(), thisFont->GetStyle(), + thisFont->GetWeight(), thisFont->GetUnderlined(), + StringImpl::computeHash(thisFont->GetFaceName().mb_str(wxConvUTF8)) }; + + return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); +} + FontPlatformData::~FontPlatformData() { m_fontState = UNINITIALIZED; + m_font = 0; } } diff --git a/WebCore/platform/graphics/wx/GlyphMapWx.cpp b/WebCore/platform/graphics/wx/GlyphMapWx.cpp index ebf86e4..ebf86e4 100755..100644 --- a/WebCore/platform/graphics/wx/GlyphMapWx.cpp +++ b/WebCore/platform/graphics/wx/GlyphMapWx.cpp diff --git a/WebCore/platform/graphics/wx/ImageSourceWx.cpp b/WebCore/platform/graphics/wx/ImageSourceWx.cpp index d523354..fc8ce71 100644 --- a/WebCore/platform/graphics/wx/ImageSourceWx.cpp +++ b/WebCore/platform/graphics/wx/ImageSourceWx.cpp @@ -170,7 +170,7 @@ void ImageSource::clear(bool destroyAll, size_t clearBeforeFrame, SharedBuffer* delete m_decoder; m_decoder = 0; if (data) - setData(data, allDataReceived); + setData(data, allDataReceived); } NativeImagePtr ImageSource::createFrameAtIndex(size_t index) diff --git a/WebCore/platform/graphics/wx/ImageWx.cpp b/WebCore/platform/graphics/wx/ImageWx.cpp index e52e9ff..e1d435e 100644 --- a/WebCore/platform/graphics/wx/ImageWx.cpp +++ b/WebCore/platform/graphics/wx/ImageWx.cpp @@ -237,7 +237,7 @@ void BitmapImage::drawPattern(GraphicsContext* ctxt, const FloatRect& srcRect, c void BitmapImage::checkForSolidColor() { - + m_checkedForSolidColor = true; } void BitmapImage::invalidatePlatformData() diff --git a/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp b/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp index a509933..ab50518 100755..100644 --- a/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp +++ b/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp @@ -45,14 +45,16 @@ namespace WebCore void SimpleFontData::platformInit() { - wxFont font = m_font.font(); - wxFontProperties props = wxFontProperties(&font); - m_ascent = props.GetAscent(); - m_descent = props.GetDescent(); - m_lineSpacing = props.GetLineSpacing(); - m_xHeight = props.GetXHeight(); - m_unitsPerEm = 1; // FIXME! - m_lineGap = props.GetLineGap(); + wxFont *font = m_font.font(); + if (font && font->IsOk()) { + wxFontProperties props = wxFontProperties(font); + m_ascent = props.GetAscent(); + m_descent = props.GetDescent(); + m_lineSpacing = props.GetLineSpacing(); + m_xHeight = props.GetXHeight(); + m_unitsPerEm = 1; // FIXME! + m_lineGap = props.GetLineGap(); + } } void SimpleFontData::platformDestroy() @@ -79,8 +81,8 @@ bool SimpleFontData::containsCharacters(const UChar* characters, int length) con void SimpleFontData::determinePitch() { - if (m_font.font().Ok()) - m_treatAsFixedPitch = m_font.font().IsFixedWidth(); + if (m_font.font() && m_font.font()->Ok()) + m_treatAsFixedPitch = m_font.font()->IsFixedWidth(); else m_treatAsFixedPitch = false; } @@ -89,7 +91,7 @@ float SimpleFontData::platformWidthForGlyph(Glyph glyph) const { // TODO: fix this! Make GetTextExtents a method of wxFont in 2.9 int width = 10; - GetTextExtent(m_font.font(), (wxChar)glyph, &width, NULL); + GetTextExtent(*m_font.font(), (wxChar)glyph, &width, NULL); return width; } diff --git a/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp b/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp index e6a02b8..f21dc17 100644 --- a/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp +++ b/WebCore/platform/graphics/wx/TransformationMatrixWx.cpp @@ -37,235 +37,14 @@ namespace WebCore { #if USE(WXGC) -TransformationMatrix::TransformationMatrix(const PlatformTransformationMatrix& matrix) -{ - m_transform = matrix; -} -#endif - -TransformationMatrix::TransformationMatrix(double a, double b, double c, double d, double e, double f) -{ -#if USE(WXGC) - wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); - m_transform = renderer->CreateMatrix(); -#endif - setMatrix(a, b, c, d, e, f); -} - -TransformationMatrix::TransformationMatrix() -{ - // NB: If we ever support using Cairo backend on Win/Mac, this will need to be - // changed somehow (though I'm not sure how as we don't have a reference to the - // graphics context here. -#if USE(WXGC) - wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); - m_transform = renderer->CreateMatrix(); -#endif -} - -TransformationMatrix TransformationMatrix::inverse() const -{ - notImplemented(); - return *this; -} - -void TransformationMatrix::setMatrix(double a, double b, double c, double d, double e, double f) -{ -#if USE(WXGC) - m_transform.Set(a, b, c, d, e, f); -#endif -} - -void TransformationMatrix::map(double x, double y, double *x2, double *y2) const -{ - notImplemented(); -} - -IntRect TransformationMatrix::mapRect(const IntRect &rect) const -{ -#if USE(WXGC) - double x, y, width, height; - x = rect.x(); - y = rect.y(); - width = rect.width(); - height = rect.height(); - - m_transform.TransformPoint(&x, &y); - m_transform.TransformDistance(&width, &height); - return IntRect(x, y, width, height); -#endif - return IntRect(); -} - -FloatRect TransformationMatrix::mapRect(const FloatRect &rect) const -{ -#if USE(WXGC) - double x, y, width, height; - x = rect.x(); - y = rect.y(); - width = rect.width(); - height = rect.height(); - - m_transform.TransformPoint(&x, &y); - m_transform.TransformDistance(&width, &height); - return FloatRect(x, y, width, height); -#endif - return FloatRect(); -} - - -TransformationMatrix& TransformationMatrix::scale(double sx, double sy) -{ -#if USE(WXGC) - m_transform.Scale((wxDouble)sx, (wxDouble)sy); -#endif - return *this; -} - -void TransformationMatrix::reset() -{ - notImplemented(); -} - -TransformationMatrix& TransformationMatrix::rotate(double d) -{ -#if USE(WXGC) - m_transform.Rotate((wxDouble)d); -#endif - return *this; -} - -TransformationMatrix& TransformationMatrix::translate(double tx, double ty) -{ -#if USE(WXGC) - m_transform.Translate((wxDouble)tx, (wxDouble)ty); -#endif - return *this; -} - -TransformationMatrix& TransformationMatrix::shear(double sx, double sy) -{ - notImplemented(); - return *this; -} - -TransformationMatrix& TransformationMatrix::operator*=(const TransformationMatrix& other) -{ - notImplemented(); - return *this; -} - -bool TransformationMatrix::operator== (const TransformationMatrix &other) const -{ -#if USE(WXGC) - return m_transform.IsEqual((wxGraphicsMatrix)other); -#else - notImplemented(); - return true; -#endif -} - -TransformationMatrix TransformationMatrix::operator* (const TransformationMatrix &other) -{ - notImplemented(); - return *this; //m_transform * other.m_transform; -} - -double TransformationMatrix::det() const -{ - notImplemented(); - return 0; -} - -#if USE(WXGC) TransformationMatrix::operator wxGraphicsMatrix() const { - return m_transform; -} -#endif - -double TransformationMatrix::a() const -{ - double a = 0; -#if USE(WXGC) - m_transform.Get(&a); -#endif - return a; -} - -void TransformationMatrix::setA(double a) -{ - setMatrix(a, b(), c(), d(), e(), f()); -} - -double TransformationMatrix::b() const -{ - double b = 0; -#if USE(WXGC) - m_transform.Get(&b); -#endif - return b; -} - -void TransformationMatrix::setB(double b) -{ - setMatrix(a(), b, c(), d(), e(), f()); -} - -double TransformationMatrix::c() const -{ - double c = 0; -#if USE(WXGC) - m_transform.Get(&c); -#endif - return c; -} - -void TransformationMatrix::setC(double c) -{ - setMatrix(a(), b(), c, d(), e(), f()); -} - -double TransformationMatrix::d() const -{ - double d = 0; -#if USE(WXGC) - m_transform.Get(&d); -#endif - return d; -} - -void TransformationMatrix::setD(double d) -{ - setMatrix(a(), b(), c(), d, e(), f()); -} - -double TransformationMatrix::e() const -{ - double e = 0; -#if USE(WXGC) - m_transform.Get(&e); -#endif - return e; -} - -void TransformationMatrix::setE(double e) -{ - setMatrix(a(), b(), c(), d(), e, f()); + wxGraphicsRenderer* renderer = wxGraphicsRenderer::GetDefaultRenderer(); + ASSERT(renderer); + + wxGraphicsMatrix matrix = renderer->CreateMatrix(a(), b(), c(), d(), e(), f()); + return matrix; } - -double TransformationMatrix::f() const -{ - double f = 0; -#if USE(WXGC) - m_transform.Get(&f); #endif - return f; -} - -void TransformationMatrix::setF(double f) -{ - setMatrix(a(), b(), c(), d(), e(), f); -} } diff --git a/WebCore/platform/gtk/ContextMenuGtk.cpp b/WebCore/platform/gtk/ContextMenuGtk.cpp index 2365379..210cfa6 100644 --- a/WebCore/platform/gtk/ContextMenuGtk.cpp +++ b/WebCore/platform/gtk/ContextMenuGtk.cpp @@ -39,12 +39,7 @@ ContextMenu::ContextMenu(const HitTestResult& result) { m_platformDescription = GTK_MENU(gtk_menu_new()); -#if GLIB_CHECK_VERSION(2,10,0) g_object_ref_sink(G_OBJECT(m_platformDescription)); -#else - g_object_ref(G_OBJECT(m_platformDescription)); - gtk_object_sink(GTK_OBJECT(m_platformDescription)); -#endif } ContextMenu::~ContextMenu() diff --git a/WebCore/platform/gtk/ContextMenuItemGtk.cpp b/WebCore/platform/gtk/ContextMenuItemGtk.cpp index 84f78c0..cf34640 100644 --- a/WebCore/platform/gtk/ContextMenuItemGtk.cpp +++ b/WebCore/platform/gtk/ContextMenuItemGtk.cpp @@ -232,12 +232,7 @@ void ContextMenuItem::setSubMenu(ContextMenu* menu) m_platformDescription.subMenu = menu->releasePlatformDescription(); m_platformDescription.type = SubmenuType; -#if GLIB_CHECK_VERSION(2,10,0) g_object_ref_sink(G_OBJECT(m_platformDescription.subMenu)); -#else - g_object_ref(G_OBJECT(m_platformDescription.subMenu)); - gtk_object_sink(GTK_OBJECT(m_platformDescription.subMenu)); -#endif } void ContextMenuItem::setChecked(bool shouldCheck) diff --git a/WebCore/platform/gtk/FileSystemGtk.cpp b/WebCore/platform/gtk/FileSystemGtk.cpp index 4f0ae01..94e06db 100644 --- a/WebCore/platform/gtk/FileSystemGtk.cpp +++ b/WebCore/platform/gtk/FileSystemGtk.cpp @@ -229,7 +229,7 @@ CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) if (!isHandleValid(fileDescriptor)) { LOG_ERROR("Can't create a temporary file."); g_free(tempPath); - return 0; + return CString(); } CString tempFilePath = tempPath; g_free(tempPath); diff --git a/WebCore/platform/gtk/GeolocationServiceGtk.cpp b/WebCore/platform/gtk/GeolocationServiceGtk.cpp index 0c80dd6..cc69d44 100644 --- a/WebCore/platform/gtk/GeolocationServiceGtk.cpp +++ b/WebCore/platform/gtk/GeolocationServiceGtk.cpp @@ -20,6 +20,21 @@ #include "config.h" #include "GeolocationServiceGtk.h" +#include "CString.h" +#include "GOwnPtr.h" +#include "NotImplemented.h" +#include "PositionOptions.h" + +namespace WTF { + template<> void freeOwnedGPtr<GeoclueAccuracy>(GeoclueAccuracy* accuracy) + { + if (!accuracy) + return; + + geoclue_accuracy_free(accuracy); + } +} + namespace WebCore { GeolocationService* GeolocationService::create(GeolocationServiceClient* client) @@ -29,33 +44,170 @@ GeolocationService* GeolocationService::create(GeolocationServiceClient* client) GeolocationServiceGtk::GeolocationServiceGtk(GeolocationServiceClient* client) : GeolocationService(client) -{} + , m_geoclueClient(0) + , m_geocluePosition(0) + , m_latitude(0.0) + , m_longitude(0.0) + , m_altitude(0.0) + , m_altitudeAccuracy(0.0) + , m_timestamp(0) +{ +} -bool GeolocationServiceGtk::startUpdating(PositionOptions*) +GeolocationServiceGtk::~GeolocationServiceGtk() { - return false; + if (m_geoclueClient) + g_object_unref(m_geoclueClient); + + if (m_geocluePosition) + g_object_unref(m_geocluePosition); +} + +// +// 1.) Initialize Geoclue with our requirements +// 2.) Try to get a GeocluePosition +// 3.) Update the Information and get the current position +// +// TODO: Also get GeoclueVelocity but there is no master client +// API for that. +// +bool GeolocationServiceGtk::startUpdating(PositionOptions* options) +{ + ASSERT(!m_geoclueClient); + + m_lastPosition = 0; + m_lastError = 0; + + GOwnPtr<GError> error; + GeoclueMaster* master = geoclue_master_get_default(); + GeoclueMasterClient* client = geoclue_master_create_client(master, 0, 0); + g_object_unref(master); + + if (!client) { + setError(PositionError::UNKNOWN_ERROR, "Could not connect to location provider."); + return false; + } + + GeoclueAccuracyLevel accuracyLevel = GEOCLUE_ACCURACY_LEVEL_LOCALITY; + int timeout = 0; + if (options) { + accuracyLevel = options->enableHighAccuracy() ? GEOCLUE_ACCURACY_LEVEL_DETAILED : GEOCLUE_ACCURACY_LEVEL_LOCALITY; + timeout = options->timeout(); + } + + gboolean result = geoclue_master_client_set_requirements(client, accuracyLevel, timeout, + true, GEOCLUE_RESOURCE_ALL, &error.outPtr()); + + if (!result) { + setError(PositionError::UNKNOWN_ERROR, error->message); + g_object_unref(client); + return false; + } + + m_geocluePosition = geoclue_master_client_create_position(client, &error.outPtr()); + if (!m_geocluePosition) { + setError(PositionError::UNKNOWN_ERROR, error->message); + g_object_unref(client); + return false; + } + + g_signal_connect(G_OBJECT(m_geocluePosition), "position-changed", + G_CALLBACK(position_changed), this); + + m_geoclueClient = client; + updateLocationInformation(); + + return true; } void GeolocationServiceGtk::stopUpdating() { + if (!m_geoclueClient) + return; + + g_object_unref(m_geocluePosition); + g_object_unref(m_geoclueClient); + + m_geocluePosition = 0; + m_geoclueClient = 0; } void GeolocationServiceGtk::suspend() { + // not available with geoclue + notImplemented(); } void GeolocationServiceGtk::resume() { + // not available with geoclue + notImplemented(); } Geoposition* GeolocationServiceGtk::lastPosition() const { - return 0; + return m_lastPosition.get(); } PositionError* GeolocationServiceGtk::lastError() const { - return 0; + return m_lastError.get(); +} + +void GeolocationServiceGtk::updateLocationInformation() +{ + ASSERT(m_geocluePosition); + + GOwnPtr<GError> error; + GOwnPtr<GeoclueAccuracy> accuracy; + + GeocluePositionFields fields = geoclue_position_get_position(m_geocluePosition, &m_timestamp, + &m_latitude, &m_longitude, + &m_altitude, &accuracy.outPtr(), + &error.outPtr()); + if (error) { + setError(PositionError::POSITION_UNAVAILABLE, error->message); + return; + } else if (!(fields & GEOCLUE_POSITION_FIELDS_LATITUDE && fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)) { + setError(PositionError::POSITION_UNAVAILABLE, "Position could not be determined."); + return; + } + + +} + +void GeolocationServiceGtk::updatePosition() +{ + m_lastError = 0; + + RefPtr<Coordinates> coordinates = Coordinates::create(m_latitude, m_longitude, + m_altitude, m_accuracy, + m_altitudeAccuracy, 0.0, 0.0); + m_lastPosition = Geoposition::create(coordinates.release(), m_timestamp * 1000.0); + positionChanged(); +} + +void GeolocationServiceGtk::position_changed(GeocluePosition*, GeocluePositionFields fields, int timestamp, double latitude, double longitude, double altitude, GeoclueAccuracy* accuracy, GeolocationServiceGtk* that) +{ + if (!(fields & GEOCLUE_POSITION_FIELDS_LATITUDE && fields & GEOCLUE_POSITION_FIELDS_LONGITUDE)) { + that->setError(PositionError::POSITION_UNAVAILABLE, "Position could not be determined."); + return; + } + + that->m_timestamp = timestamp; + that->m_latitude = latitude; + that->m_longitude = longitude; + that->m_altitude = altitude; + + GeoclueAccuracyLevel level; + geoclue_accuracy_get_details(accuracy, &level, &that->m_accuracy, &that->m_altitudeAccuracy); + that->updatePosition(); +} + +void GeolocationServiceGtk::setError(PositionError::ErrorCode errorCode, const char* message) +{ + m_lastPosition = 0; + m_lastError = PositionError::create(errorCode, String::fromUTF8(message)); } } diff --git a/WebCore/platform/gtk/GeolocationServiceGtk.h b/WebCore/platform/gtk/GeolocationServiceGtk.h index 02aff2d..90699ad 100644 --- a/WebCore/platform/gtk/GeolocationServiceGtk.h +++ b/WebCore/platform/gtk/GeolocationServiceGtk.h @@ -21,11 +21,18 @@ #define GeolocationServiceGtk_h #include "GeolocationService.h" +#include "Geoposition.h" +#include "PositionError.h" +#include "RefPtr.h" + +#include <geoclue/geoclue-master.h> +#include <geoclue/geoclue-position.h> namespace WebCore { class GeolocationServiceGtk : public GeolocationService { public: GeolocationServiceGtk(GeolocationServiceClient*); + ~GeolocationServiceGtk(); virtual bool startUpdating(PositionOptions*); virtual void stopUpdating(); @@ -35,6 +42,29 @@ namespace WebCore { Geoposition* lastPosition() const; PositionError* lastError() const; + + private: + void updateLocationInformation(); + void setError(PositionError::ErrorCode, const char* message); + void updatePosition(); + + static void position_changed(GeocluePosition*, GeocluePositionFields, int, double, double, double, GeoclueAccuracy*, GeolocationServiceGtk*); + + private: + RefPtr<Geoposition> m_lastPosition; + RefPtr<PositionError> m_lastError; + + // state objects + GeoclueMasterClient* m_geoclueClient; + GeocluePosition* m_geocluePosition; + + // Error and Position state + double m_latitude; + double m_longitude; + double m_altitude; + double m_accuracy; + double m_altitudeAccuracy; + int m_timestamp; }; } diff --git a/WebCore/platform/gtk/LoggingGtk.cpp b/WebCore/platform/gtk/LoggingGtk.cpp index 5bc1559..c46364c 100644 --- a/WebCore/platform/gtk/LoggingGtk.cpp +++ b/WebCore/platform/gtk/LoggingGtk.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,12 +21,93 @@ #include "config.h" #include "Logging.h" +#include <glib.h> +#include <string.h> + namespace WebCore { +// Inspired by the code used by the Qt port + +static WTFLogChannel* getChannelFromName(const char* channelName) +{ + if (strlen(channelName) < 3) + return 0; + + if (!g_ascii_strcasecmp(channelName, "BackForward")) + return &LogBackForward; + if (!g_ascii_strcasecmp(channelName, "Editing")) + return &LogEditing; + if (!g_ascii_strcasecmp(channelName, "Events")) + return &LogEvents; + if (!g_ascii_strcasecmp(channelName, "Frames")) + return &LogFrames; + if (!g_ascii_strcasecmp(channelName, "FTP")) + return &LogFTP; + if (!g_ascii_strcasecmp(channelName, "History")) + return &LogHistory; + if (!g_ascii_strcasecmp(channelName, "IconDatabase")) + return &LogIconDatabase; + if (!g_ascii_strcasecmp(channelName, "Loading")) + return &LogLoading; + if (!g_ascii_strcasecmp(channelName, "Media")) + return &LogMedia; + if (!g_ascii_strcasecmp(channelName, "Network")) + return &LogNetwork; + if (!g_ascii_strcasecmp(channelName, "NotYetImplemented")) + return &LogNotYetImplemented; + if (!g_ascii_strcasecmp(channelName, "PageCache")) + return &LogPageCache; + if (!g_ascii_strcasecmp(channelName, "PlatformLeaks")) + return &LogPlatformLeaks; + if (!g_ascii_strcasecmp(channelName, "Plugin")) + return &LogPlugin; + if (!g_ascii_strcasecmp(channelName, "PopupBlocking")) + return &LogPopupBlocking; + if (!g_ascii_strcasecmp(channelName, "SpellingAndGrammar")) + return &LogSpellingAndGrammar; + if (!g_ascii_strcasecmp(channelName, "SQLDatabase")) + return &LogSQLDatabase; + if (!g_ascii_strcasecmp(channelName, "StorageAPI")) + return &LogStorageAPI; + if (!g_ascii_strcasecmp(channelName, "TextConversion")) + return &LogTextConversion; + if (!g_ascii_strcasecmp(channelName, "Threading")) + return &LogThreading; + + return 0; +} + void InitializeLoggingChannelsIfNecessary() { - // FIXME: Add a way for the user to specify which - // logs he/she would like turned on. + static bool didInitializeLoggingChannels = false; + if (didInitializeLoggingChannels) + return; + + didInitializeLoggingChannels = true; + + char* logEnv = getenv("WEBKIT_DEBUG"); + if (!logEnv) + return; + + // we set up the logs anyway because some of our logging, such as + // soup's is available in release builds +#if defined(NDEBUG) + g_warning("WEBKIT_DEBUG is not empty, but this is a release build. Notice that many log messages will only appear in a debug build."); +#endif + + char** logv = g_strsplit(logEnv, " ", -1); + + for (int i = 0; logv[i]; i++) { + WTFLogChannel* channel = getChannelFromName(logv[i]); + if (!channel) + continue; + channel->state = WTFLogChannelOn; + } + + g_strfreev(logv); + + // to disable logging notImplemented set the DISABLE_NI_WARNING + // environment variable to 1 LogNotYetImplemented.state = WTFLogChannelOn; } diff --git a/WebCore/platform/gtk/PopupMenuGtk.cpp b/WebCore/platform/gtk/PopupMenuGtk.cpp index 85c5aa0..54b41ab 100644 --- a/WebCore/platform/gtk/PopupMenuGtk.cpp +++ b/WebCore/platform/gtk/PopupMenuGtk.cpp @@ -52,12 +52,7 @@ void PopupMenu::show(const IntRect& rect, FrameView* view, int index) if (!m_popup) { m_popup = GTK_MENU(gtk_menu_new()); -#if GLIB_CHECK_VERSION(2,10,0) g_object_ref_sink(G_OBJECT(m_popup)); -#else - g_object_ref(G_OBJECT(m_popup)); - gtk_object_sink(GTK_OBJECT(m_popup)); -#endif g_signal_connect(m_popup, "unmap", G_CALLBACK(menuUnmapped), this); } else gtk_container_foreach(GTK_CONTAINER(m_popup), reinterpret_cast<GtkCallback>(menuRemoveItem), this); diff --git a/WebCore/platform/gtk/ScrollViewGtk.cpp b/WebCore/platform/gtk/ScrollViewGtk.cpp index e1316ee..b3b6dd9 100644 --- a/WebCore/platform/gtk/ScrollViewGtk.cpp +++ b/WebCore/platform/gtk/ScrollViewGtk.cpp @@ -154,7 +154,7 @@ bool ScrollView::platformHandleHorizontalAdjustment(const IntSize& scroll) m_horizontalAdjustment->upper = contentsWidth(); gtk_adjustment_changed(m_horizontalAdjustment); - if (m_scrollOffset.width() != scroll.width()) { + if (m_horizontalAdjustment->value != scroll.width()) { m_horizontalAdjustment->value = scroll.width(); gtk_adjustment_value_changed(m_horizontalAdjustment); } @@ -173,7 +173,7 @@ bool ScrollView::platformHandleVerticalAdjustment(const IntSize& scroll) m_verticalAdjustment->upper = contentsHeight(); gtk_adjustment_changed(m_verticalAdjustment); - if (m_scrollOffset.height() != scroll.height()) { + if (m_verticalAdjustment->value != scroll.height()) { m_verticalAdjustment->value = scroll.height(); gtk_adjustment_value_changed(m_verticalAdjustment); } diff --git a/WebCore/platform/gtk/ScrollbarGtk.cpp b/WebCore/platform/gtk/ScrollbarGtk.cpp index df165e3..7543e23 100644 --- a/WebCore/platform/gtk/ScrollbarGtk.cpp +++ b/WebCore/platform/gtk/ScrollbarGtk.cpp @@ -44,19 +44,18 @@ static gboolean gtkScrollEventCallback(GtkWidget* widget, GdkEventScroll* event, } ScrollbarGtk::ScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orientation, - ScrollbarControlSize controlSize) + ScrollbarControlSize controlSize) : Scrollbar(client, orientation, controlSize) , m_adjustment(GTK_ADJUSTMENT(gtk_adjustment_new(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))) { - GtkScrollbar* scrollBar = orientation == HorizontalScrollbar ? - GTK_SCROLLBAR(::gtk_hscrollbar_new(m_adjustment)) : - GTK_SCROLLBAR(::gtk_vscrollbar_new(m_adjustment)); - gtk_widget_show(GTK_WIDGET(scrollBar)); - g_object_ref(G_OBJECT(scrollBar)); - g_signal_connect(G_OBJECT(scrollBar), "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this); - g_signal_connect(G_OBJECT(scrollBar), "scroll-event", G_CALLBACK(gtkScrollEventCallback), this); + GtkWidget* scrollBar = orientation == HorizontalScrollbar ? + gtk_hscrollbar_new(m_adjustment): + gtk_vscrollbar_new(m_adjustment); + gtk_widget_show(scrollBar); + g_signal_connect(scrollBar, "value-changed", G_CALLBACK(ScrollbarGtk::gtkValueChanged), this); + g_signal_connect(scrollBar, "scroll-event", G_CALLBACK(gtkScrollEventCallback), this); - setPlatformWidget(GTK_WIDGET(scrollBar)); + setPlatformWidget(scrollBar); /* * assign a sane default width and height to the Scrollbar, otherwise @@ -66,22 +65,24 @@ ScrollbarGtk::ScrollbarGtk(ScrollbarClient* client, ScrollbarOrientation orienta ScrollbarTheme::nativeTheme()->scrollbarThickness()); } -ScrollbarGtk::~ScrollbarGtk() +IntPoint ScrollbarGtk::getLocationInParentWindow(const IntRect& rect) { - /* - * the Widget does not take over ownership. - */ - g_signal_handlers_disconnect_by_func(G_OBJECT(platformWidget()), (gpointer)ScrollbarGtk::gtkValueChanged, this); - g_signal_handlers_disconnect_by_func(G_OBJECT(platformWidget()), (gpointer)gtkScrollEventCallback, this); - g_object_unref(G_OBJECT(platformWidget())); + IntPoint loc; + + if (parent()->isScrollViewScrollbar(this)) + loc = parent()->convertToContainingWindow(rect.location()); + else + loc = parent()->contentsToWindow(rect.location()); + + return loc; } void ScrollbarGtk::frameRectsChanged() { - if (!parent() || !parent()->isScrollViewScrollbar(this)) + if (!parent()) return; - IntPoint loc = parent()->convertToContainingWindow(frameRect().location()); + IntPoint loc = getLocationInParentWindow(frameRect()); // Don't allow the allocation size to be negative IntSize sz = frameRect().size(); @@ -129,5 +130,44 @@ void ScrollbarGtk::setEnabled(bool shouldEnable) gtk_widget_set_sensitive(platformWidget(), shouldEnable); } +/* + * Strategy to painting a Widget: + * 1.) do not paint if there is no GtkWidget set + * 2.) We assume that GTK_NO_WINDOW is set and that frameRectsChanged positioned + * the widget correctly. ATM we do not honor the GraphicsContext translation. + */ +void ScrollbarGtk::paint(GraphicsContext* context, const IntRect& rect) +{ + if (!platformWidget()) + return; + + if (!context->gdkExposeEvent()) + return; + + GtkWidget* widget = platformWidget(); + ASSERT(GTK_WIDGET_NO_WINDOW(widget)); + GdkEvent* event = gdk_event_new(GDK_EXPOSE); + event->expose = *context->gdkExposeEvent(); + event->expose.area = static_cast<GdkRectangle>(rect); + IntPoint loc = getLocationInParentWindow(rect); + + event->expose.area.x = loc.x(); + event->expose.area.y = loc.y(); + + event->expose.region = gdk_region_rectangle(&event->expose.area); + + /* + * This will be unref'ed by gdk_event_free. + */ + g_object_ref(event->expose.window); + + /* + * If we are going to paint do the translation and GtkAllocation manipulation. + */ + if (!gdk_region_empty(event->expose.region)) + gtk_widget_send_expose(widget, event); + + gdk_event_free(event); +} diff --git a/WebCore/platform/gtk/ScrollbarGtk.h b/WebCore/platform/gtk/ScrollbarGtk.h index 11ff079..1ef4c49 100644 --- a/WebCore/platform/gtk/ScrollbarGtk.h +++ b/WebCore/platform/gtk/ScrollbarGtk.h @@ -37,9 +37,8 @@ class ScrollbarGtk : public Scrollbar { public: friend class Scrollbar; - virtual ~ScrollbarGtk(); - virtual void setFrameRect(const IntRect&); + virtual void paint(GraphicsContext*, const IntRect&); virtual bool handleMouseMoveEvent(const PlatformMouseEvent&) { return false; } virtual bool handleMouseOutEvent(const PlatformMouseEvent&) { return false; } @@ -58,6 +57,7 @@ protected: private: static void gtkValueChanged(GtkAdjustment*, ScrollbarGtk*); + IntPoint getLocationInParentWindow(const IntRect&); GtkAdjustment* m_adjustment; }; diff --git a/WebCore/platform/gtk/WheelEventGtk.cpp b/WebCore/platform/gtk/WheelEventGtk.cpp index 64ec65a..075bed2 100644 --- a/WebCore/platform/gtk/WheelEventGtk.cpp +++ b/WebCore/platform/gtk/WheelEventGtk.cpp @@ -27,6 +27,7 @@ #include "config.h" #include "PlatformWheelEvent.h" +#include "Scrollbar.h" #include <gdk/gdk.h> @@ -52,16 +53,18 @@ PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) m_deltaY = -delta; break; case GDK_SCROLL_LEFT: - m_deltaX = -delta; + m_deltaX = delta; break; case GDK_SCROLL_RIGHT: - m_deltaX = delta; + m_deltaX = -delta; break; } + m_wheelTicksX = m_deltaX; + m_wheelTicksY = m_deltaY; - m_position = IntPoint((int)event->x, (int)event->y); - m_globalPosition = IntPoint((int)event->x_root, (int)event->y_root); - m_granularity = ScrollByLineWheelEvent; + m_position = IntPoint(static_cast<int>(event->x), static_cast<int>(event->y)); + m_globalPosition = IntPoint(static_cast<int>(event->x_root), static_cast<int>(event->y_root)); + m_granularity = ScrollByPixelWheelEvent; m_isAccepted = false; m_shiftKey = event->state & GDK_SHIFT_MASK; m_ctrlKey = event->state & GDK_CONTROL_MASK; @@ -74,8 +77,8 @@ PlatformWheelEvent::PlatformWheelEvent(GdkEventScroll* event) #endif // FIXME: retrieve the user setting for the number of lines to scroll on each wheel event - m_deltaX *= horizontalLineMultiplier(); - m_deltaY *= verticalLineMultiplier(); + m_deltaX *= static_cast<float>(cScrollbarPixelsPerLineStep); + m_deltaY *= static_cast<float>(cScrollbarPixelsPerLineStep); } } diff --git a/WebCore/platform/gtk/WidgetGtk.cpp b/WebCore/platform/gtk/WidgetGtk.cpp index 82fed74..4f09e77 100644 --- a/WebCore/platform/gtk/WidgetGtk.cpp +++ b/WebCore/platform/gtk/WidgetGtk.cpp @@ -41,23 +41,17 @@ namespace WebCore { -class WidgetPrivate { -public: - GdkCursor* cursor; -}; +static GdkCursor* lastSetCursor; Widget::Widget(PlatformWidget widget) - : m_data(new WidgetPrivate) { init(widget); - m_data->cursor = 0; } Widget::~Widget() { ASSERT(!parent()); releasePlatformWidget(); - delete m_data; } void Widget::setFocus() @@ -65,11 +59,6 @@ void Widget::setFocus() gtk_widget_grab_focus(platformWidget() ? platformWidget() : GTK_WIDGET(root()->hostWindow()->platformWindow())); } -Cursor Widget::cursor() -{ - return Cursor(m_data->cursor); -} - static GdkDrawable* gdkDrawable(PlatformWidget widget) { return widget ? widget->window : 0; @@ -77,7 +66,7 @@ static GdkDrawable* gdkDrawable(PlatformWidget widget) void Widget::setCursor(const Cursor& cursor) { - GdkCursor* pcur = cursor.impl(); + GdkCursor* platformCursor = cursor.impl(); // http://bugs.webkit.org/show_bug.cgi?id=16388 // [GTK] Widget::setCursor() gets called frequently @@ -85,11 +74,11 @@ void Widget::setCursor(const Cursor& cursor) // gdk_window_set_cursor() in certain GDK backends seems to be an // expensive operation, so avoid it if possible. - if (pcur == m_data->cursor) + if (platformCursor == lastSetCursor) return; - gdk_window_set_cursor(gdkDrawable(platformWidget()) ? GDK_WINDOW(gdkDrawable(platformWidget())) : GTK_WIDGET(root()->hostWindow()->platformWindow())->window, pcur); - m_data->cursor = pcur; + gdk_window_set_cursor(gdkDrawable(platformWidget()) ? GDK_WINDOW(gdkDrawable(platformWidget())) : GTK_WIDGET(root()->hostWindow()->platformWindow())->window, platformCursor); + lastSetCursor = platformCursor; } void Widget::show() @@ -106,41 +95,8 @@ void Widget::hide() gtk_widget_hide(platformWidget()); } -/* - * Strategy to painting a Widget: - * 1.) do not paint if there is no GtkWidget set - * 2.) We assume that GTK_NO_WINDOW is set and that frameRectsChanged positioned - * the widget correctly. ATM we do not honor the GraphicsContext translation. - */ -void Widget::paint(GraphicsContext* context, const IntRect&) +void Widget::paint(GraphicsContext* context, const IntRect& rect) { - if (!platformWidget()) - return; - - if (!context->gdkExposeEvent()) - return; - - GtkWidget* widget = platformWidget(); - ASSERT(GTK_WIDGET_NO_WINDOW(widget)); - - GdkEvent* event = gdk_event_new(GDK_EXPOSE); - event->expose = *context->gdkExposeEvent(); - event->expose.region = gtk_widget_region_intersect(widget, event->expose.region); - - /* - * This will be unref'ed by gdk_event_free. - */ - g_object_ref(event->expose.window); - - /* - * If we are going to paint do the translation and GtkAllocation manipulation. - */ - if (!gdk_region_empty(event->expose.region)) { - gdk_region_get_clipbox(event->expose.region, &event->expose.area); - gtk_widget_send_expose(widget, event); - } - - gdk_event_free(event); } void Widget::setIsSelected(bool) @@ -169,12 +125,7 @@ void Widget::retainPlatformWidget() { if (!platformWidget()) return; -#if GLIB_CHECK_VERSION(2,10,0) g_object_ref_sink(platformWidget()); -#else - g_object_ref(platformWidget()); - gtk_object_sink(GTK_OBJECT(platformWidget())); -#endif } } diff --git a/WebCore/platform/image-decoders/ImageDecoder.h b/WebCore/platform/image-decoders/ImageDecoder.h index e21ddcf..17756ac 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.h +++ b/WebCore/platform/image-decoders/ImageDecoder.h @@ -57,12 +57,11 @@ public: void clear() { m_bytes.clear(); - m_rect = IntRect(); - m_height = 0; m_status = FrameEmpty; - m_duration = 0; - m_disposalMethod = DisposeNotSpecified; - m_hasAlpha = false; + // NOTE: Do not reset other members here; clearFrameBufferCache() calls + // this to free the bitmap data, but other functions like + // initFrameBuffer() and frameComplete() may still need to read other + // metadata out of this frame later. } const RGBA32Array& bytes() const { return m_bytes; } diff --git a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index 843e65a..5b4b675 100644 --- a/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -188,7 +188,8 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) // In some cases, like if the decoder was destroyed while animating, we // can be asked to clear more frames than we currently have. if (m_frameBufferCache.isEmpty()) - return; // Nothing to do. + return; // Nothing to do. + // The "-1" here is tricky. It does not mean that |clearBeforeFrame| is the // last frame we wish to preserve, but rather that we never want to clear // the very last frame in the cache: it's empty (so clearing it is @@ -199,21 +200,36 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) // this case. clearBeforeFrame = std::min(clearBeforeFrame, m_frameBufferCache.size() - 1); const Vector<RGBA32Buffer>::iterator end(m_frameBufferCache.begin() + clearBeforeFrame); - for (Vector<RGBA32Buffer>::iterator i(m_frameBufferCache.begin()); i != end; ++i) { - if (i->status() == RGBA32Buffer::FrameEmpty) - continue; // Nothing to do. - - // The layout of frames is: - // [empty frames][complete frames][partial frame][empty frames] - // ...where each of these groups may be empty. We should not clear a - // partial frame since that's what's being decoded right now, and we - // also should not clear the last complete frame, since it may be needed - // when constructing the next frame. Note that "i + 1" is safe since - // i < end < m_frameBufferCache.end(). - if ((i->status() == RGBA32Buffer::FramePartial) || ((i + 1)->status() != RGBA32Buffer::FrameComplete)) - break; - - i->clear(); + + // We need to preserve frames such that: + // * We don't clear |end| + // * We don't clear the frame we're currently decoding + // * We don't clear any frame from which a future initFrameBuffer() call + // will copy bitmap data + // All other frames can be cleared. Because of the constraints on when + // ImageSource::clear() can be called (see ImageSource.h), we're guaranteed + // not to have non-empty frames after the frame we're currently decoding. + // So, scan backwards from |end| as follows: + // * If the frame is empty, we're still past any frames we care about. + // * If the frame is complete, but is DisposeOverwritePrevious, we'll + // skip over it in future initFrameBuffer() calls. We can clear it + // unless it's |end|, and keep scanning. For any other disposal method, + // stop scanning, as we've found the frame initFrameBuffer() will need + // next. + // * If the frame is partial, we're decoding it, so don't clear it; if it + // has a disposal method other than DisposeOverwritePrevious, stop + // scanning, as we'll only need this frame when decoding the next one. + Vector<RGBA32Buffer>::iterator i(end); + for (; (i != m_frameBufferCache.begin()) && ((i->status() == RGBA32Buffer::FrameEmpty) || (i->disposalMethod() == RGBA32Buffer::DisposeOverwritePrevious)); --i) { + if ((i->status() == RGBA32Buffer::FrameComplete) && (i != end)) + i->clear(); + } + + // Now |i| holds the last frame we need to preserve; clear prior frames. + for (Vector<RGBA32Buffer>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { + ASSERT(j->status() != RGBA32Buffer::FramePartial); + if (j->status() != RGBA32Buffer::FrameEmpty) + j->clear(); } } diff --git a/WebCore/platform/image-decoders/skia/ImageDecoder.h b/WebCore/platform/image-decoders/skia/ImageDecoder.h index b983315..cddb69b 100644 --- a/WebCore/platform/image-decoders/skia/ImageDecoder.h +++ b/WebCore/platform/image-decoders/skia/ImageDecoder.h @@ -132,10 +132,11 @@ namespace WebCore { void clear() { m_bitmapRef = RefCountedNativeImageSkia::create(); - m_rect = IntRect(); m_status = FrameEmpty; - m_duration = 0; - m_disposalMethod = DisposeNotSpecified; + // NOTE: Do not reset other members here; clearFrameBufferCache() + // calls this to free the bitmap data, but other functions like + // initFrameBuffer() and frameComplete() may still need to read + // other metadata out of this frame later. } // This function creates a new copy of the image data in |other|, so the diff --git a/WebCore/platform/image-encoders/skia/PNGImageEncoder.cpp b/WebCore/platform/image-encoders/skia/PNGImageEncoder.cpp new file mode 100644 index 0000000..aa20c62 --- /dev/null +++ b/WebCore/platform/image-encoders/skia/PNGImageEncoder.cpp @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2006-2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "IntSize.h" +#include "OwnArrayPtr.h" +#include "PNGImageEncoder.h" +#include "Vector.h" + +#include "SkBitmap.h" + +extern "C" { +#include "png.h" +} + +namespace WebCore { + +// Converts BGRA->RGBA and RGBA->BGRA. +static void convertBetweenBGRAandRGBA(const unsigned char* input, int numberOfPixels, + unsigned char* output) +{ + for (int x = 0; x < numberOfPixels; x++) { + const unsigned char* pixelIn = &input[x * 4]; + unsigned char* pixelOut = &output[x * 4]; + pixelOut[0] = pixelIn[2]; + pixelOut[1] = pixelIn[1]; + pixelOut[2] = pixelIn[0]; + pixelOut[3] = pixelIn[3]; + } +} + +// Encoder -------------------------------------------------------------------- +// +// This section of the code is based on nsPNGEncoder.cpp in Mozilla +// (Copyright 2005 Google Inc.) + +// Passed around as the io_ptr in the png structs so our callbacks know where +// to write data. +struct PNGEncoderState { + PNGEncoderState(Vector<unsigned char>* o) : m_out(o) {} + Vector<unsigned char>* m_out; +}; + +// Called by libpng to flush its internal buffer to ours. +void encoderWriteCallback(png_structp png, png_bytep data, png_size_t size) +{ + PNGEncoderState* state = static_cast<PNGEncoderState*>(png_get_io_ptr(png)); + ASSERT(state->m_out); + + size_t oldSize = state->m_out->size(); + state->m_out->resize(oldSize + size); + memcpy(&(*state->m_out)[oldSize], data, size); +} + +// Automatically destroys the given write structs on destruction to make +// cleanup and error handling code cleaner. +class PNGWriteStructDestroyer { +public: + PNGWriteStructDestroyer(png_struct** ps, png_info** pi) + : m_pngStruct(ps) + , m_pngInfo(pi) { + } + + ~PNGWriteStructDestroyer() { + png_destroy_write_struct(m_pngStruct, m_pngInfo); + } + +private: + png_struct** m_pngStruct; + png_info** m_pngInfo; +}; + +// static +bool PNGImageEncoder::encode(const SkBitmap& image, Vector<unsigned char>* output) +{ + if (image.config() != SkBitmap::kARGB_8888_Config) + return false; // Only support ARGB at 8 bpp now. + + image.lockPixels(); + bool result = PNGImageEncoder::encode(static_cast<unsigned char*>( + image.getPixels()), IntSize(image.width(), image.height()), + image.rowBytes(), output); + image.unlockPixels(); + return result; +} + +// static +bool PNGImageEncoder::encode(const unsigned char* input, const IntSize& size, + int bytesPerRow, + Vector<unsigned char>* output) +{ + int inputColorComponents = 4; + int outputColorComponents = 4; + int pngOutputColorType = PNG_COLOR_TYPE_RGB_ALPHA; + IntSize imageSize(size); + imageSize.clampNegativeToZero(); + + // Row stride should be at least as long as the length of the data. + if (inputColorComponents * imageSize.width() > bytesPerRow) { + ASSERT(false); + return false; + } + + png_struct* pngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + png_voidp_NULL, + png_error_ptr_NULL, + png_error_ptr_NULL); + if (!pngPtr) + return false; + + png_info* infoPtr = png_create_info_struct(pngPtr); + if (!infoPtr) { + png_destroy_write_struct(&pngPtr, NULL); + return false; + } + PNGWriteStructDestroyer destroyer(&pngPtr, &infoPtr); + + if (setjmp(png_jmpbuf(pngPtr))) { + // The destroyer will ensure that the structures are cleaned up in this + // case, even though we may get here as a jump from random parts of the + // PNG library called below. + return false; + } + + // Set our callback for libpng to give us the data. + PNGEncoderState state(output); + png_set_write_fn(pngPtr, &state, encoderWriteCallback, NULL); + + png_set_IHDR(pngPtr, infoPtr, imageSize.width(), imageSize.height(), 8, pngOutputColorType, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(pngPtr, infoPtr); + + OwnArrayPtr<unsigned char> rowPixels(new unsigned char[imageSize.width() * outputColorComponents]); + for (int y = 0; y < imageSize.height(); y ++) { + convertBetweenBGRAandRGBA(&input[y * bytesPerRow], imageSize.width(), rowPixels.get()); + png_write_row(pngPtr, rowPixels.get()); + } + + png_write_end(pngPtr, infoPtr); + return true; +} + +} // namespace WebCore diff --git a/WebCore/platform/image-encoders/skia/PNGImageEncoder.h b/WebCore/platform/image-encoders/skia/PNGImageEncoder.h new file mode 100644 index 0000000..b5865d2 --- /dev/null +++ b/WebCore/platform/image-encoders/skia/PNGImageEncoder.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006-2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef PNGImageEncoder_h +#define PNGImageEncoder_h + +#include "Vector.h" + +class IntSize; +class SkBitmap; + +namespace WebCore { + + // Interface for encoding PNG data. This is a wrapper around libpng. + class PNGImageEncoder { + public: + // Encodes the specific SkBitmap into the supplied vector. + static bool encode(const SkBitmap&, WTF::Vector<unsigned char>* output); + + // Encodes the specified image data into the supplied vector. + // w, h give the size of the image and bytes_per_row gives the bytes + // per row. + static bool encode(const unsigned char* input, const IntSize& size, int bytesPerRow, WTF::Vector<unsigned char>* output); + }; + +} // namespace WebCore + +#endif diff --git a/WebCore/platform/mac/FoundationExtras.h b/WebCore/platform/mac/FoundationExtras.h index 51a7df0..85ce8d7 100644 --- a/WebCore/platform/mac/FoundationExtras.h +++ b/WebCore/platform/mac/FoundationExtras.h @@ -23,6 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import <CoreFoundation/CFBase.h> +#import <Foundation/NSObject.h> + // nil-checked CFRetain/CFRelease covers for Objective-C ids // Use CFRetain, CFRelease, HardRetain, or HardRelease instead of diff --git a/WebCore/platform/mac/GeolocationServiceMac.h b/WebCore/platform/mac/GeolocationServiceMac.h new file mode 100644 index 0000000..d0342e7 --- /dev/null +++ b/WebCore/platform/mac/GeolocationServiceMac.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 GeolocationServiceMac_h +#define GeolocationServiceMac_h + +#if ENABLE(GEOLOCATION) + +#include "GeolocationService.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/RetainPtr.h> + +#ifdef __OBJC__ +@class CLLocationManager; +@class WebCoreCoreLocationObserver; +#else +class CLLocationManager; +class WebCoreCoreLocationObserver; +#endif + +namespace WebCore { + +class GeolocationServiceMac : public GeolocationService { +public: + GeolocationServiceMac(GeolocationServiceClient*); + virtual ~GeolocationServiceMac(); + + virtual bool startUpdating(PositionOptions*); + virtual void stopUpdating(); + + virtual void suspend(); + virtual void resume(); + + virtual Geoposition* lastPosition() const { return m_lastPosition.get(); } + virtual PositionError* lastError() const { return m_lastError.get(); } + + void positionChanged(PassRefPtr<Geoposition>); + void errorOccurred(PassRefPtr<PositionError>); + +private: + RetainPtr<CLLocationManager> m_locationManager; + RetainPtr<WebCoreCoreLocationObserver> m_objcObserver; + + RefPtr<Geoposition> m_lastPosition; + RefPtr<PositionError> m_lastError; +}; + +} // namespace WebCore + +#endif // ENABLE(GEOLOCATION) + +#endif // GeolocationServiceMac_h diff --git a/WebCore/platform/mac/GeolocationServiceMac.mm b/WebCore/platform/mac/GeolocationServiceMac.mm new file mode 100644 index 0000000..c21b02c --- /dev/null +++ b/WebCore/platform/mac/GeolocationServiceMac.mm @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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. + */ + +#import "config.h" + +#if ENABLE(GEOLOCATION) + +#import "GeolocationServiceMac.h" + +#import "Geoposition.h" +#import "PositionError.h" +#import "PositionOptions.h" +#import "SoftLinking.h" +#import <CoreLocation/CoreLocation.h> +#import <objc/objc-runtime.h> +#import <wtf/RefPtr.h> +#import <wtf/UnusedParam.h> + +SOFT_LINK_FRAMEWORK(CoreLocation) + +SOFT_LINK_CLASS(CoreLocation, CLLocationManager) +SOFT_LINK_CLASS(CoreLocation, CLLocation) + +SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyBest, double) +SOFT_LINK_CONSTANT(CoreLocation, kCLLocationAccuracyHundredMeters, double) + +#define kCLLocationAccuracyBest getkCLLocationAccuracyBest() +#define kCLLocationAccuracyHundredMeters getkCLLocationAccuracyHundredMeters() + +using namespace WebCore; + +@interface WebCoreCoreLocationObserver : NSObject<CLLocationManagerDelegate> +{ + GeolocationServiceMac* m_callback; +} + +- (id)initWithCallback:(GeolocationServiceMac*)callback; + +- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation; +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error; + +@end + +namespace WebCore { + +GeolocationService* GeolocationService::create(GeolocationServiceClient* client) +{ + return new GeolocationServiceMac(client); +} + +GeolocationServiceMac::GeolocationServiceMac(GeolocationServiceClient* client) + : GeolocationService(client) + , m_objcObserver(AdoptNS, [[WebCoreCoreLocationObserver alloc] initWithCallback:this]) +{ +} + +GeolocationServiceMac::~GeolocationServiceMac() +{ + [m_locationManager.get() stopUpdatingLocation]; + m_locationManager.get().delegate = nil; +} + +bool GeolocationServiceMac::startUpdating(PositionOptions* options) +{ + #define CLLocationManager getCLLocationManagerClass() + if (!m_locationManager.get()) { + m_locationManager.adoptNS([[CLLocationManager alloc] init]); + m_locationManager.get().delegate = m_objcObserver.get(); + } + + if (!m_locationManager.get().locationServicesEnabled) + return false; + + if (options) { + // CLLocationAccuracy values suggested by Ron Huang. + CLLocationAccuracy accuracy = options->enableHighAccuracy() ? kCLLocationAccuracyBest : kCLLocationAccuracyHundredMeters; + m_locationManager.get().desiredAccuracy = accuracy; + } + + // This can safely be called multiple times. + [m_locationManager.get() startUpdatingLocation]; + + return true; + #undef CLLocationManager +} + +void GeolocationServiceMac::stopUpdating() +{ + [m_locationManager.get() stopUpdatingLocation]; +} + +void GeolocationServiceMac::suspend() +{ + [m_locationManager.get() stopUpdatingLocation]; +} + +void GeolocationServiceMac::resume() +{ + [m_locationManager.get() startUpdatingLocation]; +} + +void GeolocationServiceMac::positionChanged(PassRefPtr<Geoposition> position) +{ + m_lastPosition = position; + GeolocationService::positionChanged(); +} + +void GeolocationServiceMac::errorOccurred(PassRefPtr<PositionError> error) +{ + m_lastError = error; + GeolocationService::errorOccurred(); +} + +} // namespace WebCore + +@implementation WebCoreCoreLocationObserver + +- (id)initWithCallback:(GeolocationServiceMac *)callback +{ + self = [super init]; + if (self) + m_callback = callback; + return self; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation +{ + ASSERT(m_callback); + ASSERT(newLocation); + UNUSED_PARAM(manager); + UNUSED_PARAM(oldLocation); + + // Normalize + double altitude = newLocation.altitude; + double altitudeAccuracy = newLocation.verticalAccuracy; + if (altitudeAccuracy < 0.0) { + altitudeAccuracy = 0.0; + altitude = 0.0; + } + double speed = newLocation.speed; + if (speed < 0.0) + speed = 0.0; + double heading = newLocation.course; + if (heading < 0.0) + heading = 0.0; + + WTF::RefPtr<WebCore::Coordinates> newCoordinates = WebCore::Coordinates::create( + newLocation.coordinate.latitude, + newLocation.coordinate.longitude, + altitude, + newLocation.horizontalAccuracy, + altitudeAccuracy, + heading, + speed); + WTF::RefPtr<WebCore::Geoposition> newPosition = WebCore::Geoposition::create( + newCoordinates.release(), + [newLocation.timestamp timeIntervalSince1970] * 1000.0); // seconds -> milliseconds + + m_callback->positionChanged(newPosition.release()); +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error +{ + ASSERT(m_callback); + ASSERT(error); + + UNUSED_PARAM(manager); + + PositionError::ErrorCode code; + switch ([error code]) { + case kCLErrorDenied: + code = PositionError::PERMISSION_DENIED; + break; + case kCLErrorLocationUnknown: + code = PositionError::POSITION_UNAVAILABLE; + break; + default: + code = PositionError::POSITION_UNAVAILABLE; + break; + } + + m_callback->errorOccurred(PositionError::create(code, [error localizedDescription])); +} + +@end + +#endif // ENABLE(GEOLOCATION) diff --git a/WebCore/platform/mac/LocalCurrentGraphicsContext.h b/WebCore/platform/mac/LocalCurrentGraphicsContext.h index 856cf52..1c5cae7 100644 --- a/WebCore/platform/mac/LocalCurrentGraphicsContext.h +++ b/WebCore/platform/mac/LocalCurrentGraphicsContext.h @@ -19,7 +19,11 @@ #include <wtf/Noncopyable.h> +#ifdef __OBJC__ +@class NSGraphicsContext; +#else class NSGraphicsContext; +#endif namespace WebCore { diff --git a/WebCore/platform/mac/PasteboardMac.mm b/WebCore/platform/mac/PasteboardMac.mm index de369fc..36b00f6 100644 --- a/WebCore/platform/mac/PasteboardMac.mm +++ b/WebCore/platform/mac/PasteboardMac.mm @@ -278,7 +278,7 @@ void Pasteboard::writeImage(Node* node, const KURL& url, const String& title) ASSERT(cocoaURL); ASSERT(node->renderer() && node->renderer()->isImage()); - RenderImage* renderer = static_cast<RenderImage*>(node->renderer()); + RenderImage* renderer = toRenderImage(node->renderer()); CachedImage* cachedImage = static_cast<CachedImage*>(renderer->cachedImage()); ASSERT(cachedImage); @@ -310,7 +310,7 @@ String Pasteboard::plainText(Frame* frame) NSArray *types = [m_pasteboard.get() types]; if ([types containsObject:NSStringPboardType]) - return [m_pasteboard.get() stringForType:NSStringPboardType]; + return [[m_pasteboard.get() stringForType:NSStringPboardType] precomposedStringWithCanonicalMapping]; NSAttributedString *attributedString = nil; NSString *string; @@ -320,13 +320,13 @@ String Pasteboard::plainText(Frame* frame) if (attributedString == nil && [types containsObject:NSRTFPboardType]) attributedString = [[NSAttributedString alloc] initWithRTF:[m_pasteboard.get() dataForType:NSRTFPboardType] documentAttributes:NULL]; if (attributedString != nil) { - string = [[attributedString string] copy]; + string = [[attributedString string] precomposedStringWithCanonicalMapping]; [attributedString release]; - return [string autorelease]; + return string; } if ([types containsObject:NSFilenamesPboardType]) { - string = [[m_pasteboard.get() propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"]; + string = [[[m_pasteboard.get() propertyListForType:NSFilenamesPboardType] componentsJoinedByString:@"\n"] precomposedStringWithCanonicalMapping]; if (string != nil) return string; } @@ -338,7 +338,7 @@ String Pasteboard::plainText(Frame* frame) // helper code that should either be done in a separate patch or figured out in another way. string = frame->editor()->client()->userVisibleString(url); if ([string length] > 0) - return string; + return [string precomposedStringWithCanonicalMapping]; } diff --git a/WebCore/platform/mac/PlatformScreenMac.mm b/WebCore/platform/mac/PlatformScreenMac.mm index 8f12df0..5dbfcf4 100644 --- a/WebCore/platform/mac/PlatformScreenMac.mm +++ b/WebCore/platform/mac/PlatformScreenMac.mm @@ -45,11 +45,7 @@ int screenDepthPerComponent(Widget*) bool screenIsMonochrome(Widget*) { - NSString *colorSpace = NSColorSpaceFromDepth([[NSScreen deepestScreen] depth]); - return colorSpace == NSCalibratedWhiteColorSpace - || colorSpace == NSCalibratedBlackColorSpace - || colorSpace == NSDeviceWhiteColorSpace - || colorSpace == NSDeviceBlackColorSpace; + return false; } // These functions scale between screen and page coordinates because JavaScript/DOM operations diff --git a/WebCore/platform/mac/SharedBufferMac.mm b/WebCore/platform/mac/SharedBufferMac.mm index f1d9517..c4e7528 100644 --- a/WebCore/platform/mac/SharedBufferMac.mm +++ b/WebCore/platform/mac/SharedBufferMac.mm @@ -39,7 +39,7 @@ using namespace WebCore; @interface WebCoreSharedBufferData : NSData { - SharedBuffer* sharedBuffer; + RefPtr<SharedBuffer> sharedBuffer; } - (id)initWithSharedBuffer:(SharedBuffer*)buffer; @@ -59,16 +59,12 @@ using namespace WebCore; { if (WebCoreObjCScheduleDeallocateOnMainThread([WebCoreSharedBufferData class], self)) return; - - sharedBuffer->deref(); [super dealloc]; } - (void)finalize { - sharedBuffer->deref(); - [super finalize]; } @@ -76,10 +72,8 @@ using namespace WebCore; { self = [super init]; - if (self) { + if (self) sharedBuffer = buffer; - sharedBuffer->ref(); - } return self; } diff --git a/WebCore/platform/mac/ThreadCheck.mm b/WebCore/platform/mac/ThreadCheck.mm index b862598..ddee05c 100644 --- a/WebCore/platform/mac/ThreadCheck.mm +++ b/WebCore/platform/mac/ThreadCheck.mm @@ -32,56 +32,74 @@ namespace WebCore { -static ThreadViolationBehavior defaultThreadViolationBehavior = RaiseExceptionOnThreadViolation; - static bool didReadThreadViolationBehaviorFromUserDefaults = false; -static bool threadViolationBehaviorIsDefault; -static ThreadViolationBehavior threadViolationBehavior; +static bool threadViolationBehaviorIsDefault = true; +static ThreadViolationBehavior threadViolationBehavior[MaximumThreadViolationRound] = { RaiseExceptionOnThreadViolation, RaiseExceptionOnThreadViolation }; static void readThreadViolationBehaviorFromUserDefaults() { + didReadThreadViolationBehaviorFromUserDefaults = true; + + ThreadViolationBehavior newBehavior = LogOnFirstThreadViolation; NSString *threadCheckLevel = [[NSUserDefaults standardUserDefaults] stringForKey:@"WebCoreThreadCheck"]; + if (!threadCheckLevel) + return; + if ([threadCheckLevel isEqualToString:@"None"]) - threadViolationBehavior = NoThreadCheck; + newBehavior = NoThreadCheck; else if ([threadCheckLevel isEqualToString:@"Exception"]) - threadViolationBehavior = RaiseExceptionOnThreadViolation; + newBehavior = RaiseExceptionOnThreadViolation; else if ([threadCheckLevel isEqualToString:@"Log"]) - threadViolationBehavior = LogOnThreadViolation; + newBehavior = LogOnThreadViolation; else if ([threadCheckLevel isEqualToString:@"LogOnce"]) - threadViolationBehavior = LogOnFirstThreadViolation; - else { - threadViolationBehavior = defaultThreadViolationBehavior; - threadViolationBehaviorIsDefault = true; - } - didReadThreadViolationBehaviorFromUserDefaults = true; + newBehavior = LogOnFirstThreadViolation; + else + ASSERT_NOT_REACHED(); + + threadViolationBehaviorIsDefault = false; + + for (unsigned i = 0; i < MaximumThreadViolationRound; ++i) + threadViolationBehavior[i] = newBehavior; } -void setDefaultThreadViolationBehavior(ThreadViolationBehavior behavior) +void setDefaultThreadViolationBehavior(ThreadViolationBehavior behavior, ThreadViolationRound round) { - defaultThreadViolationBehavior = behavior; + ASSERT(round < MaximumThreadViolationRound); + if (round >= MaximumThreadViolationRound) + return; + if (!didReadThreadViolationBehaviorFromUserDefaults) + readThreadViolationBehaviorFromUserDefaults(); if (threadViolationBehaviorIsDefault) - threadViolationBehavior = behavior; + threadViolationBehavior[round] = behavior; } -void reportThreadViolation(const char* function) +void reportThreadViolation(const char* function, ThreadViolationRound round) { + ASSERT(round < MaximumThreadViolationRound); + if (round >= MaximumThreadViolationRound) + return; if (!didReadThreadViolationBehaviorFromUserDefaults) - readThreadViolationBehaviorFromUserDefaults(); - if (threadViolationBehavior == NoThreadCheck) + readThreadViolationBehaviorFromUserDefaults(); + if (threadViolationBehavior[round] == NoThreadCheck) return; if (pthread_main_np()) return; - WebCoreReportThreadViolation(function); + WebCoreReportThreadViolation(function, round); } } // namespace WebCore // Split out the actual reporting of the thread violation to make it easier to set a breakpoint -void WebCoreReportThreadViolation(const char* function) +void WebCoreReportThreadViolation(const char* function, WebCore::ThreadViolationRound round) { using namespace WebCore; + + ASSERT(round < MaximumThreadViolationRound); + if (round >= MaximumThreadViolationRound) + return; + DEFINE_STATIC_LOCAL(HashSet<String>, loggedFunctions, ()); - switch (threadViolationBehavior) { + switch (threadViolationBehavior[round]) { case NoThreadCheck: break; case LogOnFirstThreadViolation: diff --git a/WebCore/platform/mac/WebCoreSystemInterface.h b/WebCore/platform/mac/WebCoreSystemInterface.h index 14d1713..3feed69 100644 --- a/WebCore/platform/mac/WebCoreSystemInterface.h +++ b/WebCore/platform/mac/WebCoreSystemInterface.h @@ -40,12 +40,21 @@ typedef struct _NSRect NSRect; #endif #ifdef __OBJC__ +@class NSArray; @class NSButtonCell; @class NSData; +@class NSDate; @class NSEvent; @class NSFont; +@class NSImage; +@class NSMenu; @class NSMutableURLRequest; +@class NSString; +@class NSTextFieldCell; +@class NSURLConnection; @class NSURLRequest; +@class NSURLResponse; +@class NSView; @class QTMovie; @class QTMovieView; #else diff --git a/WebCore/platform/mac/WebCoreSystemInterface.mm b/WebCore/platform/mac/WebCoreSystemInterface.mm index b629b4e..edd9d50 100644 --- a/WebCore/platform/mac/WebCoreSystemInterface.mm +++ b/WebCore/platform/mac/WebCoreSystemInterface.mm @@ -25,6 +25,7 @@ #import "config.h" #import "WebCoreSystemInterface.h" +#import <Foundation/Foundation.h> void (*wkAdvanceDefaultButtonPulseAnimation)(NSButtonCell *); BOOL (*wkCGContextGetShouldSmoothFonts)(CGContextRef); diff --git a/WebCore/platform/mac/WebCoreTextRenderer.h b/WebCore/platform/mac/WebCoreTextRenderer.h index 3e77434..73753bc 100644 --- a/WebCore/platform/mac/WebCoreTextRenderer.h +++ b/WebCore/platform/mac/WebCoreTextRenderer.h @@ -23,6 +23,15 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#import <AppKit/NSFontManager.h> +#import <CoreFoundation/CFString.h> + +#ifdef __OBJC__ +@class NSColor; +@class NSFont; +@class NSString; +#endif + #ifdef __cplusplus extern "C" { #endif diff --git a/WebCore/platform/mac/WebCoreTextRenderer.mm b/WebCore/platform/mac/WebCoreTextRenderer.mm index 0cd5967..ab053ef 100644 --- a/WebCore/platform/mac/WebCoreTextRenderer.mm +++ b/WebCore/platform/mac/WebCoreTextRenderer.mm @@ -31,6 +31,7 @@ #import "GraphicsContext.h" #import "IntPoint.h" #import "WebFontCache.h" +#import <AppKit/AppKit.h> using namespace WebCore; diff --git a/WebCore/platform/mac/WebFontCache.h b/WebCore/platform/mac/WebFontCache.h index b31a684..8d3e4dd 100644 --- a/WebCore/platform/mac/WebFontCache.h +++ b/WebCore/platform/mac/WebFontCache.h @@ -24,7 +24,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include <wtf/Vector.h> +#import <AppKit/NSFontManager.h> +#import <wtf/Vector.h> // This interface exists so that third party products (like Silk) can patch in to an Obj-C method to manipulate WebKit's font caching/substitution. @interface WebFontCache : NSObject diff --git a/WebCore/platform/mac/WebFontCache.mm b/WebCore/platform/mac/WebFontCache.mm index 6cf1ef4..ac70f06 100644 --- a/WebCore/platform/mac/WebFontCache.mm +++ b/WebCore/platform/mac/WebFontCache.mm @@ -31,6 +31,8 @@ #import "WebFontCache.h" #import "FontTraitsMask.h" +#import <AppKit/AppKit.h> +#import <Foundation/Foundation.h> #import <math.h> using namespace WebCore; diff --git a/WebCore/platform/mac/WheelEventMac.mm b/WebCore/platform/mac/WheelEventMac.mm index 7b60494..5821139 100644 --- a/WebCore/platform/mac/WheelEventMac.mm +++ b/WebCore/platform/mac/WheelEventMac.mm @@ -27,6 +27,7 @@ #import "PlatformWheelEvent.h" #import "PlatformMouseEvent.h" +#import "Scrollbar.h" #import "WebCoreSystemInterface.h" namespace WebCore { @@ -34,6 +35,7 @@ namespace WebCore { PlatformWheelEvent::PlatformWheelEvent(NSEvent* event) : m_position(pointForEvent(event)) , m_globalPosition(globalPointForEvent(event)) + , m_granularity(ScrollByPixelWheelEvent) , m_isAccepted(false) , m_shiftKey([event modifierFlags] & NSShiftKeyMask) , m_ctrlKey([event modifierFlags] & NSControlKeyMask) @@ -42,10 +44,14 @@ PlatformWheelEvent::PlatformWheelEvent(NSEvent* event) { BOOL continuous; wkGetWheelEventDeltas(event, &m_deltaX, &m_deltaY, &continuous); - m_granularity = continuous ? ScrollByPixelWheelEvent : ScrollByLineWheelEvent; - if (m_granularity == ScrollByLineWheelEvent) { - m_deltaX *= horizontalLineMultiplier(); - m_deltaY *= verticalLineMultiplier(); + if (continuous) { + m_wheelTicksX = m_deltaX / static_cast<float>(cScrollbarPixelsPerLineStep); + m_wheelTicksY = m_deltaY / static_cast<float>(cScrollbarPixelsPerLineStep); + } else { + m_wheelTicksX = m_deltaX; + m_wheelTicksY = m_deltaY; + m_deltaX *= static_cast<float>(cScrollbarPixelsPerLineStep); + m_deltaY *= static_cast<float>(cScrollbarPixelsPerLineStep); } } diff --git a/WebCore/platform/network/FormData.cpp b/WebCore/platform/network/FormData.cpp index 3cac168..af3b7f0 100644 --- a/WebCore/platform/network/FormData.cpp +++ b/WebCore/platform/network/FormData.cpp @@ -29,7 +29,8 @@ namespace WebCore { inline FormData::FormData() - : m_hasGeneratedFiles(false) + : m_identifier(0) + , m_hasGeneratedFiles(false) , m_alwaysStream(false) { } @@ -37,6 +38,7 @@ inline FormData::FormData() inline FormData::FormData(const FormData& data) : RefCounted<FormData>() , m_elements(data.m_elements) + , m_identifier(data.m_identifier) , m_hasGeneratedFiles(false) , m_alwaysStream(false) { @@ -98,7 +100,7 @@ PassRefPtr<FormData> FormData::deepCopy() const formData->m_alwaysStream = m_alwaysStream; size_t n = m_elements.size(); - formData->m_elements.reserveCapacity(n); + formData->m_elements.reserveInitialCapacity(n); for (size_t i = 0; i < n; ++i) { const FormDataElement& e = m_elements[i]; switch (e.m_type) { diff --git a/WebCore/platform/network/FormData.h b/WebCore/platform/network/FormData.h index 5998b1b..7278f2e 100644 --- a/WebCore/platform/network/FormData.h +++ b/WebCore/platform/network/FormData.h @@ -86,11 +86,17 @@ public: bool alwaysStream() const { return m_alwaysStream; } void setAlwaysStream(bool alwaysStream) { m_alwaysStream = alwaysStream; } + // Identifies a particular form submission instance. A value of 0 is used + // to indicate an unspecified identifier. + void setIdentifier(int64_t identifier) { m_identifier = identifier; } + int64_t identifier() const { return m_identifier; } + private: FormData(); FormData(const FormData&); - + Vector<FormDataElement> m_elements; + int64_t m_identifier; bool m_hasGeneratedFiles; bool m_alwaysStream; }; diff --git a/WebCore/platform/network/HTTPHeaderMap.cpp b/WebCore/platform/network/HTTPHeaderMap.cpp index aa9c5fa..ff470a0 100644 --- a/WebCore/platform/network/HTTPHeaderMap.cpp +++ b/WebCore/platform/network/HTTPHeaderMap.cpp @@ -41,7 +41,7 @@ namespace WebCore { auto_ptr<CrossThreadHTTPHeaderMapData> HTTPHeaderMap::copyData() const { auto_ptr<CrossThreadHTTPHeaderMapData> data(new CrossThreadHTTPHeaderMapData()); - data->reserveCapacity(size()); + data->reserveInitialCapacity(size()); HTTPHeaderMap::const_iterator end_it = end(); for (HTTPHeaderMap::const_iterator it = begin(); it != end_it; ++it) { diff --git a/WebCore/platform/network/HTTPParsers.cpp b/WebCore/platform/network/HTTPParsers.cpp index 0858fc9..f36e9fb 100644 --- a/WebCore/platform/network/HTTPParsers.cpp +++ b/WebCore/platform/network/HTTPParsers.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/ * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -131,18 +132,18 @@ String filenameFromHTTPContentDisposition(const String& value) String extractMIMETypeFromMediaType(const String& mediaType) { - String mimeType; + Vector<UChar, 64> mimeType; unsigned length = mediaType.length(); + mimeType.reserveCapacity(length); for (unsigned offset = 0; offset < length; offset++) { UChar c = mediaType[offset]; if (c == ';') break; else if (isSpaceOrNewline(c)) // FIXME: This seems wrong, " " is an invalid MIME type character according to RFC 2045. bug 8644 continue; - // FIXME: This is a very slow way to build a string, given WebCore::String's implementation. - mimeType += String(&c, 1); + mimeType.append(c); } - return mimeType; + return String(mimeType.data(), mimeType.size()); } String extractCharsetFromMediaType(const String& mediaType) diff --git a/WebCore/platform/network/ResourceErrorBase.cpp b/WebCore/platform/network/ResourceErrorBase.cpp index 1ea35b0..370650f 100644 --- a/WebCore/platform/network/ResourceErrorBase.cpp +++ b/WebCore/platform/network/ResourceErrorBase.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,6 +29,20 @@ namespace WebCore { +ResourceError ResourceErrorBase::copy() const +{ + lazyInit(); + + ResourceError errorCopy; + errorCopy.m_domain = m_domain.copy(); + errorCopy.m_errorCode = m_errorCode; + errorCopy.m_failingURL = m_failingURL.copy(); + errorCopy.m_localizedDescription = m_localizedDescription.copy(); + errorCopy.m_isNull = m_isNull; + errorCopy.m_isCancellation = m_isCancellation; + return errorCopy; +} + void ResourceErrorBase::lazyInit() const { const_cast<ResourceError*>(static_cast<const ResourceError*>(this))->platformLazyInit(); @@ -59,4 +74,4 @@ bool ResourceErrorBase::compare(const ResourceError& a, const ResourceError& b) return platformCompare(a, b); } -} +} // namespace WebCore diff --git a/WebCore/platform/network/ResourceErrorBase.h b/WebCore/platform/network/ResourceErrorBase.h index 4631324..237db9e 100644 --- a/WebCore/platform/network/ResourceErrorBase.h +++ b/WebCore/platform/network/ResourceErrorBase.h @@ -34,6 +34,9 @@ class ResourceError; class ResourceErrorBase { public: + // Makes a deep copy. Useful for when you need to use a ResourceError on another thread. + ResourceError copy() const; + bool isNull() const { return m_isNull; } const String& domain() const { lazyInit(); return m_domain; } @@ -44,7 +47,7 @@ public: void setIsCancellation(bool isCancellation) { m_isCancellation = isCancellation; } bool isCancellation() const { return m_isCancellation; } - static bool compare(const ResourceError& a, const ResourceError& b); + static bool compare(const ResourceError&, const ResourceError&); protected: ResourceErrorBase() @@ -85,4 +88,4 @@ inline bool operator!=(const ResourceError& a, const ResourceError& b) { return } // namespace WebCore -#endif // ResourceErrorBase_h_ +#endif // ResourceErrorBase_h diff --git a/WebCore/platform/network/ResourceHandle.h b/WebCore/platform/network/ResourceHandle.h index c981483..e3038ca 100644 --- a/WebCore/platform/network/ResourceHandle.h +++ b/WebCore/platform/network/ResourceHandle.h @@ -30,6 +30,10 @@ #include "HTTPHeaderMap.h" #include <wtf/OwnPtr.h> +#if USE(SOUP) +typedef struct _SoupSession SoupSession; +#endif + #if PLATFORM(CF) typedef const struct __CFData * CFDataRef; #endif @@ -79,7 +83,7 @@ class KURL; class ResourceError; class ResourceHandleClient; class ResourceHandleInternal; -class ResourceRequest; +struct ResourceRequest; class ResourceResponse; class SchedulePair; class SharedBuffer; @@ -159,6 +163,10 @@ public: ResourceHandleInternal* getInternal() { return d.get(); } #endif +#if USE(SOUP) + static SoupSession* defaultSession(); +#endif + // Used to work around the fact that you don't get any more NSURLConnection callbacks until you return from the one you're in. static bool loadsBlocked(); @@ -179,7 +187,7 @@ private: #if USE(SOUP) bool startData(String urlString); bool startHttp(String urlString); - bool startGio(String urlString); + bool startGio(KURL url); #endif void scheduleFailure(FailureType); diff --git a/WebCore/platform/network/ResourceHandleClient.h b/WebCore/platform/network/ResourceHandleClient.h index 3668d88..54c27d2 100644 --- a/WebCore/platform/network/ResourceHandleClient.h +++ b/WebCore/platform/network/ResourceHandleClient.h @@ -32,6 +32,7 @@ #if USE(CFNETWORK) #include <ConditionalMacros.h> +#include <CFNetwork/CFURLCachePriv.h> #include <CFNetwork/CFURLResponsePriv.h> #endif @@ -49,7 +50,7 @@ namespace WebCore { class KURL; class ResourceHandle; class ResourceError; - class ResourceRequest; + struct ResourceRequest; class ResourceResponse; enum CacheStoragePolicy { @@ -86,6 +87,9 @@ namespace WebCore { virtual NSCachedURLResponse* willCacheResponse(ResourceHandle*, NSCachedURLResponse* response) { return response; } virtual void willStopBufferingData(ResourceHandle*, const char*, int) { } #endif +#if USE(CFNETWORK) + virtual bool shouldCacheResponse(ResourceHandle*, CFCachedURLResponseRef response) { return true; } +#endif }; } diff --git a/WebCore/platform/network/ResourceHandleInternal.h b/WebCore/platform/network/ResourceHandleInternal.h index cc90cc8..c592a1a 100644 --- a/WebCore/platform/network/ResourceHandleInternal.h +++ b/WebCore/platform/network/ResourceHandleInternal.h @@ -47,6 +47,7 @@ #if USE(SOUP) #include <libsoup/soup.h> +class Frame; #endif #if PLATFORM(QT) @@ -116,13 +117,15 @@ namespace WebCore { #if USE(SOUP) , m_msg(0) , m_cancelled(false) + , m_reportedHeaders(false) , m_gfile(0) - , m_input_stream(0) + , m_inputStream(0) , m_cancellable(0) , m_buffer(0) - , m_bufsize(0) + , m_bufferSize(0) , m_total(0) , m_idleHandler(0) + , m_frame(0) #endif #if PLATFORM(QT) , m_job(0) @@ -190,12 +193,14 @@ namespace WebCore { SoupMessage* m_msg; ResourceResponse m_response; bool m_cancelled; + bool m_reportedHeaders; GFile* m_gfile; - GInputStream* m_input_stream; + GInputStream* m_inputStream; GCancellable* m_cancellable; char* m_buffer; - gsize m_bufsize, m_total; + gsize m_bufferSize, m_total; guint m_idleHandler; + Frame* m_frame; #endif #if PLATFORM(QT) #if QT_VERSION < 0x040400 diff --git a/WebCore/platform/network/ResourceRequestBase.cpp b/WebCore/platform/network/ResourceRequestBase.cpp index 15469a0..fd27718 100644 --- a/WebCore/platform/network/ResourceRequestBase.cpp +++ b/WebCore/platform/network/ResourceRequestBase.cpp @@ -76,7 +76,7 @@ auto_ptr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const data->m_httpMethod = httpMethod().copy(); data->m_httpHeaders.adopt(httpHeaderFields().copyData()); - data->m_responseContentDispositionEncodingFallbackArray.reserveCapacity(m_responseContentDispositionEncodingFallbackArray.size()); + data->m_responseContentDispositionEncodingFallbackArray.reserveInitialCapacity(m_responseContentDispositionEncodingFallbackArray.size()); size_t encodingArraySize = m_responseContentDispositionEncodingFallbackArray.size(); for (size_t index = 0; index < encodingArraySize; ++index) { data->m_responseContentDispositionEncodingFallbackArray.append(m_responseContentDispositionEncodingFallbackArray[index].copy()); @@ -130,7 +130,8 @@ void ResourceRequestBase::setCachePolicy(ResourceRequestCachePolicy cachePolicy) m_cachePolicy = cachePolicy; - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } double ResourceRequestBase::timeoutInterval() const @@ -146,7 +147,8 @@ void ResourceRequestBase::setTimeoutInterval(double timeoutInterval) m_timeoutInterval = timeoutInterval; - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } const KURL& ResourceRequestBase::mainDocumentURL() const @@ -178,7 +180,8 @@ void ResourceRequestBase::setHTTPMethod(const String& httpMethod) m_httpMethod = httpMethod; - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } const HTTPHeaderMap& ResourceRequestBase::httpHeaderFields() const @@ -201,7 +204,8 @@ void ResourceRequestBase::setHTTPHeaderField(const AtomicString& name, const Str m_httpHeaderFields.set(name, value); - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } void ResourceRequestBase::setResponseContentDispositionEncodingFallbackArray(const String& encoding1, const String& encoding2, const String& encoding3) @@ -216,7 +220,8 @@ void ResourceRequestBase::setResponseContentDispositionEncodingFallbackArray(con if (!encoding3.isNull()) m_responseContentDispositionEncodingFallbackArray.append(encoding3); - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } FormData* ResourceRequestBase::httpBody() const @@ -232,7 +237,8 @@ void ResourceRequestBase::setHTTPBody(PassRefPtr<FormData> httpBody) m_httpBody = httpBody; - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } bool ResourceRequestBase::allowHTTPCookies() const @@ -248,7 +254,8 @@ void ResourceRequestBase::setAllowHTTPCookies(bool allowHTTPCookies) m_allowHTTPCookies = allowHTTPCookies; - m_platformRequestUpdated = false; + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; } void ResourceRequestBase::addHTTPHeaderField(const AtomicString& name, const String& value) diff --git a/WebCore/platform/network/ResourceRequestBase.h b/WebCore/platform/network/ResourceRequestBase.h index 0f6bb47..4fd57e1 100644 --- a/WebCore/platform/network/ResourceRequestBase.h +++ b/WebCore/platform/network/ResourceRequestBase.h @@ -46,7 +46,7 @@ namespace WebCore { const int unspecifiedTimeoutInterval = INT_MAX; - class ResourceRequest; + struct ResourceRequest; struct CrossThreadResourceRequestData; // Do not use this type directly. Use ResourceRequest instead. @@ -107,12 +107,18 @@ namespace WebCore { void setAllowHTTPCookies(bool allowHTTPCookies); bool isConditional() const; - + + // Whether the associated ResourceHandleClient needs to be notified of + // upload progress made for that resource. + bool reportUploadProgress() const { return m_reportUploadProgress; } + void setReportUploadProgress(bool reportUploadProgress) { m_reportUploadProgress = reportUploadProgress; } + protected: // Used when ResourceRequest is initialized from a platform representation of the request ResourceRequestBase() : m_resourceRequestUpdated(false) , m_platformRequestUpdated(true) + , m_reportUploadProgress(false) { } @@ -124,6 +130,7 @@ namespace WebCore { , m_allowHTTPCookies(true) , m_resourceRequestUpdated(true) , m_platformRequestUpdated(false) + , m_reportUploadProgress(false) { } @@ -142,6 +149,7 @@ namespace WebCore { bool m_allowHTTPCookies; mutable bool m_resourceRequestUpdated; mutable bool m_platformRequestUpdated; + bool m_reportUploadProgress; private: const ResourceRequest& asResourceRequest() const; diff --git a/WebCore/platform/network/ResourceResponseBase.cpp b/WebCore/platform/network/ResourceResponseBase.cpp index 92ece8c..60c0097 100644 --- a/WebCore/platform/network/ResourceResponseBase.cpp +++ b/WebCore/platform/network/ResourceResponseBase.cpp @@ -39,7 +39,7 @@ static void parseCacheControlDirectiveValues(const String& directives, Vector<St auto_ptr<ResourceResponse> ResourceResponseBase::adopt(auto_ptr<CrossThreadResourceResponseData> data) { auto_ptr<ResourceResponse> response(new ResourceResponse()); - response->setUrl(data->m_url); + response->setURL(data->m_url); response->setMimeType(data->m_mimeType); response->setExpectedContentLength(data->m_expectedContentLength); response->setTextEncodingName(data->m_textEncodingName); @@ -94,7 +94,7 @@ const KURL& ResourceResponseBase::url() const return m_url; } -void ResourceResponseBase::setUrl(const KURL& url) +void ResourceResponseBase::setURL(const KURL& url) { lazyInit(); m_isNull = false; diff --git a/WebCore/platform/network/ResourceResponseBase.h b/WebCore/platform/network/ResourceResponseBase.h index c06f75b..ff34a26 100644 --- a/WebCore/platform/network/ResourceResponseBase.h +++ b/WebCore/platform/network/ResourceResponseBase.h @@ -49,7 +49,7 @@ public: bool isHTTP() const; const KURL& url() const; - void setUrl(const KURL& url); + void setURL(const KURL& url); const String& mimeType() const; void setMimeType(const String& mimeType); @@ -97,6 +97,13 @@ public: return m_cacheControlContainsMustRevalidate; } + // The ResourceResponse subclass may "shadow" this method to provide platform-specific memory usage information + unsigned memoryUsage() const + { + // average size, mostly due to URL and Header Map strings + return 1280; + } + static bool compare(const ResourceResponse& a, const ResourceResponse& b); protected: diff --git a/WebCore/platform/network/android/ResourceHandleAndroid.cpp b/WebCore/platform/network/android/ResourceHandleAndroid.cpp index 4d9199a..59084ab 100644 --- a/WebCore/platform/network/android/ResourceHandleAndroid.cpp +++ b/WebCore/platform/network/android/ResourceHandleAndroid.cpp @@ -118,7 +118,7 @@ bool ResourceHandle::loadsBlocked() // Class to handle synchronized loading of resources. class SyncLoader : public ResourceHandleClient { public: - SyncLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) { + SyncLoader(ResourceError& error, ResourceResponse& response, WTF::Vector<char>& data) { m_error = &error; m_response = &response; m_data = &data; @@ -140,11 +140,11 @@ public: private: ResourceError* m_error; ResourceResponse* m_response; - Vector<char>* m_data; + WTF::Vector<char>* m_data; }; void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, - ResourceError& error, ResourceResponse& response, Vector<char>& data, + ResourceError& error, ResourceResponse& response, WTF::Vector<char>& data, Frame* frame) { SyncLoader s(error, response, data); diff --git a/WebCore/platform/network/cf/FormDataStreamCFNet.cpp b/WebCore/platform/network/cf/FormDataStreamCFNet.cpp index 71fbfe7..3414d90 100644 --- a/WebCore/platform/network/cf/FormDataStreamCFNet.cpp +++ b/WebCore/platform/network/cf/FormDataStreamCFNet.cpp @@ -317,8 +317,7 @@ static void formEventCallback(CFReadStreamRef stream, CFStreamEventType type, vo void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData) { if (!formData) { - if (wkCanAccessCFURLRequestHTTPBodyParts()) - wkCFURLRequestSetHTTPRequestBodyParts(request, 0); + wkCFURLRequestSetHTTPRequestBodyParts(request, 0); return; } @@ -338,52 +337,20 @@ void setHTTPBody(CFMutableURLRequestRef request, PassRefPtr<FormData> formData) } } - if (wkCanAccessCFURLRequestHTTPBodyParts()) { - RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); - - for (size_t i = 0; i < count; ++i) { - const FormDataElement& element = formData->elements()[i]; - if (element.m_type == FormDataElement::data) { - RetainPtr<CFDataRef> data(AdoptCF, CFDataCreate(0, reinterpret_cast<const UInt8*>(element.m_data.data()), element.m_data.size())); - CFArrayAppendValue(array.get(), data.get()); - } else { - RetainPtr<CFStringRef> filename(AdoptCF, element.m_filename.createCFString()); - CFArrayAppendValue(array.get(), filename.get()); - } - } + RetainPtr<CFMutableArrayRef> array(AdoptCF, CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks)); - wkCFURLRequestSetHTTPRequestBodyParts(request, array.get()); - return; - } - - // Precompute the content length so CFURLConnection doesn't use chunked mode. - bool haveLength = true; - long long length = 0; for (size_t i = 0; i < count; ++i) { const FormDataElement& element = formData->elements()[i]; - if (element.m_type == FormDataElement::data) - length += element.m_data.size(); - else { - long long size; - if (getFileSize(element.m_filename, size)) - length += size; - else - haveLength = false; + if (element.m_type == FormDataElement::data) { + RetainPtr<CFDataRef> data(AdoptCF, CFDataCreate(0, reinterpret_cast<const UInt8*>(element.m_data.data()), element.m_data.size())); + CFArrayAppendValue(array.get(), data.get()); + } else { + RetainPtr<CFStringRef> filename(AdoptCF, element.m_filename.createCFString()); + CFArrayAppendValue(array.get(), filename.get()); } } - if (haveLength) { - CFStringRef lengthStr = CFStringCreateWithFormat(0, 0, CFSTR("%lld"), length); - CFURLRequestSetHTTPHeaderFieldValue(request, CFSTR("Content-Length"), lengthStr); - CFRelease(lengthStr); - } - - static WCReadStreamCallBacks formDataStreamCallbacks = - { 1, formCreate, formFinalize, 0, formOpen, 0, formRead, 0, formCanRead, formClose, 0, 0, 0, formSchedule, formUnschedule }; - - CFReadStreamRef stream = CFReadStreamCreate(0, (CFReadStreamCallBacks *)&formDataStreamCallbacks, formData.releaseRef()); - CFURLRequestSetHTTPRequestBodyStream(request, stream); - CFRelease(stream); + wkCFURLRequestSetHTTPRequestBodyParts(request, array.get()); } PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request) @@ -391,28 +358,23 @@ PassRefPtr<FormData> httpBodyFromRequest(CFURLRequestRef request) if (RetainPtr<CFDataRef> bodyData = CFURLRequestCopyHTTPRequestBody(request)) return FormData::create(CFDataGetBytePtr(bodyData.get()), CFDataGetLength(bodyData.get())); - if (wkCanAccessCFURLRequestHTTPBodyParts()) { - if (RetainPtr<CFArrayRef> bodyParts = wkCFURLRequestCopyHTTPRequestBodyParts(request)) { - RefPtr<FormData> formData = FormData::create(); - - CFIndex count = CFArrayGetCount(bodyParts.get()); - for (CFIndex i = 0; i < count; i++) { - CFTypeRef bodyPart = CFArrayGetValueAtIndex(bodyParts.get(), i); - CFTypeID typeID = CFGetTypeID(bodyPart); - if (typeID == CFStringGetTypeID()) { - String filename = (CFStringRef)bodyPart; - formData->appendFile(filename); - } else if (typeID == CFDataGetTypeID()) { - CFDataRef data = (CFDataRef)bodyPart; - formData->appendData(CFDataGetBytePtr(data), CFDataGetLength(data)); - } else - ASSERT_NOT_REACHED(); - } - return formData.release(); + if (RetainPtr<CFArrayRef> bodyParts = wkCFURLRequestCopyHTTPRequestBodyParts(request)) { + RefPtr<FormData> formData = FormData::create(); + + CFIndex count = CFArrayGetCount(bodyParts.get()); + for (CFIndex i = 0; i < count; i++) { + CFTypeRef bodyPart = CFArrayGetValueAtIndex(bodyParts.get(), i); + CFTypeID typeID = CFGetTypeID(bodyPart); + if (typeID == CFStringGetTypeID()) { + String filename = (CFStringRef)bodyPart; + formData->appendFile(filename); + } else if (typeID == CFDataGetTypeID()) { + CFDataRef data = (CFDataRef)bodyPart; + formData->appendData(CFDataGetBytePtr(data), CFDataGetLength(data)); + } else + ASSERT_NOT_REACHED(); } - } else { - if (RetainPtr<CFReadStreamRef> bodyStream = CFURLRequestCopyHTTPRequestBodyStream(request)) - return getStreamFormDatas().get(bodyStream.get()); + return formData.release(); } // FIXME: what to do about arbitrary body streams? diff --git a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp index a4000a3..2dcbbed 100644 --- a/WebCore/platform/network/cf/ResourceHandleCFNet.cpp +++ b/WebCore/platform/network/cf/ResourceHandleCFNet.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006, 2007, 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 @@ -53,51 +53,6 @@ namespace WebCore { -static HMODULE findCFNetworkModule() -{ - if (HMODULE module = GetModuleHandleA("CFNetwork")) - return module; - return GetModuleHandleA("CFNetwork_debug"); -} - -static DWORD cfNetworkVersion() -{ - HMODULE cfNetworkModule = findCFNetworkModule(); - WCHAR filename[MAX_PATH]; - GetModuleFileName(cfNetworkModule, filename, MAX_PATH); - DWORD handle; - DWORD versionInfoSize = GetFileVersionInfoSize(filename, &handle); - Vector<BYTE> versionInfo(versionInfoSize); - GetFileVersionInfo(filename, handle, versionInfoSize, versionInfo.data()); - VS_FIXEDFILEINFO* fixedFileInfo; - UINT fixedFileInfoLength; - VerQueryValue(versionInfo.data(), TEXT("\\"), reinterpret_cast<LPVOID*>(&fixedFileInfo), &fixedFileInfoLength); - return fixedFileInfo->dwProductVersionMS; -} - -static CFIndex highestSupportedCFURLConnectionClientVersion() -{ - const DWORD firstCFNetworkVersionWithConnectionClientV2 = 0x000101a8; // 1.424 - const DWORD firstCFNetworkVersionWithConnectionClientV3 = 0x000101ad; // 1.429 - -#ifndef _CFURLConnectionClientV2Present - return 1; -#else - - DWORD version = cfNetworkVersion(); - if (version < firstCFNetworkVersionWithConnectionClientV2) - return 1; -#ifndef _CFURLConnectionClientV3Present - return 2; -#else - - if (version < firstCFNetworkVersionWithConnectionClientV3) - return 2; - return 3; -#endif // _CFURLConnectionClientV3Present -#endif // _CFURLConnectionClientV2Present -} - static HashSet<String>& allowsAnyHTTPSCertificateHosts() { static HashSet<String> hosts; @@ -154,7 +109,6 @@ void didReceiveData(CFURLConnectionRef conn, CFDataRef data, CFIndex originalLen handle->client()->didReceiveData(handle, (const char*)bytes, length, originalLength); } -#ifdef _CFURLConnectionClientV2Present static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFIndex totalBytesWritten, CFIndex totalBytesExpectedToWrite, const void *clientInfo) { ResourceHandle* handle = (ResourceHandle*)clientInfo; @@ -162,9 +116,7 @@ static void didSendBodyData(CFURLConnectionRef conn, CFIndex bytesWritten, CFInd return; handle->client()->didSendData(handle, totalBytesWritten, totalBytesExpectedToWrite); } -#endif -#ifdef _CFURLConnectionClientV3Present static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const void* clientInfo) { ResourceHandle* handle = const_cast<ResourceHandle*>(static_cast<const ResourceHandle*>(clientInfo)); @@ -176,7 +128,6 @@ static Boolean shouldUseCredentialStorageCallback(CFURLConnectionRef conn, const return handle->shouldUseCredentialStorage(); } -#endif void didFinishLoading(CFURLConnectionRef conn, const void* clientInfo) { @@ -202,6 +153,9 @@ CFCachedURLResponseRef willCacheResponse(CFURLConnectionRef conn, CFCachedURLRes { ResourceHandle* handle = (ResourceHandle*)clientInfo; + if (handle->client() && !handle->client()->shouldCacheResponse(handle, cachedResponse)) + return 0; + CacheStoragePolicy policy = static_cast<CacheStoragePolicy>(CFCachedURLResponseGetStoragePolicy(cachedResponse)); if (handle->client()) @@ -297,7 +251,7 @@ void* runLoaderThread(void *unused) CFRunLoopRef ResourceHandle::loaderRunLoop() { if (!loaderRL) { - createThread(runLoaderThread, 0, "CFNetwork::Loader"); + createThread(runLoaderThread, 0, "WebCore: CFNetwork Loader"); while (loaderRL == 0) { // FIXME: sleep 10? that can't be right... Sleep(10); @@ -348,20 +302,9 @@ bool ResourceHandle::start(Frame* frame) RetainPtr<CFURLRequestRef> request(AdoptCF, makeFinalRequest(d->m_request, d->m_shouldContentSniff)); - static CFIndex clientVersion = highestSupportedCFURLConnectionClientVersion(); - CFURLConnectionClient* client; -#if defined(_CFURLConnectionClientV3Present) - CFURLConnectionClient_V3 client_V3 = {clientVersion, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0}; - client = reinterpret_cast<CFURLConnectionClient*>(&client_V3); -#elif defined(_CFURLConnectionClientV2Present) - CFURLConnectionClient_V2 client_V2 = {clientVersion, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData}; - client = reinterpret_cast<CFURLConnectionClient*>(&client_V2); -#else - CFURLConnectionClient client_V1 = {1, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge}; - client = &client_V1; -#endif - - d->m_connection.adoptCF(CFURLConnectionCreate(0, request.get(), client)); + CFURLConnectionClient_V3 client = { 3, this, 0, 0, 0, willSendRequest, didReceiveResponse, didReceiveData, NULL, didFinishLoading, didFail, willCacheResponse, didReceiveChallenge, didSendBodyData, shouldUseCredentialStorageCallback, 0}; + + d->m_connection.adoptCF(CFURLConnectionCreate(0, request.get(), reinterpret_cast<CFURLConnectionClient*>(&client))); CFURLConnectionScheduleWithCurrentMessageQueue(d->m_connection.get()); CFURLConnectionScheduleDownloadWithRunLoop(d->m_connection.get(), loaderRunLoop(), kCFRunLoopDefaultMode); @@ -519,12 +462,22 @@ bool ResourceHandle::loadsBlocked() return false; } -bool ResourceHandle::willLoadFromCache(ResourceRequest&) +bool ResourceHandle::willLoadFromCache(ResourceRequest& request) { - // Not having this function means that we'll ask the user about re-posting a form - // even when we go back to a page that's still in the cache. - notImplemented(); - return false; + request.setCachePolicy(ReturnCacheDataDontLoad); + + CFURLResponseRef cfResponse = 0; + CFErrorRef cfError = 0; + RetainPtr<CFURLRequestRef> cfRequest(AdoptCF, makeFinalRequest(request, true)); + RetainPtr<CFDataRef> data(AdoptCF, CFURLConnectionSendSynchronousRequest(cfRequest.get(), &cfResponse, &cfError, request.timeoutInterval())); + bool cached = cfResponse && !cfError; + + if (cfError) + CFRelease(cfError); + if (cfResponse) + CFRelease(cfResponse); + + return cached; } } // namespace WebCore diff --git a/WebCore/platform/network/cf/ResourceRequestCFNet.h b/WebCore/platform/network/cf/ResourceRequestCFNet.h index e9ebe76..d26072d 100644 --- a/WebCore/platform/network/cf/ResourceRequestCFNet.h +++ b/WebCore/platform/network/cf/ResourceRequestCFNet.h @@ -30,7 +30,7 @@ typedef const struct _CFURLRequest* CFURLRequestRef; namespace WebCore { - class ResourceRequest; + struct ResourceRequest; void getResourceRequest(ResourceRequest&, CFURLRequestRef); CFURLRequestRef cfURLRequest(const ResourceRequest&); diff --git a/WebCore/platform/network/cf/ResourceResponse.h b/WebCore/platform/network/cf/ResourceResponse.h index e14c79e..04cc82c 100644 --- a/WebCore/platform/network/cf/ResourceResponse.h +++ b/WebCore/platform/network/cf/ResourceResponse.h @@ -52,6 +52,18 @@ public: { } + unsigned memoryUsage() const + { + // FIXME: Find some programmatic lighweight way to calculate ResourceResponse and associated classes. + // This is a rough estimate of resource overhead based on stats collected from the stress test. + return 3072; + /* 1280 * 2 + // average size of ResourceResponse. Doubled to account for the WebCore copy and the CF copy. + // Mostly due to the size of the hash maps, the Header Map strings and the URL. + 256 * 2 // Overhead from ResourceRequest, doubled to account for WebCore copy and CF copy. + // Mostly due to the URL and Header Map. + */ + } + CFURLResponseRef cfURLResponse() const; private: diff --git a/WebCore/platform/network/chromium/ResourceRequest.h b/WebCore/platform/network/chromium/ResourceRequest.h index 76b8b99..b14dba6 100644 --- a/WebCore/platform/network/chromium/ResourceRequest.h +++ b/WebCore/platform/network/chromium/ResourceRequest.h @@ -35,7 +35,7 @@ namespace WebCore { class Frame; - class ResourceRequest : public ResourceRequestBase { + struct ResourceRequest : public ResourceRequestBase { public: enum TargetType { TargetIsMainFrame, @@ -47,16 +47,16 @@ namespace WebCore { ResourceRequest(const String& url) : ResourceRequestBase(KURL(url), UseProtocolCachePolicy) - , m_frame(0) - , m_originPid(0) + , m_requestorID(0) + , m_requestorProcessID(0) , m_targetType(TargetIsSubResource) { } ResourceRequest(const KURL& url, const CString& securityInfo) : ResourceRequestBase(url, UseProtocolCachePolicy) - , m_frame(0) - , m_originPid(0) + , m_requestorID(0) + , m_requestorProcessID(0) , m_targetType(TargetIsSubResource) , m_securityInfo(securityInfo) { @@ -64,16 +64,16 @@ namespace WebCore { ResourceRequest(const KURL& url) : ResourceRequestBase(url, UseProtocolCachePolicy) - , m_frame(0) - , m_originPid(0) + , m_requestorID(0) + , m_requestorProcessID(0) , m_targetType(TargetIsSubResource) { } ResourceRequest(const KURL& url, const String& referrer, ResourceRequestCachePolicy policy = UseProtocolCachePolicy) : ResourceRequestBase(url, policy) - , m_frame(0) - , m_originPid(0) + , m_requestorID(0) + , m_requestorProcessID(0) , m_targetType(TargetIsSubResource) { setHTTPReferrer(referrer); @@ -81,26 +81,30 @@ namespace WebCore { ResourceRequest() : ResourceRequestBase(KURL(), UseProtocolCachePolicy) - , m_frame(0) - , m_originPid(0) + , m_requestorID(0) + , m_requestorProcessID(0) , m_targetType(TargetIsSubResource) { } - // Provides context for the resource request. - Frame* frame() const { return m_frame; } - void setFrame(Frame* frame) { m_frame = frame; } + // Allows the request to be matched up with its requestor. + int requestorID() const { return m_requestorID; } + void setRequestorID(int requestorID) { m_requestorID = requestorID; } // What this request is for. - void setTargetType(TargetType type) { m_targetType = type; } TargetType targetType() const { return m_targetType; } - - // The origin pid is the process id of the process from which this - // request originated. In the case of out-of-process plugins, this - // allows to link back the request to the plugin process (as it is - // processed through a render view process). - int originPid() const { return m_originPid; } - void setOriginPid(int originPid) { m_originPid = originPid; } + void setTargetType(TargetType type) { m_targetType = type; } + + // The document's policy base url. + KURL policyURL() const { return m_policyURL; } + void setPolicyURL(const KURL& policyURL) { m_policyURL = policyURL; } + + // The process id of the process from which this request originated. In + // the case of out-of-process plugins, this allows to link back the + // request to the plugin process (as it is processed through a render + // view process). + int requestorProcessID() const { return m_requestorProcessID; } + void setRequestorProcessID(int requestorProcessID) { m_requestorProcessID = requestorProcessID; } // Opaque buffer that describes the security state (including SSL // connection state) for the resource that should be reported when the @@ -117,10 +121,11 @@ namespace WebCore { void doUpdatePlatformRequest() {} void doUpdateResourceRequest() {} - Frame* m_frame; - int m_originPid; + int m_requestorID; + int m_requestorProcessID; TargetType m_targetType; CString m_securityInfo; + KURL m_policyURL; }; } // namespace WebCore diff --git a/WebCore/platform/network/curl/ResourceHandleManager.cpp b/WebCore/platform/network/curl/ResourceHandleManager.cpp index 6a44233..6f009db 100644 --- a/WebCore/platform/network/curl/ResourceHandleManager.cpp +++ b/WebCore/platform/network/curl/ResourceHandleManager.cpp @@ -126,7 +126,7 @@ static size_t writeCallback(void* ptr, size_t size, size_t nmemb, void* data) if (!d->m_response.responseFired()) { const char* hdr; err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr); - d->m_response.setUrl(KURL(hdr)); + d->m_response.setURL(KURL(hdr)); if (d->client()) d->client()->didReceiveResponse(job, d->m_response); d->m_response.setResponseFired(true); @@ -180,7 +180,7 @@ static size_t headerCallback(char* ptr, size_t size, size_t nmemb, void* data) const char* hdr; err = curl_easy_getinfo(h, CURLINFO_EFFECTIVE_URL, &hdr); - d->m_response.setUrl(KURL(hdr)); + d->m_response.setURL(KURL(hdr)); long httpCode = 0; err = curl_easy_getinfo(h, CURLINFO_RESPONSE_CODE, &httpCode); diff --git a/WebCore/platform/network/mac/FormDataStreamMac.mm b/WebCore/platform/network/mac/FormDataStreamMac.mm index b618949..94fdb25 100644 --- a/WebCore/platform/network/mac/FormDataStreamMac.mm +++ b/WebCore/platform/network/mac/FormDataStreamMac.mm @@ -204,7 +204,7 @@ static void* formCreate(CFReadStreamRef stream, void* context) // Append in reverse order since we remove elements from the end. size_t size = formData->elements().size(); - newInfo->remainingElements.reserveCapacity(size); + newInfo->remainingElements.reserveInitialCapacity(size); for (size_t i = 0; i < size; ++i) newInfo->remainingElements.append(formData->elements()[size - i - 1]); diff --git a/WebCore/platform/network/mac/ResourceErrorMac.mm b/WebCore/platform/network/mac/ResourceErrorMac.mm index e59eadd..94c2124 100644 --- a/WebCore/platform/network/mac/ResourceErrorMac.mm +++ b/WebCore/platform/network/mac/ResourceErrorMac.mm @@ -26,6 +26,7 @@ #import "config.h" #import "ResourceError.h" +#import "BlockExceptions.h" #import "KURL.h" #import <Foundation/Foundation.h> @@ -46,8 +47,12 @@ void ResourceError::platformLazyInit() NSString* failingURLString = [[m_platformError.get() userInfo] valueForKey:@"NSErrorFailingURLStringKey"]; if (!failingURLString) failingURLString = [[[m_platformError.get() userInfo] valueForKey:@"NSErrorFailingURLKey"] absoluteString]; - + + // Workaround for <rdar://problem/6554067> + m_localizedDescription = failingURLString; + BEGIN_BLOCK_OBJC_EXCEPTIONS; m_localizedDescription = [m_platformError.get() _web_localizedDescription]; + END_BLOCK_OBJC_EXCEPTIONS; m_dataIsUpToDate = true; } diff --git a/WebCore/platform/network/mac/ResourceRequestMac.mm b/WebCore/platform/network/mac/ResourceRequestMac.mm index a9bbd40..92c37ee 100644 --- a/WebCore/platform/network/mac/ResourceRequestMac.mm +++ b/WebCore/platform/network/mac/ResourceRequestMac.mm @@ -119,8 +119,8 @@ void ResourceRequest::doUpdatePlatformRequest() for (HTTPHeaderMap::const_iterator it = httpHeaderFields().begin(); it != end; ++it) [nsRequest setValue:it->second forHTTPHeaderField:it->first]; - // The below check can be removed once we require a version of Foundation with -[NSMutableURLRequest setContentDispositionEncodingFallbackArray] method. - static bool supportsContentDispositionEncodingFallbackArray = [NSMutableURLRequest instancesRespondToSelector:@selector(setContentDispositionEncodingFallbackArray)]; + // The below check can be removed once we require a version of Foundation with -[NSMutableURLRequest setContentDispositionEncodingFallbackArray:] method. + static bool supportsContentDispositionEncodingFallbackArray = [NSMutableURLRequest instancesRespondToSelector:@selector(setContentDispositionEncodingFallbackArray:)]; if (supportsContentDispositionEncodingFallbackArray) { NSMutableArray *encodingFallbacks = [NSMutableArray array]; unsigned count = m_responseContentDispositionEncodingFallbackArray.size(); diff --git a/WebCore/platform/network/mac/ResourceResponse.h b/WebCore/platform/network/mac/ResourceResponse.h index b65760c..16b0cbf 100644 --- a/WebCore/platform/network/mac/ResourceResponse.h +++ b/WebCore/platform/network/mac/ResourceResponse.h @@ -57,6 +57,18 @@ public: { } + unsigned memoryUsage() const + { + // FIXME: Find some programmatic lighweight way to calculate ResourceResponse and associated classes. + // This is a rough estimate of resource overhead based on stats collected from the stress test. + return 3072; + /* 1280 * 2 + // average size of ResourceResponse. Doubled to account for the WebCore copy and the CF copy. + // Mostly due to the size of the hash maps, the Header Map strings and the URL. + 256 * 2 // Overhead from ResourceRequest, doubled to account for WebCore copy and CF copy. + // Mostly due to the URL and Header Map. + */ + } + NSURLResponse *nsURLResponse() const; private: diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp index 2de2125..2c730a6 100644 --- a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp +++ b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp @@ -269,8 +269,10 @@ void QNetworkReplyHandler::sendResponseIfNeeded() const bool isLocalFileReply = (m_reply->url().scheme() == QLatin1String("file")); int statusCode = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - if (!isLocalFileReply) + if (!isLocalFileReply) { response.setHTTPStatusCode(statusCode); + response.setHTTPStatusText(m_reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toByteArray().constData()); + } else if (m_reply->error() == QNetworkReply::ContentNotFoundError) response.setHTTPStatusCode(404); diff --git a/WebCore/platform/network/soup/CookieJarSoup.cpp b/WebCore/platform/network/soup/CookieJarSoup.cpp index 88109e8..e3064e1 100644 --- a/WebCore/platform/network/soup/CookieJarSoup.cpp +++ b/WebCore/platform/network/soup/CookieJarSoup.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2008 Xan Lopez <xan@gnome.org> + * Copyright (C) 2009 Igalia S.L. * Copyright (C) 2008 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or @@ -25,15 +26,35 @@ namespace WebCore { -SoupCookieJar* getCookieJar() +static bool cookiesInitialized; +static SoupCookieJar* cookieJar; + +SoupCookieJar* defaultCookieJar() +{ + if (!cookiesInitialized) { + cookiesInitialized = true; + setDefaultCookieJar(soup_cookie_jar_new()); + } + + return cookieJar; +} + +void setDefaultCookieJar(SoupCookieJar* jar) { - static SoupCookieJar* jar = soup_cookie_jar_new(); - return jar; + cookiesInitialized = true; + + if (cookieJar) + g_object_unref(cookieJar); + + cookieJar = jar; + + if (cookieJar) + g_object_ref(cookieJar); } void setCookies(Document* /*document*/, const KURL& url, const KURL& /*policyURL*/, const String& value) { - SoupCookieJar* jar = getCookieJar(); + SoupCookieJar* jar = defaultCookieJar(); if (!jar) return; @@ -45,7 +66,7 @@ void setCookies(Document* /*document*/, const KURL& url, const KURL& /*policyURL String cookies(const Document* /*document*/, const KURL& url) { - SoupCookieJar* jar = getCookieJar(); + SoupCookieJar* jar = defaultCookieJar(); if (!jar) return String(); @@ -61,7 +82,7 @@ String cookies(const Document* /*document*/, const KURL& url) bool cookiesEnabled(const Document* /*document*/) { - return getCookieJar(); + return defaultCookieJar(); } } diff --git a/WebCore/platform/network/soup/CookieJarSoup.h b/WebCore/platform/network/soup/CookieJarSoup.h index 61179ae..ab1f95c 100644 --- a/WebCore/platform/network/soup/CookieJarSoup.h +++ b/WebCore/platform/network/soup/CookieJarSoup.h @@ -31,7 +31,8 @@ #include <libsoup/soup.h> namespace WebCore { - SoupCookieJar* getCookieJar(); + SoupCookieJar* defaultCookieJar(); + void setDefaultCookieJar(SoupCookieJar* jar); } #endif diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp index da96873..1b91e32 100644 --- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -3,6 +3,9 @@ * Copyright (C) 2008 Xan Lopez <xan@gnome.org> * Copyright (C) 2008 Collabora Ltd. * Copyright (C) 2009 Holger Hans Peter Freyther + * Copyright (C) 2009 Gustavo Noronha Silva <gns@gnome.org> + * Copyright (C) 2009 Christian Dywan <christian@imendio.com> + * Copyright (C) 2009 Igalia S.L. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,41 +24,109 @@ */ #include "config.h" -#include "CString.h" #include "ResourceHandle.h" #include "Base64.h" #include "CookieJarSoup.h" +#include "ChromeClient.h" +#include "CString.h" #include "DocLoader.h" +#include "FileSystem.h" #include "Frame.h" #include "HTTPParsers.h" +#include "Logging.h" #include "MIMETypeRegistry.h" #include "NotImplemented.h" +#include "Page.h" #include "ResourceError.h" #include "ResourceHandleClient.h" #include "ResourceHandleInternal.h" #include "ResourceResponse.h" #include "TextEncoding.h" +#include <errno.h> +#include <fcntl.h> #include <gio/gio.h> +#include <gtk/gtk.h> #include <libsoup/soup.h> -#include <libsoup/soup-message.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> #if PLATFORM(GTK) - #if GLIB_CHECK_VERSION(2,12,0) - #define USE_GLIB_BASE64 - #endif +#define USE_GLIB_BASE64 #endif namespace WebCore { -static SoupSession* session = 0; +class WebCoreSynchronousLoader : public ResourceHandleClient, Noncopyable { +public: + WebCoreSynchronousLoader(ResourceError&, ResourceResponse &, Vector<char>&); + ~WebCoreSynchronousLoader(); + + virtual void didReceiveResponse(ResourceHandle*, const ResourceResponse&); + virtual void didReceiveData(ResourceHandle*, const char*, int, int lengthReceived); + virtual void didFinishLoading(ResourceHandle*); + virtual void didFail(ResourceHandle*, const ResourceError&); + + void run(); + +private: + ResourceError& m_error; + ResourceResponse& m_response; + Vector<char>& m_data; + bool m_finished; + GMainLoop* m_mainLoop; +}; + +WebCoreSynchronousLoader::WebCoreSynchronousLoader(ResourceError& error, ResourceResponse& response, Vector<char>& data) + : m_error(error) + , m_response(response) + , m_data(data) + , m_finished(false) +{ + m_mainLoop = g_main_loop_new(0, false); +} + +WebCoreSynchronousLoader::~WebCoreSynchronousLoader() +{ + g_main_loop_unref(m_mainLoop); +} + +void WebCoreSynchronousLoader::didReceiveResponse(ResourceHandle*, const ResourceResponse& response) +{ + m_response = response; +} + +void WebCoreSynchronousLoader::didReceiveData(ResourceHandle*, const char* data, int length, int) +{ + m_data.append(data, length); +} + +void WebCoreSynchronousLoader::didFinishLoading(ResourceHandle*) +{ + g_main_loop_quit(m_mainLoop); + m_finished = true; +} + +void WebCoreSynchronousLoader::didFail(ResourceHandle* handle, const ResourceError& error) +{ + m_error = error; + didFinishLoading(handle); +} + +void WebCoreSynchronousLoader::run() +{ + if (!m_finished) + g_main_loop_run(m_mainLoop); +} enum { ERROR_TRANSPORT, ERROR_UNKNOWN_PROTOCOL, - ERROR_BAD_NON_HTTP_METHOD + ERROR_BAD_NON_HTTP_METHOD, + ERROR_UNABLE_TO_OPEN_FILE, }; static void cleanupGioOperation(ResourceHandleInternal* handle); @@ -82,20 +153,47 @@ ResourceHandle::~ResourceHandle() static void fillResponseFromMessage(SoupMessage* msg, ResourceResponse* response) { SoupMessageHeadersIter iter; - const char* name = NULL; - const char* value = NULL; + const char* name = 0; + const char* value = 0; soup_message_headers_iter_init(&iter, msg->response_headers); while (soup_message_headers_iter_next(&iter, &name, &value)) response->setHTTPHeaderField(name, value); - String contentType = soup_message_headers_get(msg->response_headers, "Content-Type"); - char* uri = soup_uri_to_string(soup_message_get_uri(msg), FALSE); - response->setUrl(KURL(uri)); - g_free(uri); + GHashTable* contentTypeParameters = 0; + String contentType = soup_message_headers_get_content_type(msg->response_headers, &contentTypeParameters); + + // When the server sends multiple Content-Type headers, soup will + // give us their values concatenated with commas as a separator; + // we need to handle this and use only one value. We use the first + // value, and add all the parameters, afterwards, if any. + Vector<String> contentTypes; + contentType.split(',', true, contentTypes); + contentType = contentTypes[0]; + + if (contentTypeParameters) { + GHashTableIter hashTableIter; + gpointer hashKey; + gpointer hashValue; + + g_hash_table_iter_init(&hashTableIter, contentTypeParameters); + while (g_hash_table_iter_next(&hashTableIter, &hashKey, &hashValue)) { + contentType += String("; "); + contentType += String(static_cast<char*>(hashKey)); + contentType += String("="); + contentType += String(static_cast<char*>(hashValue)); + } + g_hash_table_destroy(contentTypeParameters); + } + response->setMimeType(extractMIMETypeFromMediaType(contentType)); + + char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); + response->setURL(KURL(KURL(), uri)); + g_free(uri); response->setTextEncodingName(extractCharsetFromMediaType(contentType)); response->setExpectedContentLength(soup_message_headers_get_content_length(msg->response_headers)); response->setHTTPStatusCode(msg->status_code); + response->setHTTPStatusText(msg->reason_phrase); response->setSuggestedFilename(filenameFromHTTPContentDisposition(response->httpHeaderField("Content-Disposition"))); } @@ -110,11 +208,20 @@ static void restartedCallback(SoupMessage* msg, gpointer data) if (d->m_cancelled) return; - char* uri = soup_uri_to_string(soup_message_get_uri(msg), FALSE); + char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); String location = String(uri); g_free(uri); KURL newURL = KURL(handle->request().url(), location); + // FIXME: This is needed because some servers use broken URIs in + // their Location header, when redirecting, such as URIs with + // white spaces instead of %20; this should be fixed in soup, in + // the future, and this work-around removed. + // See http://bugzilla.gnome.org/show_bug.cgi?id=575378. + SoupURI* soup_uri = soup_uri_new(newURL.string().utf8().data()); + soup_message_set_uri(msg, soup_uri); + soup_uri_free(soup_uri); + ResourceRequest request = handle->request(); ResourceResponse response; request.setURL(newURL); @@ -127,7 +234,32 @@ static void restartedCallback(SoupMessage* msg, gpointer data) static void gotHeadersCallback(SoupMessage* msg, gpointer data) { - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) + // For 401, we will accumulate the resource body, and only use it + // in case authentication with the soup feature doesn't happen + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + soup_message_body_set_accumulate(msg->response_body, TRUE); + return; + } + + // For all the other responses, we handle each chunk ourselves, + // and we don't need msg->response_body to contain all of the data + // we got, when we finish downloading. + soup_message_body_set_accumulate(msg->response_body, FALSE); + + // The 304 status code (SOUP_STATUS_NOT_MODIFIED) needs to be fed + // into WebCore, as opposed to other kinds of redirections, which + // are handled by soup directly, so we special-case it here and in + // gotChunk. + if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code) + || (SOUP_STATUS_IS_REDIRECTION(msg->status_code) && (msg->status_code != SOUP_STATUS_NOT_MODIFIED))) + return; + + // We still don't know anything about Content-Type, so we will try + // sniffing the contents of the file, and then report that we got + // headers; we will not do content sniffing for 304 responses, + // though, since they do not have a body. + if ((msg->status_code != SOUP_STATUS_NOT_MODIFIED) + && !soup_message_headers_get_content_type(msg->response_headers, NULL)) return; ResourceHandle* handle = static_cast<ResourceHandle*>(data); @@ -142,12 +274,14 @@ static void gotHeadersCallback(SoupMessage* msg, gpointer data) fillResponseFromMessage(msg, &d->m_response); client->didReceiveResponse(handle, d->m_response); - soup_message_set_flags(msg, SOUP_MESSAGE_OVERWRITE_CHUNKS); + d->m_reportedHeaders = true; } static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) { - if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) + if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code) + || (SOUP_STATUS_IS_REDIRECTION(msg->status_code) && (msg->status_code != SOUP_STATUS_NOT_MODIFIED)) + || (msg->status_code == SOUP_STATUS_UNAUTHORIZED)) return; ResourceHandle* handle = static_cast<ResourceHandle*>(data); @@ -160,6 +294,17 @@ static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) if (!client) return; + if (!d->m_reportedHeaders) { + gboolean uncertain; + char* contentType = g_content_type_guess(d->m_request.url().lastPathComponent().utf8().data(), reinterpret_cast<const guchar*>(chunk->data), chunk->length, &uncertain); + soup_message_headers_set_content_type(msg->response_headers, contentType, NULL); + g_free(contentType); + + fillResponseFromMessage(msg, &d->m_response); + client->didReceiveResponse(handle, d->m_response); + d->m_reportedHeaders = true; + } + client->didReceiveData(handle, chunk->data, chunk->length, false); } @@ -167,7 +312,7 @@ static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) // Doesn't get called for redirects. static void finishedCallback(SoupSession *session, SoupMessage* msg, gpointer data) { - ResourceHandle* handle = static_cast<ResourceHandle*>(data); + RefPtr<ResourceHandle>handle = adoptRef(static_cast<ResourceHandle*>(data)); // TODO: maybe we should run this code even if there's no client? if (!handle) return; @@ -182,24 +327,26 @@ static void finishedCallback(SoupSession *session, SoupMessage* msg, gpointer da return; if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { - char* uri = soup_uri_to_string(soup_message_get_uri(msg), FALSE); + char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); ResourceError error("webkit-network-error", ERROR_TRANSPORT, uri, String::fromUTF8(msg->reason_phrase)); g_free(uri); - client->didFail(handle, error); + client->didFail(handle.get(), error); return; - } else if (!SOUP_STATUS_IS_SUCCESSFUL(msg->status_code)) { + } + + if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { fillResponseFromMessage(msg, &d->m_response); - client->didReceiveResponse(handle, d->m_response); + client->didReceiveResponse(handle.get(), d->m_response); // WebCore might have cancelled the job in the while if (d->m_cancelled) return; if (msg->response_body->data) - client->didReceiveData(handle, msg->response_body->data, msg->response_body->length, true); + client->didReceiveData(handle.get(), msg->response_body->data, msg->response_body->length, true); } - client->didFinishLoading(handle); + client->didFinishLoading(handle.get()); } // parseDataUrl() is taken from the CURL http backend. @@ -212,7 +359,7 @@ static gboolean parseDataUrl(gpointer callback_data) ASSERT(client); if (!client) - return FALSE; + return false; String url = handle->request().url().string(); ASSERT(url.startsWith("data:", false)); @@ -220,14 +367,14 @@ static gboolean parseDataUrl(gpointer callback_data) int index = url.find(','); if (index == -1) { client->cannotShowURL(handle); - return FALSE; + return false; } String mediaType = url.substring(5, index - 5); String data = url.substring(index + 1); - bool base64 = mediaType.endsWith(";base64", false); - if (base64) + bool isBase64 = mediaType.endsWith(";base64", false); + if (isBase64) mediaType = mediaType.left(mediaType.length() - 7); if (mediaType.isEmpty()) @@ -239,7 +386,7 @@ static gboolean parseDataUrl(gpointer callback_data) ResourceResponse response; response.setMimeType(mimeType); - if (base64) { + if (isBase64) { data = decodeURLEscapeSequences(data); response.setTextEncodingName(charset); client->didReceiveResponse(handle, response); @@ -269,7 +416,7 @@ static gboolean parseDataUrl(gpointer callback_data) client->didFinishLoading(handle); - return FALSE; + return false; } bool ResourceHandle::startData(String urlString) @@ -282,30 +429,44 @@ bool ResourceHandle::startData(String urlString) return true; } -bool ResourceHandle::startHttp(String urlString) +static SoupSession* createSoupSession() { - if (!session) { - session = soup_session_async_new(); + return soup_session_async_new(); +} - soup_session_add_feature(session, SOUP_SESSION_FEATURE(getCookieJar())); +static void ensureSessionIsInitialized(SoupSession* session) +{ + if (g_object_get_data(G_OBJECT(session), "webkit-init")) + return; - const char* soup_debug = g_getenv("WEBKIT_SOUP_LOGGING"); - if (soup_debug) { - int soup_debug_level = atoi(soup_debug); + SoupCookieJar* jar = reinterpret_cast<SoupCookieJar*>(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + if (!jar) + soup_session_add_feature(session, SOUP_SESSION_FEATURE(defaultCookieJar())); + else + setDefaultCookieJar(jar); - SoupLogger* logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(soup_debug_level), -1); - soup_logger_attach(logger, session); - g_object_unref(logger); - } + if (!soup_session_get_feature(session, SOUP_TYPE_LOGGER) && LogNetwork.state == WTFLogChannelOn) { + SoupLogger* logger = soup_logger_new(static_cast<SoupLoggerLogLevel>(SOUP_LOGGER_LOG_BODY), -1); + soup_logger_attach(logger, session); + g_object_unref(logger); } + g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef)); +} + +bool ResourceHandle::startHttp(String urlString) +{ + SoupSession* session = defaultSession(); + ensureSessionIsInitialized(session); + SoupMessage* msg; msg = soup_message_new(request().httpMethod().utf8().data(), urlString.utf8().data()); g_signal_connect(msg, "restarted", G_CALLBACK(restartedCallback), this); - g_signal_connect(msg, "got-headers", G_CALLBACK(gotHeadersCallback), this); g_signal_connect(msg, "got-chunk", G_CALLBACK(gotChunkCallback), this); + g_object_set_data(G_OBJECT(msg), "resourceHandle", reinterpret_cast<void*>(this)); + HTTPHeaderMap customHeaders = d->m_request.httpHeaderFields(); if (!customHeaders.isEmpty()) { HTTPHeaderMap::const_iterator end = customHeaders.end(); @@ -315,68 +476,130 @@ bool ResourceHandle::startHttp(String urlString) FormData* httpBody = d->m_request.httpBody(); if (httpBody && !httpBody->isEmpty()) { - // Making a copy of the request body isn't the most efficient way to - // serialize it, but by far the most simple. Dealing with individual - // FormData elements and shared buffers should be more memory - // efficient. - // - // This possibly isn't handling file uploads/attachments, for which - // shared buffers or streaming should definitely be used. - Vector<char> body; - httpBody->flatten(body); - soup_message_set_request(msg, d->m_request.httpContentType().utf8().data(), - SOUP_MEMORY_COPY, body.data(), body.size()); + size_t numElements = httpBody->elements().size(); + + // handle the most common case (i.e. no file upload) + if (numElements < 2) { + Vector<char> body; + httpBody->flatten(body); + soup_message_set_request(msg, d->m_request.httpContentType().utf8().data(), + SOUP_MEMORY_COPY, body.data(), body.size()); + } else { + /* + * we have more than one element to upload, and some may + * be (big) files, which we will want to mmap instead of + * copying into memory; TODO: support upload of non-local + * (think sftp://) files by using GIO? + */ + soup_message_body_set_accumulate(msg->request_body, FALSE); + for (size_t i = 0; i < numElements; i++) { + const FormDataElement& element = httpBody->elements()[i]; + + if (element.m_type == FormDataElement::data) + soup_message_body_append(msg->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size()); + else { + /* + * mapping for uploaded files code inspired by technique used in + * libsoup's simple-httpd test + */ + GError* error = 0; + gchar* fileName = filenameFromString(element.m_filename); + GMappedFile* fileMapping = g_mapped_file_new(fileName, false, &error); + + g_free(fileName); + + if (error) { + ResourceError resourceError("webkit-network-error", ERROR_UNABLE_TO_OPEN_FILE, urlString, error->message); + g_error_free(error); + + d->client()->didFail(this, resourceError); + + g_object_unref(msg); + return false; + } + + SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping), + g_mapped_file_get_length(fileMapping), + fileMapping, reinterpret_cast<GDestroyNotify>(g_mapped_file_free)); + soup_message_body_append_buffer(msg->request_body, soupBuffer); + soup_buffer_free(soupBuffer); + } + } + } } d->m_msg = static_cast<SoupMessage*>(g_object_ref(msg)); + // balanced by a deref() in finishedCallback, which should always run + ref(); + soup_session_queue_message(session, d->m_msg, finishedCallback, this); return true; } +static gboolean reportUnknownProtocolError(gpointer callback_data) +{ + ResourceHandle* handle = static_cast<ResourceHandle*>(callback_data); + ResourceHandleInternal* d = handle->getInternal(); + ResourceHandleClient* client = handle->client(); + + if (d->m_cancelled || !client) { + handle->deref(); + return false; + } + + KURL url = handle->request().url(); + ResourceError error("webkit-network-error", ERROR_UNKNOWN_PROTOCOL, url.string(), url.protocol()); + client->didFail(handle, error); + + handle->deref(); + return false; +} + bool ResourceHandle::start(Frame* frame) { ASSERT(!d->m_msg); - // If we are no longer attached to a Page, this must be an attempted load from an - // onUnload handler, so let's just block it. - if (!frame->page()) + + // The frame could be null if the ResourceHandle is not associated to any + // Frame, e.g. if we are downloading a file. + // If the frame is not null but the page is null this must be an attempted + // load from an onUnload handler, so let's just block it. + if (frame && !frame->page()) return false; KURL url = request().url(); String urlString = url.string(); String protocol = url.protocol(); + // Used to set the authentication dialog toplevel; may be NULL + d->m_frame = frame; + if (equalIgnoringCase(protocol, "data")) return startData(urlString); - else if ((equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https")) && SOUP_URI_VALID_FOR_HTTP(soup_uri_new(urlString.utf8().data()))) + + if ((equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https")) && SOUP_URI_VALID_FOR_HTTP(soup_uri_new(urlString.utf8().data()))) return startHttp(urlString); - else if (equalIgnoringCase(protocol, "file") || equalIgnoringCase(protocol, "ftp") || equalIgnoringCase(protocol, "ftps")) + + if (equalIgnoringCase(protocol, "file") || equalIgnoringCase(protocol, "ftp") || equalIgnoringCase(protocol, "ftps")) // FIXME: should we be doing any other protocols here? - return startGio(urlString); - else { - // If we don't call didFail the job is not complete for webkit even false is returned. - if (d->client()) { - ResourceError error("webkit-network-error", ERROR_UNKNOWN_PROTOCOL, urlString, protocol); - d->client()->didFail(this, error); - } - return false; - } + return startGio(url); + + // Error must not be reported immediately, but through an idle function. + // Despite error, we should return true so a proper handle is created, + // to which this failure can be reported. + ref(); + d->m_idleHandler = g_idle_add(reportUnknownProtocolError, this); + return true; } void ResourceHandle::cancel() { d->m_cancelled = true; - if (d->m_msg) { - soup_session_cancel_message(session, d->m_msg, SOUP_STATUS_CANCELLED); - // For re-entrancy troubles we call didFinishLoading when the message hasn't been handled yet. - if (client()) - client()->didFinishLoading(this); - } else if (d->m_cancellable) { + if (d->m_msg) + soup_session_cancel_message(defaultSession(), d->m_msg, SOUP_STATUS_CANCELLED); + else if (d->m_cancellable) g_cancellable_cancel(d->m_cancellable); - if (client()) - client()->didFinishLoading(this); - } } PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() @@ -409,9 +632,13 @@ bool ResourceHandle::willLoadFromCache(ResourceRequest&) return false; } -void ResourceHandle::loadResourceSynchronously(const ResourceRequest&, ResourceError&, ResourceResponse&, Vector<char>&, Frame*) +void ResourceHandle::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data, Frame* frame) { - notImplemented(); + WebCoreSynchronousLoader syncLoader(error, response, data); + ResourceHandle handle(request, &syncLoader, true, false, true); + + handle.start(frame); + syncLoader.run(); } // GIO-based loader @@ -420,7 +647,7 @@ static inline ResourceError networkErrorForFile(GFile* file, GError* error) { // FIXME: Map gio errors to a more detailed error code when we have it in WebKit. gchar* uri = g_file_get_uri(file); - ResourceError resourceError("webkit-network-error", ERROR_TRANSPORT, uri, String::fromUTF8(error->message)); + ResourceError resourceError("webkit-network-error", ERROR_TRANSPORT, uri, error ? String::fromUTF8(error->message) : String()); g_free(uri); return resourceError; } @@ -430,20 +657,23 @@ static void cleanupGioOperation(ResourceHandleInternal* d) if (d->m_gfile) { g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", 0); g_object_unref(d->m_gfile); - d->m_gfile = NULL; + d->m_gfile = 0; } + if (d->m_cancellable) { g_object_unref(d->m_cancellable); - d->m_cancellable = NULL; + d->m_cancellable = 0; } - if (d->m_input_stream) { - g_object_set_data(G_OBJECT(d->m_input_stream), "webkit-resource", 0); - g_object_unref(d->m_input_stream); - d->m_input_stream = NULL; + + if (d->m_inputStream) { + g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", 0); + g_object_unref(d->m_inputStream); + d->m_inputStream = 0; } + if (d->m_buffer) { g_free(d->m_buffer); - d->m_buffer = NULL; + d->m_buffer = 0; } } @@ -456,14 +686,15 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer) ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); - g_input_stream_close_finish(d->m_input_stream, res, NULL); + g_input_stream_close_finish(d->m_inputStream, res, 0); cleanupGioOperation(d); client->didFinishLoading(handle); } static void readCallback(GObject* source, GAsyncResult* res, gpointer) { - ResourceHandle* handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); + // didReceiveData may cancel the load, which may release the last reference. + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); if (!handle) return; @@ -475,27 +706,34 @@ static void readCallback(GObject* source, GAsyncResult* res, gpointer) return; } - gssize nread; GError *error = 0; - nread = g_input_stream_read_finish(d->m_input_stream, res, &error); + gssize bytesRead = g_input_stream_read_finish(d->m_inputStream, res, &error); if (error) { ResourceError resourceError = networkErrorForFile(d->m_gfile, error); + g_error_free(error); cleanupGioOperation(d); - client->didFail(handle, resourceError); + client->didFail(handle.get(), resourceError); return; - } else if (!nread) { - g_input_stream_close_async(d->m_input_stream, G_PRIORITY_DEFAULT, - NULL, closeCallback, NULL); + } + + if (!bytesRead) { + g_input_stream_close_async(d->m_inputStream, G_PRIORITY_DEFAULT, + 0, closeCallback, 0); return; } - d->m_total += nread; - client->didReceiveData(handle, d->m_buffer, nread, d->m_total); + d->m_total += bytesRead; + client->didReceiveData(handle.get(), d->m_buffer, bytesRead, d->m_total); - g_input_stream_read_async(d->m_input_stream, d->m_buffer, d->m_bufsize, + if (d->m_cancelled) { + cleanupGioOperation(d); + return; + } + + g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize, G_PRIORITY_DEFAULT, d->m_cancellable, - readCallback, NULL); + readCallback, 0); } static void openCallback(GObject* source, GAsyncResult* res, gpointer) @@ -512,24 +750,24 @@ static void openCallback(GObject* source, GAsyncResult* res, gpointer) return; } - GFileInputStream* in; - GError *error = NULL; - in = g_file_read_finish(G_FILE(source), res, &error); + GError *error = 0; + GFileInputStream* in = g_file_read_finish(G_FILE(source), res, &error); if (error) { ResourceError resourceError = networkErrorForFile(d->m_gfile, error); + g_error_free(error); cleanupGioOperation(d); client->didFail(handle, resourceError); return; } - d->m_input_stream = G_INPUT_STREAM(in); - d->m_bufsize = 8192; - d->m_buffer = static_cast<char*>(g_malloc(d->m_bufsize)); + d->m_inputStream = G_INPUT_STREAM(in); + d->m_bufferSize = 8192; + d->m_buffer = static_cast<char*>(g_malloc(d->m_bufferSize)); d->m_total = 0; - g_object_set_data(G_OBJECT(d->m_input_stream), "webkit-resource", handle); - g_input_stream_read_async(d->m_input_stream, d->m_buffer, d->m_bufsize, + g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", handle); + g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize, G_PRIORITY_DEFAULT, d->m_cancellable, - readCallback, NULL); + readCallback, 0); } static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) @@ -549,10 +787,10 @@ static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) ResourceResponse response; char* uri = g_file_get_uri(d->m_gfile); - response.setUrl(KURL(uri)); + response.setURL(KURL(KURL(), uri)); g_free(uri); - GError *error = NULL; + GError *error = 0; GFileInfo* info = g_file_query_info_finish(d->m_gfile, res, &error); if (error) { @@ -564,6 +802,7 @@ static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) // for a while). ResourceError resourceError = networkErrorForFile(d->m_gfile, error); + g_error_free(error); cleanupGioOperation(d); client->didFail(handle, resourceError); return; @@ -573,7 +812,7 @@ static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) // FIXME: what if the URI points to a directory? Should we // generate a listing? How? What do other backends do here? - ResourceError resourceError = networkErrorForFile(d->m_gfile, error); + ResourceError resourceError = networkErrorForFile(d->m_gfile, 0); cleanupGioOperation(d); client->didFail(handle, resourceError); return; @@ -581,7 +820,6 @@ static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) response.setMimeType(g_file_info_get_content_type(info)); response.setExpectedContentLength(g_file_info_get_size(info)); - response.setHTTPStatusCode(SOUP_STATUS_OK); GTimeVal tv; g_file_info_get_modification_time(info, &tv); @@ -590,23 +828,31 @@ static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) client->didReceiveResponse(handle, response); g_file_read_async(d->m_gfile, G_PRIORITY_DEFAULT, d->m_cancellable, - openCallback, NULL); + openCallback, 0); } -bool ResourceHandle::startGio(String urlString) +bool ResourceHandle::startGio(KURL url) { - if (request().httpMethod() != "GET") { - ResourceError error("webkit-network-error", ERROR_BAD_NON_HTTP_METHOD, urlString, request().httpMethod()); + if (request().httpMethod() != "GET" && request().httpMethod() != "POST") { + ResourceError error("webkit-network-error", ERROR_BAD_NON_HTTP_METHOD, url.string(), request().httpMethod()); d->client()->didFail(this, error); return false; } - // Remove the fragment part of the URL since the file backend doesn't deal with it - int fragPos; - if ((fragPos = urlString.find("#")) != -1) - urlString = urlString.left(fragPos); - - d->m_gfile = g_file_new_for_uri(urlString.utf8().data()); + // GIO doesn't know how to handle refs and queries, so remove them + // TODO: use KURL.fileSystemPath after KURLGtk and FileSystemGtk are + // using GIO internally, and providing URIs instead of file paths + url.removeRef(); + url.setQuery(String()); + url.setPort(0); + + // we avoid the escaping for local files, because + // g_filename_from_uri (used internally by GFile) has problems + // decoding strings with arbitrary percent signs + if (url.isLocalFile()) + d->m_gfile = g_file_new_for_path(url.prettyURL().utf8().data() + sizeof("file://") - 1); + else + d->m_gfile = g_file_new_for_uri(url.string().utf8().data()); g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", this); d->m_cancellable = g_cancellable_new(); g_file_query_info_async(d->m_gfile, @@ -615,9 +861,16 @@ bool ResourceHandle::startGio(String urlString) G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, G_PRIORITY_DEFAULT, d->m_cancellable, - queryInfoCallback, NULL); + queryInfoCallback, 0); return true; } +SoupSession* ResourceHandle::defaultSession() +{ + static SoupSession* session = createSoupSession();; + + return session; +} + } diff --git a/WebCore/platform/network/win/CookieJarCFNetWin.cpp b/WebCore/platform/network/win/CookieJarCFNetWin.cpp index 56d7265..7e64813 100644 --- a/WebCore/platform/network/win/CookieJarCFNetWin.cpp +++ b/WebCore/platform/network/win/CookieJarCFNetWin.cpp @@ -41,28 +41,6 @@ namespace WebCore { static const CFStringRef s_setCookieKeyCF = CFSTR("Set-Cookie"); static const CFStringRef s_cookieCF = CFSTR("Cookie"); -typedef Boolean (*IsHTTPOnlyFunction)(CFHTTPCookieRef); - -static HMODULE findCFNetworkModule() -{ - if (HMODULE module = GetModuleHandleA("CFNetwork")) - return module; - return GetModuleHandleA("CFNetwork_debug"); -} - -static IsHTTPOnlyFunction findIsHTTPOnlyFunction() -{ - return reinterpret_cast<IsHTTPOnlyFunction>(GetProcAddress(findCFNetworkModule(), "CFHTTPCookieIsHTTPOnly")); -} - -static bool isHTTPOnly(CFHTTPCookieRef cookie) -{ - // Once we require a newer version of CFNetwork with the CFHTTPCookieIsHTTPOnly function, - // we can change this to be a normal function call and eliminate findIsHTTPOnlyFunction. - static IsHTTPOnlyFunction function = findIsHTTPOnlyFunction(); - return function && function(cookie); -} - static RetainPtr<CFArrayRef> filterCookies(CFArrayRef unfilteredCookies) { CFIndex count = CFArrayGetCount(unfilteredCookies); @@ -77,7 +55,7 @@ static RetainPtr<CFArrayRef> filterCookies(CFArrayRef unfilteredCookies) if (!CFStringGetLength(CFHTTPCookieGetName(cookie))) continue; - if (isHTTPOnly(cookie)) + if (CFHTTPCookieIsHTTPOnly(cookie)) continue; CFArrayAppendValue(filteredCookies.get(), cookie); diff --git a/WebCore/platform/qt/CookieJarQt.cpp b/WebCore/platform/qt/CookieJarQt.cpp index 0d24c7e..4077407 100644 --- a/WebCore/platform/qt/CookieJarQt.cpp +++ b/WebCore/platform/qt/CookieJarQt.cpp @@ -80,7 +80,7 @@ void setCookies(Document* document, const KURL& url, const KURL& policyURL, cons ++it; } #endif - jar->setCookiesFromUrl(cookies, p); + jar->setCookiesFromUrl(cookies, u); #else QCookieJar::cookieJar()->setCookies(u, p, (QString)value); #endif diff --git a/WebCore/platform/qt/FileSystemQt.cpp b/WebCore/platform/qt/FileSystemQt.cpp index 6dbe464..8a272c1 100644 --- a/WebCore/platform/qt/FileSystemQt.cpp +++ b/WebCore/platform/qt/FileSystemQt.cpp @@ -124,7 +124,7 @@ CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) return String(temp->fileName()).utf8(); } handle = invalidPlatformFileHandle; - return 0; + return CString(); } void closeFile(PlatformFileHandle& handle) @@ -143,32 +143,24 @@ int writeToFile(PlatformFileHandle handle, const char* data, int length) return 0; } -#if defined(Q_WS_X11) || defined(Q_WS_QWS) bool unloadModule(PlatformModule module) { +#if defined(Q_WS_MAC) + CFRelease(module); + return true; + +#elif defined(Q_OS_WIN) + return ::FreeLibrary(module); + +#else if (module->unload()) { delete module; return true; } - + return false; -} -#endif - -#if defined(Q_WS_MAC) -bool unloadModule(PlatformModule module) -{ - CFRelease(module); - return true; -} #endif - -#if defined(Q_OS_WIN) -bool unloadModule(PlatformModule module) -{ - return ::FreeLibrary(module); } -#endif } diff --git a/WebCore/platform/qt/KURLQt.cpp b/WebCore/platform/qt/KURLQt.cpp index cdc4f48..0763fe0 100644 --- a/WebCore/platform/qt/KURLQt.cpp +++ b/WebCore/platform/qt/KURLQt.cpp @@ -20,6 +20,7 @@ #include "config.h" #include "KURL.h" #include "CString.h" +#include "TextEncoding.h" #include "NotImplemented.h" #include "qurl.h" @@ -36,7 +37,7 @@ static inline char toHex(char c) KURL::KURL(const QUrl& url) { - *this = KURL(url.toEncoded().constData()); + *this = KURL(KURL(), url.toEncoded().constData(), UTF8Encoding()); } KURL::operator QUrl() const diff --git a/WebCore/platform/qt/PlatformKeyboardEventQt.cpp b/WebCore/platform/qt/PlatformKeyboardEventQt.cpp index 76342ab..88cca5a 100644 --- a/WebCore/platform/qt/PlatformKeyboardEventQt.cpp +++ b/WebCore/platform/qt/PlatformKeyboardEventQt.cpp @@ -192,6 +192,55 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode) case Qt::Key_Alt: return VK_MENU; // (12) ALT key + case Qt::Key_F1: + return VK_F1; + case Qt::Key_F2: + return VK_F2; + case Qt::Key_F3: + return VK_F3; + case Qt::Key_F4: + return VK_F4; + case Qt::Key_F5: + return VK_F5; + case Qt::Key_F6: + return VK_F6; + case Qt::Key_F7: + return VK_F7; + case Qt::Key_F8: + return VK_F8; + case Qt::Key_F9: + return VK_F9; + case Qt::Key_F10: + return VK_F11; + case Qt::Key_F11: + return VK_F11; + case Qt::Key_F12: + return VK_F12; + case Qt::Key_F13: + return VK_F13; + case Qt::Key_F14: + return VK_F14; + case Qt::Key_F15: + return VK_F15; + case Qt::Key_F16: + return VK_F16; + case Qt::Key_F17: + return VK_F17; + case Qt::Key_F18: + return VK_F18; + case Qt::Key_F19: + return VK_F19; + case Qt::Key_F20: + return VK_F20; + case Qt::Key_F21: + return VK_F21; + case Qt::Key_F22: + return VK_F22; + case Qt::Key_F23: + return VK_F23; + case Qt::Key_F24: + return VK_F24; + case Qt::Key_Pause: return VK_PAUSE; // (13) PAUSE key case Qt::Key_CapsLock: diff --git a/WebCore/platform/qt/QWebPopup.cpp b/WebCore/platform/qt/QWebPopup.cpp index d463ddf..4d57c9b 100644 --- a/WebCore/platform/qt/QWebPopup.cpp +++ b/WebCore/platform/qt/QWebPopup.cpp @@ -22,7 +22,9 @@ #include "QWebPopup.h" #include "PopupMenuStyle.h" -#include <QCoreApplication> +#include <QAbstractItemView> +#include <QApplication> +#include <QInputContext> #include <QMouseEvent> namespace WebCore { @@ -54,6 +56,16 @@ void QWebPopup::showPopup() void QWebPopup::hidePopup() { + QWidget* activeFocus = QApplication::focusWidget(); + if (activeFocus && activeFocus == view() + && activeFocus->testAttribute(Qt::WA_InputMethodEnabled)) { + QInputContext* qic = activeFocus->inputContext(); + if (qic) { + qic->reset(); + qic->setFocusWidget(0); + } + } + QComboBox::hidePopup(); if (!m_popupVisible) return; diff --git a/WebCore/platform/qt/RenderThemeQt.cpp b/WebCore/platform/qt/RenderThemeQt.cpp index eee8c86..942e95b 100644 --- a/WebCore/platform/qt/RenderThemeQt.cpp +++ b/WebCore/platform/qt/RenderThemeQt.cpp @@ -796,7 +796,7 @@ private: HTMLMediaElement* RenderThemeQt::getMediaElementFromRenderObject(RenderObject* o) const { - Node* node = o->element(); + Node* node = o->node(); Node* mediaNode = node ? node->shadowAncestorNode() : 0; if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) return 0; @@ -815,7 +815,7 @@ void RenderThemeQt::paintMediaBackground(QPainter* painter, const IntRect& r) co QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const { QColor fgColor = platformActiveSelectionBackgroundColor(); - if (o && o->element()->active()) + if (o && o->node()->active()) fgColor = fgColor.lighter(); return fgColor; } diff --git a/WebCore/platform/qt/RenderThemeQt.h b/WebCore/platform/qt/RenderThemeQt.h index 9a6cf0b..b4a5064 100644 --- a/WebCore/platform/qt/RenderThemeQt.h +++ b/WebCore/platform/qt/RenderThemeQt.h @@ -1,7 +1,7 @@ /* * This file is part of the theme implementation for form controls in WebCore. * - * Copyright (C) 2007 Trolltech + * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public diff --git a/WebCore/platform/qt/WheelEventQt.cpp b/WebCore/platform/qt/WheelEventQt.cpp index cc8acd2..9534f20 100644 --- a/WebCore/platform/qt/WheelEventQt.cpp +++ b/WebCore/platform/qt/WheelEventQt.cpp @@ -21,7 +21,9 @@ #include "PlatformWheelEvent.h" #include "PlatformMouseEvent.h" +#include "Scrollbar.h" +#include <qapplication.h> #include <QWheelEvent> namespace WebCore { @@ -35,11 +37,7 @@ PlatformWheelEvent::PlatformWheelEvent(QWheelEvent* e) #else : m_position(e->pos()) , m_globalPosition(e->globalPos()) -#ifdef QT_MAC_USE_COCOA , m_granularity(ScrollByPixelWheelEvent) -#else - , m_granularity(ScrollByLineWheelEvent) -#endif , m_isAccepted(false) , m_shiftKey(e->modifiers() & Qt::ShiftModifier) , m_ctrlKey(e->modifiers() & Qt::ControlModifier) @@ -53,10 +51,14 @@ PlatformWheelEvent::PlatformWheelEvent(QWheelEvent* e) m_deltaX = 0; m_deltaY = (e->delta() / 120); } + m_wheelTicksX = m_deltaX; + m_wheelTicksY = m_deltaY; - // FIXME: retrieve the user setting for the number of lines to scroll on each wheel event - m_deltaX *= horizontalLineMultiplier(); - m_deltaY *= verticalLineMultiplier(); + // use the same single scroll step as QTextEdit (in + // QTextEditPrivate::init [h,v]bar->setSingleStep ) + static const float cDefaultQtScrollStep = 20.f; + m_deltaX *= QApplication::wheelScrollLines() * cDefaultQtScrollStep; + m_deltaY *= QApplication::wheelScrollLines() * cDefaultQtScrollStep; } #endif // QT_NO_WHEELEVENT diff --git a/WebCore/platform/text/AtomicString.cpp b/WebCore/platform/text/AtomicString.cpp index 5f9abfd..d85f5ee 100644 --- a/WebCore/platform/text/AtomicString.cpp +++ b/WebCore/platform/text/AtomicString.cpp @@ -101,7 +101,7 @@ static inline bool equal(StringImpl* string, const UChar* characters, unsigned l if (string->length() != length) return false; -#if PLATFORM(ARM) +#if PLATFORM(ARM) || PLATFORM(SH4) const UChar* stringCharacters = string->characters(); for (unsigned i = 0; i != length; ++i) { if (*stringCharacters++ != *characters++) diff --git a/WebCore/platform/text/Base64.cpp b/WebCore/platform/text/Base64.cpp index 920fa89..be19164 100644 --- a/WebCore/platform/text/Base64.cpp +++ b/WebCore/platform/text/Base64.cpp @@ -97,8 +97,8 @@ void base64Encode(const Vector<char>& in, Vector<char>& out, bool insertLFs) count += 4; } out[didx++] = base64EncMap[(data[sidx] >> 2) & 077]; - out[didx++] = base64EncMap[(data[sidx + 1] >> 4) & 017 | (data[sidx] << 4) & 077]; - out[didx++] = base64EncMap[(data[sidx + 2] >> 6) & 003 | (data[sidx + 1] << 2) & 077]; + out[didx++] = base64EncMap[((data[sidx + 1] >> 4) & 017) | ((data[sidx] << 4) & 077)]; + out[didx++] = base64EncMap[((data[sidx + 2] >> 6) & 003) | ((data[sidx + 1] << 2) & 077)]; out[didx++] = base64EncMap[data[sidx + 2] & 077]; sidx += 3; } @@ -110,7 +110,7 @@ void base64Encode(const Vector<char>& in, Vector<char>& out, bool insertLFs) out[didx++] = base64EncMap[(data[sidx] >> 2) & 077]; if (sidx < len - 1) { - out[didx++] = base64EncMap[(data[sidx + 1] >> 4) & 017 | (data[sidx] << 4) & 077]; + out[didx++] = base64EncMap[((data[sidx + 1] >> 4) & 017) | ((data[sidx] << 4) & 077)]; out[didx++] = base64EncMap[(data[sidx + 1] << 2) & 077]; } else out[didx++] = base64EncMap[(data[sidx] << 4) & 077]; diff --git a/WebCore/platform/text/BidiResolver.h b/WebCore/platform/text/BidiResolver.h index ffd3d51..8288be4 100644 --- a/WebCore/platform/text/BidiResolver.h +++ b/WebCore/platform/text/BidiResolver.h @@ -254,7 +254,16 @@ template <class Iterator, class Run> void BidiResolver<Iterator, Run>::appendRun() { if (!emptyRun && !eor.atEnd()) { - addRun(new Run(sor.offset(), eor.offset() + 1, context(), m_direction)); + unsigned startOffset = sor.offset(); + unsigned endOffset = eor.offset(); + + if (!endOfLine.atEnd() && endOffset >= endOfLine.offset()) { + reachedEndOfLine = true; + endOffset = endOfLine.offset(); + } + + if (endOffset >= startOffset) + addRun(new Run(startOffset, endOffset + 1, context(), m_direction)); eor.increment(); sor = eor; @@ -352,8 +361,8 @@ void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Dire m_direction = LeftToRight; } } else if (m_status.eor == ArabicNumber - || m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft) - || m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft) { + || (m_status.eor == EuropeanNumber && (m_status.lastStrong != LeftToRight || from == RightToLeft)) + || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && from == RightToLeft)) { appendRun(); m_direction = RightToLeft; } @@ -722,8 +731,8 @@ void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, boo case WhiteSpaceNeutral: case OtherNeutral: if (m_status.eor == ArabicNumber - || m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft) - || m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft) { + || (m_status.eor == EuropeanNumber && (m_status.lastStrong == RightToLeft || context()->dir() == RightToLeft)) + || (m_status.eor != EuropeanNumber && m_status.lastStrong == LeftToRight && context()->dir() == RightToLeft)) { // Terminate the run before the neutrals. appendRun(); // Begin an R run for the neutrals. diff --git a/WebCore/platform/text/CString.cpp b/WebCore/platform/text/CString.cpp index 8e68628..90990f8 100644 --- a/WebCore/platform/text/CString.cpp +++ b/WebCore/platform/text/CString.cpp @@ -47,8 +47,8 @@ void CString::init(const char* str, unsigned length) return; m_buffer = CStringBuffer::create(length + 1); - memcpy(m_buffer->data(), str, length); - m_buffer->data()[length] = '\0'; + memcpy(m_buffer->mutableData(), str, length); + m_buffer->mutableData()[length] = '\0'; } const char* CString::data() const @@ -61,7 +61,7 @@ char* CString::mutableData() copyBufferIfNeeded(); if (!m_buffer) return 0; - return m_buffer->data(); + return m_buffer->mutableData(); } unsigned CString::length() const @@ -73,7 +73,7 @@ CString CString::newUninitialized(size_t length, char*& characterBuffer) { CString result; result.m_buffer = CStringBuffer::create(length + 1); - char* bytes = result.m_buffer->data(); + char* bytes = result.m_buffer->mutableData(); bytes[length] = '\0'; characterBuffer = bytes; return result; @@ -87,7 +87,7 @@ void CString::copyBufferIfNeeded() int len = m_buffer->length(); RefPtr<CStringBuffer> m_temp = m_buffer; m_buffer = CStringBuffer::create(len); - memcpy(m_buffer->data(), m_temp->data(), len); + memcpy(m_buffer->mutableData(), m_temp->data(), len); } bool operator==(const CString& a, const CString& b) @@ -99,17 +99,4 @@ bool operator==(const CString& a, const CString& b) return !strncmp(a.data(), b.data(), min(a.length(), b.length())); } -PassRefPtr<SharedBuffer> CString::releaseBuffer() -{ - if (!m_buffer) - return 0; - - copyBufferIfNeeded(); - - RefPtr<SharedBuffer> result = m_buffer->releaseBuffer(); - m_buffer = 0; - return result.release(); -} - - -} +} // namespace WebCore diff --git a/WebCore/platform/text/CString.h b/WebCore/platform/text/CString.h index 09f112f..f084ddf 100644 --- a/WebCore/platform/text/CString.h +++ b/WebCore/platform/text/CString.h @@ -36,15 +36,15 @@ namespace WebCore { class CStringBuffer : public RefCounted<CStringBuffer> { public: - static PassRefPtr<CStringBuffer> create(unsigned length) { return adoptRef(new CStringBuffer(length)); } - - char* data() { return m_vector.data(); } - size_t length() const { return m_vector.size(); } + const char* data() { return m_vector.data(); } + size_t length() { return m_vector.size(); } - PassRefPtr<SharedBuffer> releaseBuffer() { return SharedBuffer::adoptVector(m_vector); } - private: + friend class CString; + + static PassRefPtr<CStringBuffer> create(unsigned length) { return adoptRef(new CStringBuffer(length)); } CStringBuffer(unsigned length) : m_vector(length) { } + char* mutableData() { return m_vector.data(); } Vector<char> m_vector; }; @@ -56,6 +56,7 @@ namespace WebCore { CString() { } CString(const char*); CString(const char*, unsigned length); + CString(CStringBuffer* buffer) : m_buffer(buffer) { } static CString newUninitialized(size_t length, char*& characterBuffer); const char* data() const; @@ -63,8 +64,8 @@ namespace WebCore { unsigned length() const; bool isNull() const { return !m_buffer; } - - PassRefPtr<SharedBuffer> releaseBuffer(); + + CStringBuffer* buffer() const { return m_buffer.get(); } private: void copyBufferIfNeeded(); diff --git a/WebCore/platform/text/PlatformString.h b/WebCore/platform/text/PlatformString.h index 35d3079..a1541d2 100644 --- a/WebCore/platform/text/PlatformString.h +++ b/WebCore/platform/text/PlatformString.h @@ -27,15 +27,18 @@ #include "StringImpl.h" -#include <wtf/PassRefPtr.h> +#ifdef __OBJC__ +#include <objc/objc.h> +#endif #if USE(JSC) #include <runtime/Identifier.h> #else -// runtime/Identifier.h includes HashMap.h and HashSet.h. We explicitly include -// them in the case of non-JSC builds to keep things consistent. +// runtime/Identifier.h brings in a variety of wtf headers. We explicitly +// include them in the case of non-JSC builds to keep things consistent. #include <wtf/HashMap.h> #include <wtf/HashSet.h> +#include <wtf/OwnPtr.h> #endif #if PLATFORM(CF) || (PLATFORM(QT) && PLATFORM(DARWIN)) @@ -228,6 +231,9 @@ public: static String fromUTF8(const char*, size_t); static String fromUTF8(const char*); + // Tries to convert the passed in string to UTF-8, but will fall back to Latin-1 if the string is not valid UTF-8. + static String fromUTF8WithLatin1Fallback(const char*, size_t); + // Determines the writing direction using the Unicode Bidi Algorithm rules P2 and P3. WTF::Unicode::Direction defaultWritingDirection() const { return m_impl ? m_impl->defaultWritingDirection() : WTF::Unicode::LeftToRight; } diff --git a/WebCore/platform/text/String.cpp b/WebCore/platform/text/String.cpp index 638e45f..733b661 100644 --- a/WebCore/platform/text/String.cpp +++ b/WebCore/platform/text/String.cpp @@ -623,6 +623,15 @@ String String::fromUTF8(const char* string) return UTF8Encoding().decode(string, strlen(string)); } +String String::fromUTF8WithLatin1Fallback(const char* string, size_t size) +{ + String result = fromUTF8(string, size); + if (!result) + result = String(string, size); + + return result; +} + #if USE(JSC) String::String(const Identifier& str) { diff --git a/WebCore/platform/text/StringImpl.cpp b/WebCore/platform/text/StringImpl.cpp index 0556f8e..6bba990 100644 --- a/WebCore/platform/text/StringImpl.cpp +++ b/WebCore/platform/text/StringImpl.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2001 Dirk Mueller ( mueller@kde.org ) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) * * This library is free software; you can redistribute it and/or @@ -54,6 +54,27 @@ static inline void deleteUCharVector(const UChar* p) fastFree(const_cast<UChar*>(p)); } +// Some of the factory methods create buffers using fastMalloc. +// We must ensure that ll allocations of StringImpl are allocated using +// fastMalloc so that we don't have mis-matched frees. We accomplish +// this by overriding the new and delete operators. +void* StringImpl::operator new(size_t size, void* address) +{ + if (address) + return address; // Allocating using an internal buffer + return fastMalloc(size); +} + +void* StringImpl::operator new(size_t size) +{ + return fastMalloc(size); +} + +void StringImpl::operator delete(void* address) +{ + fastFree(address); +} + // This constructor is used only to create the empty string. StringImpl::StringImpl() : m_length(0) @@ -61,6 +82,7 @@ StringImpl::StringImpl() , m_hash(0) , m_inTable(false) , m_hasTerminatingNullCharacter(false) + , m_bufferIsInternal(false) { // Ensure that the hash is computed so that AtomicStringHash can call existingHash() // with impunity. The empty string is special because it is never entered into @@ -76,6 +98,7 @@ inline StringImpl::StringImpl(const UChar* characters, unsigned length) , m_hash(0) , m_inTable(false) , m_hasTerminatingNullCharacter(false) + , m_bufferIsInternal(false) { UChar* data = newUCharVector(length); memcpy(data, characters, length * sizeof(UChar)); @@ -87,6 +110,7 @@ inline StringImpl::StringImpl(const StringImpl& str, WithTerminatingNullCharacte , m_hash(str.m_hash) , m_inTable(false) , m_hasTerminatingNullCharacter(true) + , m_bufferIsInternal(false) { UChar* data = newUCharVector(str.m_length + 1); memcpy(data, str.m_data, str.m_length * sizeof(UChar)); @@ -99,6 +123,7 @@ inline StringImpl::StringImpl(const char* characters, unsigned length) , m_hash(0) , m_inTable(false) , m_hasTerminatingNullCharacter(false) + , m_bufferIsInternal(false) { ASSERT(characters); ASSERT(length); @@ -117,6 +142,7 @@ inline StringImpl::StringImpl(UChar* characters, unsigned length, AdoptBuffer) , m_hash(0) , m_inTable(false) , m_hasTerminatingNullCharacter(false) + , m_bufferIsInternal(false) { ASSERT(characters); ASSERT(length); @@ -128,6 +154,7 @@ StringImpl::StringImpl(const UChar* characters, unsigned length, unsigned hash) , m_hash(hash) , m_inTable(true) , m_hasTerminatingNullCharacter(false) + , m_bufferIsInternal(false) { ASSERT(hash); ASSERT(characters); @@ -144,6 +171,7 @@ StringImpl::StringImpl(const char* characters, unsigned length, unsigned hash) , m_hash(hash) , m_inTable(true) , m_hasTerminatingNullCharacter(false) + , m_bufferIsInternal(false) { ASSERT(hash); ASSERT(characters); @@ -161,7 +189,8 @@ StringImpl::~StringImpl() { if (m_inTable) AtomicString::remove(this); - deleteUCharVector(m_data); + if (!m_bufferIsInternal) + deleteUCharVector(m_data); } StringImpl* StringImpl::empty() @@ -907,26 +936,8 @@ WTF::Unicode::Direction StringImpl::defaultWritingDirection() } // This is a hot function because it's used when parsing HTML. -PassRefPtr<StringImpl> StringImpl::createStrippingNullCharacters(const UChar* characters, unsigned length) +PassRefPtr<StringImpl> StringImpl::createStrippingNullCharactersSlowCase(const UChar* characters, unsigned length) { - ASSERT(characters); - ASSERT(length); - - // Optimize for the case where there are no Null characters by quickly - // searching for nulls, and then using StringImpl::create, which will - // memcpy the whole buffer. This is faster than assigning character by - // character during the loop. - - // Fast case. - int foundNull = 0; - for (unsigned i = 0; !foundNull && i < length; i++) { - int c = characters[i]; // more efficient than using UChar here (at least on Intel Mac OS) - foundNull |= !c; - } - if (!foundNull) - return StringImpl::create(characters, length); - - // Slow case. StringBuffer strippedCopy(length); unsigned strippedLength = 0; for (unsigned i = 0; i < length; i++) { @@ -958,24 +969,44 @@ PassRefPtr<StringImpl> StringImpl::create(const UChar* characters, unsigned leng { if (!characters || !length) return empty(); - return adoptRef(new StringImpl(characters, length)); + + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + size_t size = sizeof(StringImpl) + length * sizeof(UChar); + char* buffer = static_cast<char*>(fastMalloc(size)); + UChar* data = reinterpret_cast<UChar*>(buffer + sizeof(StringImpl)); + memcpy(data, characters, length * sizeof(UChar)); + StringImpl* string = new (buffer) StringImpl(data, length, AdoptBuffer()); + string->m_bufferIsInternal = true; + return adoptRef(string); } PassRefPtr<StringImpl> StringImpl::create(const char* characters, unsigned length) { if (!characters || !length) return empty(); - return adoptRef(new StringImpl(characters, length)); + + // Allocate a single buffer large enough to contain the StringImpl + // struct as well as the data which it contains. This removes one + // heap allocation from this call. + size_t size = sizeof(StringImpl) + length * sizeof(UChar); + char* buffer = static_cast<char*>(fastMalloc(size)); + UChar* data = reinterpret_cast<UChar*>(buffer + sizeof(StringImpl)); + for (unsigned i = 0; i != length; ++i) { + unsigned char c = characters[i]; + data[i] = c; + } + StringImpl* string = new (buffer) StringImpl(data, length, AdoptBuffer()); + string->m_bufferIsInternal = true; + return adoptRef(string); } PassRefPtr<StringImpl> StringImpl::create(const char* string) { if (!string) return empty(); - unsigned length = strlen(string); - if (!length) - return empty(); - return adoptRef(new StringImpl(string, length)); + return create(string, strlen(string)); } PassRefPtr<StringImpl> StringImpl::createWithTerminatingNullCharacter(const StringImpl& string) @@ -985,7 +1016,7 @@ PassRefPtr<StringImpl> StringImpl::createWithTerminatingNullCharacter(const Stri PassRefPtr<StringImpl> StringImpl::copy() { - return adoptRef(new StringImpl(m_data, m_length)); + return create(m_data, m_length); } } // namespace WebCore diff --git a/WebCore/platform/text/StringImpl.h b/WebCore/platform/text/StringImpl.h index 281aa37..1242f27 100644 --- a/WebCore/platform/text/StringImpl.h +++ b/WebCore/platform/text/StringImpl.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,7 +24,7 @@ #include <limits.h> #include <wtf/ASCIICType.h> -#include <wtf/Forward.h> +#include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> #include <wtf/Vector.h> #include <wtf/unicode/Unicode.h> @@ -166,12 +166,25 @@ public: operator NSString*(); #endif + void operator delete(void*); + private: + // Allocation from a custom buffer is only allowed internally to avoid + // mismatched allocators. Callers should use create(). + void* operator new(size_t size); + void* operator new(size_t size, void* address); + + static PassRefPtr<StringImpl> createStrippingNullCharactersSlowCase(const UChar*, unsigned length); + unsigned m_length; const UChar* m_data; mutable unsigned m_hash; bool m_inTable; bool m_hasTerminatingNullCharacter; + // In some cases, we allocate the StringImpl struct and its data + // within a single heap buffer. In this case, the m_data pointer + // is an "internal buffer", and does not need to be deallocated. + bool m_bufferIsInternal; }; bool equal(StringImpl*, StringImpl*); @@ -274,6 +287,29 @@ static inline bool isSpaceOrNewline(UChar c) return c <= 0x7F ? WTF::isASCIISpace(c) : WTF::Unicode::direction(c) == WTF::Unicode::WhiteSpaceNeutral; } +// This is a hot function because it's used when parsing HTML. +inline PassRefPtr<StringImpl> StringImpl::createStrippingNullCharacters(const UChar* characters, unsigned length) +{ + ASSERT(characters); + ASSERT(length); + + // Optimize for the case where there are no Null characters by quickly + // searching for nulls, and then using StringImpl::create, which will + // memcpy the whole buffer. This is faster than assigning character by + // character during the loop. + + // Fast case. + int foundNull = 0; + for (unsigned i = 0; !foundNull && i < length; i++) { + int c = characters[i]; // more efficient than using UChar here (at least on Intel Mac OS) + foundNull |= !c; + } + if (!foundNull) + return StringImpl::create(characters, length); + + return StringImpl::createStrippingNullCharactersSlowCase(characters, length); +} + } namespace WTF { diff --git a/WebCore/platform/text/TextBreakIterator.h b/WebCore/platform/text/TextBreakIterator.h index 64717a4..7b3b963 100644 --- a/WebCore/platform/text/TextBreakIterator.h +++ b/WebCore/platform/text/TextBreakIterator.h @@ -29,7 +29,19 @@ namespace WebCore { class TextBreakIterator; // Note: The returned iterator is good only until you get another iterator. + + // Iterates over "extended grapheme clusters", as defined in UAX #29. + // Note that platform implementations may be less sophisticated - e.g. ICU prior to + // version 4.0 only supports "legacy grapheme clusters". + // Use this for general text processing, e.g. string truncation. TextBreakIterator* characterBreakIterator(const UChar*, int length); + + // This is similar to character break iterator in most cases, but is subject to + // platform UI conventions. One notable example where this can be different + // from character break iterator is Thai prepend characters, see bug 24342. + // Use this for insertion point and selection manipulations. + TextBreakIterator* cursorMovementIterator(const UChar*, int length); + TextBreakIterator* wordBreakIterator(const UChar*, int length); TextBreakIterator* lineBreakIterator(const UChar*, int length); TextBreakIterator* sentenceBreakIterator(const UChar*, int length); diff --git a/WebCore/platform/text/TextBreakIteratorICU.cpp b/WebCore/platform/text/TextBreakIteratorICU.cpp index 9941f58..c4fc1b0 100644 --- a/WebCore/platform/text/TextBreakIteratorICU.cpp +++ b/WebCore/platform/text/TextBreakIteratorICU.cpp @@ -22,6 +22,7 @@ #include "config.h" #include "TextBreakIterator.h" +#include "PlatformString.h" #include "TextBreakIteratorInternalICU.h" #include <unicode/ubrk.h> @@ -114,4 +115,119 @@ bool isTextBreak(TextBreakIterator* bi, int pos) return ubrk_isBoundary(bi, pos); } +#ifndef BUILDING_ON_TIGER +static TextBreakIterator* setUpIteratorWithRules(bool& createdIterator, TextBreakIterator*& iterator, + const char* breakRules, const UChar* string, int length) +{ + if (!string) + return 0; + + if (!createdIterator) { + UParseError parseStatus; + UErrorCode openStatus = U_ZERO_ERROR; + String rules(breakRules); + iterator = static_cast<TextBreakIterator*>(ubrk_openRules(rules.characters(), rules.length(), 0, 0, &parseStatus, &openStatus)); + createdIterator = true; + ASSERT_WITH_MESSAGE(U_SUCCESS(openStatus), "ICU could not open a break iterator: %s (%d)", u_errorName(openStatus), openStatus); + } + if (!iterator) + return 0; + + UErrorCode setTextStatus = U_ZERO_ERROR; + ubrk_setText(iterator, string, length, &setTextStatus); + if (U_FAILURE(setTextStatus)) + return 0; + + return iterator; +} +#endif // BUILDING_ON_TIGER + +TextBreakIterator* cursorMovementIterator(const UChar* string, int length) +{ +#ifdef BUILDING_ON_TIGER + // ICU 3.2 cannot compile the below rules. + return characterBreakIterator(string, length); +#else + // This rule set is based on character-break iterator rules of ICU 4.0 + // <http://source.icu-project.org/repos/icu/icu/tags/release-4-0/source/data/brkitr/char.txt>. + // The major differences from the original ones are listed below: + // * Replaced '[\p{Grapheme_Cluster_Break = SpacingMark}]' with '[\p{General_Category = Spacing Mark} - $Extend]' for ICU 3.8 or earlier; + // * Removed rules that prevent a cursor from moving after prepend characters (Bug 24342); + // * Added rules that prevent a cursor from moving after virama signs of Indic languages except Tamil (Bug 15790), and; + // * Added rules that prevent a cursor from moving before Japanese half-width katakara voiced marks. + static const char* kRules = + "$CR = [\\p{Grapheme_Cluster_Break = CR}];" + "$LF = [\\p{Grapheme_Cluster_Break = LF}];" + "$Control = [\\p{Grapheme_Cluster_Break = Control}];" + "$VoiceMarks = [\\uFF9E\\uFF9F];" // Japanese half-width katakana voiced marks + "$Extend = [\\p{Grapheme_Cluster_Break = Extend} $VoiceMarks];" + "$SpacingMark = [[\\p{General_Category = Spacing Mark}] - $Extend];" + "$L = [\\p{Grapheme_Cluster_Break = L}];" + "$V = [\\p{Grapheme_Cluster_Break = V}];" + "$T = [\\p{Grapheme_Cluster_Break = T}];" + "$LV = [\\p{Grapheme_Cluster_Break = LV}];" + "$LVT = [\\p{Grapheme_Cluster_Break = LVT}];" + "$Hin0 = [\\u0905-\\u0939];" // Devanagari Letter A,...,Ha + "$HinV = \\u094D;" // Devanagari Sign Virama + "$Hin1 = [\\u0915-\\u0939];" // Devanagari Letter Ka,...,Ha + "$Ben0 = [\\u0985-\\u09B9];" // Bengali Letter A,...,Ha + "$BenV = \\u09CD;" // Bengali Sign Virama + "$Ben1 = [\\u0995-\\u09B9];" // Bengali Letter Ka,...,Ha + "$Pan0 = [\\u0A05-\\u0A39];" // Gurmukhi Letter A,...,Ha + "$PanV = \\u0A4D;" // Gurmukhi Sign Virama + "$Pan1 = [\\u0A15-\\u0A39];" // Gurmukhi Letter Ka,...,Ha + "$Guj0 = [\\u0A85-\\u0AB9];" // Gujarati Letter A,...,Ha + "$GujV = \\u0ACD;" // Gujarati Sign Virama + "$Guj1 = [\\u0A95-\\u0AB9];" // Gujarati Letter Ka,...,Ha + "$Ori0 = [\\u0B05-\\u0B39];" // Oriya Letter A,...,Ha + "$OriV = \\u0B4D;" // Oriya Sign Virama + "$Ori1 = [\\u0B15-\\u0B39];" // Oriya Letter Ka,...,Ha + "$Tel0 = [\\u0C05-\\u0C39];" // Telugu Letter A,...,Ha + "$TelV = \\u0C4D;" // Telugu Sign Virama + "$Tel1 = [\\u0C14-\\u0C39];" // Telugu Letter Ka,...,Ha + "$Kan0 = [\\u0C85-\\u0CB9];" // Kannada Letter A,...,Ha + "$KanV = \\u0CCD;" // Kannada Sign Virama + "$Kan1 = [\\u0C95-\\u0CB9];" // Kannada Letter A,...,Ha + "$Mal0 = [\\u0D05-\\u0D39];" // Malayalam Letter A,...,Ha + "$MalV = \\u0D4D;" // Malayalam Sign Virama + "$Mal1 = [\\u0D15-\\u0D39];" // Malayalam Letter A,...,Ha + "!!chain;" + "!!forward;" + "$CR $LF;" + "$L ($L | $V | $LV | $LVT);" + "($LV | $V) ($V | $T);" + "($LVT | $T) $T;" + "[^$Control $CR $LF] $Extend;" + "[^$Control $CR $LF] $SpacingMark;" + "$Hin0 $HinV $Hin1;" // Devanagari Virama (forward) + "$Ben0 $BenV $Ben1;" // Bengali Virama (forward) + "$Pan0 $PanV $Pan1;" // Gurmukhi Virama (forward) + "$Guj0 $GujV $Guj1;" // Gujarati Virama (forward) + "$Ori0 $OriV $Ori1;" // Oriya Virama (forward) + "$Tel0 $TelV $Tel1;" // Telugu Virama (forward) + "$Kan0 $KanV $Kan1;" // Kannada Virama (forward) + "$Mal0 $MalV $Mal1;" // Malayalam Virama (forward) + "!!reverse;" + "$LF $CR;" + "($L | $V | $LV | $LVT) $L;" + "($V | $T) ($LV | $V);" + "$T ($LVT | $T);" + "$Extend [^$Control $CR $LF];" + "$SpacingMark [^$Control $CR $LF];" + "$Hin1 $HinV $Hin0;" // Devanagari Virama (backward) + "$Ben1 $BenV $Ben0;" // Bengali Virama (backward) + "$Pan1 $PanV $Pan0;" // Gurmukhi Virama (backward) + "$Guj1 $GujV $Guj0;" // Gujarati Virama (backward) + "$Ori1 $OriV $Ori0;" // Gujarati Virama (backward) + "$Tel1 $TelV $Tel0;" // Telugu Virama (backward) + "$Kan1 $KanV $Kan0;" // Kannada Virama (backward) + "$Mal1 $MalV $Mal0;" // Malayalam Virama (backward) + "!!safe_reverse;" + "!!safe_forward;"; + static bool createdCursorMovementIterator = false; + static TextBreakIterator* staticCursorMovementIterator; + return setUpIteratorWithRules(createdCursorMovementIterator, staticCursorMovementIterator, kRules, string, length); +#endif // BUILDING_ON_TIGER +} + } diff --git a/WebCore/platform/text/TextCodecICU.cpp b/WebCore/platform/text/TextCodecICU.cpp index 72d45ad..72054fa 100644 --- a/WebCore/platform/text/TextCodecICU.cpp +++ b/WebCore/platform/text/TextCodecICU.cpp @@ -334,7 +334,7 @@ String TextCodecICU::decode(const char* bytes, size_t length, bool flush, bool s // <http://bugs.webkit.org/show_bug.cgi?id=17014> // Simplified Chinese pages use the code A3A0 to mean "full-width space", but ICU decodes it as U+E5E5. - if (m_encoding == "GBK" || m_encoding == "gb18030") + if (strcmp(m_encoding.name(), "GBK") == 0 || strcasecmp(m_encoding.name(), "gb18030") == 0) resultString.replace(0xE5E5, ideographicSpace); return resultString; diff --git a/WebCore/platform/text/TextDecoder.cpp b/WebCore/platform/text/TextDecoder.cpp deleted file mode 100644 index e39a6b7..0000000 --- a/WebCore/platform/text/TextDecoder.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "TextDecoder.h" - -#include "TextEncodingRegistry.h" - -// FIXME: Would be nice to also handle BOM for UTF-7 and UTF-32. - -namespace WebCore { - -TextDecoder::TextDecoder(const TextEncoding& encoding) - : m_encoding(encoding) - , m_checkedForBOM(false) - , m_numBufferedBytes(0) -{ -} - -void TextDecoder::reset(const TextEncoding& encoding) -{ - m_encoding = encoding; - m_codec.clear(); - m_checkedForBOM = false; - m_numBufferedBytes = 0; -} - -String TextDecoder::checkForBOM(const char* data, size_t length, bool flush, bool stopOnError, bool& sawError) -{ - ASSERT(!m_checkedForBOM); - - // Check to see if we found a BOM. - size_t numBufferedBytes = m_numBufferedBytes; - size_t buf1Len = numBufferedBytes; - size_t buf2Len = length; - const unsigned char* buf1 = m_bufferedBytes; - const unsigned char* buf2 = reinterpret_cast<const unsigned char*>(data); - unsigned char c1 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0; - unsigned char c2 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0; - unsigned char c3 = buf1Len ? (--buf1Len, *buf1++) : buf2Len ? (--buf2Len, *buf2++) : 0; - unsigned char c4 = buf2Len ? (--buf2Len, *buf2++) : 0; - - const TextEncoding* encodingConsideringBOM = &m_encoding; - bool foundBOM = true; - size_t lengthOfBOM = 0; - if (c1 == 0xFF && c2 == 0xFE) { - if (c3 != 0 || c4 != 0) { - encodingConsideringBOM = &UTF16LittleEndianEncoding(); - lengthOfBOM = 2; - } else if (numBufferedBytes + length > sizeof(m_bufferedBytes)) { - encodingConsideringBOM = &UTF32LittleEndianEncoding(); - lengthOfBOM = 4; - } else - foundBOM = false; - } else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { - encodingConsideringBOM = &UTF8Encoding(); - lengthOfBOM = 3; - } else if (c1 == 0xFE && c2 == 0xFF) { - encodingConsideringBOM = &UTF16BigEndianEncoding(); - lengthOfBOM = 2; - } else if (c1 == 0 && c2 == 0 && c3 == 0xFE && c4 == 0xFF) { - encodingConsideringBOM = &UTF32BigEndianEncoding(); - lengthOfBOM = 4; - } else - foundBOM = false; - - if (!foundBOM && numBufferedBytes + length <= sizeof(m_bufferedBytes) && !flush) { - // Continue to look for the BOM. - memcpy(&m_bufferedBytes[numBufferedBytes], data, length); - m_numBufferedBytes += length; - return ""; - } - - // Done checking for BOM. - m_codec.set(newTextCodec(*encodingConsideringBOM).release()); - if (!m_codec) - return String(); - m_checkedForBOM = true; - - // Skip the BOM. - if (foundBOM) { - ASSERT(numBufferedBytes < lengthOfBOM); - size_t numUnbufferedBOMBytes = lengthOfBOM - numBufferedBytes; - ASSERT(numUnbufferedBOMBytes <= length); - - data += numUnbufferedBOMBytes; - length -= numUnbufferedBOMBytes; - numBufferedBytes = 0; - m_numBufferedBytes = 0; - } - - // Handle case where we have some buffered bytes to deal with. - if (numBufferedBytes) { - char bufferedBytes[sizeof(m_bufferedBytes)]; - memcpy(bufferedBytes, m_bufferedBytes, numBufferedBytes); - m_numBufferedBytes = 0; - - String bufferedResult = m_codec->decode(bufferedBytes, numBufferedBytes, false, stopOnError, sawError); - if (stopOnError && sawError) - return bufferedResult; - return bufferedResult + m_codec->decode(data, length, flush, stopOnError, sawError); - } - - return m_codec->decode(data, length, flush, stopOnError, sawError); -} - -} // namespace WebCore diff --git a/WebCore/platform/text/TextEncoding.cpp b/WebCore/platform/text/TextEncoding.cpp index 063d96b..ed58412 100644 --- a/WebCore/platform/text/TextEncoding.cpp +++ b/WebCore/platform/text/TextEncoding.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2004, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> * * Redistribution and use in source and binary forms, with or without @@ -30,7 +30,6 @@ #include "CString.h" #include "PlatformString.h" #include "TextCodec.h" -#include "TextDecoder.h" #include "TextEncodingRegistry.h" #if USE(ICU_UNICODE) #include <unicode/unorm.h> @@ -73,7 +72,7 @@ String TextEncoding::decode(const char* data, size_t length, bool stopOnError, b if (!m_name) return String(); - return TextDecoder(*this).decode(data, length, true, stopOnError, sawError); + return newTextCodec(*this)->decode(data, length, true, stopOnError, sawError); } CString TextEncoding::encode(const UChar* characters, size_t length, UnencodableHandling handling) const @@ -165,10 +164,23 @@ UChar TextEncoding::backslashAsCurrencySymbol() const bool TextEncoding::isNonByteBasedEncoding() const { + if (noExtendedTextEncodingNameUsed()) { + return *this == UTF16LittleEndianEncoding() + || *this == UTF16BigEndianEncoding(); + } + return *this == UTF16LittleEndianEncoding() - || *this == UTF16BigEndianEncoding() - || *this == UTF32BigEndianEncoding() - || *this == UTF32LittleEndianEncoding(); + || *this == UTF16BigEndianEncoding() + || *this == UTF32BigEndianEncoding() + || *this == UTF32LittleEndianEncoding(); +} + +bool TextEncoding::isUTF7Encoding() const +{ + if (noExtendedTextEncodingNameUsed()) + return false; + + return *this == UTF7Encoding(); } const TextEncoding& TextEncoding::closestByteBasedEquivalent() const @@ -185,7 +197,7 @@ const TextEncoding& TextEncoding::closestByteBasedEquivalent() const // but it's fraught with problems and we'd rather steer clear of it. const TextEncoding& TextEncoding::encodingForFormSubmission() const { - if (isNonByteBasedEncoding() || *this == UTF7Encoding()) + if (isNonByteBasedEncoding() || isUTF7Encoding()) return UTF8Encoding(); return *this; } diff --git a/WebCore/platform/text/TextEncoding.h b/WebCore/platform/text/TextEncoding.h index b2bb816..b3909f7 100644 --- a/WebCore/platform/text/TextEncoding.h +++ b/WebCore/platform/text/TextEncoding.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 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 @@ -45,12 +45,14 @@ namespace WebCore { bool usesVisualOrdering() const; bool isJapanese() const; - PassRefPtr<StringImpl> displayString(PassRefPtr<StringImpl> str) const { + PassRefPtr<StringImpl> displayString(PassRefPtr<StringImpl> str) const + { if (m_backslashAsCurrencySymbol == '\\' || !str) return str; return str->replace('\\', m_backslashAsCurrencySymbol); } - void displayBuffer(UChar* characters, unsigned len) const { + void displayBuffer(UChar* characters, unsigned len) const + { if (m_backslashAsCurrencySymbol == '\\') return; for (unsigned i = 0; i < len; ++i) { @@ -72,10 +74,11 @@ namespace WebCore { private: UChar backslashAsCurrencySymbol() const; + bool isNonByteBasedEncoding() const; + bool isUTF7Encoding() const; const char* m_name; UChar m_backslashAsCurrencySymbol; - bool isNonByteBasedEncoding() const; }; inline bool operator==(const TextEncoding& a, const TextEncoding& b) { return a.name() == b.name(); } diff --git a/WebCore/platform/text/TextEncodingDetector.h b/WebCore/platform/text/TextEncodingDetector.h new file mode 100644 index 0000000..9f16ab0 --- /dev/null +++ b/WebCore/platform/text/TextEncodingDetector.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextEncodingDetector_h +#define TextEncodingDetector_h + +namespace WebCore { + + class TextEncoding; + + // Given a sequence of bytes in |data| of length |len| and an optional + // hintEncodingName, detect the most likely character encoding. + // The way hintEncodingName is used is up to an implementation. + // Currently, the only caller sets it to the parent frame encoding. + bool detectTextEncoding(const char* data, size_t len, + const char* hintEncodingName, + TextEncoding* detectedEncoding); + +} // namespace WebCore + +#endif diff --git a/WebCore/platform/text/TextEncodingDetectorICU.cpp b/WebCore/platform/text/TextEncodingDetectorICU.cpp new file mode 100644 index 0000000..26c997e --- /dev/null +++ b/WebCore/platform/text/TextEncodingDetectorICU.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2008, 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextEncodingDetector.h" + +#include "TextEncoding.h" +#include "UnusedParam.h" + +#ifndef BUILDING_ON_TIGER +#include "unicode/ucnv.h" +#include "unicode/ucsdet.h" +#endif + +namespace WebCore { + +bool detectTextEncoding(const char* data, size_t len, + const char* hintEncodingName, + TextEncoding* detectedEncoding) +{ + *detectedEncoding = TextEncoding(); +#ifdef BUILDING_ON_TIGER + // Tiger came with ICU 3.2 and does not have the encoding detector. + UNUSED_PARAM(data); + UNUSED_PARAM(len); + UNUSED_PARAM(hintEncodingName); + return false; +#else + int matchesCount = 0; + UErrorCode status = U_ZERO_ERROR; + UCharsetDetector* detector = ucsdet_open(&status); + if (U_FAILURE(status)) + return false; + ucsdet_enableInputFilter(detector, true); + ucsdet_setText(detector, data, static_cast<int32_t>(len), &status); + if (U_FAILURE(status)) + return false; + + // FIXME: A few things we can do other than improving + // the ICU detector itself. + // 1. Use ucsdet_detectAll and pick the most likely one given + // "the context" (parent-encoding, referrer encoding, etc). + // 2. 'Emulate' Firefox/IE's non-Universal detectors (e.g. + // Chinese, Japanese, Russian, Korean and Hebrew) by picking the + // encoding with a highest confidence among the detetctor-specific + // limited set of candidate encodings. + // Below is a partial implementation of the first part of what's outlined + // above. + const UCharsetMatch** matches = ucsdet_detectAll(detector, &matchesCount, &status); + if (U_FAILURE(status)) { + ucsdet_close(detector); + return false; + } + + const char* encoding = 0; + if (hintEncodingName) { + TextEncoding hintEncoding(hintEncodingName); + // 10 is the minimum confidence value consistent with the codepoint + // allocation in a given encoding. The size of a chunk passed to + // us varies even for the same html file (apparently depending on + // the network load). When we're given a rather short chunk, we + // don't have a sufficiently reliable signal other than the fact that + // the chunk is consistent with a set of encodings. So, instead of + // setting an arbitrary threshold, we have to scan all the encodings + // consistent with the data. + const int32_t kThresold = 10; + for (int i = 0; i < matchesCount; ++i) { + int32_t confidence = ucsdet_getConfidence(matches[i], &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + continue; + } + if (confidence < kThresold) + break; + const char* matchEncoding = ucsdet_getName(matches[i], &status); + if (U_FAILURE(status)) { + status = U_ZERO_ERROR; + continue; + } + if (TextEncoding(matchEncoding) == hintEncoding) { + encoding = hintEncodingName; + break; + } + } + } + // If no match is found so far, just pick the top match. + // This can happen, say, when a parent frame in EUC-JP refers to + // a child frame in Shift_JIS and both frames do NOT specify the encoding + // making us resort to auto-detection (when it IS turned on). + if (!encoding && matchesCount > 0) + encoding = ucsdet_getName(matches[0], &status); + if (U_SUCCESS(status)) { + *detectedEncoding = TextEncoding(encoding); + ucsdet_close(detector); + return true; + } + ucsdet_close(detector); + return false; +#endif +} + +} diff --git a/WebCore/platform/text/TextEncodingDetectorNone.cpp b/WebCore/platform/text/TextEncodingDetectorNone.cpp new file mode 100644 index 0000000..2655f08 --- /dev/null +++ b/WebCore/platform/text/TextEncodingDetectorNone.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextEncodingDetector.h" + +#include "TextEncoding.h" +#include "UnusedParam.h" + +namespace WebCore { + +bool detectTextEncoding(const char* data, size_t len, + const char* hintEncodingName, + TextEncoding* detectedEncoding) +{ + UNUSED_PARAM(data) + UNUSED_PARAM(len) + UNUSED_PARAM(hintEncodingName) + + *detectedEncoding = TextEncoding(); + return false; +} + +} diff --git a/WebCore/platform/text/TextEncodingRegistry.h b/WebCore/platform/text/TextEncodingRegistry.h index 5ca2039..d204734 100644 --- a/WebCore/platform/text/TextEncodingRegistry.h +++ b/WebCore/platform/text/TextEncodingRegistry.h @@ -34,11 +34,8 @@ namespace WebCore { class TextCodec; class TextEncoding; - // Only TextEncoding and TextDecoder should use this function directly. - // - Use TextDecoder::decode to decode, since it handles BOMs. - // - Use TextEncoding::decode to decode if you have all the data at once. - // It's implemented by calling TextDecoder::decode so works just as well. - // - Use TextEncoding::encode to encode, since it takes care of normalization. + // Use TextResourceDecoder::decode to decode resources, since it handles BOMs. + // Use TextEncoding::encode to encode, since it takes care of normalization. std::auto_ptr<TextCodec> newTextCodec(const TextEncoding&); // Only TextEncoding should use this function directly. diff --git a/WebCore/platform/android/TextBreakIteratorInternalICU.cpp b/WebCore/platform/text/android/TextBreakIteratorInternalICU.cpp index 9bebe74..9bebe74 100644 --- a/WebCore/platform/android/TextBreakIteratorInternalICU.cpp +++ b/WebCore/platform/text/android/TextBreakIteratorInternalICU.cpp diff --git a/WebCore/platform/text/cf/StringImplCF.cpp b/WebCore/platform/text/cf/StringImplCF.cpp index ff595a5..8a2ae79 100644 --- a/WebCore/platform/text/cf/StringImplCF.cpp +++ b/WebCore/platform/text/cf/StringImplCF.cpp @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2006 Apple Computer, Inc. +/* + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,14 +24,139 @@ #if PLATFORM(CF) || (PLATFORM(QT) && PLATFORM(DARWIN)) #include <CoreFoundation/CoreFoundation.h> +#include <wtf/MainThread.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Threading.h> + +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) +#include <objc/objc-auto.h> +#endif namespace WebCore { +namespace StringWrapperCFAllocator { + + static StringImpl* currentString; + + static const void* retain(const void* info) + { + return info; + } + + static void release(const void*) + { + ASSERT_NOT_REACHED(); + } + + static CFStringRef copyDescription(const void*) + { + return CFSTR("WebCore::String-based allocator"); + } + + static void* allocate(CFIndex size, CFOptionFlags, void*) + { + StringImpl* underlyingString = 0; + if (isMainThread()) { + underlyingString = currentString; + if (underlyingString) { + currentString = 0; + underlyingString->ref(); // Balanced by call to deref in deallocate below. + } + } + StringImpl** header = static_cast<StringImpl**>(fastMalloc(sizeof(StringImpl*) + size)); + *header = underlyingString; + return header + 1; + } + + static void* reallocate(void* pointer, CFIndex newSize, CFOptionFlags, void*) + { + size_t newAllocationSize = sizeof(StringImpl*) + newSize; + StringImpl** header = static_cast<StringImpl**>(pointer) - 1; + ASSERT(!*header); + header = static_cast<StringImpl**>(fastRealloc(header, newAllocationSize)); + return header + 1; + } + + static void deallocateOnMainThread(void* headerPointer) + { + StringImpl** header = static_cast<StringImpl**>(headerPointer); + StringImpl* underlyingString = *header; + ASSERT(underlyingString); + underlyingString->deref(); // Balanced by call to ref in allocate above. + fastFree(header); + } + + static void deallocate(void* pointer, void*) + { + StringImpl** header = static_cast<StringImpl**>(pointer) - 1; + StringImpl* underlyingString = *header; + if (!underlyingString) + fastFree(header); + else { + if (!isMainThread()) + callOnMainThread(deallocateOnMainThread, header); + else { + underlyingString->deref(); // Balanced by call to ref in allocate above. + fastFree(header); + } + } + } + + static CFIndex preferredSize(CFIndex size, CFOptionFlags, void*) + { + // FIXME: If FastMalloc provided a "good size" callback, we'd want to use it here. + // Note that this optimization would help performance for strings created with the + // allocator that are mutable, and those typically are only created by callers who + // make a new string using the old string's allocator, such as some of the call + // sites in CFURL. + return size; + } + + static CFAllocatorRef create() + { +#if PLATFORM(MAC) && !defined(BUILDING_ON_TIGER) + // Since garbage collection isn't compatible with custom allocators, don't use this at all when garbage collection is active. + if (objc_collectingEnabled()) + return 0; +#endif + CFAllocatorContext context = { 0, 0, retain, release, copyDescription, allocate, reallocate, deallocate, preferredSize }; + return CFAllocatorCreate(0, &context); + } + + static CFAllocatorRef allocator() + { + static CFAllocatorRef allocator = create(); + return allocator; + } + +} + CFStringRef StringImpl::createCFString() { - return CFStringCreateWithCharacters(NULL, reinterpret_cast<const UniChar*>(m_data), m_length); + CFAllocatorRef allocator = (m_length && isMainThread()) ? StringWrapperCFAllocator::allocator() : 0; + if (!allocator) + return CFStringCreateWithCharacters(0, reinterpret_cast<const UniChar*>(m_data), m_length); + + // Put pointer to the StringImpl in a global so the allocator can store it with the CFString. + ASSERT(!StringWrapperCFAllocator::currentString); + StringWrapperCFAllocator::currentString = this; + + CFStringRef string = CFStringCreateWithCharactersNoCopy(allocator, reinterpret_cast<const UniChar*>(m_data), m_length, kCFAllocatorNull); + + // The allocator cleared the global when it read it, but also clear it here just in case. + ASSERT(!StringWrapperCFAllocator::currentString); + StringWrapperCFAllocator::currentString = 0; + + return string; } +// On StringImpl creation we could check if the allocator is the StringWrapperCFAllocator. +// If it is, then we could find the original StringImpl and just return that. But to +// do that we'd have to compute the offset from CFStringRef to the allocated block; +// the CFStringRef is *not* at the start of an allocated block. Testing shows 1000x +// more calls to createCFString than calls to the create functions with the appropriate +// allocator, so it's probably not urgent optimize that case. + } #endif // PLATFORM(CF) || (PLATFORM(QT) && PLATFORM(DARWIN)) diff --git a/WebCore/platform/text/mac/ShapeArabic.c b/WebCore/platform/text/mac/ShapeArabic.c index 1e0d91b..dd61ce5 100644 --- a/WebCore/platform/text/mac/ShapeArabic.c +++ b/WebCore/platform/text/mac/ShapeArabic.c @@ -36,6 +36,8 @@ #include "ShapeArabic.h" +#include <stdbool.h> +#include <string.h> #include <unicode/utypes.h> #include <unicode/uchar.h> #include <unicode/ustring.h> diff --git a/WebCore/platform/text/mac/StringImplMac.mm b/WebCore/platform/text/mac/StringImplMac.mm index 3e0731c..d14c6d8 100644 --- a/WebCore/platform/text/mac/StringImplMac.mm +++ b/WebCore/platform/text/mac/StringImplMac.mm @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2006 Apple Computer, Inc. +/* + * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,13 +21,13 @@ #include "config.h" #include "StringImpl.h" -#include <Foundation/Foundation.h> +#include "FoundationExtras.h" namespace WebCore { StringImpl::operator NSString *() { - return [NSString stringWithCharacters:m_data length:m_length]; + return HardAutorelease(createCFString()); } } diff --git a/WebCore/platform/text/mac/StringMac.mm b/WebCore/platform/text/mac/StringMac.mm index 77942ea..758ae1d 100644 --- a/WebCore/platform/text/mac/StringMac.mm +++ b/WebCore/platform/text/mac/StringMac.mm @@ -20,6 +20,7 @@ #include "config.h" #include "PlatformString.h" +#include <CoreFoundation/CFString.h> namespace WebCore { diff --git a/WebCore/platform/text/qt/TextBreakIteratorQt.cpp b/WebCore/platform/text/qt/TextBreakIteratorQt.cpp index 88b9680..4dc23ee 100644 --- a/WebCore/platform/text/qt/TextBreakIteratorQt.cpp +++ b/WebCore/platform/text/qt/TextBreakIteratorQt.cpp @@ -63,6 +63,11 @@ namespace WebCore { return static_cast<TextBreakIterator*>(iterator); } + TextBreakIterator* cursorMovementIterator(const UChar* string, int length) + { + return characterBreakIterator(string, length); + } + TextBreakIterator* lineBreakIterator(const UChar* string, int length) { static QTextBoundaryFinder *iterator = 0; @@ -250,6 +255,11 @@ TextBreakIterator* characterBreakIterator(const UChar* string, int length) return iterator; } +TextBreakIterator* cursorMovementIterator(const UChar* string, int length) +{ + return characterBreakIterator(string, length); +} + TextBreakIterator* lineBreakIterator(const UChar*, int) { // not yet implemented diff --git a/WebCore/platform/win/FileSystemWin.cpp b/WebCore/platform/win/FileSystemWin.cpp index 5671462..746fceb 100644 --- a/WebCore/platform/win/FileSystemWin.cpp +++ b/WebCore/platform/win/FileSystemWin.cpp @@ -191,11 +191,11 @@ CString openTemporaryFile(const char*, PlatformFileHandle& handle) char tempPath[MAX_PATH]; int tempPathLength = ::GetTempPathA(_countof(tempPath), tempPath); if (tempPathLength <= 0 || tempPathLength > _countof(tempPath)) - return 0; + return CString(); HCRYPTPROV hCryptProv = 0; if (!CryptAcquireContext(&hCryptProv, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - return 0; + return CString(); char proposedPath[MAX_PATH]; while (1) { @@ -226,7 +226,7 @@ CString openTemporaryFile(const char*, PlatformFileHandle& handle) CryptReleaseContext(hCryptProv, 0); if (!isHandleValid(handle)) - return 0; + return CString(); return proposedPath; } diff --git a/WebCore/platform/win/PopupMenuWin.cpp b/WebCore/platform/win/PopupMenuWin.cpp index 59ea563..52f2eb9 100644 --- a/WebCore/platform/win/PopupMenuWin.cpp +++ b/WebCore/platform/win/PopupMenuWin.cpp @@ -527,6 +527,8 @@ void PopupMenu::paint(const IntRect& damageRect, HDC hdc) // Draw the item text if (itemStyle.isVisible()) { int textX = max(0, client()->clientPaddingLeft() - client()->clientInsetLeft()); + if (theme()->popupOptionSupportsTextIndent() && itemStyle.textDirection() == LTR) + textX += itemStyle.textIndent().calcMinValue(itemRect.width()); int textY = itemRect.y() + itemFont.ascent() + (itemRect.height() - itemFont.height()) / 2; context.drawBidiText(itemFont, textRun, IntPoint(textX, textY)); } @@ -683,6 +685,9 @@ static LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT message, WPARAM wParam, LPA ::SendMessage(popup->client()->hostWindow()->platformWindow(), message, wParam, lParam); popup->client()->hidePopup(); break; + case VK_ESCAPE: + popup->client()->hidePopup(); + break; default: if (isASCIIPrintable(wParam)) // Send the keydown to the WebView so it can be used for type-to-select. diff --git a/WebCore/platform/win/WheelEventWin.cpp b/WebCore/platform/win/WheelEventWin.cpp index d272ba7..e6670a4 100644 --- a/WebCore/platform/win/WheelEventWin.cpp +++ b/WebCore/platform/win/WheelEventWin.cpp @@ -46,23 +46,23 @@ static IntPoint globalPositionForEvent(HWND hWnd, LPARAM lParam) return point; } -int PlatformWheelEvent::horizontalLineMultiplier() const +static int horizontalScrollChars() { static ULONG scrollChars; if (!scrollChars && !SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &scrollChars, 0)) - scrollChars = cLineMultiplier; + scrollChars = 1; return scrollChars; } -int PlatformWheelEvent::verticalLineMultiplier() const +static int verticalScrollLines() { static ULONG scrollLines; if (!scrollLines && !SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &scrollLines, 0)) - scrollLines = cLineMultiplier; + scrollLines = 3; return scrollLines; } - -PlatformWheelEvent::PlatformWheelEvent(HWND hWnd, WPARAM wParam, LPARAM lParam, bool isHorizontal) + +PlatformWheelEvent::PlatformWheelEvent(HWND hWnd, WPARAM wParam, LPARAM lParam, bool isMouseHWheel) : m_position(positionForEvent(hWnd, lParam)) , m_globalPosition(globalPositionForEvent(hWnd, lParam)) , m_isAccepted(false) @@ -71,23 +71,40 @@ PlatformWheelEvent::PlatformWheelEvent(HWND hWnd, WPARAM wParam, LPARAM lParam, , m_altKey(GetKeyState(VK_MENU) & HIGH_BIT_MASK_SHORT) , m_metaKey(m_altKey) // FIXME: We'll have to test other browsers { - static ULONG scrollLines, scrollChars; - float delta = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - if (isHorizontal) { - // Windows sends a positive delta for scrolling right, while AppKit - // sends a negative delta. EventHandler expects the AppKit values, - // so we have to negate our horizontal delta to match. - m_deltaX = -delta * horizontalLineMultiplier(); + // How many pixels should we scroll per line? Gecko uses the height of the + // current line, which means scroll distance changes as you go through the + // page or go to different pages. IE 7 is ~50 px/line, although the value + // seems to vary slightly by page and zoom level. Since IE 7 has a + // smoothing algorithm on scrolling, it can get away with slightly larger + // scroll values without feeling jerky. Here we use 100 px per three lines + // (the default scroll amount on Windows is three lines per wheel tick). + static const float cScrollbarPixelsPerLine = 100.0f / 3.0f; + float delta = GET_WHEEL_DELTA_WPARAM(wParam) / static_cast<float>(WHEEL_DELTA); + if (isMouseHWheel) { + // Windows is <-- -/+ -->, WebKit wants <-- +/- -->, so we negate + // |delta| after saving the original value on the wheel tick member. + m_wheelTicksX = delta; + m_wheelTicksY = 0; + delta = -delta; + } else { + // Even though we use shift + vertical wheel to scroll horizontally in + // WebKit, we still note it as a vertical scroll on the wheel tick + // member, so that the DOM event we later construct will match the real + // hardware event better. + m_wheelTicksX = 0; + m_wheelTicksY = delta; + } + if (isMouseHWheel || m_shiftKey) { + m_deltaX = delta * static_cast<float>(horizontalScrollChars()) * cScrollbarPixelsPerLine; m_deltaY = 0; - m_granularity = ScrollByLineWheelEvent; + m_granularity = ScrollByPixelWheelEvent; } else { m_deltaX = 0; m_deltaY = delta; - int verticalMultiplier = verticalLineMultiplier(); - // A multiplier of -1 is used to mean that vertical wheel scrolling should be done by page. - m_granularity = (verticalMultiplier == -1) ? ScrollByPageWheelEvent : ScrollByLineWheelEvent; - if (m_granularity == ScrollByLineWheelEvent) - m_deltaY *= verticalMultiplier; + int verticalMultiplier = verticalScrollLines(); + m_granularity = (verticalMultiplier == WHEEL_PAGESCROLL) ? ScrollByPageWheelEvent : ScrollByPixelWheelEvent; + if (m_granularity == ScrollByPixelWheelEvent) + m_deltaY *= static_cast<float>(verticalMultiplier) * cScrollbarPixelsPerLine; } } diff --git a/WebCore/platform/wx/MouseWheelEventWx.cpp b/WebCore/platform/wx/MouseWheelEventWx.cpp index 154a710..9f3923d 100644 --- a/WebCore/platform/wx/MouseWheelEventWx.cpp +++ b/WebCore/platform/wx/MouseWheelEventWx.cpp @@ -25,6 +25,7 @@ #include "config.h" #include "PlatformWheelEvent.h" +#include "Scrollbar.h" #include <wx/defs.h> #include <wx/event.h> @@ -34,17 +35,19 @@ namespace WebCore { PlatformWheelEvent::PlatformWheelEvent(const wxMouseEvent& event, const wxPoint& globalPoint) : m_position(event.GetPosition()) , m_globalPosition(globalPoint) - , m_granularity(ScrollByLineWheelEvent) + , m_granularity(ScrollByPixelWheelEvent) , m_shiftKey(event.ShiftDown()) , m_ctrlKey(event.ControlDown()) , m_altKey(event.AltDown()) , m_metaKey(event.MetaDown()) // FIXME: We'll have to test other browsers , m_deltaX(0) // wx doesn't support horizontal mouse wheel scrolling , m_deltaY(event.GetWheelRotation() / event.GetWheelDelta()) + , m_wheelTicksX(m_deltaX) + , m_wheelTicksY(m_deltaY) , m_isAccepted(false) { // FIXME: retrieve the user setting for the number of lines to scroll on each wheel event - m_deltaY *= horizontalLineMultiplier(); + m_deltaY *= static_cast<float>(cScrollbarPixelsPerLineStep); } } diff --git a/WebCore/platform/wx/TemporaryLinkStubs.cpp b/WebCore/platform/wx/TemporaryLinkStubs.cpp index d8c6046..d8c6046 100755..100644 --- a/WebCore/platform/wx/TemporaryLinkStubs.cpp +++ b/WebCore/platform/wx/TemporaryLinkStubs.cpp diff --git a/WebCore/platform/wx/WidgetWx.cpp b/WebCore/platform/wx/WidgetWx.cpp index 37097fe..37097fe 100755..100644 --- a/WebCore/platform/wx/WidgetWx.cpp +++ b/WebCore/platform/wx/WidgetWx.cpp diff --git a/WebCore/platform/wx/wxcode/fontprops.h b/WebCore/platform/wx/wxcode/fontprops.h index e6f0b16..7f38bcf 100644 --- a/WebCore/platform/wx/wxcode/fontprops.h +++ b/WebCore/platform/wx/wxcode/fontprops.h @@ -46,4 +46,5 @@ private: float m_lineSpacing; float m_xHeight; -};
\ No newline at end of file +}; + diff --git a/WebCore/platform/wx/wxcode/gtk/fontprops.cpp b/WebCore/platform/wx/wxcode/gtk/fontprops.cpp index 369fcc5..df14812 100644 --- a/WebCore/platform/wx/wxcode/gtk/fontprops.cpp +++ b/WebCore/platform/wx/wxcode/gtk/fontprops.cpp @@ -23,17 +23,42 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include "config.h" + #include <wx/defs.h> #include <wx/gdicmn.h> #include <wx/font.h> #include <wx/fontutil.h> #include "fontprops.h" +#include "non-kerned-drawing.h" #include <gdk/gdk.h> +#include <cairo.h> +#include <pango/pango.h> +#include <pango/pangocairo.h> + wxFontProperties::wxFontProperties(wxFont* font): m_ascent(0), m_descent(0), m_lineGap(0), m_lineSpacing(0), m_xHeight(0) { + ASSERT(font && font->Ok()); + +#if USE(WXGC) + cairo_font_extents_t font_extents; + cairo_text_extents_t text_extents; + cairo_scaled_font_t* scaled_font = WebCore::createScaledFontForFont(font); + + cairo_scaled_font_extents(scaled_font, &font_extents); + m_ascent = static_cast<int>(font_extents.ascent); + m_descent = static_cast<int>(font_extents.descent); + m_lineSpacing = static_cast<int>(font_extents.height); + cairo_scaled_font_text_extents(scaled_font, "x", &text_extents); + m_xHeight = text_extents.height; + cairo_scaled_font_text_extents(scaled_font, " ", &text_extents); + m_lineGap = m_lineSpacing - m_ascent - m_descent; + + cairo_scaled_font_destroy(scaled_font); +#else PangoContext* context = gdk_pango_context_get_for_screen( gdk_screen_get_default() ); PangoLayout* layout = pango_layout_new(context); // and use it if it's valid @@ -64,6 +89,7 @@ m_ascent(0), m_descent(0), m_lineGap(0), m_lineSpacing(0), m_xHeight(0) m_lineSpacing = m_ascent + m_descent; pango_font_metrics_unref(metrics); +#endif } void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxCoord *height, @@ -80,16 +106,34 @@ void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxC if (str.empty()) return; - + +// FIXME: Doesn't support height, descent or external leading, though we don't need this for WebKit +// it will need to be implemented before merging into wx unless we craft a new API. +#if USE(WXGC) + PangoFont* pangoFont = WebCore::createPangoFontForFont(&font); + PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(WebCore::pangoFontMap())); + PangoGlyph pangoGlyph = WebCore::pango_font_get_glyph(pangoFont, pangoContext, (gunichar)g_utf8_get_char(str.ToUTF8())); + cairo_glyph_t cglyph = { pangoGlyph, 0, 0 }; + cairo_text_extents_t extents; + cairo_scaled_font_t* scaled_font = WebCore::createScaledFontForFont(&font); + cairo_scaled_font_glyph_extents(scaled_font, &cglyph, 1, &extents); + + if (cairo_scaled_font_status(scaled_font) == CAIRO_STATUS_SUCCESS && extents.x_advance != 0) + *width = (wxCoord)extents.x_advance; + + cairo_scaled_font_destroy(scaled_font); + g_object_unref(pangoContext); + g_object_unref(pangoFont); +#else PangoContext* context = gdk_pango_context_get_for_screen( gdk_screen_get_default() ); PangoLayout* m_layout = pango_layout_new(context); // and use it if it's valid - if ( font != wxNullFont ) + if ( font && font->IsOk() ) { pango_layout_set_font_description ( m_layout, - font.GetNativeFontInfo()->description + font->GetNativeFontInfo()->description ); } @@ -103,24 +147,19 @@ void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxC pango_layout_set_text( m_layout, dataUTF8, strlen(dataUTF8) ); + int h = 0; + pango_layout_get_pixel_size( m_layout, width, &h ); + if (descent) { - int h; - pango_layout_get_pixel_size( m_layout, width, &h ); PangoLayoutIter *iter = pango_layout_get_iter(m_layout); int baseline = pango_layout_iter_get_baseline(iter); pango_layout_iter_free(iter); *descent = h - PANGO_PIXELS(baseline); - - if (height) - *height = (wxCoord) h; - } - else - { - pango_layout_get_pixel_size( m_layout, width, height ); } - // Reset old font description - //if (font != wxNullFont) - // pango_layout_set_font_description( m_layout, m_fontdesc ); -}
\ No newline at end of file + if (height) + *height = (wxCoord) h; +#endif +} + diff --git a/WebCore/platform/wx/wxcode/gtk/non-kerned-drawing.cpp b/WebCore/platform/wx/wxcode/gtk/non-kerned-drawing.cpp index ed8fb09..bf745e0 100644 --- a/WebCore/platform/wx/wxcode/gtk/non-kerned-drawing.cpp +++ b/WebCore/platform/wx/wxcode/gtk/non-kerned-drawing.cpp @@ -28,25 +28,201 @@ #include "GraphicsContext.h" #include "SimpleFontData.h" +#include <wx/dc.h> #include <wx/dcgraph.h> #include <wx/defs.h> #include <wx/dcclient.h> #include <wx/gdicmn.h> #include <vector> +#if USE(WXGC) +#include <cairo.h> +#include <assert.h> + +#include <pango/pango.h> +#include <pango/pangocairo.h> + +// Use cairo-ft if a recent enough Pango version isn't available +#if !PANGO_VERSION_CHECK(1,18,0) +#include <cairo-ft.h> +#include <pango/pangofc-fontmap.h> +#endif + +#endif + +#include <gtk/gtk.h> + namespace WebCore { +#if USE(WXGC) +static PangoFontMap* g_fontMap; + +PangoFontMap* pangoFontMap() +{ + if (!g_fontMap) + g_fontMap = pango_cairo_font_map_get_default(); + + return g_fontMap; +} + +PangoFont* createPangoFontForFont(const wxFont* wxfont) +{ + ASSERT(wxfont && wxfont->Ok()); + + const char* face = wxfont->GetFaceName().mb_str(wxConvUTF8); + char const* families[] = { + face, + 0 + }; + + switch (wxfont->GetFamily()) { + case wxFONTFAMILY_ROMAN: + families[1] = "serif"; + break; + case wxFONTFAMILY_SWISS: + families[1] = "sans"; + break; + case wxFONTFAMILY_MODERN: + families[1] = "monospace"; + break; + default: + families[1] = "sans"; + } + + PangoFontDescription* description = pango_font_description_new(); + pango_font_description_set_absolute_size(description, wxfont->GetPointSize() * PANGO_SCALE); + + PangoFont* pangoFont = 0; + PangoContext* pangoContext = 0; + + switch (wxfont->GetWeight()) { + case wxFONTWEIGHT_LIGHT: + pango_font_description_set_weight(description, PANGO_WEIGHT_LIGHT); + break; + case wxFONTWEIGHT_NORMAL: + pango_font_description_set_weight(description, PANGO_WEIGHT_NORMAL); + break; + case wxFONTWEIGHT_BOLD: + pango_font_description_set_weight(description, PANGO_WEIGHT_BOLD); + break; + } + + switch (wxfont->GetStyle()) { + case wxFONTSTYLE_NORMAL: + pango_font_description_set_style(description, PANGO_STYLE_NORMAL); + break; + case wxFONTSTYLE_ITALIC: + pango_font_description_set_style(description, PANGO_STYLE_ITALIC); + break; + case wxFONTSTYLE_SLANT: + pango_font_description_set_style(description, PANGO_STYLE_OBLIQUE); + break; + } + + PangoFontMap* fontMap = pangoFontMap(); + + pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(fontMap)); + for (unsigned i = 0; !pangoFont && i < G_N_ELEMENTS(families); i++) { + pango_font_description_set_family(description, families[i]); + pango_context_set_font_description(pangoContext, description); + pangoFont = pango_font_map_load_font(fontMap, pangoContext, description); + } + pango_font_description_free(description); + + return pangoFont; +} + +cairo_scaled_font_t* createScaledFontForFont(const wxFont* wxfont) +{ + ASSERT(wxfont && wxfont->Ok()); + + cairo_scaled_font_t* scaledFont = NULL; + PangoFont* pangoFont = createPangoFontForFont(wxfont); + +#if PANGO_VERSION_CHECK(1,18,0) + if (pangoFont) + scaledFont = cairo_scaled_font_reference(pango_cairo_font_get_scaled_font(PANGO_CAIRO_FONT(pangoFont))); +#endif + + return scaledFont; +} + +PangoGlyph pango_font_get_glyph(PangoFont* font, PangoContext* context, gunichar wc) +{ + PangoGlyph result = 0; + gchar buffer[7]; + + gint length = g_unichar_to_utf8(wc, buffer); + g_return_val_if_fail(length, 0); + + GList* items = pango_itemize(context, buffer, 0, length, NULL, NULL); + + if (g_list_length(items) == 1) { + PangoItem* item = static_cast<PangoItem*>(items->data); + PangoFont* tmpFont = item->analysis.font; + item->analysis.font = font; + + PangoGlyphString* glyphs = pango_glyph_string_new(); + pango_shape(buffer, length, &item->analysis, glyphs); + + item->analysis.font = tmpFont; + + if (glyphs->num_glyphs == 1) + result = glyphs->glyphs[0].glyph; + else + g_warning("didn't get 1 glyph but %d", glyphs->num_glyphs); + + pango_glyph_string_free(glyphs); + } + + g_list_foreach(items, (GFunc)pango_item_free, NULL); + g_list_free(items); + + return result; +} +#endif // USE(WXGC) + + void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) { #if USE(WXGC) wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); + wxGraphicsContext* gc = dc->GetGraphicsContext(); + gc->PushState(); + cairo_t* cr = (cairo_t*)gc->GetNativeContext(); + + wxFont* wxfont = font->getWxFont(); + PangoFont* pangoFont = createPangoFontForFont(wxfont); + PangoFontMap* fontMap = pangoFontMap(); + PangoContext* pangoContext = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(fontMap)); + cairo_scaled_font_t* scaled_font = createScaledFontForFont(wxfont); + ASSERT(scaled_font); + + cairo_glyph_t* glyphs = NULL; + glyphs = static_cast<cairo_glyph_t*>(malloc(sizeof(cairo_glyph_t) * numGlyphs)); + + float offset = point.x(); + + for (int i = 0; i < numGlyphs; i++) { + glyphs[i].index = pango_font_get_glyph(pangoFont, pangoContext, glyphBuffer.glyphAt(from + i)); + glyphs[i].x = offset; + glyphs[i].y = point.y(); + offset += glyphBuffer.advanceAt(from + i); + } + + cairo_set_source_rgba(cr, color.Red()/255.0, color.Green()/255.0, color.Blue()/255.0, color.Alpha()/255.0); + cairo_set_scaled_font(cr, scaled_font); + + cairo_show_glyphs(cr, glyphs, numGlyphs); + + cairo_scaled_font_destroy(scaled_font); + gc->PopState(); #else wxDC* dc = graphicsContext->platformContext(); -#endif - wxFont wxfont = font->getWxFont(); - if (wxfont.IsOk()) - dc->SetFont(wxfont); + wxFont* wxfont = font->getWxFont(); + if (wxfont && wxfont->IsOk()) + dc->SetFont(*wxfont); dc->SetTextForeground(color); // convert glyphs to wxString @@ -63,6 +239,7 @@ void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* wxCoord ypoint = (wxCoord) (point.y() - height); dc->DrawText(text, (wxCoord)point.x(), ypoint); +#endif } } diff --git a/WebCore/platform/wx/wxcode/mac/carbon/fontprops.cpp b/WebCore/platform/wx/wxcode/mac/carbon/fontprops.cpp index b649eb4..447a3d0 100644 --- a/WebCore/platform/wx/wxcode/mac/carbon/fontprops.cpp +++ b/WebCore/platform/wx/wxcode/mac/carbon/fontprops.cpp @@ -36,14 +36,19 @@ static inline float scaleEmToUnits(float x, unsigned unitsPerEm) { return x * (c wxFontProperties::wxFontProperties(wxFont* font): m_ascent(0), m_descent(0), m_lineGap(0), m_lineSpacing(0), m_xHeight(0) { - ATSFontRef fontRef; CGFontRef cgFont; + +#ifdef wxOSX_USE_CORE_TEXT && wxOSX_USE_CORE_TEXT + cgFont = CTFontCopyGraphicsFont((CTFontRef)font->MacGetCTFont(), NULL); +#else + ATSFontRef fontRef; fontRef = FMGetATSFontRefFromFont(font->MacGetATSUFontID()); if (fontRef) cgFont = CGFontCreateWithPlatformFont((void*)&fontRef); - +#endif + if (cgFont) { int iAscent; int iDescent; @@ -89,7 +94,7 @@ void GetTextExtent( const wxFont& font, const wxString& str, wxCoord *width, wxC // we need the scale here ... - Fixed atsuSize = IntToFixed( int( /*m_scaleY*/ 1 * font.MacGetFontSize()) ) ; + Fixed atsuSize = IntToFixed( int( /*m_scaleY*/ 1 * font.GetPointSize()) ) ; //RGBColor atsuColor = MAC_WXCOLORREF( m_textForegroundColor.GetPixel() ) ; ATSUAttributeTag atsuTags[] = { diff --git a/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp b/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp index 126f7ec..6eaa765 100644 --- a/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp +++ b/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp @@ -30,6 +30,7 @@ #include <wx/defs.h> #include <wx/dcclient.h> +#include <wx/dcgraph.h> #include <wx/gdicmn.h> #include <vector> @@ -43,9 +44,9 @@ void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* wxDC* dc = graphicsContext->platformContext(); #endif - wxFont wxfont = font->getWxFont(); - if (wxfont.IsOk()) - dc->SetFont(wxfont); + wxFont* wxfont = font->getWxFont(); + if (wxfont->IsOk()) + dc->SetFont(*wxfont); dc->SetTextForeground(color); // convert glyphs to wxString diff --git a/WebCore/platform/wx/wxcode/non-kerned-drawing.h b/WebCore/platform/wx/wxcode/non-kerned-drawing.h index d005985..b995d37 100644 --- a/WebCore/platform/wx/wxcode/non-kerned-drawing.h +++ b/WebCore/platform/wx/wxcode/non-kerned-drawing.h @@ -27,10 +27,25 @@ #include "GlyphBuffer.h"
#include <wx/defs.h>
-#include <wx/dcclient.h>
+#include <wx/dcclient.h> + +#if __WXGTK__ && USE(WXGC) +#include <cairo.h> +#include <pango/pango.h> +#include <pango/pangocairo.h> +#endif
namespace WebCore {
extern void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* font, const wxColour& color, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point);
-
-}
+ +#if __WXGTK__ && USE(WXGC) +PangoFontMap* pangoFontMap(); + +PangoFont* createPangoFontForFont(const wxFont* wxfont); +cairo_scaled_font_t* createScaledFontForFont(const wxFont* wxfont); +PangoGlyph pango_font_get_glyph(PangoFont* font, PangoContext* context, gunichar wc); + +#endif +} + diff --git a/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp b/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp index f05923a..7d1e924 100644 --- a/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp +++ b/WebCore/platform/wx/wxcode/win/non-kerned-drawing.cpp @@ -101,7 +101,9 @@ void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* for (unsigned i = 0; i < numGlyphs; ++i) spacing[i] = advances[i].width(); - ::SelectObject(hdc, GetHfontOf(font->getWxFont())); + wxFont* wxfont = font->getWxFont(); + if (wxfont && wxfont->IsOk()) + ::SelectObject(hdc, GetHfontOf(*wxfont)); if (color.Ok()) ::SetTextColor(hdc, color.GetPixel()); |