diff options
Diffstat (limited to 'Source/WebCore/platform')
419 files changed, 11646 insertions, 9771 deletions
diff --git a/Source/WebCore/platform/AsyncFileSystem.cpp b/Source/WebCore/platform/AsyncFileSystem.cpp index b85a487..b1a3fe2 100644 --- a/Source/WebCore/platform/AsyncFileSystem.cpp +++ b/Source/WebCore/platform/AsyncFileSystem.cpp @@ -46,7 +46,7 @@ bool AsyncFileSystem::isAvailable() return false; } -PassOwnPtr<AsyncFileSystem> AsyncFileSystem::create(const String&) +PassOwnPtr<AsyncFileSystem> AsyncFileSystem::create(Type, const String&) { notImplemented(); return 0; @@ -68,7 +68,7 @@ void AsyncFileSystem::openFileSystem(const String& basePath, const String& stora rootPath += typeString; rootPath.append(PlatformFilePathSeparator); - callbacks->didOpenFileSystem(name, AsyncFileSystem::create(rootPath)); + callbacks->didOpenFileSystem(name, AsyncFileSystem::create(type, rootPath)); } #endif diff --git a/Source/WebCore/platform/AsyncFileSystem.h b/Source/WebCore/platform/AsyncFileSystem.h index c34a644..f5207ce 100644 --- a/Source/WebCore/platform/AsyncFileSystem.h +++ b/Source/WebCore/platform/AsyncFileSystem.h @@ -65,7 +65,7 @@ public: virtual bool waitForOperationToComplete() { return false; } // Creates and returns a new platform-specific AsyncFileSystem instance if the platform has its own implementation. - static PassOwnPtr<AsyncFileSystem> create(const String& rootPath); + static PassOwnPtr<AsyncFileSystem> create(Type, const String& rootPath); // Opens a new file system. The create parameter specifies whether or not to create the path if it does not already exists. static void openFileSystem(const String& basePath, const String& storageIdentifier, Type, bool create, PassOwnPtr<AsyncFileSystemCallbacks>); @@ -132,12 +132,16 @@ public: // Getter for this file system's root path. String root() const { return m_platformRootPath; } + Type type() const { return m_type; } + protected: - AsyncFileSystem(const String& platformRootPath) - : m_platformRootPath(platformRootPath) + AsyncFileSystem(Type type, const String& platformRootPath) + : m_type(type) + , m_platformRootPath(platformRootPath) { } + Type m_type; String m_platformRootPath; }; diff --git a/Source/WebCore/platform/ContextMenuItem.h b/Source/WebCore/platform/ContextMenuItem.h index 6e84131..6595711 100644 --- a/Source/WebCore/platform/ContextMenuItem.h +++ b/Source/WebCore/platform/ContextMenuItem.h @@ -65,6 +65,9 @@ namespace WebCore { ContextMenuItemTagOpenImageInNewWindow, ContextMenuItemTagDownloadImageToDisk, ContextMenuItemTagCopyImageToClipboard, +#if PLATFORM(QT) + ContextMenuItemTagCopyImageUrlToClipboard, +#endif ContextMenuItemTagOpenFrameInNewWindow, ContextMenuItemTagCopy, ContextMenuItemTagGoBack, diff --git a/Source/WebCore/platform/DragData.h b/Source/WebCore/platform/DragData.h index 42d0d3a..b89748e 100644 --- a/Source/WebCore/platform/DragData.h +++ b/Source/WebCore/platform/DragData.h @@ -31,6 +31,7 @@ #include "IntPoint.h" #include <wtf/Forward.h> +#include <wtf/HashMap.h> #include <wtf/Vector.h> #if PLATFORM(MAC) @@ -51,6 +52,7 @@ QT_END_NAMESPACE typedef const QMimeData* DragDataRef; #elif PLATFORM(WIN) typedef struct IDataObject* DragDataRef; +#include <wtf/text/WTFString.h> #elif PLATFORM(WX) typedef class wxDataObject* DragDataRef; #elif PLATFORM(GTK) @@ -84,7 +86,11 @@ enum DragApplicationFlags { DragApplicationHasAttachedSheet = 4, DragApplicationIsCopyKeyDown = 8 }; - + +#if PLATFORM(WIN) +typedef HashMap<UINT, Vector<String> > DragDataMap; +#endif + class DragData { public: enum FilenameConversionPolicy { DoNotConvertFilenames, ConvertFilenames }; @@ -92,7 +98,10 @@ public: // clientPosition is taken to be the position of the drag event within the target window, with (0,0) at the top left DragData(DragDataRef, const IntPoint& clientPosition, const IntPoint& globalPosition, DragOperation, DragApplicationFlags = DragApplicationNone); DragData(const String& dragStorageName, const IntPoint& clientPosition, const IntPoint& globalPosition, DragOperation, DragApplicationFlags = DragApplicationNone); - +#if PLATFORM(WIN) + DragData(const DragDataMap&, const IntPoint& clientPosition, const IntPoint& globalPosition, DragOperation sourceOperationMask, DragApplicationFlags = DragApplicationNone); + const DragDataMap& dragDataMap(); +#endif const IntPoint& clientPosition() const { return m_clientPosition; } const IntPoint& globalPosition() const { return m_globalPosition; } DragApplicationFlags flags() { return m_applicationFlags; } @@ -119,6 +128,9 @@ private: #if PLATFORM(MAC) RetainPtr<NSPasteboard> m_pasteboard; #endif +#if PLATFORM(WIN) + DragDataMap m_dragDataMap; +#endif }; } diff --git a/Source/WebCore/platform/DragImage.cpp b/Source/WebCore/platform/DragImage.cpp index 5fcafc1..64aaa0e 100644 --- a/Source/WebCore/platform/DragImage.cpp +++ b/Source/WebCore/platform/DragImage.cpp @@ -73,6 +73,13 @@ DragImageRef createDragImageForSelection(Frame* frame) return image; } +#if !PLATFORM(MAC) && (!PLATFORM(WIN) || OS(WINCE)) +DragImageRef createDragImageForLink(KURL&, const String&, Frame*) +{ + return 0; +} +#endif + } // namespace WebCore #endif // ENABLE(DRAG_SUPPORT) diff --git a/Source/WebCore/platform/DragImage.h b/Source/WebCore/platform/DragImage.h index a371821..fcb980a 100644 --- a/Source/WebCore/platform/DragImage.h +++ b/Source/WebCore/platform/DragImage.h @@ -96,6 +96,7 @@ namespace WebCore { DragImageRef createDragImageFromImage(Image*); DragImageRef createDragImageForSelection(Frame*); DragImageRef createDragImageIconForCachedImage(CachedImage*); + DragImageRef createDragImageForLink(KURL&, const String& label, Frame*); void deleteDragImage(DragImageRef); } diff --git a/Source/WebCore/platform/FileChooser.cpp b/Source/WebCore/platform/FileChooser.cpp index 90dd567..7e6d4ae 100644 --- a/Source/WebCore/platform/FileChooser.cpp +++ b/Source/WebCore/platform/FileChooser.cpp @@ -42,13 +42,19 @@ inline FileChooser::FileChooser(FileChooserClient* client, const Vector<String>& , m_isInitializing(true) { m_filenames = initialFilenames; +} + +void FileChooser::initialize() +{ loadIcon(); m_isInitializing = false; } PassRefPtr<FileChooser> FileChooser::create(FileChooserClient* client, const Vector<String>& initialFilenames) { - return adoptRef(new FileChooser(client, initialFilenames)); + RefPtr<FileChooser> chooser(adoptRef(new FileChooser(client, initialFilenames))); + chooser->initialize(); + return chooser; } FileChooser::~FileChooser() diff --git a/Source/WebCore/platform/FileChooser.h b/Source/WebCore/platform/FileChooser.h index fa25406..ac5e0e6 100644 --- a/Source/WebCore/platform/FileChooser.h +++ b/Source/WebCore/platform/FileChooser.h @@ -81,6 +81,7 @@ public: private: FileChooser(FileChooserClient*, const Vector<String>& initialFilenames); + void initialize(); void loadIcon(); FileChooserClient* m_client; diff --git a/Source/WebCore/platform/FileSystem.cpp b/Source/WebCore/platform/FileSystem.cpp index 511f8aa..0f69b7f 100644 --- a/Source/WebCore/platform/FileSystem.cpp +++ b/Source/WebCore/platform/FileSystem.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -101,4 +101,18 @@ String encodeForFileName(const String& inputStr) return String(buffer.data(), p - buffer.data()); } +#if !PLATFORM(MAC) + +bool canExcludeFromBackup() +{ + return false; +} + +bool excludeFromBackup(const String&) +{ + return false; +} + +#endif + } // namespace WebCore diff --git a/Source/WebCore/platform/FileSystem.h b/Source/WebCore/platform/FileSystem.h index 4f088e1..d923fe6 100644 --- a/Source/WebCore/platform/FileSystem.h +++ b/Source/WebCore/platform/FileSystem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008 Collabora, Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,6 +30,15 @@ #ifndef FileSystem_h #define FileSystem_h +#include "PlatformString.h" +#include <time.h> +#include <wtf/Forward.h> +#include <wtf/Vector.h> + +#if PLATFORM(CF) +#include <wtf/RetainPtr.h> +#endif + #if PLATFORM(QT) #include <QFile> #include <QLibrary> @@ -39,15 +48,9 @@ #endif #if PLATFORM(CF) || (PLATFORM(QT) && defined(Q_WS_MAC)) -#include <CoreFoundation/CFBundle.h> -#endif - -#include "PlatformString.h" -#include <time.h> -#include <wtf/Forward.h> -#include <wtf/Vector.h> - +typedef struct __CFBundle* CFBundleRef; typedef const struct __CFData* CFDataRef; +#endif #if OS(WINDOWS) // These are to avoid including <winbase.h> in a header for Chromium @@ -122,8 +125,6 @@ const PlatformFileHandle invalidPlatformFileHandle = reinterpret_cast<HANDLE>(-1 #elif PLATFORM(BREWMP) typedef IFile* PlatformFileHandle; const PlatformFileHandle invalidPlatformFileHandle = 0; -typedef void* PlatformModule; -typedef unsigned PlatformModuleVersion; #elif PLATFORM(GTK) typedef GFileIOStream* PlatformFileHandle; const PlatformFileHandle invalidPlatformFileHandle = 0; @@ -161,6 +162,9 @@ String homeDirectoryPath(); String pathGetFileName(const String&); String directoryName(const String&); +bool canExcludeFromBackup(); // Returns true if any file can ever be excluded from backup. +bool excludeFromBackup(const String&); // Returns true if successful. + Vector<String> listDirectory(const String& path, const String& filter = String()); CString fileSystemRepresentation(const String&); @@ -179,17 +183,18 @@ int writeToFile(PlatformFileHandle, const char* data, int length); // Returns number of bytes actually written if successful, -1 otherwise. int readFromFile(PlatformFileHandle, char* data, int length); -// Methods for dealing with loadable modules +// Functions for working with loadable modules. bool unloadModule(PlatformModule); // Encode a string for use within a file name. String encodeForFileName(const String&); -#if PLATFORM(WIN) -String localUserSpecificStorageDirectory(); -String roamingUserSpecificStorageDirectory(); +#if PLATFORM(CF) +RetainPtr<CFURLRef> pathAsURL(const String&); +#endif -bool safeCreateFile(const String&, CFDataRef); +#if PLATFORM(CHROMIUM) +String pathGetDisplayFileName(const String&); #endif #if PLATFORM(GTK) @@ -198,8 +203,10 @@ String filenameForDisplay(const String&); CString applicationDirectoryPath(); #endif -#if PLATFORM(CHROMIUM) -String pathGetDisplayFileName(const String&); +#if PLATFORM(WIN) && !OS(WINCE) +String localUserSpecificStorageDirectory(); +String roamingUserSpecificStorageDirectory(); +bool safeCreateFile(const String&, CFDataRef); #endif } // namespace WebCore diff --git a/Source/WebCore/platform/KURL.cpp b/Source/WebCore/platform/KURL.cpp index 60049df..f6a66ef 100644 --- a/Source/WebCore/platform/KURL.cpp +++ b/Source/WebCore/platform/KURL.cpp @@ -322,6 +322,12 @@ KURL::KURL(ParsedURLStringTag, const String& url) ASSERT(url == m_string); } +KURL::KURL(ParsedURLStringTag, const URLString& url) +{ + parse(url.string()); + ASSERT(url.string() == m_string); +} + KURL::KURL(const KURL& base, const String& relative) { init(base, relative, UTF8Encoding()); @@ -921,11 +927,11 @@ String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) && isASCIIHexDigit(str[encodedRunEnd + 1]) && isASCIIHexDigit(str[encodedRunEnd + 2])) encodedRunEnd += 3; + searchPosition = encodedRunEnd; if (encodedRunEnd == encodedRunPosition) { ++searchPosition; continue; } - searchPosition = encodedRunEnd; // Decode the %-escapes into bytes. unsigned runLength = (encodedRunEnd - encodedRunPosition) / 3; @@ -962,6 +968,14 @@ bool KURL::isLocalFile() const return protocolIs("file"); } +// Caution: This function does not bounds check. +static void appendEscapedChar(char*& buffer, unsigned char c) +{ + *buffer++ = '%'; + *buffer++ = hexDigits[c >> 4]; + *buffer++ = hexDigits[c & 0xF]; +} + static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t length) { char* p = buffer; @@ -971,16 +985,37 @@ static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t l while (str < strEnd) { unsigned char c = *str++; if (isBadChar(c)) { - if (c == '%' || c == '?') { + if (c == '%' || c == '?') *p++ = c; - } else if (c != 0x09 && c != 0x0a && c != 0x0d) { - *p++ = '%'; - *p++ = hexDigits[c >> 4]; - *p++ = hexDigits[c & 0xF]; - } - } else { + else if (c != 0x09 && c != 0x0a && c != 0x0d) + appendEscapedChar(p, c); + } else *p++ = c; + } + + buffer = p; +} + +static void escapeAndAppendFragment(char*& buffer, const char* strStart, size_t length) +{ + char* p = buffer; + + const char* str = strStart; + const char* strEnd = strStart + length; + while (str < strEnd) { + unsigned char c = *str++; + // Strip CR, LF and Tab from fragments, per: + // https://bugs.webkit.org/show_bug.cgi?id=8770 + if (c == 0x09 || c == 0x0a || c == 0x0d) + continue; + + // Chrome and IE allow non-ascii characters in fragments, however doing + // so would hit an ASSERT in checkEncodedString, so for now we don't. + if (c < 0x20 || c >= 127) { + appendEscapedChar(p, c); + continue; } + *p++ = c; } buffer = p; @@ -1021,11 +1056,6 @@ static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int sr baseStringPos += 3; if (dst > bufferPathStart + 1) dst--; - // Note that these two while blocks differ subtly. - // The first helps to remove multiple adjoining slashes as we rewind. - // The +1 to bufferPathStart in the first while block prevents eating a leading slash - while (dst > bufferPathStart + 1 && dst[-1] == '/') - dst--; while (dst > bufferPathStart && dst[-1] != '/') dst--; continue; @@ -1070,6 +1100,38 @@ void KURL::parse(const String& string) parse(buffer.data(), &string); } +static inline bool equal(const char* a, size_t lenA, const char* b, size_t lenB) +{ + if (lenA != lenB) + return false; + return !strncmp(a, b, lenA); +} + +// List of default schemes is taken from google-url: +// http://code.google.com/p/google-url/source/browse/trunk/src/url_canon_stdurl.cc#120 +static inline bool isDefaultPortForScheme(const char* port, size_t portLength, const char* scheme, size_t schemeLength) +{ + // This switch is theoretically a performance optimization. It came over when + // the code was moved from google-url, but may be removed later. + switch (schemeLength) { + case 2: + return equal("ws", 2, scheme, schemeLength) && equal("80", 2, port, portLength); + case 3: + if (equal("ftp", 3, scheme, schemeLength)) + return equal("21", 2, port, portLength); + if (equal("wss", 3, scheme, schemeLength)) + return equal("443", 3, port, portLength); + break; + case 4: + return equal("http", 4, scheme, schemeLength) && equal("80", 2, port, portLength); + case 5: + return equal("https", 5, scheme, schemeLength) && equal("443", 3, port, portLength); + case 6: + return equal("gopher", 6, scheme, schemeLength) && equal("70", 2, port, portLength); + } + return false; +} + void KURL::parse(const char* url, const String* originalString) { if (!url || url[0] == '\0') { @@ -1246,7 +1308,7 @@ void KURL::parse(const char* url, const String* originalString) // copy in the scheme const char *schemeEndPtr = url + schemeEnd; while (strPtr < schemeEndPtr) - *p++ = *strPtr++; + *p++ = toASCIILower(*strPtr++); m_schemeEnd = p - buffer.data(); bool hostIsLocalHost = portEnd - userStart == 9 @@ -1305,13 +1367,16 @@ void KURL::parse(const char* url, const String* originalString) } m_hostEnd = p - buffer.data(); - // copy in the port + // Copy in the port if the URL has one (and it's not default). if (hostEnd != portStart) { - *p++ = ':'; - strPtr = url + portStart; - const char *portEndPtr = url + portEnd; - while (strPtr < portEndPtr) - *p++ = *strPtr++; + const char* portStr = url + portStart; + size_t portLength = portEnd - portStart; + if (portLength && !isDefaultPortForScheme(portStr, portLength, buffer.data(), m_schemeEnd)) { + *p++ = ':'; + const char* portEndPtr = url + portEnd; + while (portStr < portEndPtr) + *p++ = *portStr++; + } } m_portEnd = p - buffer.data(); } else @@ -1349,7 +1414,7 @@ void KURL::parse(const char* url, const String* originalString) // add fragment, escaping bad characters if (fragmentEnd != queryEnd) { *p++ = '#'; - appendEscapingBadChars(p, url + fragmentStart, fragmentEnd - fragmentStart); + escapeAndAppendFragment(p, url + fragmentStart, fragmentEnd - fragmentStart); } m_fragmentEnd = p - buffer.data(); @@ -1415,11 +1480,9 @@ String encodeWithURLEscapeSequences(const String& notEncodedString) const char* strEnd = str + asUTF8.length(); while (str < strEnd) { unsigned char c = *str++; - if (isBadChar(c)) { - *p++ = '%'; - *p++ = hexDigits[c >> 4]; - *p++ = hexDigits[c & 0xF]; - } else + if (isBadChar(c)) + appendEscapedChar(p, c); + else *p++ = c; } diff --git a/Source/WebCore/platform/KURL.h b/Source/WebCore/platform/KURL.h index cbf0d8b..521e7ca 100644 --- a/Source/WebCore/platform/KURL.h +++ b/Source/WebCore/platform/KURL.h @@ -27,6 +27,7 @@ #define KURL_h #include "PlatformString.h" +#include "URLString.h" #include <wtf/HashMap.h> #if PLATFORM(CF) @@ -74,6 +75,7 @@ public: // It is usually best to avoid repeatedly parsing a string, unless memory saving outweigh the possible slow-downs. KURL(ParsedURLStringTag, const char*); KURL(ParsedURLStringTag, const String&); + KURL(ParsedURLStringTag, const URLString&); // Resolves the relative URL with the given base URL. If provided, the // TextEncoding is used to encode non-ASCII characers. The base URL can be @@ -117,11 +119,13 @@ public: bool canSetHostOrPort() const { return isHierarchical(); } bool canSetPathname() const { return isHierarchical(); } - + #if USE(GOOGLEURL) const String& string() const { return m_url.string(); } + URLString urlString() const { return URLString(m_url.string()); } #else const String& string() const { return m_string; } + URLString urlString() const { return URLString(m_string); } #endif String protocol() const; diff --git a/Source/WebCore/platform/KURLGoogle.cpp b/Source/WebCore/platform/KURLGoogle.cpp index 6d63734..0697b33 100644 --- a/Source/WebCore/platform/KURLGoogle.cpp +++ b/Source/WebCore/platform/KURLGoogle.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2004, 2007, 2008, 2009 Apple Inc. All rights reserved. - * Copyright (C) 2008, 2009 Google Inc. All rights reserved. + * Copyright (C) 2008, 2009, 2011 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 @@ -63,8 +63,7 @@ static const int invalidPortNumber = 0xFFFF; // canonicalizer. class KURLCharsetConverter : public url_canon::CharsetConverter { public: - // The encoding parameter may be NULL, but in this case the object must not - // be called. + // The encoding parameter may be 0, but in this case the object must not be called. KURLCharsetConverter(const TextEncoding* encoding) : m_encoding(encoding) { @@ -96,8 +95,8 @@ static inline void assertProtocolIsGood(const char* protocol) } // 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. +// 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; @@ -194,15 +193,15 @@ void KURLGooglePrivate::init(const KURL& base, init(base, relative.characters(), relative.length(), queryEncoding); } -// Note: code mostly duplicated below. -void KURLGooglePrivate::init(const KURL& base, const char* rel, int relLength, +template <typename CHAR> +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. + // 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. @@ -233,37 +232,11 @@ void KURLGooglePrivate::init(const KURL& base, const char* rel, int relLength, else setAscii(CString(output.data(), output.length())); } else { - // WebCore expects resolved URLs to be empty rather than NULL. + // 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) { @@ -295,8 +268,8 @@ void KURLGooglePrivate::copyTo(KURLGooglePrivate* dest) const 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. + // KURL returns a null string if the URL is itself a null string, and an + // empty string for other nonexistent entities. if (utf8String().isNull()) return String(); return String("", 0); @@ -330,9 +303,9 @@ void KURLGooglePrivate::replaceComponents(const Replacements& replacements) 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. + // Handle the null case separately. Otherwise, constructing + // the string like we do below would generate the empty string, + // not the null string. if (m_utf8.isNull()) m_string = String(); else if (m_utf8IsASCII) @@ -346,17 +319,17 @@ const String& KURLGooglePrivate::string() const // KURL ------------------------------------------------------------------------ -// Creates with NULL-terminated string input representing an absolute URL. +// 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. +// ASCII. We treat it as UTF-8 just in case. KURL::KURL(ParsedURLStringTag, const char *url) { - // FIXME The Mac code checks for beginning with a slash and converting to a + // FIXME The Mac code checks for beginning with a slash and converts it to // 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. + // 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)); @@ -365,7 +338,7 @@ KURL::KURL(ParsedURLStringTag, const char *url) // 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 +// canonical and in proper escaped form so needs no encoding. We treat it as // UTF-8 just in case. KURL::KURL(ParsedURLStringTag, const String& url) { @@ -533,8 +506,8 @@ String KURL::user() const String KURL::fragmentIdentifier() 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. + // string, which m_url.componentString will produce. Nonexistent refs + // should be the null string. if (!m_url.m_parsed.ref.is_valid()) return String(); @@ -745,7 +718,7 @@ void KURL::setQuery(const String& query) { KURLGooglePrivate::Replacements replacements; if (query.isNull()) { - // KURL.cpp sets to NULL to clear any query. + // 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 @@ -946,7 +919,7 @@ String decodeURLEscapeSequences(const String& str) } // In KURL.cpp's implementation, this is called by every component getter. -// It will unescape every character, including NULL. This is scary, and may +// It will unescape every character, including '\0'. This is scary, and may // cause security holes. We never call this function for components, and // just return the ASCII versions instead. // @@ -1005,10 +978,10 @@ bool KURL::isLocalFile() const // 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 +// There is a possibility that a future caller 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. +// to protect against here is accidentally getting '\0' characters in a string +// that is not supposed to have them. Therefore, we escape these characters. String encodeWithURLEscapeSequences(const String& notEncodedString) { CString utf8 = UTF8Encoding().encode( @@ -1115,7 +1088,7 @@ bool protocolIs(const String& url, const char* protocol) // Check the scheme like GURL does. return url_util::FindAndCompareScheme(url.characters(), url.length(), - protocol, NULL); + protocol, 0); } inline bool KURL::protocolIs(const String& string, const char* protocol) diff --git a/Source/WebCore/platform/KURLGooglePrivate.h b/Source/WebCore/platform/KURLGooglePrivate.h index c71e56b..c74a6b4 100644 --- a/Source/WebCore/platform/KURLGooglePrivate.h +++ b/Source/WebCore/platform/KURLGooglePrivate.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * Copyright (c) 2008, 2009, 2011 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 @@ -41,7 +41,7 @@ namespace WebCore { class KURL; class TextEncoding; - // Wraps the internals related to using Google-URL as the bnackend for KURL. + // Wraps the internals related to using Google-URL as the backend 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 { @@ -49,19 +49,17 @@ namespace WebCore { 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. + // Initializes the object. This will call through the backend initializer + // below. 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 + // Backend initializer. The query encoding parameters are optional and can + // be 0 (this implies UTF-8). This initializer requires 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, + template <typename CHAR> + void init(const KURL& base, const CHAR* rel, int relLength, const TextEncoding* queryEncoding); // Does a deep copy to the given output object. diff --git a/Source/WebCore/platform/Length.cpp b/Source/WebCore/platform/Length.cpp index e2fd9b8..323705a 100644 --- a/Source/WebCore/platform/Length.cpp +++ b/Source/WebCore/platform/Length.cpp @@ -28,6 +28,7 @@ #include "PlatformString.h" #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> +#include <wtf/OwnArrayPtr.h> #include <wtf/text/StringBuffer.h> using namespace WTF; @@ -83,7 +84,7 @@ static int countCharacter(const UChar* data, unsigned length, UChar character) return count; } -Length* newCoordsArray(const String& string, int& len) +PassOwnArrayPtr<Length> newCoordsArray(const String& string, int& len) { unsigned length = string.length(); const UChar* data = string.characters(); @@ -100,7 +101,7 @@ Length* newCoordsArray(const String& string, int& len) str = str->simplifyWhiteSpace(); len = countCharacter(str->characters(), str->length(), ' ') + 1; - Length* r = new Length[len]; + OwnArrayPtr<Length> r = adoptArrayPtr(new Length[len]); int i = 0; unsigned pos = 0; @@ -114,10 +115,10 @@ Length* newCoordsArray(const String& string, int& len) ASSERT(i == len - 1); - return r; + return r.release(); } -Length* newLengthArray(const String& string, int& len) +PassOwnArrayPtr<Length> newLengthArray(const String& string, int& len) { RefPtr<StringImpl> str = string.impl()->simplifyWhiteSpace(); if (!str->length()) { @@ -126,7 +127,7 @@ Length* newLengthArray(const String& string, int& len) } len = countCharacter(str->characters(), str->length(), ',') + 1; - Length* r = new Length[len]; + OwnArrayPtr<Length> r = adoptArrayPtr(new Length[len]); int i = 0; unsigned pos = 0; @@ -145,7 +146,7 @@ Length* newLengthArray(const String& string, int& len) else len--; - return r; + return r.release(); } } // namespace WebCore diff --git a/Source/WebCore/platform/Length.h b/Source/WebCore/platform/Length.h index 9da71c7..1571944 100644 --- a/Source/WebCore/platform/Length.h +++ b/Source/WebCore/platform/Length.h @@ -25,6 +25,7 @@ #include <wtf/FastAllocBase.h> #include <wtf/Forward.h> #include <wtf/MathExtras.h> +#include <wtf/PassOwnArrayPtr.h> namespace WebCore { @@ -193,8 +194,8 @@ private: int m_value; }; -Length* newCoordsArray(const String&, int& len); -Length* newLengthArray(const String&, int& len); +PassOwnArrayPtr<Length> newCoordsArray(const String&, int& len); +PassOwnArrayPtr<Length> newLengthArray(const String&, int& len); } // namespace WebCore diff --git a/Source/WebCore/platform/LinkHash.cpp b/Source/WebCore/platform/LinkHash.cpp index ac3aa3c..a2acad1 100644 --- a/Source/WebCore/platform/LinkHash.cpp +++ b/Source/WebCore/platform/LinkHash.cpp @@ -197,7 +197,7 @@ static inline bool needsTrailingSlash(const UChar* characters, unsigned length) static ALWAYS_INLINE LinkHash visitedLinkHashInline(const UChar* url, unsigned length) { - return AlreadyHashed::avoidDeletedValue(StringImpl::computeHash(url, length)); + return AlreadyHashed::avoidDeletedValue(WTF::StringHasher::createHash(url, length)); } LinkHash visitedLinkHash(const UChar* url, unsigned length) diff --git a/Source/WebCore/platform/LocalizationStrategy.h b/Source/WebCore/platform/LocalizationStrategy.h index 5596c81..3ed84d9 100644 --- a/Source/WebCore/platform/LocalizationStrategy.h +++ b/Source/WebCore/platform/LocalizationStrategy.h @@ -55,6 +55,9 @@ public: virtual String contextMenuItemTagOpenImageInNewWindow() = 0; virtual String contextMenuItemTagDownloadImageToDisk() = 0; virtual String contextMenuItemTagCopyImageToClipboard() = 0; +#if PLATFORM(QT) + virtual String contextMenuItemTagCopyImageUrlToClipboard() = 0; +#endif virtual String contextMenuItemTagOpenFrameInNewWindow() = 0; virtual String contextMenuItemTagCopy() = 0; virtual String contextMenuItemTagGoBack() = 0; diff --git a/Source/WebCore/platform/LocalizedStrings.cpp b/Source/WebCore/platform/LocalizedStrings.cpp index f342c6d..77144a9 100644 --- a/Source/WebCore/platform/LocalizedStrings.cpp +++ b/Source/WebCore/platform/LocalizedStrings.cpp @@ -104,6 +104,13 @@ String contextMenuItemTagCopyImageToClipboard() return platformStrategies()->localizationStrategy()->contextMenuItemTagCopyImageToClipboard(); } +#if PLATFORM(QT) +String contextMenuItemTagCopyImageUrlToClipboard() +{ + return platformStrategies()->localizationStrategy()->contextMenuItemTagCopyImageUrlToClipboard(); +} +#endif + String contextMenuItemTagOpenFrameInNewWindow() { return platformStrategies()->localizationStrategy()->contextMenuItemTagOpenFrameInNewWindow(); diff --git a/Source/WebCore/platform/LocalizedStrings.h b/Source/WebCore/platform/LocalizedStrings.h index da0fe86..812300f 100644 --- a/Source/WebCore/platform/LocalizedStrings.h +++ b/Source/WebCore/platform/LocalizedStrings.h @@ -51,6 +51,9 @@ namespace WebCore { String contextMenuItemTagOpenImageInNewWindow(); String contextMenuItemTagDownloadImageToDisk(); String contextMenuItemTagCopyImageToClipboard(); +#if PLATFORM(QT) + String contextMenuItemTagCopyImageUrlToClipboard(); +#endif String contextMenuItemTagOpenFrameInNewWindow(); String contextMenuItemTagCopy(); String contextMenuItemTagGoBack(); diff --git a/Source/WebCore/platform/MIMETypeRegistry.cpp b/Source/WebCore/platform/MIMETypeRegistry.cpp index 0f4cc0e..8017c90 100644 --- a/Source/WebCore/platform/MIMETypeRegistry.cpp +++ b/Source/WebCore/platform/MIMETypeRegistry.cpp @@ -27,9 +27,12 @@ #include "config.h" #include "MIMETypeRegistry.h" +<<<<<<< HEAD #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size #include "ArchiveFactory.h" #endif +======= +>>>>>>> webkit.org at r78450 #include "MediaPlayer.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> @@ -46,6 +49,10 @@ #include <qimagewriter.h> #endif +#if ENABLE(WEB_ARCHIVE) +#include "ArchiveFactory.h" +#endif + namespace WebCore { static HashSet<String>* supportedImageResourceMIMETypes; @@ -249,7 +256,11 @@ static void initializeSupportedNonImageMimeTypes() for (size_t i = 0; i < WTF_ARRAY_LENGTH(types); ++i) supportedNonImageMIMETypes->add(types[i]); +<<<<<<< HEAD #if ENABLE(ARCHIVE) // ANDROID extension: disabled to reduce code size +======= +#if ENABLE(WEB_ARCHIVE) +>>>>>>> webkit.org at r78450 ArchiveFactory::registerKnownArchiveMIMETypes(); #endif } @@ -369,6 +380,13 @@ static MediaMIMETypeMap& mediaMIMETypeMap() return mediaMIMETypeForExtensionMap; } +#if ENABLE(FILE_SYSTEM) && ENABLE(WORKERS) +String MIMETypeRegistry::getMIMETypeForExtension(const String& extension) +{ + return getMIMETypeForExtensionThreadSafe(extension); +} +#endif + String MIMETypeRegistry::getMediaMIMETypeForExtension(const String& ext) { // Look in the system-specific registry first. diff --git a/Source/WebCore/platform/MIMETypeRegistry.h b/Source/WebCore/platform/MIMETypeRegistry.h index 64abea8..d069035 100644 --- a/Source/WebCore/platform/MIMETypeRegistry.h +++ b/Source/WebCore/platform/MIMETypeRegistry.h @@ -36,6 +36,9 @@ namespace WebCore { class MIMETypeRegistry { public: static String getMIMETypeForExtension(const String& extension); +#if ENABLE(FILE_SYSTEM) && ENABLE(WORKERS) + static String getMIMETypeForExtensionThreadSafe(const String& extension); +#endif static Vector<String> getExtensionsForMIMEType(const String& type); static String getPreferredExtensionForMIMEType(const String& type); static String getMediaMIMETypeForExtension(const String& extension); diff --git a/Source/WebCore/platform/PlatformGestureEvent.h b/Source/WebCore/platform/PlatformGestureEvent.h new file mode 100644 index 0000000..99157e1 --- /dev/null +++ b/Source/WebCore/platform/PlatformGestureEvent.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2011 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 PlatformGestureEvent_h +#define PlatformGestureEvent_h + +#if ENABLE(GESTURE_EVENTS) + +#include "IntPoint.h" + +namespace WebCore { + +class PlatformGestureEvent { +public: + enum Type { + ScrollBeginType, + ScrollEndType, + }; + + PlatformGestureEvent() + : m_type(ScrollBeginType) + , m_timestamp(0) + { + } + + Type type() const { return m_type; } + + const IntPoint& position() const { return m_position; } // PlatformWindow coordinates. + const IntPoint& globalPosition() const { return m_globalPosition; } // Screen coordinates. + + double timestamp() const { return m_timestamp; } + +protected: + Type m_type; + IntPoint m_position; + IntPoint m_globalPosition; + double m_timestamp; +}; + +} // namespace WebCore + +#endif // ENABLE(GESTURE_EVENTS) + +#endif // PlatformGestureEvent_h diff --git a/Source/WebCore/platform/PlatformWheelEvent.h b/Source/WebCore/platform/PlatformWheelEvent.h index 1e5cd53..2698e6e 100644 --- a/Source/WebCore/platform/PlatformWheelEvent.h +++ b/Source/WebCore/platform/PlatformWheelEvent.h @@ -98,7 +98,9 @@ namespace WebCore { , m_altKey(false) , m_metaKey(false) #if PLATFORM(MAC) + , m_hasPreciseScrollingDeltas(false) , m_phase(PlatformWheelEventPhaseNone) + , m_timestamp(0) #endif { } @@ -151,6 +153,8 @@ namespace WebCore { #endif PlatformWheelEventPhase phase() const { return m_phase; } + bool hasPreciseScrollingDeltas() const { return m_hasPreciseScrollingDeltas; } + double timestamp() const { return m_timestamp; } #endif #if PLATFORM(QT) @@ -186,7 +190,9 @@ namespace WebCore { bool m_altKey; bool m_metaKey; #if PLATFORM(MAC) + bool m_hasPreciseScrollingDeltas; PlatformWheelEventPhase m_phase; + double m_timestamp; #endif }; diff --git a/Source/WebCore/platform/PopupMenuStyle.h b/Source/WebCore/platform/PopupMenuStyle.h index b9a7abc..3cb33cc 100644 --- a/Source/WebCore/platform/PopupMenuStyle.h +++ b/Source/WebCore/platform/PopupMenuStyle.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2008, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -35,7 +35,7 @@ namespace WebCore { class PopupMenuStyle { public: - PopupMenuStyle(const Color& foreground, const Color& background, const Font& font, bool visible, bool isDisplayNone, Length textIndent, TextDirection textDirection) + PopupMenuStyle(const Color& foreground, const Color& background, const Font& font, bool visible, bool isDisplayNone, Length textIndent, TextDirection textDirection, bool hasTextDirectionOverride) : m_foregroundColor(foreground) , m_backgroundColor(background) , m_font(font) @@ -43,6 +43,7 @@ public: , m_isDisplayNone(isDisplayNone) , m_textIndent(textIndent) , m_textDirection(textDirection) + , m_hasTextDirectionOverride(hasTextDirectionOverride) { } @@ -53,6 +54,7 @@ public: bool isDisplayNone() const { return m_isDisplayNone; } Length textIndent() const { return m_textIndent; } TextDirection textDirection() const { return m_textDirection; } + bool hasTextDirectionOverride() const { return m_hasTextDirectionOverride; } private: Color m_foregroundColor; @@ -62,6 +64,7 @@ private: bool m_isDisplayNone; Length m_textIndent; TextDirection m_textDirection; + bool m_hasTextDirectionOverride; }; } // namespace WebCore diff --git a/Source/WebCore/platform/SchemeRegistry.cpp b/Source/WebCore/platform/SchemeRegistry.cpp index 71697cb..2bc655e 100644 --- a/Source/WebCore/platform/SchemeRegistry.cpp +++ b/Source/WebCore/platform/SchemeRegistry.cpp @@ -86,6 +86,24 @@ static URLSchemesMap& emptyDocumentSchemes() return emptyDocumentSchemes; } +static URLSchemesMap& canDisplayOnlyIfCanRequestSchemes() +{ + DEFINE_STATIC_LOCAL(URLSchemesMap, canDisplayOnlyIfCanRequestSchemes, ()); + +#if ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + if (canDisplayOnlyIfCanRequestSchemes.isEmpty()) { +#if ENABLE(BLOB) + canDisplayOnlyIfCanRequestSchemes.add("blob"); +#endif +#if ENABLE(FILE_SYSTEM) + canDisplayOnlyIfCanRequestSchemes.add("filesystem"); +#endif + } +#endif // ENABLE(BLOB) || ENABLE(FILE_SYSTEM) + + return canDisplayOnlyIfCanRequestSchemes; +} + void SchemeRegistry::registerURLSchemeAsLocal(const String& scheme) { localURLSchemes().add(scheme); @@ -121,6 +139,8 @@ void SchemeRegistry::registerURLSchemeAsNoAccess(const String& scheme) bool SchemeRegistry::shouldTreatURLSchemeAsNoAccess(const String& scheme) { + if (scheme.isEmpty()) + return false; return schemesWithUniqueOrigins().contains(scheme); } @@ -131,6 +151,8 @@ void SchemeRegistry::registerURLSchemeAsDisplayIsolated(const String& scheme) bool SchemeRegistry::shouldTreatURLSchemeAsDisplayIsolated(const String& scheme) { + if (scheme.isEmpty()) + return false; return displayIsolatedURLSchemes().contains(scheme); } @@ -141,6 +163,8 @@ void SchemeRegistry::registerURLSchemeAsSecure(const String& scheme) bool SchemeRegistry::shouldTreatURLSchemeAsSecure(const String& scheme) { + if (scheme.isEmpty()) + return false; return secureSchemes().contains(scheme); } @@ -151,7 +175,21 @@ void SchemeRegistry::registerURLSchemeAsEmptyDocument(const String& scheme) bool SchemeRegistry::shouldLoadURLSchemeAsEmptyDocument(const String& scheme) { + if (scheme.isEmpty()) + return false; return emptyDocumentSchemes().contains(scheme); } +bool SchemeRegistry::canDisplayOnlyIfCanRequest(const String& scheme) +{ + if (scheme.isEmpty()) + return false; + return canDisplayOnlyIfCanRequestSchemes().contains(scheme); +} + +void SchemeRegistry::registerAsCanDisplayOnlyIfCanRequest(const String& scheme) +{ + canDisplayOnlyIfCanRequestSchemes().add(scheme); +} + } // namespace WebCore diff --git a/Source/WebCore/platform/SchemeRegistry.h b/Source/WebCore/platform/SchemeRegistry.h index 530fcab..c9cb476 100644 --- a/Source/WebCore/platform/SchemeRegistry.h +++ b/Source/WebCore/platform/SchemeRegistry.h @@ -58,6 +58,11 @@ public: static void registerURLSchemeAsEmptyDocument(const String&); static bool shouldLoadURLSchemeAsEmptyDocument(const String&); + + // Such schemes should delegate to SecurityOrigin::canRequest for any URL + // passed to SecurityOrigin::canDisplay. + static bool canDisplayOnlyIfCanRequest(const String& scheme); + static void registerAsCanDisplayOnlyIfCanRequest(const String& scheme); }; } // namespace WebCore diff --git a/Source/WebCore/platform/ScrollAnimator.cpp b/Source/WebCore/platform/ScrollAnimator.cpp index 428a79d..6fc78e7 100644 --- a/Source/WebCore/platform/ScrollAnimator.cpp +++ b/Source/WebCore/platform/ScrollAnimator.cpp @@ -32,10 +32,13 @@ #include "ScrollAnimator.h" #include "FloatPoint.h" +#include "PlatformWheelEvent.h" #include "ScrollableArea.h" #include <algorithm> #include <wtf/PassOwnPtr.h> +using namespace std; + namespace WebCore { #if !ENABLE(SMOOTH_SCROLLING) @@ -78,6 +81,44 @@ void ScrollAnimator::scrollToOffsetWithoutAnimation(const FloatPoint& offset) } } +void ScrollAnimator::handleWheelEvent(PlatformWheelEvent& e) +{ + Scrollbar* horizontalScrollbar = m_scrollableArea->horizontalScrollbar(); + Scrollbar* verticalScrollbar = m_scrollableArea->verticalScrollbar(); + + // Accept the event if we have a scrollbar in that direction and can still + // scroll any further. + float deltaX = horizontalScrollbar ? e.deltaX() : 0; + float deltaY = verticalScrollbar ? e.deltaY() : 0; + + IntSize maxForwardScrollDelta = m_scrollableArea->maximumScrollPosition() - m_scrollableArea->scrollPosition(); + IntSize maxBackwardScrollDelta = m_scrollableArea->scrollPosition() - m_scrollableArea->minimumScrollPosition(); + if ((deltaX < 0 && maxForwardScrollDelta.width() > 0) + || (deltaX > 0 && maxBackwardScrollDelta.width() > 0) + || (deltaY < 0 && maxForwardScrollDelta.height() > 0) + || (deltaY > 0 && maxBackwardScrollDelta.height() > 0)) { + e.accept(); + if (e.granularity() == ScrollByPageWheelEvent) { + ASSERT(!e.deltaX()); + bool negative = deltaY < 0; + deltaY = max(max(static_cast<float>(m_scrollableArea->visibleHeight()) * Scrollbar::minFractionToStepWhenPaging(), static_cast<float>(m_scrollableArea->visibleHeight() - Scrollbar::maxOverlapBetweenPages())), 1.0f); + if (negative) + deltaY = -deltaY; + } + + if (deltaY) + scroll(VerticalScrollbar, ScrollByPixel, verticalScrollbar->pixelStep(), -deltaY); + if (deltaX) + scroll(HorizontalScrollbar, ScrollByPixel, horizontalScrollbar->pixelStep(), -deltaX); + } +} + +#if ENABLE(GESTURE_EVENTS) +void ScrollAnimator::handleGestureEvent(const PlatformGestureEvent&) +{ +} +#endif + FloatPoint ScrollAnimator::currentPosition() const { return FloatPoint(m_currentPosX, m_currentPosY); diff --git a/Source/WebCore/platform/ScrollAnimator.h b/Source/WebCore/platform/ScrollAnimator.h index 155c6e5..060511c 100644 --- a/Source/WebCore/platform/ScrollAnimator.h +++ b/Source/WebCore/platform/ScrollAnimator.h @@ -37,7 +37,13 @@ namespace WebCore { class FloatPoint; +class PlatformWheelEvent; class ScrollableArea; +class Scrollbar; + +#if ENABLE(GESTURE_EVENTS) +class PlatformGestureEvent; +#endif class ScrollAnimator { public: @@ -53,12 +59,34 @@ public: virtual void scrollToOffsetWithoutAnimation(const FloatPoint&); + ScrollableArea* scrollableArea() const { return m_scrollableArea; } + + virtual void handleWheelEvent(PlatformWheelEvent&); +#if ENABLE(GESTURE_EVENTS) + virtual void handleGestureEvent(const PlatformGestureEvent&); +#endif + FloatPoint currentPosition() const; + virtual void contentAreaWillPaint() const { } + virtual void mouseEnteredContentArea() const { } + virtual void mouseExitedContentArea() const { } + virtual void mouseMovedInContentArea() const { } + virtual void willStartLiveResize() { } + virtual void contentsResized() const { } + virtual void willEndLiveResize() { } + virtual void contentAreaDidShow() const { } + virtual void contentAreaDidHide() const { } + + virtual void didAddVerticalScrollbar(Scrollbar*) { } + virtual void willRemoveVerticalScrollbar(Scrollbar*) { } + virtual void didAddHorizontalScrollbar(Scrollbar*) { } + virtual void willRemoveHorizontalScrollbar(Scrollbar*) { } + protected: ScrollAnimator(ScrollableArea*); - void notityPositionChanged(); + virtual void notityPositionChanged(); ScrollableArea* m_scrollableArea; float m_currentPosX; // We avoid using a FloatPoint in order to reduce diff --git a/Source/WebCore/platform/ScrollView.cpp b/Source/WebCore/platform/ScrollView.cpp index b07c743..cab58e4 100644 --- a/Source/WebCore/platform/ScrollView.cpp +++ b/Source/WebCore/platform/ScrollView.cpp @@ -31,12 +31,12 @@ #include "HostWindow.h" #include "PlatformMouseEvent.h" #include "PlatformWheelEvent.h" +#include "ScrollAnimator.h" #include "Scrollbar.h" #include "ScrollbarTheme.h" #include <wtf/StdLibExtras.h> - -using std::max; +using namespace std; namespace WebCore { @@ -92,8 +92,10 @@ void ScrollView::setHasHorizontalScrollbar(bool hasBar) if (hasBar && !m_horizontalScrollbar) { m_horizontalScrollbar = createScrollbar(HorizontalScrollbar); addChild(m_horizontalScrollbar.get()); + ScrollableArea::didAddHorizontalScrollbar(m_horizontalScrollbar.get()); m_horizontalScrollbar->styleChanged(); } else if (!hasBar && m_horizontalScrollbar) { + ScrollableArea::willRemoveHorizontalScrollbar(m_horizontalScrollbar.get()); removeChild(m_horizontalScrollbar.get()); m_horizontalScrollbar = 0; } @@ -110,8 +112,10 @@ void ScrollView::setHasVerticalScrollbar(bool hasBar) if (hasBar && !m_verticalScrollbar) { m_verticalScrollbar = createScrollbar(VerticalScrollbar); addChild(m_verticalScrollbar.get()); + ScrollableArea::didAddVerticalScrollbar(m_verticalScrollbar.get()); m_verticalScrollbar->styleChanged(); } else if (!hasBar && m_verticalScrollbar) { + ScrollableArea::willRemoveVerticalScrollbar(m_verticalScrollbar.get()); removeChild(m_verticalScrollbar.get()); m_verticalScrollbar = 0; } @@ -344,16 +348,49 @@ IntPoint ScrollView::adjustScrollPositionWithinRange(const IntPoint& scrollPoint return newScrollPosition; } +int ScrollView::scrollXForFixedPosition() const +{ + int x = scrollX(); + if (x < 0) + x = 0; + else if (x > contentsWidth() - visibleContentRect().width()) + x = contentsWidth() - visibleContentRect().width(); + return x; +} + +int ScrollView::scrollYForFixedPosition() const +{ + int y = scrollY(); + if (y < 0) + y = 0; + else if (y > contentsHeight() - visibleContentRect().height()) + y = contentsHeight() - visibleContentRect().height(); + return y; +} + +IntSize ScrollView::scrollOffsetForFixedPosition() const +{ + return IntSize(scrollXForFixedPosition(), scrollYForFixedPosition()); +} + int ScrollView::scrollSize(ScrollbarOrientation orientation) const { Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_horizontalScrollbar : m_verticalScrollbar).get(); return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0; } +void ScrollView::didCompleteRubberBand(const IntSize&) const +{ +} + void ScrollView::setScrollOffset(const IntPoint& offset) { - int horizontalOffset = std::max(std::min(offset.x(), contentsWidth() - visibleWidth()), 0); - int verticalOffset = std::max(std::min(offset.y(), contentsHeight() - visibleHeight()), 0); + int horizontalOffset = offset.x(); + int verticalOffset = offset.y(); + if (constrainsScrollingToContentEdge()) { + horizontalOffset = max(min(horizontalOffset, contentsWidth() - visibleWidth()), 0); + verticalOffset = max(min(verticalOffset, contentsHeight() - visibleHeight()), 0); + } IntSize newOffset = m_scrollOffset; newOffset.setWidth(horizontalOffset - m_scrollOrigin.x()); @@ -425,6 +462,22 @@ bool ScrollView::logicalScroll(ScrollLogicalDirection direction, ScrollGranulari return scroll(logicalToPhysical(direction, isVerticalDocument(), isFlippedDocument()), granularity); } +IntSize ScrollView::overhangAmount() const +{ + IntSize stretch; + if (scrollY() < 0) + stretch.setHeight(scrollY()); + else if (scrollY() > contentsHeight() - visibleContentRect().height()) + stretch.setHeight(scrollY() - (contentsHeight() - visibleContentRect().height())); + + if (scrollX() < 0) + stretch.setWidth(scrollX()); + else if (scrollX() > contentsWidth() - visibleContentRect().width()) + stretch.setWidth(scrollX() - (contentsWidth() - visibleContentRect().width())); + + return stretch; +} + void ScrollView::windowResizerRectChanged() { if (platformWidget()) @@ -528,7 +581,7 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset) m_inUpdateScrollbars = true; - IntPoint scrollPoint = adjustScrollPositionWithinRange(IntPoint(desiredOffset.width(), desiredOffset.height())); + IntPoint scrollPoint = adjustScrollPositionWithinRange(IntPoint(desiredOffset)); IntSize scroll(scrollPoint.x(), scrollPoint.y()); if (m_horizontalScrollbar) { @@ -580,6 +633,12 @@ void ScrollView::updateScrollbars(const IntSize& desiredOffset) ScrollableArea::scrollToOffsetWithoutAnimation(FloatPoint(scroll.width() + m_scrollOrigin.x(), scroll.height() + m_scrollOrigin.y())); + // Make sure the scrollbar offsets are up to date. + if (m_horizontalScrollbar) + m_horizontalScrollbar->offsetDidChange(); + if (m_verticalScrollbar) + m_verticalScrollbar->offsetDidChange(); + m_inUpdateScrollbars = false; } @@ -594,6 +653,14 @@ void ScrollView::scrollContents(const IntSize& scrollDelta) // with the clip rect every time to keep it smooth. IntRect clipRect = windowClipRect(); IntRect scrollViewRect = convertToContainingWindow(IntRect(0, 0, visibleWidth(), visibleHeight())); + if (ScrollbarTheme::nativeTheme()->usesOverlayScrollbars()) { + int verticalScrollbarWidth = verticalScrollbar() ? verticalScrollbar()->width() : 0; + int horizontalScrollbarHeight = horizontalScrollbar() ? horizontalScrollbar()->height() : 0; + + scrollViewRect.setWidth(scrollViewRect.width() - verticalScrollbarWidth); + scrollViewRect.setHeight(scrollViewRect.height() - horizontalScrollbarHeight); + } + IntRect updateRect = clipRect; updateRect.intersect(scrollViewRect); @@ -761,32 +828,18 @@ void ScrollView::wheelEvent(PlatformWheelEvent& e) return; } - // Accept the event if we have a scrollbar in that direction and can still - // scroll any further. - float deltaX = m_horizontalScrollbar ? e.deltaX() : 0; - float deltaY = m_verticalScrollbar ? e.deltaY() : 0; - - IntSize maxForwardScrollDelta = maximumScrollPosition() - scrollPosition(); - IntSize maxBackwardScrollDelta = scrollPosition() - minimumScrollPosition(); - if ((deltaX < 0 && maxForwardScrollDelta.width() > 0) - || (deltaX > 0 && maxBackwardScrollDelta.width() >0) - || (deltaY < 0 && maxForwardScrollDelta.height() > 0) - || (deltaY > 0 && maxBackwardScrollDelta.height() > 0)) { - e.accept(); - if (e.granularity() == ScrollByPageWheelEvent) { - ASSERT(!e.deltaX()); - bool negative = deltaY < 0; - deltaY = max(max(static_cast<float>(visibleHeight()) * Scrollbar::minFractionToStepWhenPaging(), static_cast<float>(visibleHeight() - Scrollbar::maxOverlapBetweenPages())), 1.0f); - if (negative) - deltaY = -deltaY; - } + ScrollableArea::handleWheelEvent(e); +} - if (deltaY) - ScrollableArea::scroll(ScrollUp, ScrollByPixel, deltaY); - if (deltaX) - ScrollableArea::scroll(ScrollLeft, ScrollByPixel, deltaX); - } +#if ENABLE(GESTURE_EVENTS) +void ScrollView::gestureEvent(const PlatformGestureEvent& gestureEvent) +{ + if (platformWidget()) + return; + + ScrollableArea::handleGestureEvent(gestureEvent); } +#endif void ScrollView::setFrameRect(const IntRect& newRect) { @@ -898,6 +951,8 @@ void ScrollView::paint(GraphicsContext* context, const IntRect& rect) if (context->paintingDisabled() && !context->updatingControlTints()) return; + scrollAnimator()->contentAreaWillPaint(); + IntRect documentDirtyRect = rect; documentDirtyRect.intersect(frameRect()); @@ -917,6 +972,13 @@ void ScrollView::paint(GraphicsContext* context, const IntRect& rect) context->restore(); + IntRect horizontalOverhangRect; + IntRect verticalOverhangRect; + calculateOverhangAreasForPainting(horizontalOverhangRect, verticalOverhangRect); + + if (rect.intersects(horizontalOverhangRect) || rect.intersects(verticalOverhangRect)) + paintOverhangAreas(context, horizontalOverhangRect, verticalOverhangRect, rect); + // Now paint the scrollbars. if (!m_scrollbarsSuppressed && (m_horizontalScrollbar || m_verticalScrollbar)) { context->save(); @@ -935,6 +997,55 @@ void ScrollView::paint(GraphicsContext* context, const IntRect& rect) paintPanScrollIcon(context); } +void ScrollView::calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect) +{ + bool hasOverlayScrollbars = ScrollbarTheme::nativeTheme()->usesOverlayScrollbars(); + int verticalScrollbarWidth = (verticalScrollbar() && !hasOverlayScrollbars) ? verticalScrollbar()->width() : 0; + int horizontalScrollbarHeight = (horizontalScrollbar() && !hasOverlayScrollbars) ? horizontalScrollbar()->height() : 0; + + if (scrollY() < 0) { + horizontalOverhangRect = frameRect(); + horizontalOverhangRect.setHeight(-scrollY()); + } else if (scrollY() > contentsHeight() - visibleContentRect().height()) { + int height = scrollY() - (contentsHeight() - visibleContentRect().height()); + horizontalOverhangRect = frameRect(); + horizontalOverhangRect.setY(frameRect().maxY() - height - horizontalScrollbarHeight); + horizontalOverhangRect.setHeight(height); + } + + if (scrollX() < 0) { + verticalOverhangRect.setWidth(-scrollX()); + verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height()); + verticalOverhangRect.setX(frameRect().x()); + if (horizontalOverhangRect.y() == frameRect().y()) + verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height()); + else + verticalOverhangRect.setY(frameRect().y()); + } else if (scrollX() > contentsWidth() - visibleContentRect().width()) { + int width = scrollX() - (contentsWidth() - visibleContentRect().width()); + verticalOverhangRect.setWidth(width); + verticalOverhangRect.setHeight(frameRect().height() - horizontalOverhangRect.height()); + verticalOverhangRect.setX(frameRect().maxX() - width - verticalScrollbarWidth); + if (horizontalOverhangRect.y() == frameRect().y()) + verticalOverhangRect.setY(frameRect().y() + horizontalOverhangRect.height()); + else + verticalOverhangRect.setY(frameRect().y()); + } +} + +void ScrollView::paintOverhangAreas(GraphicsContext* context, const IntRect& horizontalOverhangRect, const IntRect& verticalOverhangRect, const IntRect&) +{ + // FIXME: This should be checking the dirty rect. + + context->setFillColor(Color::white, ColorSpaceDeviceRGB); + if (!horizontalOverhangRect.isEmpty()) + context->fillRect(horizontalOverhangRect); + + context->setFillColor(Color::white, ColorSpaceDeviceRGB); + if (!verticalOverhangRect.isEmpty()) + context->fillRect(verticalOverhangRect); +} + bool ScrollView::isPointInScrollbarCorner(const IntPoint& windowPoint) { if (!scrollbarCornerPresent()) @@ -1070,7 +1181,7 @@ void ScrollView::removePanScrollIcon() hostWindow()->invalidateContentsAndWindow(IntRect(m_panScrollIconPoint, IntSize(panIconSizeLength, panIconSizeLength)), true /*immediate*/); } -void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePosition) +void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously) { if (m_scrollOrigin == origin) return; @@ -1078,12 +1189,12 @@ void ScrollView::setScrollOrigin(const IntPoint& origin, bool updatePosition) m_scrollOrigin = origin; if (platformWidget()) { - platformSetScrollOrigin(origin, updatePosition); + platformSetScrollOrigin(origin, updatePositionAtAll, updatePositionSynchronously); return; } // Update if the scroll origin changes, since our position will be different if the content size did not change. - if (updatePosition) + if (updatePositionAtAll && updatePositionSynchronously) updateScrollbars(scrollOffset()); } @@ -1117,7 +1228,7 @@ void ScrollView::platformSetScrollbarsSuppressed(bool) { } -void ScrollView::platformSetScrollOrigin(const IntPoint&, bool updatePosition) +void ScrollView::platformSetScrollOrigin(const IntPoint&, bool updatePositionAtAll, bool updatePositionSynchronously) { } diff --git a/Source/WebCore/platform/ScrollView.h b/Source/WebCore/platform/ScrollView.h index cb895f6..2bd1447 100644 --- a/Source/WebCore/platform/ScrollView.h +++ b/Source/WebCore/platform/ScrollView.h @@ -51,7 +51,6 @@ class wxScrollWinEvent; namespace WebCore { class HostWindow; -class PlatformWheelEvent; class Scrollbar; class ScrollView : public Widget, public ScrollableArea { @@ -62,7 +61,8 @@ public: virtual int scrollSize(ScrollbarOrientation orientation) const; virtual int scrollPosition(Scrollbar*) const; virtual void setScrollOffset(const IntPoint&); - + virtual void didCompleteRubberBand(const IntSize&) const; + // NOTE: This should only be called by the overriden setScrollOffset from ScrollableArea. virtual void scrollTo(const IntSize& newOffset); @@ -162,6 +162,7 @@ public: int contentsWidth() const { return contentsSize().width(); } int contentsHeight() const { return contentsSize().height(); } virtual void setContentsSize(const IntSize&); +<<<<<<< HEAD #if PLATFORM(ANDROID) int actualWidth() const; @@ -169,6 +170,8 @@ public: int actualScrollX() const; int actualScrollY() const; #endif +======= +>>>>>>> webkit.org at r78450 // Functions for querying the current scrolled position (both as a point, a size, or as individual X and Y values). IntPoint scrollPosition() const { return visibleContentRect().location(); } @@ -179,7 +182,14 @@ public: IntPoint adjustScrollPositionWithinRange(const IntPoint&) const; int scrollX() const { return scrollPosition().x(); } int scrollY() const { return scrollPosition().y(); } - + + // Functions for querying the current scrolled position, negating the effects of overhang. + int scrollXForFixedPosition() const; + int scrollYForFixedPosition() const; + IntSize scrollOffsetForFixedPosition() const; + + IntSize overhangAmount() const; + // Functions for scrolling the view. void setScrollPosition(const IntPoint&); void scrollBy(const IntSize& s) { return setScrollPosition(scrollPosition() + s); } @@ -235,6 +245,9 @@ public: // On Mac the underlying NSScrollView just does the scrolling, but on other platforms // (like Windows), we need this function in order to do the scroll ourselves. void wheelEvent(PlatformWheelEvent&); +#if ENABLE(GESTURE_EVENTS) + void gestureEvent(const PlatformGestureEvent&); +#endif IntPoint convertChildToSelf(const Widget* child, const IntPoint& point) const { @@ -282,7 +295,10 @@ protected: virtual void repaintContentRectangle(const IntRect&, bool now = false); virtual void paintContents(GraphicsContext*, const IntRect& damageRect) = 0; - + + void calculateOverhangAreasForPainting(IntRect& horizontalOverhangRect, IntRect& verticalOverhangRect); + virtual void paintOverhangAreas(GraphicsContext*, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect); + virtual void contentsResized() = 0; virtual void visibleContentsResized() = 0; @@ -299,7 +315,7 @@ protected: // Scroll the content by invalidating everything. virtual void scrollContentsSlowPath(const IntRect& updateRect); - void setScrollOrigin(const IntPoint&, bool updatePosition); + void setScrollOrigin(const IntPoint&, bool updatePositionAtAll, bool updatePositionSynchronously); IntPoint scrollOrigin() { return m_scrollOrigin; } // Subclassed by FrameView to check the writing-mode of the document. @@ -383,7 +399,7 @@ private: void platformRepaintContentRectangle(const IntRect&, bool now); bool platformIsOffscreen() const; - void platformSetScrollOrigin(const IntPoint&, bool updatePosition); + void platformSetScrollOrigin(const IntPoint&, bool updatePositionAtAll, bool updatePositionSynchronously); #if PLATFORM(ANDROID) int platformActualWidth() const; diff --git a/Source/WebCore/platform/ScrollableArea.cpp b/Source/WebCore/platform/ScrollableArea.cpp index 176cb7e..00017bd 100644 --- a/Source/WebCore/platform/ScrollableArea.cpp +++ b/Source/WebCore/platform/ScrollableArea.cpp @@ -35,12 +35,15 @@ #include "FloatPoint.h" #include "PlatformWheelEvent.h" #include "ScrollAnimator.h" +#include "ScrollbarTheme.h" #include <wtf/PassOwnPtr.h> namespace WebCore { ScrollableArea::ScrollableArea() : m_scrollAnimator(ScrollAnimator::create(this)) + , m_constrainsScrollingToContentEdge(true) + , m_inLiveResize(false) { } @@ -108,16 +111,72 @@ void ScrollableArea::scrollToYOffsetWithoutAnimation(float y) scrollToOffsetWithoutAnimation(FloatPoint(m_scrollAnimator->currentPosition().x(), y)); } +void ScrollableArea::handleWheelEvent(PlatformWheelEvent& wheelEvent) +{ + m_scrollAnimator->handleWheelEvent(wheelEvent); +} + +#if ENABLE(GESTURE_EVENTS) +void ScrollableArea::handleGestureEvent(const PlatformGestureEvent& gestureEvent) +{ + m_scrollAnimator->handleGestureEvent(gestureEvent); +} +#endif + void ScrollableArea::setScrollOffsetFromAnimation(const IntPoint& offset) { // Tell the derived class to scroll its contents. setScrollOffset(offset); + bool hasOverlayScrollbars = ScrollbarTheme::nativeTheme()->usesOverlayScrollbars(); + // Tell the scrollbars to update their thumb postions. - if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) + if (Scrollbar* horizontalScrollbar = this->horizontalScrollbar()) { horizontalScrollbar->offsetDidChange(); - if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) + if (hasOverlayScrollbars) + horizontalScrollbar->invalidate(); + } + if (Scrollbar* verticalScrollbar = this->verticalScrollbar()) { verticalScrollbar->offsetDidChange(); + if (hasOverlayScrollbars) + verticalScrollbar->invalidate(); + } +} + +void ScrollableArea::willStartLiveResize() +{ + if (m_inLiveResize) + return; + m_inLiveResize = true; + scrollAnimator()->willStartLiveResize(); +} + +void ScrollableArea::willEndLiveResize() +{ + if (!m_inLiveResize) + return; + m_inLiveResize = false; + scrollAnimator()->willEndLiveResize(); +} + +void ScrollableArea::didAddVerticalScrollbar(Scrollbar* scrollbar) +{ + scrollAnimator()->didAddVerticalScrollbar(scrollbar); +} + +void ScrollableArea::willRemoveVerticalScrollbar(Scrollbar* scrollbar) +{ + scrollAnimator()->willRemoveVerticalScrollbar(scrollbar); +} + +void ScrollableArea::didAddHorizontalScrollbar(Scrollbar* scrollbar) +{ + scrollAnimator()->didAddHorizontalScrollbar(scrollbar); +} + +void ScrollableArea::willRemoveHorizontalScrollbar(Scrollbar* scrollbar) +{ + scrollAnimator()->willRemoveHorizontalScrollbar(scrollbar); } } // namespace WebCore diff --git a/Source/WebCore/platform/ScrollableArea.h b/Source/WebCore/platform/ScrollableArea.h index 148ecdb..9839abc 100644 --- a/Source/WebCore/platform/ScrollableArea.h +++ b/Source/WebCore/platform/ScrollableArea.h @@ -33,6 +33,7 @@ namespace WebCore { class FloatPoint; +class PlatformGestureEvent; class PlatformWheelEvent; class ScrollAnimator; @@ -47,6 +48,26 @@ public: void scrollToXOffsetWithoutAnimation(float x); void scrollToYOffsetWithoutAnimation(float x); + void handleWheelEvent(PlatformWheelEvent&); +#if ENABLE(GESTURE_EVENTS) + void handleGestureEvent(const PlatformGestureEvent&); +#endif + + // Functions for controlling if you can scroll past the end of the document. + bool constrainsScrollingToContentEdge() const { return m_constrainsScrollingToContentEdge; } + void setConstrainsScrollingToContentEdge(bool constrainsScrollingToContentEdge) { m_constrainsScrollingToContentEdge = constrainsScrollingToContentEdge; } + + bool inLiveResize() const { return m_inLiveResize; } + void willStartLiveResize(); + void willEndLiveResize(); + + void didAddVerticalScrollbar(Scrollbar*); + void willRemoveVerticalScrollbar(Scrollbar*); + void didAddHorizontalScrollbar(Scrollbar*); + void willRemoveHorizontalScrollbar(Scrollbar*); + + ScrollAnimator* scrollAnimator() const { return m_scrollAnimator.get(); } + virtual int scrollSize(ScrollbarOrientation) const = 0; virtual int scrollPosition(Scrollbar*) const = 0; virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&) = 0; @@ -58,7 +79,6 @@ public: // scroll of the content. virtual void setScrollOffset(const IntPoint&) = 0; - // Convert points and rects between the scrollbar and its containing view. // The client needs to implement these in order to be aware of layout effects // like CSS transforms. @@ -82,12 +102,28 @@ public: virtual Scrollbar* horizontalScrollbar() const { return 0; } virtual Scrollbar* verticalScrollbar() const { return 0; } + virtual IntPoint scrollPosition() const { ASSERT_NOT_REACHED(); return IntPoint(); } + virtual IntPoint minimumScrollPosition() const { ASSERT_NOT_REACHED(); return IntPoint(); } + virtual IntPoint maximumScrollPosition() const { ASSERT_NOT_REACHED(); return IntPoint(); } + virtual IntRect visibleContentRect(bool = false) const { ASSERT_NOT_REACHED(); return IntRect(); } + virtual int visibleHeight() const { ASSERT_NOT_REACHED(); return 0; } + virtual int visibleWidth() const { ASSERT_NOT_REACHED(); return 0; } + virtual IntSize contentsSize() const { ASSERT_NOT_REACHED(); return IntSize(); } + virtual IntSize overhangAmount() const { ASSERT_NOT_REACHED(); return IntSize(); } + virtual IntPoint currentMousePosition() const { return IntPoint(); } + virtual void didCompleteRubberBand(const IntSize&) const { ASSERT_NOT_REACHED(); } + + virtual bool scrollbarWillRenderIntoCompositingLayer() const { return false; } + private: // NOTE: Only called from the ScrollAnimator. friend class ScrollAnimator; void setScrollOffsetFromAnimation(const IntPoint&); OwnPtr<ScrollAnimator> m_scrollAnimator; + bool m_constrainsScrollingToContentEdge; + + bool m_inLiveResize; }; } // namespace WebCore diff --git a/Source/WebCore/platform/Scrollbar.cpp b/Source/WebCore/platform/Scrollbar.cpp index 4c625f4..5d9a43d 100644 --- a/Source/WebCore/platform/Scrollbar.cpp +++ b/Source/WebCore/platform/Scrollbar.cpp @@ -196,7 +196,7 @@ void Scrollbar::autoscrollPressedPart(double delay) } // Handle the arrows and track. - if (scrollableArea()->scroll(pressedPartScrollDirection(), pressedPartScrollGranularity())) + if (m_scrollableArea && m_scrollableArea->scroll(pressedPartScrollDirection(), pressedPartScrollGranularity())) startTimerIfNeeded(delay); } @@ -268,7 +268,8 @@ void Scrollbar::moveThumb(int pos) if (delta) { float newPosition = static_cast<float>(thumbPos + delta) * maximum() / (trackLen - thumbLen); - scrollableArea()->scrollToOffsetWithoutAnimation(m_orientation, newPosition); + if (m_scrollableArea) + m_scrollableArea->scrollToOffsetWithoutAnimation(m_orientation, newPosition); } } @@ -300,9 +301,10 @@ void Scrollbar::setPressedPart(ScrollbarPart part) bool Scrollbar::mouseMoved(const PlatformMouseEvent& evt) { if (m_pressedPart == ThumbPart) { - if (theme()->shouldSnapBackToDragOrigin(this, evt)) - scrollableArea()->scrollToOffsetWithoutAnimation(m_orientation, m_dragOrigin); - else { + if (theme()->shouldSnapBackToDragOrigin(this, evt)) { + if (m_scrollableArea) + m_scrollableArea->scrollToOffsetWithoutAnimation(m_orientation, m_dragOrigin); + } else { moveThumb(m_orientation == HorizontalScrollbar ? convertFromContainingWindow(evt.pos()).x() : convertFromContainingWindow(evt.pos()).y()); @@ -393,14 +395,14 @@ void Scrollbar::setFrameRect(const IntRect& rect) IntRect resizerRect = view->convertFromContainingWindow(view->windowResizerRect()); if (rect.intersects(resizerRect)) { if (orientation() == HorizontalScrollbar) { - int overlap = rect.right() - resizerRect.x(); - if (overlap > 0 && resizerRect.right() >= rect.right()) { + int overlap = rect.maxX() - resizerRect.x(); + if (overlap > 0 && resizerRect.maxX() >= rect.maxX()) { adjustedRect.setWidth(rect.width() - overlap); overlapsResizer = true; } } else { - int overlap = rect.bottom() - resizerRect.y(); - if (overlap > 0 && resizerRect.bottom() >= rect.bottom()) { + int overlap = rect.maxY() - resizerRect.y(); + if (overlap > 0 && resizerRect.maxY() >= rect.maxY()) { adjustedRect.setHeight(rect.height() - overlap); overlapsResizer = true; } diff --git a/Source/WebCore/platform/ScrollbarThemeComposite.cpp b/Source/WebCore/platform/ScrollbarThemeComposite.cpp index 7bc266f..26f1494 100644 --- a/Source/WebCore/platform/ScrollbarThemeComposite.cpp +++ b/Source/WebCore/platform/ScrollbarThemeComposite.cpp @@ -37,6 +37,8 @@ #include "ScrollableArea.h" #include "Settings.h" +using namespace std; + namespace WebCore { #if PLATFORM(WIN) @@ -246,18 +248,28 @@ void ScrollbarThemeComposite::splitTrack(Scrollbar* scrollbar, const IntRect& un if (scrollbar->orientation() == HorizontalScrollbar) { thumbRect = IntRect(trackRect.x() + thumbPos, trackRect.y() + (trackRect.height() - thickness) / 2, thumbLength(scrollbar), thickness); beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), thumbPos + thumbRect.width() / 2, trackRect.height()); - afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.right() - beforeThumbRect.right(), trackRect.height()); + afterThumbRect = IntRect(trackRect.x() + beforeThumbRect.width(), trackRect.y(), trackRect.maxX() - beforeThumbRect.maxX(), trackRect.height()); } else { thumbRect = IntRect(trackRect.x() + (trackRect.width() - thickness) / 2, trackRect.y() + thumbPos, thickness, thumbLength(scrollbar)); beforeThumbRect = IntRect(trackRect.x(), trackRect.y(), trackRect.width(), thumbPos + thumbRect.height() / 2); - afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.bottom() - beforeThumbRect.bottom()); + afterThumbRect = IntRect(trackRect.x(), trackRect.y() + beforeThumbRect.height(), trackRect.width(), trackRect.maxY() - beforeThumbRect.maxY()); } } +// Returns the size represented by track taking into account scrolling past +// the end of the document. +static float usedTotalSize(Scrollbar* scrollbar) +{ + float overhangAtStart = -scrollbar->currentPos(); + float overhangAtEnd = scrollbar->currentPos() + scrollbar->visibleSize() - scrollbar->totalSize(); + float overhang = max(0.0f, max(overhangAtStart, overhangAtEnd)); + return scrollbar->totalSize() + overhang; +} + int ScrollbarThemeComposite::thumbPosition(Scrollbar* scrollbar) { if (scrollbar->enabled()) - return scrollbar->currentPos() * (trackLength(scrollbar) - thumbLength(scrollbar)) / scrollbar->maximum(); + return max(0.0f, scrollbar->currentPos()) * (trackLength(scrollbar) - thumbLength(scrollbar)) / (usedTotalSize(scrollbar) - scrollbar->visibleSize()); return 0; } @@ -266,7 +278,7 @@ int ScrollbarThemeComposite::thumbLength(Scrollbar* scrollbar) if (!scrollbar->enabled()) return 0; - float proportion = (float)scrollbar->visibleSize() / scrollbar->totalSize(); + float proportion = scrollbar->visibleSize() / usedTotalSize(scrollbar); int trackLen = trackLength(scrollbar); int length = proportion * trackLen; length = max(length, minimumThumbLength(scrollbar)); diff --git a/Source/WebCore/platform/URLString.h b/Source/WebCore/platform/URLString.h new file mode 100644 index 0000000..78e4b33 --- /dev/null +++ b/Source/WebCore/platform/URLString.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 GOOGLE, 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 URLString_h +#define URLString_h + +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class KURL; + +class URLString { +public: + const String& string() const { return m_string; } + +private: + friend class KURL; + + explicit URLString(const String& string) + : m_string(string) + { + } + + String m_string; +}; + +} + +#endif diff --git a/Source/WebCore/platform/android/TemporaryLinkStubs.cpp b/Source/WebCore/platform/android/TemporaryLinkStubs.cpp index d3c8183..cf15462 100644 --- a/Source/WebCore/platform/android/TemporaryLinkStubs.cpp +++ b/Source/WebCore/platform/android/TemporaryLinkStubs.cpp @@ -76,6 +76,8 @@ #include "Widget.h" #include <stdio.h> #include <stdlib.h> +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> #include <wtf/text/CString.h> #if USE(JSC) @@ -296,6 +298,16 @@ void* WebCore::Frame::dragImageForSelection() return 0; } +<<<<<<< HEAD +======= + +WTF::String WebCore::MIMETypeRegistry::getMIMETypeForExtension(WTF::String const&) +{ + ASSERT(isMainThread()); + return WTF::String(); +} + +>>>>>>> webkit.org at r78450 void WebCore::Pasteboard::writeImage(WebCore::Node*, WebCore::KURL const&, WTF::String const&) {} namespace WebCore { diff --git a/Source/WebCore/platform/audio/FFTFrame.h b/Source/WebCore/platform/audio/FFTFrame.h index 1a82ef0..f6e2066 100644 --- a/Source/WebCore/platform/audio/FFTFrame.h +++ b/Source/WebCore/platform/audio/FFTFrame.h @@ -35,12 +35,18 @@ #include <Accelerate/Accelerate.h> #endif -#if !OS(DARWIN) && USE(WEBAUDIO_MKL) +#if !OS(DARWIN) +#if USE(WEBAUDIO_MKL) #include "mkl_dfti.h" +#endif // USE(WEBAUDIO_MKL) +#if USE(WEBAUDIO_FFTW) +#include "fftw3.h" +#endif // USE(WEBAUDIO_FFTW) #endif #include <wtf/PassOwnPtr.h> #include <wtf/Platform.h> +#include <wtf/Threading.h> namespace WebCore { @@ -56,6 +62,7 @@ public: FFTFrame(const FFTFrame& frame); ~FFTFrame(); + static void initialize(); static void cleanup(); void doFFT(float* data); void doInverseFFT(float* data); @@ -98,8 +105,8 @@ private: DSPSplitComplex m_frame; AudioFloatArray m_realData; AudioFloatArray m_imagData; -#endif // OS(DARWIN) -#if !OS(DARWIN) && USE(WEBAUDIO_MKL) +#else // !OS(DARWIN) +#if USE(WEBAUDIO_MKL) // Interleaves the planar real and imaginary data and returns a // pointer to the resulting storage which can be used for in-place // or out-of-place operations. FIXME: ideally all of the MKL @@ -115,7 +122,31 @@ private: AudioFloatArray m_complexData; AudioFloatArray m_realData; AudioFloatArray m_imagData; -#endif // !OS(DARWIN) && USE(WEBAUDIO_MKL) +#endif // USE(WEBAUDIO_MKL) +#if USE(WEBAUDIO_FFTW) + fftwf_plan m_forwardPlan; + fftwf_plan m_backwardPlan; + + enum Direction { + Forward, + Backward + }; + + // Both the real and imaginary data are stored here. + // The real data is stored first, followed by three float values of padding. + // The imaginary data is stored after the padding and is 16-byte aligned (if m_data itself is aligned). + // The reason we don't use separate arrays for real and imaginary is because the FFTW plans are shared + // between FFTFrame instances and require that the real and imaginary data pointers be the same distance apart. + AudioFloatArray m_data; + + static Mutex *s_planLock; + static fftwf_plan* fftwForwardPlans; + static fftwf_plan* fftwBackwardPlans; + + static fftwf_plan fftwPlanForSize(unsigned fftSize, Direction, + float*, float*, float*); +#endif // USE(WEBAUDIO_FFTW) +#endif // !OS(DARWIN) }; } // namespace WebCore diff --git a/Source/WebCore/platform/audio/FFTFrameStub.cpp b/Source/WebCore/platform/audio/FFTFrameStub.cpp index 17405c9..c76c0e2 100644 --- a/Source/WebCore/platform/audio/FFTFrameStub.cpp +++ b/Source/WebCore/platform/audio/FFTFrameStub.cpp @@ -36,9 +36,9 @@ namespace WebCore { // Normal constructor: allocates for a given fftSize. -FFTFrame::FFTFrame(unsigned fftSize) - : m_FFTSize(fftSize) - , m_log2FFTSize(static_cast<unsigned>(log2(fftSize))) +FFTFrame::FFTFrame(unsigned /*fftSize*/) + : m_FFTSize(0) + , m_log2FFTSize(0) { ASSERT_NOT_REACHED(); } diff --git a/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp b/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp index 83ab9bf..de44b1c 100644 --- a/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp +++ b/Source/WebCore/platform/audio/chromium/AudioBusChromium.cpp @@ -42,7 +42,7 @@ PassOwnPtr<AudioBus> AudioBus::loadPlatformResource(const char* name, double sam PassOwnPtr<AudioBus> createBusFromInMemoryAudioFile(const void* data, size_t dataSize, bool mixToMono, double sampleRate) { OwnPtr<AudioBus> audioBus = PlatformBridge::decodeAudioFileData(static_cast<const char*>(data), dataSize, sampleRate); - if (audioBus->numberOfChannels() == 2 && mixToMono) { + if (audioBus.get() && audioBus->numberOfChannels() == 2 && mixToMono) { OwnPtr<AudioBus> monoAudioBus = adoptPtr(new AudioBus(1, audioBus->length())); // FIXME: AudioBus::copyFrom() should be able to do a downmix to mono. diff --git a/Source/WebCore/platform/audio/fftw/FFTFrameFFTW.cpp b/Source/WebCore/platform/audio/fftw/FFTFrameFFTW.cpp new file mode 100644 index 0000000..878ed9f --- /dev/null +++ b/Source/WebCore/platform/audio/fftw/FFTFrameFFTW.cpp @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2011 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 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. + */ + +// FFTFrame implementation using the FFTW library. + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "FFTFrame.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +const int kMaxFFTPow2Size = 24; + +fftwf_plan* FFTFrame::fftwForwardPlans = 0; +fftwf_plan* FFTFrame::fftwBackwardPlans = 0; + +Mutex* FFTFrame::s_planLock = 0; + +namespace { + +unsigned unpackedFFTWDataSize(unsigned fftSize) +{ + return fftSize / 2 + 1; +} + +} // anonymous namespace + + +// Normal constructor: allocates for a given fftSize. +FFTFrame::FFTFrame(unsigned fftSize) + : m_FFTSize(fftSize) + , m_log2FFTSize(static_cast<unsigned>(log2(fftSize))) + , m_forwardPlan(0) + , m_backwardPlan(0) + , m_data(2 * (3 + unpackedFFTWDataSize(fftSize))) // enough space for real and imaginary data plus 16-byte alignment padding +{ + // We only allow power of two. + ASSERT(1UL << m_log2FFTSize == m_FFTSize); + + // FFTW won't create a plan without being able to look at non-null + // pointers for the input and output data; it wants to be able to + // see whether these arrays are aligned properly for vector + // operations. Ideally we would use fftw_malloc and fftw_free for + // the input and output arrays to ensure proper alignment for SIMD + // operations, so that we don't have to specify FFTW_UNALIGNED + // when creating the plan. However, since we don't have control + // over the alignment of the array passed to doFFT / doInverseFFT, + // we would need to memcpy it in to or out of the FFTFrame, adding + // overhead. For the time being, we just assume unaligned data and + // pass a temporary pointer down. + + float temporary; + m_forwardPlan = fftwPlanForSize(fftSize, Forward, + &temporary, realData(), imagData()); + m_backwardPlan = fftwPlanForSize(fftSize, Backward, + realData(), imagData(), &temporary); +} + +// Creates a blank/empty frame (interpolate() must later be called). +FFTFrame::FFTFrame() + : m_FFTSize(0) + , m_log2FFTSize(0) + , m_forwardPlan(0) + , m_backwardPlan(0) +{ +} + +// Copy constructor. +FFTFrame::FFTFrame(const FFTFrame& frame) + : m_FFTSize(frame.m_FFTSize) + , m_log2FFTSize(frame.m_log2FFTSize) + , m_forwardPlan(0) + , m_backwardPlan(0) + , m_data(2 * (3 + unpackedFFTWDataSize(fftSize()))) // enough space for real and imaginary data plus 16-byte alignment padding +{ + // See the normal constructor for an explanation of the temporary pointer. + float temporary; + m_forwardPlan = fftwPlanForSize(m_FFTSize, Forward, + &temporary, realData(), imagData()); + m_backwardPlan = fftwPlanForSize(m_FFTSize, Backward, + realData(), imagData(), &temporary); + + // Copy/setup frame data. + size_t nbytes = sizeof(float) * unpackedFFTWDataSize(fftSize()); + memcpy(realData(), frame.realData(), nbytes); + memcpy(imagData(), frame.imagData(), nbytes); +} + +FFTFrame::~FFTFrame() +{ +} + +void FFTFrame::multiply(const FFTFrame& frame) +{ + FFTFrame& frame1 = *this; + FFTFrame& frame2 = const_cast<FFTFrame&>(frame); + + float* realP1 = frame1.realData(); + float* imagP1 = frame1.imagData(); + const float* realP2 = frame2.realData(); + const float* imagP2 = frame2.imagData(); + + // Scale accounts the peculiar scaling of vecLib on the Mac. + // This ensures the right scaling all the way back to inverse FFT. + // FIXME: if we change the scaling on the Mac then this scale + // factor will need to change too. + float scale = 0.5f; + + // Multiply the packed DC/nyquist component + realP1[0] *= scale * realP2[0]; + imagP1[0] *= scale * imagP2[0]; + + // Complex multiplication. If this loop turns out to be hot then + // we should use SSE or other intrinsics to accelerate it. + unsigned halfSize = fftSize() / 2; + + for (unsigned i = 1; i < halfSize; ++i) { + float realResult = realP1[i] * realP2[i] - imagP1[i] * imagP2[i]; + float imagResult = realP1[i] * imagP2[i] + imagP1[i] * realP2[i]; + + realP1[i] = scale * realResult; + imagP1[i] = scale * imagResult; + } +} + +void FFTFrame::doFFT(float* data) +{ + fftwf_execute_split_dft_r2c(m_forwardPlan, data, realData(), imagData()); + + // Scale the frequency domain data to match vecLib's scale factor + // on the Mac. FIXME: if we change the definition of FFTFrame to + // eliminate this scale factor then this code will need to change. + // Also, if this loop turns out to be hot then we should use SSE + // or other intrinsics to accelerate it. + float scaleFactor = 2; + unsigned length = unpackedFFTWDataSize(fftSize()); + float* realData = this->realData(); + float* imagData = this->imagData(); + + for (unsigned i = 0; i < length; ++i) { + realData[i] = realData[i] * scaleFactor; + imagData[i] = imagData[i] * scaleFactor; + } + + // Move the Nyquist component to the location expected by the + // FFTFrame API. + imagData[0] = realData[length - 1]; +} + +void FFTFrame::doInverseFFT(float* data) +{ + unsigned length = unpackedFFTWDataSize(fftSize()); + float* realData = this->realData(); + float* imagData = this->imagData(); + + // Move the Nyquist component to the location expected by FFTW. + realData[length - 1] = imagData[0]; + imagData[length - 1] = 0; + imagData[0] = 0; + + fftwf_execute_split_dft_c2r(m_backwardPlan, realData, imagData, data); + + // Restore the original scaling of the time domain data. + // FIXME: if we change the definition of FFTFrame to eliminate the + // scale factor then this code will need to change. Also, if this + // loop turns out to be hot then we should use SSE or other + // intrinsics to accelerate it. + float scaleFactor = 1.0 / (2.0 * fftSize()); + unsigned n = fftSize(); + for (unsigned i = 0; i < n; ++i) + data[i] *= scaleFactor; + + // Move the Nyquist component back to the location expected by the + // FFTFrame API. + imagData[0] = realData[length - 1]; +} + +void FFTFrame::initialize() +{ + if (!fftwForwardPlans) { + fftwForwardPlans = new fftwf_plan[kMaxFFTPow2Size]; + fftwBackwardPlans = new fftwf_plan[kMaxFFTPow2Size]; + for (int i = 0; i < kMaxFFTPow2Size; ++i) { + fftwForwardPlans[i] = 0; + fftwBackwardPlans[i] = 0; + } + } + + if (!s_planLock) + s_planLock = new Mutex(); +} + +void FFTFrame::cleanup() +{ + if (!fftwForwardPlans) + return; + + for (int i = 0; i < kMaxFFTPow2Size; ++i) { + if (fftwForwardPlans[i]) + fftwf_destroy_plan(fftwForwardPlans[i]); + if (fftwBackwardPlans[i]) + fftwf_destroy_plan(fftwBackwardPlans[i]); + } + + delete[] fftwForwardPlans; + delete[] fftwBackwardPlans; + + fftwForwardPlans = 0; + fftwBackwardPlans = 0; + + delete s_planLock; + s_planLock = 0; +} + +float* FFTFrame::realData() const +{ + return const_cast<float*>(m_data.data()); +} + +float* FFTFrame::imagData() const +{ + // Imaginary data is stored following the real data with enough padding for 16-byte alignment. + return const_cast<float*>(realData() + unpackedFFTWDataSize(fftSize()) + 3); +} + +fftwf_plan FFTFrame::fftwPlanForSize(unsigned fftSize, Direction direction, + float* data1, float* data2, float* data3) +{ + // initialize() must be called first. + ASSERT(fftwForwardPlans); + if (!fftwForwardPlans) + return 0; + + ASSERT(s_planLock); + if (!s_planLock) + return 0; + MutexLocker locker(*s_planLock); + + ASSERT(fftSize); + int pow2size = static_cast<int>(log2(fftSize)); + ASSERT(pow2size < kMaxFFTPow2Size); + fftwf_plan* plans = (direction == Forward) ? fftwForwardPlans : fftwBackwardPlans; + if (!plans[pow2size]) { + fftwf_iodim dimension; + dimension.n = fftSize; + dimension.is = 1; + dimension.os = 1; + + // For the time being, we do not take the input data into + // account when choosing a plan, so that we can most easily + // reuse plans with different input data. + + // FIXME: allocate input and output data inside this class to + // be able to take advantage of alignment and SIMD optimizations. + unsigned flags = FFTW_ESTIMATE | FFTW_PRESERVE_INPUT | FFTW_UNALIGNED; + switch (direction) { + case Forward: + plans[pow2size] = fftwf_plan_guru_split_dft_r2c(1, &dimension, 0, 0, + data1, data2, data3, + flags); + break; + case Backward: + plans[pow2size] = fftwf_plan_guru_split_dft_c2r(1, &dimension, 0, 0, + data1, data2, data3, + flags); + break; + } + } + ASSERT(plans[pow2size]); + return plans[pow2size]; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp b/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp index 0f7efb7..6456da2 100644 --- a/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp +++ b/Source/WebCore/platform/audio/mac/FFTFrameMac.cpp @@ -162,6 +162,10 @@ FFTSetup FFTFrame::fftSetupForSize(unsigned fftSize) return fftSetups[pow2size]; } +void FFTFrame::initialize() +{ +} + void FFTFrame::cleanup() { if (!fftSetups) diff --git a/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp b/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp index f66a485..3ac6b36 100644 --- a/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp +++ b/Source/WebCore/platform/audio/mkl/FFTFrameMKL.cpp @@ -200,6 +200,10 @@ void FFTFrame::doInverseFFT(float* data) ASSERT(DftiErrorClass(status, DFTI_NO_ERROR)); } +void FFTFrame::initialize() +{ +} + void FFTFrame::cleanup() { if (!descriptorHandles) diff --git a/Source/WebCore/platform/brew/MIMETypeRegistryBrew.cpp b/Source/WebCore/platform/brew/MIMETypeRegistryBrew.cpp index 0a538c2..eae4e3b 100644 --- a/Source/WebCore/platform/brew/MIMETypeRegistryBrew.cpp +++ b/Source/WebCore/platform/brew/MIMETypeRegistryBrew.cpp @@ -30,6 +30,8 @@ #include "MIMETypeRegistry.h" #include "PlatformString.h" +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> namespace WebCore { @@ -63,6 +65,8 @@ static const ExtensionMap extensionMap[] = { String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + String str = ext.lower(); const ExtensionMap* e = extensionMap; diff --git a/Source/WebCore/platform/brew/SystemTimeBrew.cpp b/Source/WebCore/platform/brew/SystemTimeBrew.cpp index c1e39fb..6e2fe7e 100644 --- a/Source/WebCore/platform/brew/SystemTimeBrew.cpp +++ b/Source/WebCore/platform/brew/SystemTimeBrew.cpp @@ -26,15 +26,16 @@ #include "config.h" #include "SystemTime.h" -#include <float.h> +#include "NotImplemented.h" +#include <limits> namespace WebCore { float userIdleTime() { - // return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed - return FLT_MAX; -} - + notImplemented(); + // Return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed. + return std::numeric_limits<float>::max(); } +} // namespace WebCore diff --git a/Source/WebCore/platform/cf/BinaryPropertyList.cpp b/Source/WebCore/platform/cf/BinaryPropertyList.cpp index 27b44d4..41769e8 100644 --- a/Source/WebCore/platform/cf/BinaryPropertyList.cpp +++ b/Source/WebCore/platform/cf/BinaryPropertyList.cpp @@ -92,7 +92,7 @@ struct IntegerArrayHash { unsigned IntegerArrayHash::hash(const IntegerArray& array) { - return StringImpl::computeHash(reinterpret_cast<const UChar*>(array.integers()), array.size() / (sizeof(int) / sizeof(UChar))); + return WTF::StringHasher::createBlobHash(array.integers(), array.size()); } bool IntegerArrayHash::equal(const IntegerArray& a, const IntegerArray& b) diff --git a/Source/WebCore/platform/cf/FileSystemCF.cpp b/Source/WebCore/platform/cf/FileSystemCF.cpp index e3a144c..a4b422b 100644 --- a/Source/WebCore/platform/cf/FileSystemCF.cpp +++ b/Source/WebCore/platform/cf/FileSystemCF.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,12 +25,13 @@ * (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" #import "FileSystem.h" #import "PlatformString.h" -#import <wtf/text/CString.h> #import <wtf/RetainPtr.h> +#import <wtf/text/CString.h> namespace WebCore { @@ -54,4 +55,16 @@ CString fileSystemRepresentation(const String& path) return string; } +RetainPtr<CFURLRef> pathAsURL(const String& path) +{ + CFURLPathStyle pathStyle; +#if PLATFORM(WIN) + pathStyle = kCFURLWindowsPathStyle; +#else + pathStyle = kCFURLPOSIXPathStyle; +#endif + return RetainPtr<CFURLRef>(AdoptCF, CFURLCreateWithFileSystemPath(0, + RetainPtr<CFStringRef>(AdoptCF, path.createCFString()).get(), pathStyle, FALSE)); +} + } // namespace WebCore diff --git a/Source/WebCore/platform/chromium/ChromiumDataObject.cpp b/Source/WebCore/platform/chromium/ChromiumDataObject.cpp index 78b794b..f5732df 100644 --- a/Source/WebCore/platform/chromium/ChromiumDataObject.cpp +++ b/Source/WebCore/platform/chromium/ChromiumDataObject.cpp @@ -56,9 +56,9 @@ PassRefPtr<ChromiumDataObject> ChromiumDataObject::create(PassRefPtr<ChromiumDat return adoptRef(new ChromiumDataObject(data)); } -PassRefPtr<ChromiumDataObject> ChromiumDataObject::createReadable(Clipboard::ClipboardType clipboardType) +PassRefPtr<ChromiumDataObject> ChromiumDataObject::createReadable(const Frame* frame, Clipboard::ClipboardType clipboardType) { - return adoptRef(new ChromiumDataObject(ReadableDataObject::create(clipboardType))); + return adoptRef(new ChromiumDataObject(ReadableDataObject::create(frame, clipboardType))); } PassRefPtr<ChromiumDataObject> ChromiumDataObject::createWritable(Clipboard::ClipboardType clipboardType) diff --git a/Source/WebCore/platform/chromium/ChromiumDataObject.h b/Source/WebCore/platform/chromium/ChromiumDataObject.h index 4aac5c9..919c269 100644 --- a/Source/WebCore/platform/chromium/ChromiumDataObject.h +++ b/Source/WebCore/platform/chromium/ChromiumDataObject.h @@ -41,7 +41,7 @@ namespace WebCore { class ChromiumDataObject : public RefCounted<ChromiumDataObject> { public: static PassRefPtr<ChromiumDataObject> create(PassRefPtr<ChromiumDataObjectLegacy> data); - static PassRefPtr<ChromiumDataObject> createReadable(Clipboard::ClipboardType); + static PassRefPtr<ChromiumDataObject> createReadable(const Frame*, Clipboard::ClipboardType); static PassRefPtr<ChromiumDataObject> createWritable(Clipboard::ClipboardType); void clearData(const String& type); diff --git a/Source/WebCore/platform/chromium/ClipboardChromium.cpp b/Source/WebCore/platform/chromium/ClipboardChromium.cpp index 46b4339..d6ba2d2 100644 --- a/Source/WebCore/platform/chromium/ClipboardChromium.cpp +++ b/Source/WebCore/platform/chromium/ClipboardChromium.cpp @@ -91,7 +91,7 @@ PassRefPtr<ClipboardChromium> ClipboardChromium::create(ClipboardType clipboardT RefPtr<ChromiumDataObject> dataObject = policy == ClipboardWritable ? ChromiumDataObject::createWritable(clipboardType) : - ChromiumDataObject::createReadable(clipboardType); + ChromiumDataObject::createReadable(frame, clipboardType); return adoptRef(new ClipboardChromium(clipboardType, dataObject, policy, frame)); } diff --git a/Source/WebCore/platform/chromium/MIMETypeRegistryChromium.cpp b/Source/WebCore/platform/chromium/MIMETypeRegistryChromium.cpp index fec0e9b..91bfccb 100644 --- a/Source/WebCore/platform/chromium/MIMETypeRegistryChromium.cpp +++ b/Source/WebCore/platform/chromium/MIMETypeRegistryChromium.cpp @@ -42,6 +42,17 @@ namespace WebCore { +#if ENABLE(FILE_SYSTEM) && ENABLE(WORKERS) +String MIMETypeRegistry::getMIMETypeForExtensionThreadSafe(const String &ext) +{ + return PlatformBridge::mimeTypeForExtension(ext); +} +#endif + +// NOTE: We have to define getMIMETypeForExtension() here though the shared +// implementation has getMIMETypeForExtension() since we don't use the shared +// implementation bits in MIMETypeRegistry.cpp. + String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { return PlatformBridge::mimeTypeForExtension(ext); diff --git a/Source/WebCore/platform/chromium/PlatformBridge.h b/Source/WebCore/platform/chromium/PlatformBridge.h index f6a2564..9a09b90 100644 --- a/Source/WebCore/platform/chromium/PlatformBridge.h +++ b/Source/WebCore/platform/chromium/PlatformBridge.h @@ -109,9 +109,9 @@ public: static void clipboardWriteData(const String& type, const String& data, const String& metadata); // Interface for handling copy and paste, drag and drop, and selection copy. - static HashSet<String> clipboardReadAvailableTypes(PasteboardPrivate::ClipboardBuffer, bool* containsFilenames); - static bool clipboardReadData(PasteboardPrivate::ClipboardBuffer, const String& type, String& data, String& metadata); - static Vector<String> clipboardReadFilenames(PasteboardPrivate::ClipboardBuffer); + static HashSet<String> clipboardReadAvailableTypes(const Frame*, PasteboardPrivate::ClipboardBuffer, bool* containsFilenames); + static bool clipboardReadData(const Frame*, PasteboardPrivate::ClipboardBuffer, const String& type, String& data, String& metadata); + static Vector<String> clipboardReadFilenames(const Frame*, PasteboardPrivate::ClipboardBuffer); // Cookies ------------------------------------------------------------ static void setCookies(const Document*, const KURL&, const String& value); @@ -171,7 +171,6 @@ public: // IndexedDB ---------------------------------------------------------- static PassRefPtr<IDBFactoryBackendInterface> idbFactory(); - static void idbShutdown(); // Extracts keyPath from values and returns the corresponding keys. static void createIDBKeysFromSerializedValuesAndKeyPath(const Vector<RefPtr<SerializedScriptValue> >& values, const String& keyPath, Vector<RefPtr<IDBKey> >& keys); diff --git a/Source/WebCore/platform/chromium/PopupMenuChromium.cpp b/Source/WebCore/platform/chromium/PopupMenuChromium.cpp index 075cef0..59441d0 100644 --- a/Source/WebCore/platform/chromium/PopupMenuChromium.cpp +++ b/Source/WebCore/platform/chromium/PopupMenuChromium.cpp @@ -32,7 +32,6 @@ #include "config.h" #include "PopupMenuChromium.h" -#include "CharacterNames.h" #include "Chrome.h" #include "ChromeClientChromium.h" #include "Font.h" @@ -56,8 +55,8 @@ #include "SystemTime.h" #include "TextRun.h" #include "UserGestureIndicator.h" - #include <wtf/CurrentTime.h> +#include <wtf/unicode/CharacterNames.h> using namespace WTF; using namespace Unicode; @@ -74,17 +73,16 @@ static const int kMaxHeight = 500; static const int kBorderSize = 1; static const int kTextToLabelPadding = 10; static const int kLabelToIconPadding = 5; +static const int kMinEndOfLinePadding = 2; 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, // setTextOnIndexChange - true, // acceptOnAbandon - false, // loopSelectionNavigation - false, // restrictWidthOfListBox - // display item text in its first strong directional character's directionality. - PopupContainerSettings::FirstStrongDirectionalCharacterDirection, + true, // setTextOnIndexChange + true, // acceptOnAbandon + false, // loopSelectionNavigation + false // restrictWidthOfListBox }; // This class uses WebCore code to paint and handle events for a drop-down list @@ -330,7 +328,7 @@ PopupContainer::~PopupContainer() removeChild(m_listBox.get()); } -IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, const IntPoint& popupInitialCoordinate) +IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, int popupInitialY) { // Reset the max height to its default value, it will be recomputed below // if necessary. @@ -350,8 +348,10 @@ IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, co // If the popup would extend past the bottom of the screen, open upwards // instead. FloatRect screen = screenAvailableRect(m_frameView.get()); - widgetRect = chromeClient->windowToScreen(IntRect(popupInitialCoordinate, targetSize)); - if (widgetRect.bottom() > static_cast<int>(screen.bottom())) { + // Use this::x() for location because RTL position is considered + // in layout(). + widgetRect = chromeClient->windowToScreen(IntRect(x(), popupInitialY, targetSize.width(), targetSize.height())); + if (widgetRect.maxY() > static_cast<int>(screen.maxY())) { if (widgetRect.y() - widgetRect.height() - targetControlHeight > 0) { // There is enough room to open upwards. widgetRect.move(0, -(widgetRect.height() + targetControlHeight)); @@ -359,7 +359,7 @@ IntRect PopupContainer::layoutAndCalculateWidgetRect(int targetControlHeight, co // Figure whether upwards or downwards has more room and set the // maximum number of items. int spaceAbove = widgetRect.y() - targetControlHeight; - int spaceBelow = screen.bottom() - widgetRect.y(); + int spaceBelow = screen.maxY() - widgetRect.y(); if (spaceAbove > spaceBelow) m_listBox->setMaxHeight(spaceAbove); else @@ -383,7 +383,7 @@ void PopupContainer::showPopup(FrameView* view) ChromeClientChromium* chromeClient = chromeClientChromium(); if (chromeClient) { IntRect popupRect = frameRect(); - chromeClient->popupOpened(this, layoutAndCalculateWidgetRect(popupRect.height(), popupRect.location()), false); + chromeClient->popupOpened(this, layoutAndCalculateWidgetRect(popupRect.height(), popupRect.y()), false); m_popupOpen = true; } @@ -399,34 +399,6 @@ void PopupContainer::showPopup(FrameView* view) invalidate(); } -void PopupContainer::showExternal(const IntRect& rect, FrameView* v, int index) -{ - if (!listBox()) - return; - - listBox()->setBaseWidth(rect.width()); - listBox()->updateFromElement(); - - if (listBox()->numItems() < 1) { - hidePopup(); - return; - } - - // Adjust the popup position to account for scrolling. - IntPoint location = v->contentsToWindow(rect.location()); - IntRect popupRect(location, rect.size()); - - // Get the ChromeClient and pass it the popup menu's listbox data. - m_frameView = v; - chromeClientChromium()->popupOpened(this, popupRect, true); - - // The popup sends its "closed" notification through its parent. Set the - // parent, even though external popups have no real on-screen widget but a - // native menu (see |PopupListBox::hidePopup()|); - if (!m_listBox->parent()) - addChild(m_listBox.get()); -} - void PopupContainer::hidePopup() { listBox()->hidePopup(); @@ -581,7 +553,7 @@ void PopupContainer::refresh(const IntRect& targetControlRect) listBox()->updateFromElement(); // Store the original height to check if we need to request the location. int originalHeight = height(); - IntRect widgetRect = layoutAndCalculateWidgetRect(targetControlRect.height(), location); + IntRect widgetRect = layoutAndCalculateWidgetRect(targetControlRect.height(), location.y()); if (originalHeight != widgetRect.height()) setFrameRect(widgetRect); @@ -967,20 +939,15 @@ void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd } // Prepare the directionality to draw text. - bool rtl = false; - if (m_settings.itemTextDirectionalityHint == PopupContainerSettings::DOMElementDirection) - rtl = style.textDirection() == RTL; - else if (m_settings.itemTextDirectionalityHint == - PopupContainerSettings::FirstStrongDirectionalCharacterDirection) - rtl = itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft; - TextRun textRun(itemText.characters(), itemText.length(), false, 0, 0, rtl); + bool rtl = style.textDirection() == RTL; + TextRun textRun(itemText.characters(), itemText.length(), false, 0, 0, TextRun::AllowTrailingExpansion, rtl, style.hasTextDirectionOverride()); // If the text is right-to-left, make it right-aligned by adjusting its // beginning position. if (rightAligned) textX += maxWidth - itemFont.width(textRun); // Draw the item text. - int textY = rowRect.y() + itemFont.ascent() + (rowRect.height() - itemFont.height()) / 2; + int textY = rowRect.y() + itemFont.fontMetrics().ascent() + (rowRect.height() - itemFont.fontMetrics().height()) / 2; gc->drawBidiText(itemFont, textRun, IntPoint(textX, textY)); // We are using the left padding as the right padding includes room for the scroll-bar which @@ -1001,7 +968,7 @@ void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd // Draw the the label if applicable. if (itemLabel.isEmpty()) return; - TextRun labelTextRun(itemLabel.characters(), itemLabel.length(), false, 0, 0, rtl); + TextRun labelTextRun(itemLabel.characters(), itemLabel.length(), false, 0, 0, TextRun::AllowTrailingExpansion, rtl, style.hasTextDirectionOverride()); if (rightAligned) textX = max(0, m_popupClient->clientPaddingLeft() - m_popupClient->clientInsetLeft()); else @@ -1120,7 +1087,7 @@ int PopupListBox::getRowHeight(int index) String icon = m_popupClient->itemIcon(index); RefPtr<Image> image(Image::loadPlatformResource(icon.utf8().data())); - int fontHeight = getRowFont(index).height(); + int fontHeight = getRowFont(index).fontMetrics().height(); int iconHeight = (image && !image->isNull()) ? image->rect().height() : 0; return max(fontHeight, iconHeight); @@ -1154,9 +1121,9 @@ void PopupListBox::scrollToRevealRow(int index) if (rowRect.y() < scrollY()) { // Row is above current scroll position, scroll up. ScrollView::setScrollPosition(IntPoint(0, rowRect.y())); - } else if (rowRect.bottom() > scrollY() + visibleHeight()) { + } else if (rowRect.maxY() > scrollY() + visibleHeight()) { // Row is below current scroll position, scroll down. - ScrollView::setScrollPosition(IntPoint(0, rowRect.bottom() - visibleHeight())); + ScrollView::setScrollPosition(IntPoint(0, rowRect.maxY() - visibleHeight())); } } @@ -1267,6 +1234,9 @@ void PopupListBox::updateFromElement() type = PopupItem::TypeOption; m_items.append(new PopupItem(m_popupClient->itemText(i), type)); m_items[i]->enabled = isSelectableItem(i); + PopupMenuStyle style = m_popupClient->itemStyle(i); + m_items[i]->textDirection = style.textDirection(); + m_items[i]->hasTextDirectionOverride = style.hasTextDirectionOverride(); } m_selectedIndex = m_popupClient->selectedIndex(); @@ -1277,9 +1247,12 @@ void PopupListBox::updateFromElement() void PopupListBox::layout() { + bool isRightAligned = m_popupClient->menuStyle().textDirection() == RTL; + // Size our child items. int baseWidth = 0; int paddingWidth = 0; + int lineEndPaddingWidth = 0; int y = 0; for (int i = 0; i < numItems(); ++i) { // Place the item vertically. @@ -1312,58 +1285,56 @@ void PopupListBox::layout() // FIXME: http://b/1210481 We should get the padding of individual option elements. paddingWidth = max(paddingWidth, m_popupClient->clientPaddingLeft() + m_popupClient->clientPaddingRight()); + lineEndPaddingWidth = max(lineEndPaddingWidth, + isRightAligned ? m_popupClient->clientPaddingLeft() : m_popupClient->clientPaddingRight()); } // Calculate scroll bar width. int windowHeight = 0; - -#if OS(DARWIN) - // Set the popup's window to contain all available items on Mac only, which - // uses native controls that manage their own scrolling. This allows hit - // testing to work when selecting items in popups that have more menu entries - // than the maximum window size. - m_visibleRows = numItems(); -#else m_visibleRows = min(numItems(), kMaxVisibleRows); -#endif for (int i = 0; i < m_visibleRows; ++i) { int rowHeight = getRowHeight(i); -#if !OS(DARWIN) // Only clip the window height for non-Mac platforms. if (windowHeight + rowHeight > m_maxHeight) { m_visibleRows = i; break; } -#endif windowHeight += rowHeight; } // Set our widget and scrollable contents sizes. int scrollbarWidth = 0; - if (m_visibleRows < numItems()) + if (m_visibleRows < numItems()) { scrollbarWidth = ScrollbarTheme::nativeTheme()->scrollbarThickness(); + // Use kMinEndOfLinePadding when there is a scrollbar so that we use + // as much as (lineEndPaddingWidth - kMinEndOfLinePadding) padding + // space for scrollbar and allow user to use CSS padding to make the + // popup listbox align with the select element. + paddingWidth = paddingWidth - lineEndPaddingWidth + kMinEndOfLinePadding; + } + int windowWidth; int contentWidth; if (m_settings.restrictWidthOfListBox) { windowWidth = m_baseWidth; - contentWidth = m_baseWidth - scrollbarWidth - paddingWidth; + contentWidth = m_baseWidth - scrollbarWidth; } else { windowWidth = baseWidth + scrollbarWidth + paddingWidth; - contentWidth = baseWidth; + contentWidth = baseWidth + paddingWidth; if (windowWidth < m_baseWidth) { windowWidth = m_baseWidth; - contentWidth = m_baseWidth - scrollbarWidth - paddingWidth; + contentWidth = m_baseWidth - scrollbarWidth; } else m_baseWidth = baseWidth; } resize(windowWidth, windowHeight); - setContentsSize(IntSize(contentWidth, getRowBounds(numItems() - 1).bottom())); + setContentsSize(IntSize(contentWidth, getRowBounds(numItems() - 1).maxY())); if (hostWindow()) scrollToRevealSelection(); @@ -1404,19 +1375,11 @@ PopupMenuChromium::~PopupMenuChromium() hide(); } -// The Mac Chromium implementation relies on external control (a Cocoa control) -// to display, handle the input tracking and menu item selection for the popup. -// Windows and Linux Chromium let our WebKit port handle the display, while -// another process manages the popup window and input handling. void PopupMenuChromium::show(const IntRect& r, FrameView* v, int index) { if (!p.popup) p.popup = PopupContainer::create(client(), PopupContainer::Select, dropDownSettings); -#if OS(DARWIN) - p.popup->showExternal(r, v, index); -#else p.popup->show(r, v, index); -#endif } void PopupMenuChromium::hide() diff --git a/Source/WebCore/platform/chromium/PopupMenuChromium.h b/Source/WebCore/platform/chromium/PopupMenuChromium.h index f326b48..43b8b0e 100644 --- a/Source/WebCore/platform/chromium/PopupMenuChromium.h +++ b/Source/WebCore/platform/chromium/PopupMenuChromium.h @@ -65,6 +65,8 @@ struct PopupItem { String label; Type type; int yOffset; // y offset of this item, relative to the top of the popup. + TextDirection textDirection; + bool hasTextDirectionOverride; bool enabled; }; @@ -97,22 +99,6 @@ struct PopupContainerSettings { // Whether we should restrict the width of the PopupListBox or not. // Autocomplete popups are restricted, combo-boxes (select tags) aren't. bool restrictWidthOfListBox; - - // A hint on the display directionality of the item text in popup menu. - // - // We could either display the items in the drop-down using its DOM element's - // directionality, or we could display the items in the drop-down using heuristics: - // such as in its first strong directionality character's direction. - // Please refer to the discussion (especially comment #7 and #10) in - // https://bugs.webkit.org/show_bug.cgi?id=27889 for details. - enum DirectionalityHint { - // Use the DOM element's directionality to display the item text in popup menu. - DOMElementDirection, - // Use the item text's first strong-directional character's directionality - // to display the item text in popup menu. - FirstStrongDirectionalCharacterDirection, - }; - DirectionalityHint itemTextDirectionalityHint; }; class PopupContainer : public FramelessScrollView { @@ -142,9 +128,6 @@ public: // Show the popup void showPopup(FrameView*); - // Used on Mac Chromium for HTML select popup menus. - void showExternal(const IntRect&, FrameView*, int index); - // Show the popup in the specified rect for the specified frame. // Note: this code was somehow arbitrarily factored-out of the Popup class // so WebViewImpl can create a PopupContainer. This method is used for @@ -194,7 +177,7 @@ private: void paintBorder(GraphicsContext*, const IntRect&); // Layout and calculate popup widget size and location and returns it as IntRect. - IntRect layoutAndCalculateWidgetRect(int targetControlHeight, const IntPoint& popupInitialCoordinate); + IntRect layoutAndCalculateWidgetRect(int targetControlHeight, int popupInitialY); // Returns the ChromeClient of the page this popup is associated with. ChromeClientChromium* chromeClientChromium(); diff --git a/Source/WebCore/platform/chromium/ReadableDataObject.cpp b/Source/WebCore/platform/chromium/ReadableDataObject.cpp index dbf4739..484a1b3 100644 --- a/Source/WebCore/platform/chromium/ReadableDataObject.cpp +++ b/Source/WebCore/platform/chromium/ReadableDataObject.cpp @@ -43,13 +43,14 @@ static PasteboardPrivate::ClipboardBuffer clipboardBuffer(Clipboard::ClipboardTy return clipboardType == Clipboard::DragAndDrop ? PasteboardPrivate::DragBuffer : PasteboardPrivate::StandardBuffer; } -PassRefPtr<ReadableDataObject> ReadableDataObject::create(Clipboard::ClipboardType clipboardType) +PassRefPtr<ReadableDataObject> ReadableDataObject::create(const Frame* frame, Clipboard::ClipboardType clipboardType) { - return adoptRef(new ReadableDataObject(clipboardType)); + return adoptRef(new ReadableDataObject(frame, clipboardType)); } -ReadableDataObject::ReadableDataObject(Clipboard::ClipboardType clipboardType) - : m_clipboardType(clipboardType) +ReadableDataObject::ReadableDataObject(const Frame* frame, Clipboard::ClipboardType clipboardType) + : m_frame(frame) + , m_clipboardType(clipboardType) , m_containsFilenames(false) , m_isTypeCacheInitialized(false) { @@ -93,7 +94,7 @@ String ReadableDataObject::getData(const String& type, bool& succeeded) const return data; } succeeded = PlatformBridge::clipboardReadData( - clipboardBuffer(m_clipboardType), type, data, ignoredMetadata); + m_frame, clipboardBuffer(m_clipboardType), type, data, ignoredMetadata); return data; } @@ -102,7 +103,7 @@ String ReadableDataObject::urlTitle() const String ignoredData; String urlTitle; PlatformBridge::clipboardReadData( - clipboardBuffer(m_clipboardType), mimeTypeTextURIList, ignoredData, urlTitle); + m_frame, clipboardBuffer(m_clipboardType), mimeTypeTextURIList, ignoredData, urlTitle); return urlTitle; } @@ -111,7 +112,7 @@ KURL ReadableDataObject::htmlBaseUrl() const String ignoredData; String htmlBaseUrl; PlatformBridge::clipboardReadData( - clipboardBuffer(m_clipboardType), mimeTypeTextHTML, ignoredData, htmlBaseUrl); + m_frame, clipboardBuffer(m_clipboardType), mimeTypeTextHTML, ignoredData, htmlBaseUrl); return KURL(ParsedURLString, htmlBaseUrl); } @@ -123,7 +124,7 @@ bool ReadableDataObject::containsFilenames() const Vector<String> ReadableDataObject::filenames() const { - return PlatformBridge::clipboardReadFilenames(clipboardBuffer(m_clipboardType)); + return PlatformBridge::clipboardReadFilenames(m_frame, clipboardBuffer(m_clipboardType)); } void ReadableDataObject::ensureTypeCacheInitialized() const @@ -132,7 +133,7 @@ void ReadableDataObject::ensureTypeCacheInitialized() const return; m_types = PlatformBridge::clipboardReadAvailableTypes( - clipboardBuffer(m_clipboardType), &m_containsFilenames); + m_frame, clipboardBuffer(m_clipboardType), &m_containsFilenames); m_isTypeCacheInitialized = true; } diff --git a/Source/WebCore/platform/chromium/ReadableDataObject.h b/Source/WebCore/platform/chromium/ReadableDataObject.h index 027e0ed..c6cc310 100644 --- a/Source/WebCore/platform/chromium/ReadableDataObject.h +++ b/Source/WebCore/platform/chromium/ReadableDataObject.h @@ -44,7 +44,7 @@ namespace WebCore { // browser to the renderer. class ReadableDataObject : public RefCounted<ReadableDataObject> { public: - static PassRefPtr<ReadableDataObject> create(Clipboard::ClipboardType); + static PassRefPtr<ReadableDataObject> create(const Frame*, Clipboard::ClipboardType); bool hasData() const; HashSet<String> types() const; @@ -57,11 +57,14 @@ public: Vector<String> filenames() const; private: - explicit ReadableDataObject(Clipboard::ClipboardType); + explicit ReadableDataObject(const Frame*, Clipboard::ClipboardType); // This isn't always const... but most of the time it is. void ensureTypeCacheInitialized() const; + // The owner frame. Used to send IPCs back to the correspdonging view via WebFrameClient. + const Frame* m_frame; + Clipboard::ClipboardType m_clipboardType; // To avoid making a lot of IPC calls for each drag event, we cache some diff --git a/Source/WebCore/platform/chromium/ScrollbarThemeChromium.cpp b/Source/WebCore/platform/chromium/ScrollbarThemeChromium.cpp index b23e625..9e700c2 100644 --- a/Source/WebCore/platform/chromium/ScrollbarThemeChromium.cpp +++ b/Source/WebCore/platform/chromium/ScrollbarThemeChromium.cpp @@ -128,7 +128,7 @@ void ScrollbarThemeChromium::paintTickmarks(GraphicsContext* context, Scrollbar* const float percent = static_cast<float>(i->y()) / scrollbar->totalSize(); // Calculate how far down (in pixels) the tick-mark should appear. - const int yPos = rect.topLeft().y() + (rect.height() * percent); + const int yPos = rect.y() + (rect.height() * percent); IntPoint tick(scrollbar->x(), yPos); context->drawImage(dash.get(), ColorSpaceDeviceRGB, tick); diff --git a/Source/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm b/Source/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm index fe1a422..b0ba95d 100644 --- a/Source/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm +++ b/Source/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm @@ -450,11 +450,11 @@ bool ScrollbarThemeChromiumMac::paint(Scrollbar* scrollbar, GraphicsContext* con continue; // Calculate how far down (in pixels) the tick-mark should appear. - const int yPos = static_cast<int>((thumbArea.topLeft().y() + (thumbArea.height() * percent))) & ~1; + const int yPos = static_cast<int>((thumbArea.y() + (thumbArea.height() * percent))) & ~1; // Paint. const int indent = 2; - FloatRect tickRect(thumbArea.topLeft().x() + indent, yPos, thumbArea.width() - 2 * indent - 1, 2); + FloatRect tickRect(thumbArea.x() + indent, yPos, thumbArea.width() - 2 * indent - 1, 2); drawingContext->fillRect(tickRect); drawingContext->strokeRect(tickRect, 1); } diff --git a/Source/WebCore/platform/efl/MIMETypeRegistryEfl.cpp b/Source/WebCore/platform/efl/MIMETypeRegistryEfl.cpp index d0c95bf..d883e5d 100644 --- a/Source/WebCore/platform/efl/MIMETypeRegistryEfl.cpp +++ b/Source/WebCore/platform/efl/MIMETypeRegistryEfl.cpp @@ -31,6 +31,9 @@ #include "config.h" #include "MIMETypeRegistry.h" +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> + namespace WebCore { struct ExtensionMap { @@ -72,6 +75,8 @@ static const ExtensionMap extensionMap[] = { String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + String s = ext.lower(); const ExtensionMap *e = extensionMap; while (e->extension) { diff --git a/Source/WebCore/platform/efl/RenderThemeEfl.cpp b/Source/WebCore/platform/efl/RenderThemeEfl.cpp index 9df12e9..9102d17 100644 --- a/Source/WebCore/platform/efl/RenderThemeEfl.cpp +++ b/Source/WebCore/platform/efl/RenderThemeEfl.cpp @@ -1043,4 +1043,83 @@ bool RenderThemeEfl::paintProgressBar(RenderObject* o, const PaintInfo& i, const } #endif +#if ENABLE(VIDEO) +String RenderThemeEfl::extraMediaControlsStyleSheet() +{ + notImplemented(); + return String(); +} + +String RenderThemeEfl::formatMediaControlsCurrentTime(float currentTime, float duration) const +{ + notImplemented(); + return String(); +} + +bool RenderThemeEfl::paintMediaFullscreenButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaMuteButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaPlayButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaSeekBackButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaSeekForwardButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaSliderTrack(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaVolumeSliderTrack(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaVolumeSliderThumb(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} + +bool RenderThemeEfl::paintMediaCurrentTime(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + notImplemented(); + return false; +} +#endif } diff --git a/Source/WebCore/platform/efl/RenderThemeEfl.h b/Source/WebCore/platform/efl/RenderThemeEfl.h index 087e2aa..d4887cf 100644 --- a/Source/WebCore/platform/efl/RenderThemeEfl.h +++ b/Source/WebCore/platform/efl/RenderThemeEfl.h @@ -153,6 +153,23 @@ public: virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); #endif +#if ENABLE(VIDEO) + virtual String extraMediaControlsStyleSheet(); + virtual String formatMediaControlsCurrentTime(float currentTime, float duration) const; + + virtual bool paintMediaFullscreenButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaPlayButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaMuteButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSeekBackButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSeekForwardButton(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderContainer(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderTrack(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaVolumeSliderThumb(RenderObject*, const PaintInfo&, const IntRect&); + virtual bool paintMediaCurrentTime(RenderObject*, const PaintInfo&, const IntRect&); +#endif + protected: static float defaultFontSize; diff --git a/Source/WebCore/platform/efl/SystemTimeEfl.cpp b/Source/WebCore/platform/efl/SystemTimeEfl.cpp index de8c87c..ec6d662 100644 --- a/Source/WebCore/platform/efl/SystemTimeEfl.cpp +++ b/Source/WebCore/platform/efl/SystemTimeEfl.cpp @@ -28,7 +28,9 @@ #include "config.h" #include "SystemTime.h" +#include "NotImplemented.h" #include <Ecore.h> +#include <limits> namespace WebCore { @@ -37,4 +39,11 @@ double currentTime() return ecore_time_get(); } +float userIdleTime() +{ + notImplemented(); + // Return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed. + return std::numeric_limits<float>::max(); } + +} // namespace WebCore diff --git a/Source/WebCore/platform/efl/TemporaryLinkStubs.cpp b/Source/WebCore/platform/efl/TemporaryLinkStubs.cpp index ef6e6f7..07ea2e6 100644 --- a/Source/WebCore/platform/efl/TemporaryLinkStubs.cpp +++ b/Source/WebCore/platform/efl/TemporaryLinkStubs.cpp @@ -56,12 +56,6 @@ String signedPublicKeyAndChallengeString(unsigned keySizeIndex, const String &ch return String(); } -float userIdleTime() -{ - notImplemented(); - return FLT_MAX; -} - void setCookieStoragePrivateBrowsingEnabled(bool) { notImplemented(); diff --git a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp index f416b47..9e64904 100644 --- a/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp +++ b/Source/WebCore/platform/graphics/ANGLEWebKitBridge.cpp @@ -25,7 +25,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "ANGLEWebKitBridge.h" #include <wtf/OwnArrayPtr.h> @@ -93,7 +93,7 @@ bool ANGLEWebKitBridge::validateShaderSource(const char* shaderSource, ANGLEShad int logSize = 0; ShGetInfo(compiler, SH_INFO_LOG_LENGTH, &logSize); if (logSize > 1) { - OwnArrayPtr<char> logBuffer(new char[logSize]); + OwnArrayPtr<char> logBuffer = adoptArrayPtr(new char[logSize]); if (logBuffer) { ShGetInfoLog(compiler, logBuffer.get()); shaderValidationLog = logBuffer.get(); @@ -105,7 +105,7 @@ bool ANGLEWebKitBridge::validateShaderSource(const char* shaderSource, ANGLEShad int translationLength = 0; ShGetInfo(compiler, SH_OBJECT_CODE_LENGTH, &translationLength); if (translationLength > 1) { - OwnArrayPtr<char> translationBuffer(new char[translationLength]); + OwnArrayPtr<char> translationBuffer = adoptArrayPtr(new char[translationLength]); if (!translationBuffer) return false; ShGetObjectCode(compiler, translationBuffer.get()); @@ -117,4 +117,4 @@ bool ANGLEWebKitBridge::validateShaderSource(const char* shaderSource, ANGLEShad } -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/ContextShadow.h b/Source/WebCore/platform/graphics/ContextShadow.h index a1fba5c..c0571f0 100644 --- a/Source/WebCore/platform/graphics/ContextShadow.h +++ b/Source/WebCore/platform/graphics/ContextShadow.h @@ -68,6 +68,8 @@ typedef void* PlatformContext; // This class should be copyable since GraphicsContextQt keeps a stack of // the shadow state for savePlatformState and restorePlatformState. +// This class is Deprecated. Platforms should migrate to ShadowBlur. + class ContextShadow { public: enum { diff --git a/Source/WebCore/platform/graphics/Extensions3D.h b/Source/WebCore/platform/graphics/Extensions3D.h index 1a2b7a1..6d6efe5 100644 --- a/Source/WebCore/platform/graphics/Extensions3D.h +++ b/Source/WebCore/platform/graphics/Extensions3D.h @@ -52,6 +52,7 @@ public: // GL_ANGLE_framebuffer_blit / GL_ANGLE_framebuffer_multisample // GL_OES_texture_float // GL_OES_standard_derivatives + // GL_OES_rgb8_rgba8 // Takes full name of extension; for example, // "GL_EXT_texture_format_BGRA8888". @@ -87,6 +88,10 @@ public: // GL_OES_standard_derivatives names FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B, + + // GL_OES_rgb8_rgba8 names + RGB8_OES = 0x8051, + RGBA8_OES = 0x8058, }; // GL_ARB_robustness diff --git a/Source/WebCore/platform/graphics/FloatQuad.h b/Source/WebCore/platform/graphics/FloatQuad.h index 6cd86f6..e913723 100644 --- a/Source/WebCore/platform/graphics/FloatQuad.h +++ b/Source/WebCore/platform/graphics/FloatQuad.h @@ -54,9 +54,9 @@ public: FloatQuad(const FloatRect& inRect) : m_p1(inRect.location()) - , m_p2(inRect.right(), inRect.y()) - , m_p3(inRect.right(), inRect.bottom()) - , m_p4(inRect.x(), inRect.bottom()) + , m_p2(inRect.maxX(), inRect.y()) + , m_p3(inRect.maxX(), inRect.maxY()) + , m_p4(inRect.x(), inRect.maxY()) { } diff --git a/Source/WebCore/platform/graphics/FloatRect.cpp b/Source/WebCore/platform/graphics/FloatRect.cpp index 0d8a24e..36f3d3a 100644 --- a/Source/WebCore/platform/graphics/FloatRect.cpp +++ b/Source/WebCore/platform/graphics/FloatRect.cpp @@ -51,22 +51,22 @@ bool FloatRect::intersects(const FloatRect& other) const { // Checking emptiness handles negative widths as well as zero. return !isEmpty() && !other.isEmpty() - && x() < other.right() && other.x() < right() - && y() < other.bottom() && other.y() < bottom(); + && x() < other.maxX() && other.x() < maxX() + && y() < other.maxY() && other.y() < maxY(); } bool FloatRect::contains(const FloatRect& other) const { - return x() <= other.x() && right() >= other.right() - && y() <= other.y() && bottom() >= other.bottom(); + return x() <= other.x() && maxX() >= other.maxX() + && y() <= other.y() && maxY() >= other.maxY(); } void FloatRect::intersect(const FloatRect& other) { float l = max(x(), other.x()); float t = max(y(), other.y()); - float r = min(right(), other.right()); - float b = min(bottom(), other.bottom()); + float r = min(maxX(), other.maxX()); + float b = min(maxY(), other.maxY()); // Return a clean empty rectangle for non-intersecting cases. if (l >= r || t >= b) { @@ -91,8 +91,8 @@ void FloatRect::unite(const FloatRect& other) float l = min(x(), other.x()); float t = min(y(), other.y()); - float r = max(right(), other.right()); - float b = max(bottom(), other.bottom()); + float r = max(maxX(), other.maxX()); + float b = max(maxY(), other.maxY()); setLocationAndSizeFromEdges(l, t, r, b); } @@ -180,8 +180,8 @@ IntRect enclosingIntRect(const FloatRect& rect) { float left = floorf(rect.x()); float top = floorf(rect.y()); - float width = ceilf(rect.right()) - left; - float height = ceilf(rect.bottom()) - top; + float width = ceilf(rect.maxX()) - left; + float height = ceilf(rect.maxY()) - top; return IntRect(safeFloatToInt(left), safeFloatToInt(top), safeFloatToInt(width), safeFloatToInt(height)); } diff --git a/Source/WebCore/platform/graphics/FloatRect.h b/Source/WebCore/platform/graphics/FloatRect.h index 10ad838..733f7cc 100644 --- a/Source/WebCore/platform/graphics/FloatRect.h +++ b/Source/WebCore/platform/graphics/FloatRect.h @@ -90,6 +90,8 @@ public: float x() const { return m_location.x(); } float y() const { return m_location.y(); } + float maxX() const { return x() + width(); } + float maxY() const { return y() + height(); } float width() const { return m_size.width(); } float height() const { return m_size.height(); } @@ -100,11 +102,6 @@ public: bool isEmpty() const { return m_size.isEmpty(); } - float left() const { return x(); } - float right() const { return x() + width(); } - float top() const { return y(); } - float bottom() const { return y() + height(); } - FloatPoint center() const { return FloatPoint(x() + width() / 2, y() + height() / 2); } void move(const FloatSize& delta) { m_location += delta; } @@ -119,10 +116,9 @@ public: // Note, this doesn't match what IntRect::contains(IntPoint&) does; the int version // is really checking for containment of 1x1 rect, but that doesn't make sense with floats. bool contains(float px, float py) const - { return px >= x() && px <= right() && py >= y() && py <= bottom(); } + { return px >= x() && px <= maxX() && py >= y() && py <= maxY(); } bool contains(const FloatPoint& point) const { return contains(point.x(), point.y()); } - void inflateX(float dx) { m_location.setX(m_location.x() - dx); m_size.setWidth(m_size.width() + dx + dx); diff --git a/Source/WebCore/platform/graphics/Font.cpp b/Source/WebCore/platform/graphics/Font.cpp index 394de35..6bdddfc 100644 --- a/Source/WebCore/platform/graphics/Font.cpp +++ b/Source/WebCore/platform/graphics/Font.cpp @@ -2,7 +2,7 @@ * Copyright (C) 1999 Lars Knoll (knoll@kde.org) * (C) 1999 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2010, 2011 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 @@ -247,21 +247,15 @@ bool Font::isSVGFont() const } #endif -String Font::normalizeSpaces(const String& string) +String Font::normalizeSpaces(const UChar* characters, unsigned length) { - const UChar* characters = string.characters(); - unsigned length = string.length(); - Vector<UChar, 256> buffer(length); - bool didReplacement = false; - - for (unsigned i = 0; i < length; ++i) { - UChar originalCharacter = characters[i]; - buffer[i] = normalizeSpaces(originalCharacter); - if (buffer[i] != originalCharacter) - didReplacement = true; - } + UChar* buffer; + String normalized = String::createUninitialized(length, buffer); + + for (unsigned i = 0; i < length; ++i) + buffer[i] = normalizeSpaces(characters[i]); - return didReplacement ? String(buffer.data(), length) : string; + return normalized; } static bool shouldUseFontSmoothing = true; @@ -293,7 +287,7 @@ Font::CodePath Font::codePath(const TextRun& run) const return s_codePath; #if PLATFORM(QT) - if (run.padding() || run.rtl() || isSmallCaps() || wordSpacing() || letterSpacing()) + if (run.expansion() || run.rtl() || isSmallCaps() || wordSpacing() || letterSpacing()) return Complex; #endif @@ -458,6 +452,56 @@ bool Font::isCJKIdeographOrSymbol(UChar32 c) return isCJKIdeograph(c); } +unsigned Font::expansionOpportunityCount(const UChar* characters, size_t length, TextDirection direction, bool& isAfterExpansion) +{ + static bool expandAroundIdeographs = canExpandAroundIdeographsInComplexText(); + unsigned count = 0; + if (direction == LTR) { + for (size_t i = 0; i < length; ++i) { + UChar32 character = characters[i]; + if (treatAsSpace(character)) { + count++; + isAfterExpansion = true; + continue; + } + if (U16_IS_LEAD(character) && i + 1 < length && U16_IS_TRAIL(characters[i + 1])) { + character = U16_GET_SUPPLEMENTARY(character, characters[i + 1]); + i++; + } + if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { + if (!isAfterExpansion) + count++; + count++; + isAfterExpansion = true; + continue; + } + isAfterExpansion = false; + } + } else { + for (size_t i = length; i > 0; --i) { + UChar32 character = characters[i - 1]; + if (treatAsSpace(character)) { + count++; + isAfterExpansion = true; + continue; + } + if (U16_IS_TRAIL(character) && i > 1 && U16_IS_LEAD(characters[i - 2])) { + character = U16_GET_SUPPLEMENTARY(characters[i - 2], character); + i--; + } + if (expandAroundIdeographs && isCJKIdeographOrSymbol(character)) { + if (!isAfterExpansion) + count++; + count++; + isAfterExpansion = true; + continue; + } + isAfterExpansion = false; + } + } + return count; +} + bool Font::canReceiveTextEmphasis(UChar32 c) { CharCategory category = Unicode::category(c); diff --git a/Source/WebCore/platform/graphics/Font.h b/Source/WebCore/platform/graphics/Font.h index 2957c0a..258240b 100644 --- a/Source/WebCore/platform/graphics/Font.h +++ b/Source/WebCore/platform/graphics/Font.h @@ -2,7 +2,7 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006, 2007, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2010, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008 Holger Hans Peter Freyther * * This library is free software; you can redistribute it and/or @@ -25,14 +25,15 @@ #ifndef Font_h #define Font_h -#include "CharacterNames.h" #include "FontDescription.h" #include "FontFallbackList.h" #include "SimpleFontData.h" +#include "TextDirection.h" #include "TypesettingFeatures.h" #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/MathExtras.h> +#include <wtf/unicode/CharacterNames.h> #if PLATFORM(QT) #include <QFont> @@ -43,7 +44,7 @@ namespace WebCore { class FloatPoint; class FloatRect; class FontData; -class FontFallbackList; +class FontMetrics; class FontPlatformData; class FontSelector; class GlyphBuffer; @@ -54,8 +55,6 @@ class TextRun; struct GlyphData; -const unsigned defaultUnitsPerEm = 1000; - struct GlyphOverflow { GlyphOverflow() : left(0) @@ -124,17 +123,12 @@ public: bool italic() const { return m_fontDescription.italic(); } FontWeight weight() const { return m_fontDescription.weight(); } + FontWidthVariant widthVariant() const { return m_fontDescription.widthVariant(); } bool isPlatformFont() const { return m_isPlatformFont; } // Metrics that we query the FontFallbackList for. - int ascent(FontBaseline baselineType = AlphabeticBaseline) const { return primaryFont()->ascent(baselineType); } - int descent(FontBaseline baselineType = AlphabeticBaseline) const { return primaryFont()->descent(baselineType); } - int height() const { return ascent() + descent(); } - int lineSpacing() const { return primaryFont()->lineSpacing(); } - int lineGap() const { return primaryFont()->lineGap(); } - float xHeight() const { return primaryFont()->xHeight(); } - unsigned unitsPerEm() const { return primaryFont()->unitsPerEm(); } + const FontMetrics& fontMetrics() const { return primaryFont()->fontMetrics(); } int spaceWidth() const { return (int)ceilf(primaryFont()->adjustedSpaceWidth() + m_letterSpacing); } float tabWidth(const SimpleFontData& fontData) const { return 8 * ceilf(fontData.adjustedSpaceWidth() + letterSpacing()); } int emphasisMarkAscent(const AtomicString&) const; @@ -150,7 +144,9 @@ public: static bool isCJKIdeograph(UChar32); static bool isCJKIdeographOrSymbol(UChar32); - + + static unsigned expansionOpportunityCount(const UChar*, size_t length, TextDirection, bool& isAfterExpansion); + #if PLATFORM(QT) QFont font() const; #endif @@ -185,6 +181,7 @@ private: bool getEmphasisMarkGlyphData(const AtomicString&, GlyphData&) const; static bool canReturnFallbackFontsForComplexText(); + static bool canExpandAroundIdeographsInComplexText(); CodePath codePath(const TextRun&) const; @@ -226,7 +223,7 @@ public: return character; } - static String normalizeSpaces(const String&); + static String normalizeSpaces(const UChar*, unsigned length); #if ENABLE(SVG_FONTS) bool isSVGFont() const; diff --git a/Source/WebCore/platform/graphics/FontCache.cpp b/Source/WebCore/platform/graphics/FontCache.cpp index cfca980..ca82ebd 100644 --- a/Source/WebCore/platform/graphics/FontCache.cpp +++ b/Source/WebCore/platform/graphics/FontCache.cpp @@ -57,7 +57,7 @@ struct FontPlatformDataCacheKey { WTF_MAKE_FAST_ALLOCATED; public: FontPlatformDataCacheKey(const AtomicString& family = AtomicString(), unsigned size = 0, unsigned weight = 0, bool italic = false, - bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal) + bool isPrinterFont = false, FontRenderingMode renderingMode = NormalRenderingMode, FontOrientation orientation = Horizontal, FontWidthVariant widthVariant = RegularWidth) : m_size(size) , m_weight(weight) , m_family(family) @@ -65,6 +65,7 @@ public: , m_printerFont(isPrinterFont) , m_renderingMode(renderingMode) , m_orientation(orientation) + , m_widthVariant(widthVariant) { } @@ -75,7 +76,7 @@ public: { return equalIgnoringCase(m_family, other.m_family) && m_size == other.m_size && m_weight == other.m_weight && m_italic == other.m_italic && m_printerFont == other.m_printerFont && - m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation; + m_renderingMode == other.m_renderingMode && m_orientation == other.m_orientation && m_widthVariant == other.m_widthVariant; } unsigned m_size; @@ -85,6 +86,7 @@ public: bool m_printerFont; FontRenderingMode m_renderingMode; FontOrientation m_orientation; + FontWidthVariant m_widthVariant; private: static unsigned hashTableDeletedSize() { return 0xFFFFFFFFU; } @@ -92,10 +94,11 @@ private: inline unsigned computeHash(const FontPlatformDataCacheKey& fontKey) { - unsigned hashCodes[4] = { + unsigned hashCodes[5] = { CaseFoldingHash::hash(fontKey.m_family), fontKey.m_size, fontKey.m_weight, + fontKey.m_widthVariant, static_cast<unsigned>(fontKey.m_orientation) << 3 | static_cast<unsigned>(fontKey.m_italic) << 2 | static_cast<unsigned>(fontKey.m_printerFont) << 1 | static_cast<unsigned>(fontKey.m_renderingMode) }; return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); @@ -195,7 +198,7 @@ FontPlatformData* FontCache::getCachedFontPlatformData(const FontDescription& fo } FontPlatformDataCacheKey key(familyName, fontDescription.computedPixelSize(), fontDescription.weight(), fontDescription.italic(), - fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation()); + fontDescription.usePrinterFont(), fontDescription.renderingMode(), fontDescription.orientation(), fontDescription.widthVariant()); FontPlatformData* result = 0; bool foundResult; FontPlatformDataCache::iterator it = gFontPlatformDataCache->find(key); diff --git a/Source/WebCore/platform/graphics/FontDescription.h b/Source/WebCore/platform/graphics/FontDescription.h index 12900bf..283d297 100644 --- a/Source/WebCore/platform/graphics/FontDescription.h +++ b/Source/WebCore/platform/graphics/FontDescription.h @@ -30,6 +30,7 @@ #include "FontRenderingMode.h" #include "FontSmoothingMode.h" #include "FontTraitsMask.h" +#include "FontWidthVariant.h" #include "TextRenderingMode.h" namespace WebCore { @@ -57,6 +58,7 @@ public: : m_specifiedSize(0) , m_computedSize(0) , m_orientation(Horizontal) + , m_widthVariant(RegularWidth) , m_italic(false) , m_smallCaps(false) , m_isAbsoluteSize(false) @@ -97,6 +99,7 @@ public: FontTraitsMask traitsMask() const; bool isSpecifiedFont() const { return m_isSpecifiedFont; } FontOrientation orientation() const { return m_orientation; } + FontWidthVariant widthVariant() const { return m_widthVariant; } void setFamily(const FontFamily& family) { m_familyList = family; } void setComputedSize(float s) { m_computedSize = s; } @@ -117,6 +120,7 @@ public: void setTextRenderingMode(TextRenderingMode rendering) { m_textRendering = rendering; } void setIsSpecifiedFont(bool isSpecifiedFont) { m_isSpecifiedFont = isSpecifiedFont; } void setOrientation(FontOrientation orientation) { m_orientation = orientation; } + void setWidthVariant(FontWidthVariant widthVariant) { m_widthVariant = widthVariant; } private: FontFamily m_familyList; // The list of font families to be used. @@ -126,6 +130,8 @@ private: float m_computedSize; // Computed size adjusted for the minimum font size and the zoom factor. FontOrientation m_orientation; + + FontWidthVariant m_widthVariant; bool m_italic : 1; bool m_smallCaps : 1; @@ -162,7 +168,8 @@ inline bool FontDescription::operator==(const FontDescription& other) const && m_fontSmoothing == other.m_fontSmoothing && m_textRendering == other.m_textRendering && m_isSpecifiedFont == other.m_isSpecifiedFont - && m_orientation == other.m_orientation; + && m_orientation == other.m_orientation + && m_widthVariant == other.m_widthVariant; } } diff --git a/Source/WebCore/platform/graphics/FontFastPath.cpp b/Source/WebCore/platform/graphics/FontFastPath.cpp index f927c13..034ac22 100644 --- a/Source/WebCore/platform/graphics/FontFastPath.cpp +++ b/Source/WebCore/platform/graphics/FontFastPath.cpp @@ -23,7 +23,6 @@ #include "config.h" #include "Font.h" -#include "CharacterNames.h" #include "FloatRect.h" #include "FontCache.h" #include "FontFallbackList.h" @@ -32,8 +31,8 @@ #include "SimpleFontData.h" #include "TextRun.h" #include "WidthIterator.h" - #include <wtf/MathExtras.h> +#include <wtf/unicode/CharacterNames.h> #include <wtf/unicode/Unicode.h> using namespace WTF; @@ -252,7 +251,7 @@ int Font::emphasisMarkAscent(const AtomicString& mark) const if (!markFontData) return 0; - return markFontData->ascent(); + return markFontData->fontMetrics().ascent(); } int Font::emphasisMarkDescent(const AtomicString& mark) const @@ -266,7 +265,7 @@ int Font::emphasisMarkDescent(const AtomicString& mark) const if (!markFontData) return 0; - return markFontData->descent(); + return markFontData->fontMetrics().descent(); } int Font::emphasisMarkHeight(const AtomicString& mark) const @@ -280,7 +279,7 @@ int Font::emphasisMarkHeight(const AtomicString& mark) const if (!markFontData) return 0; - return markFontData->height(); + return markFontData->fontMetrics().height(); } float Font::getGlyphsAndAdvancesForSimpleText(const TextRun& run, int from, int to, GlyphBuffer& glyphBuffer, ForTextEmphasisOrNot forTextEmphasis) const @@ -414,8 +413,8 @@ float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer it.advance(run.length(), glyphBuffer); if (glyphOverflow) { - glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - ascent()); - glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - descent()); + glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-it.minGlyphBoundingBoxY()) - fontMetrics().ascent()); + glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(it.maxGlyphBoundingBoxY()) - fontMetrics().descent()); glyphOverflow->left = ceilf(it.firstGlyphOverflow()); glyphOverflow->right = ceilf(it.lastGlyphOverflow()); } diff --git a/Source/WebCore/platform/graphics/FontMetrics.h b/Source/WebCore/platform/graphics/FontMetrics.h new file mode 100644 index 0000000..89c5545 --- /dev/null +++ b/Source/WebCore/platform/graphics/FontMetrics.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) Research In Motion Limited 2010-2011. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef FontMetrics_h +#define FontMetrics_h + +#include <wtf/MathExtras.h> + +namespace WebCore { + +const unsigned gDefaultUnitsPerEm = 1000; + +class FontMetrics { +public: + FontMetrics() + : m_unitsPerEm(gDefaultUnitsPerEm) + , m_ascent(0) + , m_descent(0) + , m_lineGap(0) + , m_lineSpacing(0) + , m_xHeight(0) + { + } + + unsigned unitsPerEm() const { return m_unitsPerEm; } + void setUnitsPerEm(unsigned unitsPerEm) { m_unitsPerEm = unitsPerEm; } + + float floatAscent(FontBaseline baselineType = AlphabeticBaseline) const + { + if (baselineType == AlphabeticBaseline) + return m_ascent; + return floatHeight() / 2; + } + + void setAscent(float ascent) { m_ascent = ascent; } + + float floatDescent(FontBaseline baselineType = AlphabeticBaseline) const + { + if (baselineType == AlphabeticBaseline) + return m_descent; + return floatHeight() / 2; + } + + void setDescent(float descent) { m_descent = descent; } + + float floatHeight(FontBaseline baselineType = AlphabeticBaseline) const + { + return floatAscent(baselineType) + floatDescent(baselineType); + } + + float floatLineGap() const { return m_lineGap; } + void setLineGap(float lineGap) { m_lineGap = lineGap; } + + float floatLineSpacing() const { return m_lineSpacing; } + void setLineSpacing(float lineSpacing) { m_lineSpacing = lineSpacing; } + + float xHeight() const { return m_xHeight; } + void setXHeight(float xHeight) { m_xHeight = xHeight; } + + // Integer variants of certain metrics, used for HTML rendering. + int ascent(FontBaseline baselineType = AlphabeticBaseline) const + { + if (baselineType == AlphabeticBaseline) + return lroundf(m_ascent); + return height() - height() / 2; + } + + int descent(FontBaseline baselineType = AlphabeticBaseline) const + { + if (baselineType == AlphabeticBaseline) + return lroundf(m_descent); + return height() / 2; + } + + int height(FontBaseline baselineType = AlphabeticBaseline) const + { + return ascent(baselineType) + descent(baselineType); + } + + int lineGap() const { return lroundf(m_lineGap); } + int lineSpacing() const { return lroundf(m_lineSpacing); } + +private: + friend class SimpleFontData; + + void reset() + { + m_unitsPerEm = gDefaultUnitsPerEm; + m_ascent = 0; + m_descent = 0; + m_lineGap = 0; + m_lineSpacing = 0; + m_xHeight = 0; + } + + unsigned m_unitsPerEm; + float m_ascent; + float m_descent; + float m_lineGap; + float m_lineSpacing; + float m_xHeight; +}; + +} // namespace WebCore + +#endif // FontMetrics_h diff --git a/Source/WebCore/platform/graphics/FontWidthVariant.h b/Source/WebCore/platform/graphics/FontWidthVariant.h new file mode 100644 index 0000000..bbc98ee --- /dev/null +++ b/Source/WebCore/platform/graphics/FontWidthVariant.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 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 FontWidthVariant_h +#define FontWidthVariant_h + +namespace WebCore { + +enum FontWidthVariant { RegularWidth, HalfWidth, ThirdWidth, QuarterWidth }; + +} // namespace WebCore + +#endif // FontWidthVariant_h diff --git a/Source/WebCore/platform/graphics/GlyphBuffer.h b/Source/WebCore/platform/graphics/GlyphBuffer.h index 6f1fe7b..7aac1e3 100644 --- a/Source/WebCore/platform/graphics/GlyphBuffer.h +++ b/Source/WebCore/platform/graphics/GlyphBuffer.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2009, 2011 Apple Inc. All rights reserved. * Copyright (C) 2007-2008 Torch Mobile Inc. * * Redistribution and use in source and binary forms, with or without @@ -187,7 +187,20 @@ public: m_advances.append(advance); } #endif - + + void expandLastAdvance(float width) + { + ASSERT(!isEmpty()); + GlyphBufferAdvance& lastAdvance = m_advances.last(); +#if PLATFORM(CG) || (PLATFORM(WX) && OS(DARWIN)) + lastAdvance.width += width; +#elif OS(WINCE) + lastAdvance += width; +#else + lastAdvance += FloatSize(width, 0); +#endif + } + private: Vector<const SimpleFontData*, 2048> m_fontData; Vector<GlyphBufferGlyph, 2048> m_glyphs; diff --git a/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp b/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp index 3df14b9..e7ed193 100644 --- a/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp +++ b/Source/WebCore/platform/graphics/GlyphPageTreeNode.cpp @@ -29,12 +29,12 @@ #include "config.h" #include "GlyphPageTreeNode.h" -#include "CharacterNames.h" #include "PlatformString.h" #include "SegmentedFontData.h" #include "SimpleFontData.h" #include <stdio.h> #include <wtf/text/CString.h> +#include <wtf/unicode/CharacterNames.h> #include <wtf/unicode/Unicode.h> namespace WebCore { diff --git a/Source/WebCore/platform/graphics/GraphicsContext.cpp b/Source/WebCore/platform/graphics/GraphicsContext.cpp index 9f94ac9..a0a7ea9 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext.cpp @@ -142,6 +142,18 @@ void GraphicsContext::setShadow(const FloatSize& offset, float blur, const Color setPlatformShadow(offset, blur, color, colorSpace); } +void GraphicsContext::setLegacyShadow(const FloatSize& offset, float blur, const Color& color, ColorSpace colorSpace) +{ + m_state.shadowOffset = offset; + m_state.shadowBlur = blur; + m_state.shadowColor = color; + m_state.shadowColorSpace = colorSpace; +#if PLATFORM(CG) + m_state.shadowsUseLegacyRadius = true; +#endif + setPlatformShadow(offset, blur, color, colorSpace); +} + void GraphicsContext::clearShadow() { m_state.shadowOffset = FloatSize(); @@ -532,10 +544,10 @@ void GraphicsContext::drawImageBuffer(ImageBuffer* image, ColorSpace styleColorS InterpolationQuality previousInterpolationQuality = imageInterpolationQuality(); // FIXME: Should be InterpolationLow setImageInterpolationQuality(InterpolationNone); - image->draw(this, styleColorSpace, dest, src, op, useLowQualityScale); + image->draw(this, styleColorSpace, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op, useLowQualityScale); setImageInterpolationQuality(previousInterpolationQuality); } else - image->draw(this, styleColorSpace, dest, src, op, useLowQualityScale); + image->draw(this, styleColorSpace, FloatRect(dest.location(), FloatSize(tw, th)), FloatRect(src.location(), FloatSize(tsw, tsh)), op, useLowQualityScale); } void GraphicsContext::addRoundedRectClip(const RoundedIntRect& rect) @@ -565,6 +577,14 @@ void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& re buffer->clip(this, rect); } +#if !PLATFORM(CG) +IntRect GraphicsContext::clipBounds() const +{ + ASSERT_NOT_REACHED(); + return IntRect(); +} +#endif + TextDrawingModeFlags GraphicsContext::textDrawingMode() const { return m_state.textDrawingMode; @@ -590,6 +610,34 @@ void GraphicsContext::fillRoundedRect(const RoundedIntRect& rect, const Color& c fillRoundedRect(rect.rect(), rect.radii().topLeft(), rect.radii().topRight(), rect.radii().bottomLeft(), rect.radii().bottomRight(), color, colorSpace); } +#if !PLATFORM(CG) +void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedIntRect& roundedHoleRect, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + Path path; + path.addRect(rect); + + if (!roundedHoleRect.radii().isZero()) + path.addRoundedRect(roundedHoleRect.rect(), roundedHoleRect.radii().topLeft(), roundedHoleRect.radii().topRight(), roundedHoleRect.radii().bottomLeft(), roundedHoleRect.radii().bottomRight()); + else + path.addRect(roundedHoleRect.rect()); + + WindRule oldFillRule = fillRule(); + Color oldFillColor = fillColor(); + ColorSpace oldFillColorSpace = fillColorSpace(); + + setFillRule(RULE_EVENODD); + setFillColor(color, colorSpace); + + fillPath(path); + + setFillRule(oldFillRule); + setFillColor(oldFillColor, oldFillColorSpace); +} +#endif + void GraphicsContext::setCompositeOperation(CompositeOperator compositeOperation) { m_state.compositeOperator = compositeOperation; diff --git a/Source/WebCore/platform/graphics/GraphicsContext.h b/Source/WebCore/platform/graphics/GraphicsContext.h index 77321e2..21a9067 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext.h +++ b/Source/WebCore/platform/graphics/GraphicsContext.h @@ -176,6 +176,11 @@ namespace WebCore { , shouldSmoothFonts(true) , paintingDisabled(false) , shadowsIgnoreTransforms(false) +#if PLATFORM(CG) + // Core Graphics incorrectly renders shadows with radius > 8px (<rdar://problem/8103442>), + // but we need to preserve this buggy behavior for canvas and -webkit-box-shadow. + , shadowsUseLegacyRadius(false) +#endif { } @@ -212,6 +217,9 @@ namespace WebCore { bool shouldSmoothFonts : 1; bool paintingDisabled : 1; bool shadowsIgnoreTransforms : 1; +#if PLATFORM(CG) + bool shadowsUseLegacyRadius : 1; +#endif }; class GraphicsContext { @@ -320,6 +328,7 @@ namespace WebCore { void fillRect(const FloatRect&, Generator&); void fillRoundedRect(const IntRect&, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color&, ColorSpace); void fillRoundedRect(const RoundedIntRect&, const Color&, ColorSpace); + void fillRectWithRoundedHole(const IntRect&, const RoundedIntRect& roundedHoleRect, const Color&, ColorSpace); void clearRect(const FloatRect&); @@ -355,6 +364,8 @@ namespace WebCore { void clipPath(const Path&, WindRule); void clipConvexPolygon(size_t numPoints, const FloatPoint*, bool antialias = true); void clipToImageBuffer(ImageBuffer*, const FloatRect&); + + IntRect clipBounds() const; TextDrawingModeFlags textDrawingMode() const; void setTextDrawingMode(TextDrawingModeFlags); @@ -385,6 +396,10 @@ namespace WebCore { bool hasShadow() const; void setShadow(const FloatSize&, float blur, const Color&, ColorSpace); + // Legacy shadow blur radius is used for canvas, and -webkit-box-shadow. + // It has different treatment of radii > 8px. + void setLegacyShadow(const FloatSize&, float blur, const Color&, ColorSpace); + bool getShadow(FloatSize&, float&, Color&, ColorSpace&) const; void clearShadow(); @@ -404,16 +419,11 @@ namespace WebCore { void setCompositeOperation(CompositeOperator); CompositeOperator compositeOperation() const; -#if PLATFORM(SKIA) - void beginPath(); - void addPath(const Path&); -#endif - void clip(const Path&); // This clip function is used only by <canvas> code. It allows // implementations to handle clipping on the canvas differently since - // the disipline is different. + // the discipline is different. void canvasClip(const Path&); void clipOut(const Path&); @@ -500,7 +510,6 @@ namespace WebCore { bool inTransparencyLayer() const; void pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask); void takeOwnershipOfPlatformContext(); - static QPainter::CompositionMode toQtCompositionMode(CompositeOperator op); #endif #if PLATFORM(QT) || PLATFORM(CAIRO) diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp index f7c5a66..324fed8 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext3D.cpp +++ b/Source/WebCore/platform/graphics/GraphicsContext3D.cpp @@ -26,7 +26,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" @@ -1437,4 +1437,4 @@ bool GraphicsContext3D::packPixels(const uint8_t* sourceData, } // namespace WebCore -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/GraphicsContext3D.h b/Source/WebCore/platform/graphics/GraphicsContext3D.h index 24827e5..d6c1cec 100644 --- a/Source/WebCore/platform/graphics/GraphicsContext3D.h +++ b/Source/WebCore/platform/graphics/GraphicsContext3D.h @@ -47,7 +47,9 @@ typedef unsigned int GC3Denum; typedef unsigned char GC3Dboolean; typedef unsigned int GC3Dbitfield; +typedef signed char GC3Dbyte; typedef unsigned char GC3Dubyte; +typedef short GC3Dshort; typedef unsigned short GC3Dushort; typedef int GC3Dint; typedef int GC3Dsizei; @@ -117,7 +119,7 @@ class GraphicsContext3DInternal; class GraphicsContext3D : public RefCounted<GraphicsContext3D> { public: - enum WebGLEnumType { + enum { DEPTH_BUFFER_BIT = 0x00000100, STENCIL_BUFFER_BIT = 0x00000400, COLOR_BUFFER_BIT = 0x00004000, @@ -452,6 +454,14 @@ public: RenderDirectlyToHostWindow }; + class ContextLostCallback { + public: + virtual void onContextLost() = 0; + virtual ~ContextLostCallback() {} + }; + + void setContextLostCallback(PassOwnPtr<ContextLostCallback>); + static PassRefPtr<GraphicsContext3D> create(Attributes, HostWindow*, RenderStyle = RenderOffscreen); ~GraphicsContext3D(); @@ -487,10 +497,6 @@ public: void prepareTexture(); #endif - // Helper to return the size in bytes of OpenGL data types - // like GL_FLOAT, GL_INT, etc. - unsigned int sizeInBytes(GC3Denum type); - // Helper to texImage2D with pixel==0 case: pixels are initialized to 0. // Return true if no GL error is synthesized. // By default, alignment is 4, the OpenGL default setting. diff --git a/Source/WebCore/platform/graphics/Icon.h b/Source/WebCore/platform/graphics/Icon.h index c83685f..2797133 100644 --- a/Source/WebCore/platform/graphics/Icon.h +++ b/Source/WebCore/platform/graphics/Icon.h @@ -42,6 +42,7 @@ typedef struct _GdkPixbuf GdkPixbuf; #elif PLATFORM(EFL) typedef struct _Evas_Object Evas_Object; #elif PLATFORM(CHROMIUM) +#include "Image.h" #include "PlatformIcon.h" #endif @@ -60,6 +61,8 @@ public: #if PLATFORM(WIN) static PassRefPtr<Icon> create(HICON hIcon) { return adoptRef(new Icon(hIcon)); } +#elif PLATFORM(CHROMIUM) + static PassRefPtr<Icon> create(PassRefPtr<PlatformIcon> icon) { return adoptRef(new Icon(icon)); } #endif private: @@ -79,8 +82,8 @@ private: Icon(); Evas_Object* m_icon; #elif PLATFORM(CHROMIUM) - Icon(const PlatformIcon&); - PlatformIcon m_icon; + Icon(PassRefPtr<PlatformIcon>); + RefPtr<PlatformIcon> m_icon; #endif }; diff --git a/Source/WebCore/platform/graphics/IntRect.cpp b/Source/WebCore/platform/graphics/IntRect.cpp index 188b5f9..7591c41 100644 --- a/Source/WebCore/platform/graphics/IntRect.cpp +++ b/Source/WebCore/platform/graphics/IntRect.cpp @@ -44,22 +44,22 @@ bool IntRect::intersects(const IntRect& other) const { // Checking emptiness handles negative widths as well as zero. return !isEmpty() && !other.isEmpty() - && x() < other.right() && other.x() < right() - && y() < other.bottom() && other.y() < bottom(); + && x() < other.maxX() && other.x() < maxX() + && y() < other.maxY() && other.y() < maxY(); } bool IntRect::contains(const IntRect& other) const { - return x() <= other.x() && right() >= other.right() - && y() <= other.y() && bottom() >= other.bottom(); + return x() <= other.x() && maxX() >= other.maxX() + && y() <= other.y() && maxY() >= other.maxY(); } void IntRect::intersect(const IntRect& other) { int l = max(x(), other.x()); int t = max(y(), other.y()); - int r = min(right(), other.right()); - int b = min(bottom(), other.bottom()); + int r = min(maxX(), other.maxX()); + int b = min(maxY(), other.maxY()); // Return a clean empty rectangle for non-intersecting cases. if (l >= r || t >= b) { @@ -87,8 +87,8 @@ void IntRect::unite(const IntRect& other) int l = min(x(), other.x()); int t = min(y(), other.y()); - int r = max(right(), other.right()); - int b = max(bottom(), other.bottom()); + int r = max(maxX(), other.maxX()); + int b = max(maxY(), other.maxY()); m_location.setX(l); m_location.setY(t); diff --git a/Source/WebCore/platform/graphics/IntRect.h b/Source/WebCore/platform/graphics/IntRect.h index 638db75..3a2433d 100644 --- a/Source/WebCore/platform/graphics/IntRect.h +++ b/Source/WebCore/platform/graphics/IntRect.h @@ -91,6 +91,8 @@ public: int x() const { return m_location.x(); } int y() const { return m_location.y(); } + int maxX() const { return x() + width(); } + int maxY() const { return y() + height(); } int width() const { return m_size.width(); } int height() const { return m_size.height(); } @@ -99,18 +101,8 @@ public: void setWidth(int width) { m_size.setWidth(width); } void setHeight(int height) { m_size.setHeight(height); } - // Be careful with these functions. The point is considered to be to the right and below. These are not - // substitutes for right() and bottom(). - IntPoint topLeft() const { return m_location; } - IntPoint topRight() const { return IntPoint(right() - 1, y()); } - IntPoint bottomLeft() const { return IntPoint(x(), bottom() - 1); } - IntPoint bottomRight() const { return IntPoint(right() - 1, bottom() - 1); } - bool isEmpty() const { return m_size.isEmpty(); } - int right() const { return x() + width(); } - int bottom() const { return y() + height(); } - // NOTE: The result is rounded to integer values, and thus may be not the exact // center point. IntPoint center() const { return IntPoint(x() + width() / 2, y() + height() / 2); } @@ -118,26 +110,26 @@ public: void move(const IntSize& s) { m_location += s; } void move(int dx, int dy) { m_location.move(dx, dy); } - void shiftLeftEdgeTo(int edge) + void shiftXEdgeTo(int edge) { int delta = edge - x(); setX(edge); setWidth(std::max(0, width() - delta)); } - void shiftRightEdgeTo(int edge) + void shiftMaxXEdgeTo(int edge) { - int delta = edge - right(); + int delta = edge - maxX(); setWidth(std::max(0, width() + delta)); } - void shiftTopEdgeTo(int edge) + void shiftYEdgeTo(int edge) { int delta = edge - y(); setY(edge); setHeight(std::max(0, height() - delta)); } - void shiftBottomEdgeTo(int edge) + void shiftMaxYEdgeTo(int edge) { - int delta = edge - bottom(); + int delta = edge - maxY(); setHeight(std::max(0, height() + delta)); } @@ -147,7 +139,7 @@ public: // This checks to see if the rect contains x,y in the traditional sense. // Equivalent to checking if the rect contains a 1x1 rect below and to the right of (px,py). bool contains(int px, int py) const - { return px >= x() && px < right() && py >= y() && py < bottom(); } + { return px >= x() && px < maxX() && py >= y() && py < maxY(); } bool contains(const IntPoint& point) const { return contains(point.x(), point.y()); } void intersect(const IntRect&); diff --git a/Source/WebCore/platform/graphics/IntRectHash.h b/Source/WebCore/platform/graphics/IntRectHash.h new file mode 100644 index 0000000..7138f33 --- /dev/null +++ b/Source/WebCore/platform/graphics/IntRectHash.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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. 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 INC. 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 IntRectHash_h +#define IntRectHash_h + +#include "IntPointHash.h" +#include "IntRect.h" +#include "IntSizeHash.h" +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> + +namespace WTF { + +template<> struct IntHash<WebCore::IntRect> { + static unsigned hash(const WebCore::IntRect& key) + { + return intHash(static_cast<uint64_t>(DefaultHash<WebCore::IntPoint>::Hash::hash(key.location())) << 32 | DefaultHash<WebCore::IntSize>::Hash::hash(key.size())); + } + static bool equal(const WebCore::IntRect& a, const WebCore::IntRect& b) + { + return DefaultHash<WebCore::IntPoint>::Hash::equal(a.location(), b.location()) && DefaultHash<WebCore::IntSize>::Hash::equal(a.size(), b.size()); + } + static const bool safeToCompareToEmptyOrDeleted = true; +}; +template<> struct DefaultHash<WebCore::IntRect> { typedef IntHash<WebCore::IntRect> Hash; }; + +template<> struct HashTraits<WebCore::IntRect> : GenericHashTraits<WebCore::IntRect> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(WebCore::IntRect& slot) { new (&slot) WebCore::IntRect(-1, -1, -1, -1); } + static bool isDeletedValue(const WebCore::IntRect& value) { return value.x() == -1 && value.y() == -1 && value.width() == -1 && value.height() == -1; } +}; + +} + +#endif diff --git a/Source/WebCore/platform/graphics/IntSizeHash.h b/Source/WebCore/platform/graphics/IntSizeHash.h index ad6eac3..0cca000 100644 --- a/Source/WebCore/platform/graphics/IntSizeHash.h +++ b/Source/WebCore/platform/graphics/IntSizeHash.h @@ -24,22 +24,20 @@ #include <wtf/HashMap.h> #include <wtf/HashSet.h> -using WebCore::IntSize; - namespace WTF { - template<> struct IntHash<IntSize> { - static unsigned hash(const IntSize& key) { return intHash((static_cast<uint64_t>(key.width()) << 32 | key.height())); } - static bool equal(const IntSize& a, const IntSize& b) { return a == b; } + template<> struct IntHash<WebCore::IntSize> { + static unsigned hash(const WebCore::IntSize& key) { return intHash((static_cast<uint64_t>(key.width()) << 32 | key.height())); } + static bool equal(const WebCore::IntSize& a, const WebCore::IntSize& b) { return a == b; } static const bool safeToCompareToEmptyOrDeleted = true; }; - template<> struct DefaultHash<IntSize> { typedef IntHash<IntSize> Hash; }; + template<> struct DefaultHash<WebCore::IntSize> { typedef IntHash<WebCore::IntSize> Hash; }; - template<> struct HashTraits<IntSize> : GenericHashTraits<IntSize> { + template<> struct HashTraits<WebCore::IntSize> : GenericHashTraits<WebCore::IntSize> { static const bool emptyValueIsZero = true; static const bool needsDestruction = false; - static void constructDeletedValue(IntSize& slot) { new (&slot) IntSize(-1, -1); } - static bool isDeletedValue(const IntSize& value) { return value.width() == -1 && value.height() == -1; } + static void constructDeletedValue(WebCore::IntSize& slot) { new (&slot) WebCore::IntSize(-1, -1); } + static bool isDeletedValue(const WebCore::IntSize& value) { return value.width() == -1 && value.height() == -1; } }; } // namespace WTF diff --git a/Source/WebCore/platform/graphics/MediaPlayer.cpp b/Source/WebCore/platform/graphics/MediaPlayer.cpp index 60f55a5..8eed0d2 100644 --- a/Source/WebCore/platform/graphics/MediaPlayer.cpp +++ b/Source/WebCore/platform/graphics/MediaPlayer.cpp @@ -698,6 +698,26 @@ double MediaPlayer::maximumDurationToCacheMediaTime() const return m_private->maximumDurationToCacheMediaTime(); } +unsigned long MediaPlayer::decodedFrames() const +{ + return m_private->decodedFrames(); +} + +unsigned long MediaPlayer::droppedFrames() const +{ + return m_private->droppedFrames(); +} + +unsigned long MediaPlayer::audioBytesDecoded() const +{ + return m_private->audioBytesDecoded(); +} + +unsigned long MediaPlayer::videoBytesDecoded() const +{ + return m_private->videoBytesDecoded(); +} + void MediaPlayer::reloadTimerFired(Timer<MediaPlayer>*) { m_private->cancelLoad(); diff --git a/Source/WebCore/platform/graphics/MediaPlayer.h b/Source/WebCore/platform/graphics/MediaPlayer.h index ef0b3eb..1112148 100644 --- a/Source/WebCore/platform/graphics/MediaPlayer.h +++ b/Source/WebCore/platform/graphics/MediaPlayer.h @@ -287,6 +287,11 @@ public: double maximumDurationToCacheMediaTime() const; + unsigned long decodedFrames() const; + unsigned long droppedFrames() const; + unsigned long audioBytesDecoded() const; + unsigned long videoBytesDecoded() const; + private: MediaPlayer(MediaPlayerClient*); void loadWithNextMediaEngine(MediaPlayerFactory*); diff --git a/Source/WebCore/platform/graphics/MediaPlayerPrivate.h b/Source/WebCore/platform/graphics/MediaPlayerPrivate.h index 6abe258..04b2612 100644 --- a/Source/WebCore/platform/graphics/MediaPlayerPrivate.h +++ b/Source/WebCore/platform/graphics/MediaPlayerPrivate.h @@ -136,6 +136,10 @@ public: // it is OK to calculate movie time before refreshing the cached time. virtual double maximumDurationToCacheMediaTime() const { return 0; } + virtual unsigned long decodedFrames() const { return 0; } + virtual unsigned long droppedFrames() const { return 0; } + virtual unsigned long audioBytesDecoded() const { return 0; } + virtual unsigned long videoBytesDecoded() const { return 0; } }; } diff --git a/Source/WebCore/platform/graphics/ShadowBlur.cpp b/Source/WebCore/platform/graphics/ShadowBlur.cpp new file mode 100644 index 0000000..f61ecff --- /dev/null +++ b/Source/WebCore/platform/graphics/ShadowBlur.cpp @@ -0,0 +1,733 @@ +/* + * Copyright (C) 2011 Apple Inc. + * Copyright (C) 2010 Sencha, Inc. + * Copyright (C) 2010 Igalia S.L. + * 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 "ShadowBlur.h" + +#include "AffineTransform.h" +#include "FloatQuad.h" +#include "GraphicsContext.h" +#include "ImageBuffer.h" +#include "Timer.h" +#include <wtf/MathExtras.h> +#include <wtf/Noncopyable.h> +#include <wtf/UnusedParam.h> + +using namespace std; + +namespace WebCore { + +static inline int roundUpToMultipleOf32(int d) +{ + return (1 + (d >> 5)) << 5; +} + +// ShadowBlur needs a scratch image as the buffer for the blur filter. +// Instead of creating and destroying the buffer for every operation, +// we create a buffer which will be automatically purged via a timer. +class ScratchBuffer { +public: + ScratchBuffer() + : m_purgeTimer(this, &ScratchBuffer::timerFired) +#if !ASSERT_DISABLED + , m_bufferInUse(false) +#endif + { + } + + ImageBuffer* getScratchBuffer(const IntSize& size) + { + ASSERT(!m_bufferInUse); +#if !ASSERT_DISABLED + m_bufferInUse = true; +#endif + // We do not need to recreate the buffer if the current buffer is large enough. + if (m_imageBuffer && m_imageBuffer->width() >= size.width() && m_imageBuffer->height() >= size.height()) + return m_imageBuffer.get(); + + // Round to the nearest 32 pixels so we do not grow the buffer for similar sized requests. + IntSize roundedSize(roundUpToMultipleOf32(size.width()), roundUpToMultipleOf32(size.height())); + + m_imageBuffer = ImageBuffer::create(roundedSize); + return m_imageBuffer.get(); + } + + void scheduleScratchBufferPurge() + { +#if !ASSERT_DISABLED + m_bufferInUse = false; +#endif + if (m_purgeTimer.isActive()) + m_purgeTimer.stop(); + + const double scratchBufferPurgeInterval = 2; + m_purgeTimer.startOneShot(scratchBufferPurgeInterval); + } + + static ScratchBuffer& shared(); + +private: + void timerFired(Timer<ScratchBuffer>*) + { + clearScratchBuffer(); + } + + void clearScratchBuffer() + { + m_imageBuffer = 0; + } + + OwnPtr<ImageBuffer> m_imageBuffer; + Timer<ScratchBuffer> m_purgeTimer; +#if !ASSERT_DISABLED + bool m_bufferInUse; +#endif +}; + +ScratchBuffer& ScratchBuffer::shared() +{ + DEFINE_STATIC_LOCAL(ScratchBuffer, scratchBuffer, ()); + return scratchBuffer; +} + +static const int templateSideLength = 1; + +ShadowBlur::ShadowBlur(float radius, const FloatSize& offset, const Color& color, ColorSpace colorSpace) + : m_color(color) + , m_colorSpace(colorSpace) + , m_blurRadius(radius) + , m_offset(offset) + , m_layerImage(0) + , m_shadowsIgnoreTransforms(false) +{ + // Limit blur radius to 128 to avoid lots of very expensive blurring. + m_blurRadius = min<float>(m_blurRadius, 128); + + // The type of shadow is decided by the blur radius, shadow offset, and shadow color. + if (!m_color.isValid() || !color.alpha()) { + // Can't paint the shadow with invalid or invisible color. + m_type = NoShadow; + } else if (m_blurRadius > 0) { + // Shadow is always blurred, even the offset is zero. + m_type = BlurShadow; + } else if (!m_offset.width() && !m_offset.height()) { + // Without blur and zero offset means the shadow is fully hidden. + m_type = NoShadow; + } else + m_type = SolidShadow; +} + +// Instead of integer division, we use 17.15 for fixed-point division. +static const int blurSumShift = 15; +static const float gaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat); + +void ShadowBlur::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride) +{ + const int channels[4] = +#if CPU(BIG_ENDIAN) + { 0, 3, 2, 0 }; +#elif CPU(MIDDLE_ENDIAN) + { 1, 2, 3, 1 }; +#else + { 3, 0, 1, 3 }; +#endif + + int diameter; + if (m_shadowsIgnoreTransforms) + diameter = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurRadius))); // Canvas shadow. FIXME: we should adjust the blur radius higher up. + else { + // http://dev.w3.org/csswg/css3-background/#box-shadow + // Approximate a Gaussian blur with a standard deviation equal to half the blur radius, + // which http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement tell us how to do. + // However, shadows rendered according to that spec will extend a little further than m_blurRadius, + // so we apply a fudge factor to bring the radius down slightly. + float stdDev = m_blurRadius / 2; + const float fudgeFactor = 0.88f; + diameter = max(2, static_cast<int>(floorf(stdDev * gaussianKernelFactor * fudgeFactor + 0.5f))); + } + + enum { + leftLobe = 0, + rightLobe = 1 + }; + + int lobes[3][2]; // indexed by pass, and left/right lobe + + if (diameter & 1) { + // if d is odd, use three box-blurs of size 'd', centered on the output pixel. + int lobeSize = (diameter - 1) / 2; + lobes[0][leftLobe] = lobeSize; + lobes[0][rightLobe] = lobeSize; + lobes[1][leftLobe] = lobeSize; + lobes[1][rightLobe] = lobeSize; + lobes[2][leftLobe] = lobeSize; + lobes[2][rightLobe] = lobeSize; + } else { + // if d is even, two box-blurs of size 'd' (the first one centered on the pixel boundary + // between the output pixel and the one to the left, the second one centered on the pixel + // boundary between the output pixel and the one to the right) and one box blur of size 'd+1' centered on the output pixel + int lobeSize = diameter / 2; + lobes[0][leftLobe] = lobeSize; + lobes[0][rightLobe] = lobeSize - 1; + lobes[1][leftLobe] = lobeSize - 1; + lobes[1][rightLobe] = lobeSize; + lobes[2][leftLobe] = lobeSize; + lobes[2][rightLobe] = lobeSize; + } + + // First pass is horizontal. + int stride = 4; + int delta = rowStride; + int final = size.height(); + int dim = size.width(); + + // Two stages: horizontal and vertical + for (int pass = 0; pass < 2; ++pass) { + unsigned char* pixels = imageData; + + for (int j = 0; j < final; ++j, pixels += delta) { + // For each step, we blur the alpha in a channel and store the result + // in another channel for the subsequent step. + // We use sliding window algorithm to accumulate the alpha values. + // This is much more efficient than computing the sum of each pixels + // covered by the box kernel size for each x. + for (int step = 0; step < 3; ++step) { + int side1 = lobes[step][leftLobe]; + int side2 = lobes[step][rightLobe]; + int pixelCount = side1 + 1 + side2; + int invCount = ((1 << blurSumShift) + pixelCount - 1) / pixelCount; + int ofs = 1 + side2; + int alpha1 = pixels[channels[step]]; + int alpha2 = pixels[(dim - 1) * stride + channels[step]]; + + unsigned char* ptr = pixels + channels[step + 1]; + unsigned char* prev = pixels + stride + channels[step]; + unsigned char* next = pixels + ofs * stride + channels[step]; + + int i; + int sum = side1 * alpha1 + alpha1; + int limit = (dim < side2 + 1) ? dim : side2 + 1; + + for (i = 1; i < limit; ++i, prev += stride) + sum += *prev; + + if (limit <= side2) + sum += (side2 - limit + 1) * alpha2; + + limit = (side1 < dim) ? side1 : dim; + for (i = 0; i < limit; ptr += stride, next += stride, ++i, ++ofs) { + *ptr = (sum * invCount) >> blurSumShift; + sum += ((ofs < dim) ? *next : alpha2) - alpha1; + } + + prev = pixels + channels[step]; + for (; ofs < dim; ptr += stride, prev += stride, next += stride, ++i, ++ofs) { + *ptr = (sum * invCount) >> blurSumShift; + sum += (*next) - (*prev); + } + + for (; i < dim; ptr += stride, prev += stride, ++i) { + *ptr = (sum * invCount) >> blurSumShift; + sum += alpha2 - (*prev); + } + } + } + + // Last pass is vertical. + stride = rowStride; + delta = 4; + final = size.width(); + dim = size.height(); + } +} + +void ShadowBlur::adjustBlurRadius(GraphicsContext* context) +{ + if (!m_shadowsIgnoreTransforms) + return; + + const AffineTransform transform = context->getCTM(); + + // Adjust blur if we're scaling, since the radius must not be affected by transformations. + // FIXME: use AffineTransform::isIdentityOrTranslationOrFlipped()? + if (transform.isIdentity()) + return; + + // Calculate transformed unit vectors. + const FloatQuad unitQuad(FloatPoint(0, 0), FloatPoint(1, 0), + FloatPoint(0, 1), FloatPoint(1, 1)); + const FloatQuad transformedUnitQuad = transform.mapQuad(unitQuad); + + // Calculate X axis scale factor. + const FloatSize xUnitChange = transformedUnitQuad.p2() - transformedUnitQuad.p1(); + const float xAxisScale = sqrtf(xUnitChange.width() * xUnitChange.width() + + xUnitChange.height() * xUnitChange.height()); + + // Calculate Y axis scale factor. + const FloatSize yUnitChange = transformedUnitQuad.p3() - transformedUnitQuad.p1(); + const float yAxisScale = sqrtf(yUnitChange.width() * yUnitChange.width() + + yUnitChange.height() * yUnitChange.height()); + + // blurLayerImage() does not support per-axis blurring, so calculate a balanced scaling. + // FIXME: does AffineTransform.xScale()/yScale() help? + const float scale = sqrtf(xAxisScale * yAxisScale); + m_blurRadius = roundf(m_blurRadius / scale); +} + +IntRect ShadowBlur::calculateLayerBoundingRect(GraphicsContext* context, const FloatRect& shadowedRect, const IntRect& clipRect) +{ + const float roundedRadius = ceilf(m_blurRadius); + + // Calculate the destination of the blurred and/or transformed layer. + FloatRect layerRect; + float inflation = 0; + + const AffineTransform transform = context->getCTM(); + if (m_shadowsIgnoreTransforms && !transform.isIdentity()) { + FloatQuad transformedPolygon = transform.mapQuad(FloatQuad(shadowedRect)); + transformedPolygon.move(m_offset); + layerRect = transform.inverse().mapQuad(transformedPolygon).boundingBox(); + } else { + layerRect = shadowedRect; + layerRect.move(m_offset); + } + + // We expand the area by the blur radius to give extra space for the blur transition. + if (m_type == BlurShadow) { + layerRect.inflate(roundedRadius); + inflation = roundedRadius; + } + + FloatRect unclippedLayerRect = layerRect; + + if (!clipRect.contains(enclosingIntRect(layerRect))) { + // If we are totally outside the clip region, we aren't painting at all. + if (intersection(layerRect, clipRect).isEmpty()) + return IntRect(); + + IntRect inflatedClip = clipRect; + // Pixels at the edges can be affected by pixels outside the buffer, + // so intersect with the clip inflated by the blur. + if (m_type == BlurShadow) + inflatedClip.inflate(roundedRadius); + + layerRect.intersect(inflatedClip); + } + + const float frameSize = inflation * 2; + m_sourceRect = FloatRect(0, 0, shadowedRect.width() + frameSize, shadowedRect.height() + frameSize); + m_layerOrigin = FloatPoint(layerRect.x(), layerRect.y()); + m_layerSize = layerRect.size(); + + const FloatPoint unclippedLayerOrigin = FloatPoint(unclippedLayerRect.x(), unclippedLayerRect.y()); + const FloatSize clippedOut = unclippedLayerOrigin - m_layerOrigin; + + // Set the origin as the top left corner of the scratch image, or, in case there's a clipped + // out region, set the origin accordingly to the full bounding rect's top-left corner. + float translationX = -shadowedRect.x() + inflation - fabsf(clippedOut.width()); + float translationY = -shadowedRect.y() + inflation - fabsf(clippedOut.height()); + m_layerContextTranslation = FloatSize(translationX, translationY); + + return enclosingIntRect(layerRect); +} + +GraphicsContext* ShadowBlur::beginShadowLayer(GraphicsContext* graphicsContext, const IntRect& layerRect) +{ + adjustBlurRadius(graphicsContext); + + // Don't paint if we are totally outside the clip region. + if (layerRect.isEmpty()) + return 0; + + m_layerImage = ScratchBuffer::shared().getScratchBuffer(layerRect.size()); + GraphicsContext* layerContext = m_layerImage->context(); + + layerContext->save(); // Balanced by restore() in endShadowLayer(). + + // Always clear the surface first. FIXME: we could avoid the clear on first allocation. + // Add a pixel to avoid later edge aliasing when rotated. + layerContext->clearRect(FloatRect(0, 0, m_layerSize.width() + 1, m_layerSize.height() + 1)); + layerContext->translate(m_layerContextTranslation); + + return layerContext; +} + +void ShadowBlur::endShadowLayer(GraphicsContext* graphicsContext) +{ + if (!m_layerImage) + return; + + m_layerImage->context()->restore(); + + if (m_type == BlurShadow) { + IntRect blurRect = enclosingIntRect(FloatRect(FloatPoint(), m_layerSize)); + RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect); + blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4); + m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint()); + } + + graphicsContext->save(); + + IntSize bufferSize = m_layerImage->size(); + if (bufferSize != m_layerSize) { + // The rect passed to clipToImageBuffer() has to be the size of the entire buffer, + // but we may not have cleared it all, so clip to the filled part first. + graphicsContext->clip(FloatRect(m_layerOrigin, m_layerSize)); + } + graphicsContext->clipToImageBuffer(m_layerImage, FloatRect(m_layerOrigin, bufferSize)); + graphicsContext->setFillColor(m_color, m_colorSpace); + + graphicsContext->clearShadow(); + graphicsContext->fillRect(FloatRect(m_layerOrigin, m_sourceRect.size())); + + graphicsContext->restore(); + + m_layerImage = 0; + + // Schedule a purge of the scratch buffer. We do not need to destroy the surface. + ScratchBuffer::shared().scheduleScratchBufferPurge(); +} + +static void computeSliceSizesFromRadii(int twiceRadius, const RoundedIntRect::Radii& radii, int& leftSlice, int& rightSlice, int& topSlice, int& bottomSlice) +{ + leftSlice = twiceRadius + max(radii.topLeft().width(), radii.bottomLeft().width()); + rightSlice = twiceRadius + max(radii.topRight().width(), radii.bottomRight().width()); + + topSlice = twiceRadius + max(radii.topLeft().height(), radii.topRight().height()); + bottomSlice = twiceRadius + max(radii.bottomLeft().height(), radii.bottomRight().height()); +} + +IntSize ShadowBlur::templateSize(const RoundedIntRect::Radii& radii) const +{ + const int templateSideLength = 1; + + int leftSlice; + int rightSlice; + int topSlice; + int bottomSlice; + computeSliceSizesFromRadii(2 * ceilf(m_blurRadius), radii, leftSlice, rightSlice, topSlice, bottomSlice); + + return IntSize(templateSideLength + leftSlice + rightSlice, + templateSideLength + topSlice + bottomSlice); +} + +void ShadowBlur::drawRectShadow(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii) +{ + IntRect layerRect = calculateLayerBoundingRect(graphicsContext, shadowedRect, graphicsContext->clipBounds()); + if (layerRect.isEmpty()) + return; + + // drawRectShadowWithTiling does not work with rotations. + // https://bugs.webkit.org/show_bug.cgi?id=45042 + if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) { + drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect); + return; + } + + IntSize templateSize = this->templateSize(radii); + + if (templateSize.width() > shadowedRect.width() || templateSize.height() > shadowedRect.height() + || (templateSize.width() * templateSize.height() > m_sourceRect.width() * m_sourceRect.height())) { + drawRectShadowWithoutTiling(graphicsContext, shadowedRect, radii, layerRect); + return; + } + + drawRectShadowWithTiling(graphicsContext, shadowedRect, radii, templateSize); +} + +void ShadowBlur::drawInsetShadow(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii) +{ + IntRect layerRect = calculateLayerBoundingRect(graphicsContext, rect, graphicsContext->clipBounds()); + if (layerRect.isEmpty()) + return; + + // drawInsetShadowWithTiling does not work with rotations. + // https://bugs.webkit.org/show_bug.cgi?id=45042 + if (!graphicsContext->getCTM().isIdentityOrTranslationOrFlipped() || m_type != BlurShadow) { + drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect); + return; + } + + IntSize templateSize = this->templateSize(holeRadii); + + if (templateSize.width() > holeRect.width() || templateSize.height() > holeRect.height() + || (templateSize.width() * templateSize.height() > holeRect.width() * holeRect.height())) { + drawInsetShadowWithoutTiling(graphicsContext, rect, holeRect, holeRadii, layerRect); + return; + } + + drawInsetShadowWithTiling(graphicsContext, rect, holeRect, holeRadii, templateSize); +} + +void ShadowBlur::drawRectShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntRect& layerRect) +{ + GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect); + if (!shadowContext) + return; + + Path path; + path.addRoundedRect(shadowedRect, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); + + shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + shadowContext->fillPath(path); + + endShadowLayer(graphicsContext); +} + +void ShadowBlur::drawInsetShadowWithoutTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii, const IntRect& layerRect) +{ + GraphicsContext* shadowContext = beginShadowLayer(graphicsContext, layerRect); + if (!shadowContext) + return; + + Path path; + path.addRect(rect); + path.addRoundedRect(holeRect, holeRadii.topLeft(), holeRadii.topRight(), holeRadii.bottomLeft(), holeRadii.bottomRight()); + + shadowContext->setFillRule(RULE_EVENODD); + shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + shadowContext->fillPath(path); + + endShadowLayer(graphicsContext); +} + +/* + These functions use tiling to improve the performance of the shadow + drawing of rounded rectangles. The code basically does the following + steps: + + 1. Calculate the size of the shadow template, a rectangle that + contains all the necessary tiles to draw the complete shadow. + + 2. If that size is smaller than the real rectangle render the new + template rectangle and its shadow in a new surface, in other case + render the shadow of the real rectangle in the destination + surface. + + 3. Calculate the sizes and positions of the tiles and their + destinations and use drawPattern to render the final shadow. The + code divides the rendering in 8 tiles: + + 1 | 2 | 3 + ----------- + 4 | | 5 + ----------- + 6 | 7 | 8 + + The corners are directly copied from the template rectangle to the + real one and the side tiles are 1 pixel width, we use them as + tiles to cover the destination side. The corner tiles are bigger + than just the side of the rounded corner, we need to increase it + because the modifications caused by the corner over the blur + effect. We fill the central or outer part with solid color to complete + the shadow. + */ + +void ShadowBlur::drawInsetShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& rect, const FloatRect& holeRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize) +{ + graphicsContext->save(); + graphicsContext->clearShadow(); + + const float roundedRadius = ceilf(m_blurRadius); + const float twiceRadius = roundedRadius * 2; + + m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize); + + // Draw the rectangle with hole. + FloatRect templateBounds(0, 0, templateSize.width(), templateSize.height()); + FloatRect templateHole = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius); + Path path; + path.addRect(templateBounds); + path.addRoundedRect(templateHole, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); + + // Draw shadow into a new ImageBuffer. + GraphicsContext* shadowContext = m_layerImage->context(); + shadowContext->save(); + shadowContext->clearRect(templateBounds); + shadowContext->setFillRule(RULE_EVENODD); + shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + shadowContext->fillPath(path); + blurAndColorShadowBuffer(templateSize); + shadowContext->restore(); + + FloatRect boundingRect = rect; + boundingRect.move(m_offset); + + FloatRect destHoleRect = holeRect; + destHoleRect.move(m_offset); + FloatRect destHoleBounds = destHoleRect; + destHoleBounds.inflate(roundedRadius); + + // Fill the external part of the shadow (which may be visible because of offset). + Path exteriorPath; + exteriorPath.addRect(boundingRect); + exteriorPath.addRect(destHoleBounds); + + graphicsContext->save(); + graphicsContext->setFillRule(RULE_EVENODD); + graphicsContext->setFillColor(m_color, m_colorSpace); + graphicsContext->fillPath(exteriorPath); + graphicsContext->restore(); + + drawLayerPieces(graphicsContext, destHoleBounds, radii, roundedRadius, templateSize, InnerShadow); + + graphicsContext->restore(); + + m_layerImage = 0; + // Schedule a purge of the scratch buffer. + ScratchBuffer::shared().scheduleScratchBufferPurge(); +} + +void ShadowBlur::drawRectShadowWithTiling(GraphicsContext* graphicsContext, const FloatRect& shadowedRect, const RoundedIntRect::Radii& radii, const IntSize& templateSize) +{ + graphicsContext->save(); + graphicsContext->clearShadow(); + + const float roundedRadius = ceilf(m_blurRadius); + const float twiceRadius = roundedRadius * 2; + + m_layerImage = ScratchBuffer::shared().getScratchBuffer(templateSize); + + // Draw the rectangle. + FloatRect templateShadow = FloatRect(roundedRadius, roundedRadius, templateSize.width() - twiceRadius, templateSize.height() - twiceRadius); + Path path; + path.addRoundedRect(templateShadow, radii.topLeft(), radii.topRight(), radii.bottomLeft(), radii.bottomRight()); + + // Draw shadow into the ImageBuffer. + GraphicsContext* shadowContext = m_layerImage->context(); + shadowContext->save(); + shadowContext->clearRect(FloatRect(0, 0, templateSize.width(), templateSize.height())); + shadowContext->setFillColor(Color::black, ColorSpaceDeviceRGB); + shadowContext->fillPath(path); + blurAndColorShadowBuffer(templateSize); + shadowContext->restore(); + + FloatRect shadowBounds = shadowedRect; + shadowBounds.move(m_offset.width(), m_offset.height()); + shadowBounds.inflate(roundedRadius); + + drawLayerPieces(graphicsContext, shadowBounds, radii, roundedRadius, templateSize, OuterShadow); + + graphicsContext->restore(); + + m_layerImage = 0; + // Schedule a purge of the scratch buffer. + ScratchBuffer::shared().scheduleScratchBufferPurge(); +} + +void ShadowBlur::drawLayerPieces(GraphicsContext* graphicsContext, const FloatRect& shadowBounds, const RoundedIntRect::Radii& radii, float roundedRadius, const IntSize& templateSize, ShadowDirection direction) +{ + const float twiceRadius = roundedRadius * 2; + + int leftSlice; + int rightSlice; + int topSlice; + int bottomSlice; + computeSliceSizesFromRadii(twiceRadius, radii, leftSlice, rightSlice, topSlice, bottomSlice); + + int centerWidth = shadowBounds.width() - leftSlice - rightSlice; + int centerHeight = shadowBounds.height() - topSlice - bottomSlice; + + if (direction == OuterShadow) { + FloatRect shadowInterior(shadowBounds.x() + leftSlice, shadowBounds.y() + topSlice, centerWidth, centerHeight); + if (!shadowInterior.isEmpty()) { + graphicsContext->save(); + + graphicsContext->setFillColor(m_color, m_colorSpace); + graphicsContext->fillRect(shadowInterior); + + graphicsContext->restore(); + } + } + + // Note that drawing the ImageBuffer is faster than creating a Image and drawing that, + // because ImageBuffer::draw() knows that it doesn't have to copy the image bits. + + // Top side. + FloatRect tileRect = FloatRect(leftSlice, 0, templateSideLength, topSlice); + FloatRect destRect = FloatRect(shadowBounds.x() + leftSlice, shadowBounds.y(), centerWidth, topSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Draw the bottom side. + tileRect.setY(templateSize.height() - bottomSlice); + tileRect.setHeight(bottomSlice); + destRect.setY(shadowBounds.maxY() - bottomSlice); + destRect.setHeight(bottomSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Left side. + tileRect = FloatRect(0, topSlice, leftSlice, templateSideLength); + destRect = FloatRect(shadowBounds.x(), shadowBounds.y() + topSlice, leftSlice, centerHeight); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Right side. + tileRect.setX(templateSize.width() - rightSlice); + tileRect.setWidth(rightSlice); + destRect.setX(shadowBounds.maxX() - rightSlice); + destRect.setWidth(rightSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Top left corner. + tileRect = FloatRect(0, 0, leftSlice, topSlice); + destRect = FloatRect(shadowBounds.x(), shadowBounds.y(), leftSlice, topSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Top right corner. + tileRect = FloatRect(templateSize.width() - rightSlice, 0, rightSlice, topSlice); + destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.y(), rightSlice, topSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Bottom right corner. + tileRect = FloatRect(templateSize.width() - rightSlice, templateSize.height() - bottomSlice, rightSlice, bottomSlice); + destRect = FloatRect(shadowBounds.maxX() - rightSlice, shadowBounds.maxY() - bottomSlice, rightSlice, bottomSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); + + // Bottom left corner. + tileRect = FloatRect(0, templateSize.height() - bottomSlice, leftSlice, bottomSlice); + destRect = FloatRect(shadowBounds.x(), shadowBounds.maxY() - bottomSlice, leftSlice, bottomSlice); + graphicsContext->drawImageBuffer(m_layerImage, ColorSpaceDeviceRGB, destRect, tileRect); +} + + +void ShadowBlur::blurAndColorShadowBuffer(const IntSize& templateSize) +{ + { + IntRect blurRect(IntPoint(), templateSize); + RefPtr<ByteArray> layerData = m_layerImage->getUnmultipliedImageData(blurRect); + blurLayerImage(layerData->data(), blurRect.size(), blurRect.width() * 4); + m_layerImage->putUnmultipliedImageData(layerData.get(), blurRect.size(), blurRect, IntPoint()); + } + + // Mask the image with the shadow color. + GraphicsContext* shadowContext = m_layerImage->context(); + shadowContext->setCompositeOperation(CompositeSourceIn); + shadowContext->setFillColor(m_color, m_colorSpace); + shadowContext->fillRect(FloatRect(0, 0, templateSize.width(), templateSize.height())); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/ShadowBlur.h b/Source/WebCore/platform/graphics/ShadowBlur.h new file mode 100644 index 0000000..e52d6dc --- /dev/null +++ b/Source/WebCore/platform/graphics/ShadowBlur.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2011 Apple Inc. + * Copyright (C) 2010 Sencha, Inc. + * Copyright (C) 2010 Igalia S.L. + * 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 ShadowBlur_h +#define ShadowBlur_h + +#include "Color.h" +#include "ColorSpace.h" +#include "FloatRect.h" +#include "RoundedIntRect.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class AffineTransform; +class GraphicsContext; +class ImageBuffer; + +class ShadowBlur { + WTF_MAKE_NONCOPYABLE(ShadowBlur); +public: + ShadowBlur(float radius, const FloatSize& offset, const Color&, ColorSpace); + + void setShadowsIgnoreTransforms(bool ignoreTransforms) { m_shadowsIgnoreTransforms = ignoreTransforms; } + bool shadowsIgnoreTransforms() const { return m_shadowsIgnoreTransforms; } + + void drawRectShadow(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&); + void drawInsetShadow(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedIntRect::Radii& holeRadii); + +private: + GraphicsContext* beginShadowLayer(GraphicsContext*, const IntRect& layerRect); + void endShadowLayer(GraphicsContext*); + + void adjustBlurRadius(GraphicsContext*); + void blurLayerImage(unsigned char*, const IntSize&, int stride); + + enum ShadowDirection { + OuterShadow, + InnerShadow + }; + + IntRect calculateLayerBoundingRect(GraphicsContext*, const FloatRect& layerArea, const IntRect& clipRect); + IntSize templateSize(const RoundedIntRect::Radii&) const; + + void drawRectShadowWithoutTiling(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&, const IntRect& layerRect); + void drawRectShadowWithTiling(GraphicsContext*, const FloatRect&, const RoundedIntRect::Radii&, const IntSize& shadowTemplateSize); + + void drawInsetShadowWithoutTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedIntRect::Radii&, const IntRect& layerRect); + void drawInsetShadowWithTiling(GraphicsContext*, const FloatRect&, const FloatRect& holeRect, const RoundedIntRect::Radii&, const IntSize& shadowTemplateSize); + + void drawLayerPieces(GraphicsContext*, const FloatRect& shadowBounds, const RoundedIntRect::Radii&, float roundedRadius, const IntSize& templateSize, ShadowDirection); + + void blurAndColorShadowBuffer(const IntSize& templateSize); + + enum ShadowType { + NoShadow, + SolidShadow, + BlurShadow + }; + + ShadowType m_type; + + Color m_color; + ColorSpace m_colorSpace; + float m_blurRadius; + FloatSize m_offset; + + ImageBuffer* m_layerImage; // Buffer to where the temporary shadow will be drawn to. + + FloatRect m_sourceRect; // Sub-rect of m_layerImage that contains the shadow pixels. + FloatPoint m_layerOrigin; // Top-left corner of the (possibly clipped) bounding rect to draw the shadow to. + FloatSize m_layerSize; // Size of m_layerImage pixels that need blurring. + FloatSize m_layerContextTranslation; // Translation to apply to m_layerContext for the shadow to be correctly clipped. + + bool m_shadowsIgnoreTransforms; +}; + +} // namespace WebCore + +#endif // ShadowBlur_h diff --git a/Source/WebCore/platform/graphics/SimpleFontData.cpp b/Source/WebCore/platform/graphics/SimpleFontData.cpp index e773880..2693609 100644 --- a/Source/WebCore/platform/graphics/SimpleFontData.cpp +++ b/Source/WebCore/platform/graphics/SimpleFontData.cpp @@ -50,7 +50,6 @@ namespace WebCore { SimpleFontData::SimpleFontData(const FontPlatformData& platformData, bool isCustomFont, bool isLoading) : m_maxCharWidth(-1) , m_avgCharWidth(-1) - , m_unitsPerEm(defaultUnitsPerEm) , m_orientation(platformData.orientation()) , m_platformData(platformData) , m_treatAsFixedPitch(false) @@ -74,31 +73,36 @@ SimpleFontData::SimpleFontData(PassOwnPtr<SVGFontData> svgFontData, int size, bo , m_isBrokenIdeographFont(false) { SVGFontFaceElement* svgFontFaceElement = m_svgFontData->svgFontFaceElement(); - m_unitsPerEm = svgFontFaceElement->unitsPerEm(); - - double scale = size; - if (m_unitsPerEm) - scale /= m_unitsPerEm; - - m_ascent = static_cast<int>(svgFontFaceElement->ascent() * scale); - m_descent = static_cast<int>(svgFontFaceElement->descent() * scale); - m_xHeight = static_cast<int>(svgFontFaceElement->xHeight() * scale); - m_lineGap = 0.1f * size; - m_lineSpacing = m_ascent + m_descent + m_lineGap; + unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); + + float scale = size; + if (unitsPerEm) + scale /= unitsPerEm; + + float xHeight = svgFontFaceElement->xHeight() * scale; + float ascent = svgFontFaceElement->ascent() * scale; + float descent = svgFontFaceElement->descent() * scale; + float lineGap = 0.1f * size; + m_fontMetrics.setUnitsPerEm(unitsPerEm); + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap)); + m_fontMetrics.setXHeight(xHeight); SVGFontElement* associatedFontElement = svgFontFaceElement->associatedFontElement(); Vector<SVGGlyphIdentifier> spaceGlyphs; associatedFontElement->getGlyphIdentifiersForString(String(" ", 1), spaceGlyphs); - m_spaceWidth = spaceGlyphs.isEmpty() ? m_xHeight : static_cast<float>(spaceGlyphs.first().horizontalAdvanceX * scale); + m_spaceWidth = spaceGlyphs.isEmpty() ? xHeight : spaceGlyphs.first().horizontalAdvanceX * scale; Vector<SVGGlyphIdentifier> numeralZeroGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("0", 1), numeralZeroGlyphs); - m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : static_cast<float>(numeralZeroGlyphs.first().horizontalAdvanceX * scale); + m_avgCharWidth = numeralZeroGlyphs.isEmpty() ? m_spaceWidth : numeralZeroGlyphs.first().horizontalAdvanceX * scale; Vector<SVGGlyphIdentifier> letterWGlyphs; associatedFontElement->getGlyphIdentifiersForString(String("W", 1), letterWGlyphs); - m_maxCharWidth = letterWGlyphs.isEmpty() ? m_ascent : static_cast<float>(letterWGlyphs.first().horizontalAdvanceX * scale); + m_maxCharWidth = letterWGlyphs.isEmpty() ? ascent : letterWGlyphs.first().horizontalAdvanceX * scale; // FIXME: is there a way we can get the space glyph from the SVGGlyphIdentifier above? m_spaceGlyph = 0; @@ -126,10 +130,10 @@ void SimpleFontData::initCharWidths() // If we can't retrieve the width of a '0', fall back to the x height. if (m_avgCharWidth <= 0.f) - m_avgCharWidth = m_xHeight; + m_avgCharWidth = m_fontMetrics.xHeight(); if (m_maxCharWidth <= 0.f) - m_maxCharWidth = max<float>(m_avgCharWidth, m_ascent); + m_maxCharWidth = max(m_avgCharWidth, m_fontMetrics.floatAscent()); } void SimpleFontData::platformGlyphInit() diff --git a/Source/WebCore/platform/graphics/SimpleFontData.h b/Source/WebCore/platform/graphics/SimpleFontData.h index 90713af..07c2bd1 100644 --- a/Source/WebCore/platform/graphics/SimpleFontData.h +++ b/Source/WebCore/platform/graphics/SimpleFontData.h @@ -26,6 +26,7 @@ #include "FontBaseline.h" #include "FontData.h" +#include "FontMetrics.h" #include "FontPlatformData.h" #include "FloatRect.h" #include "GlyphMetricsMap.h" @@ -101,15 +102,9 @@ public: // FIXME: Use the actual metrics for fonts with vertical tables instead of just hard-coding. If the font is horizontally oriented or // a broken ideographic font, then just hard-code to split ascent/descent down the middle. Otherwise we should actually use the metrics // from the font itself. - int ascent(FontBaseline baselineType = AlphabeticBaseline) const { return baselineType == AlphabeticBaseline ? m_ascent : height() - height() / 2; } - int descent(FontBaseline baselineType = AlphabeticBaseline) const { return baselineType == AlphabeticBaseline ? m_descent : height() / 2; } - int height() const { return m_ascent + m_descent; } - int lineSpacing() const { return m_lineSpacing; } - int lineGap() const { return m_lineGap; } + const FontMetrics& fontMetrics() const { return m_fontMetrics; } float maxCharWidth() const { return m_maxCharWidth; } float avgCharWidth() const { return m_avgCharWidth; } - float xHeight() const { return m_xHeight; } - unsigned unitsPerEm() const { return m_unitsPerEm; } FloatRect boundsForGlyph(Glyph) const; float widthForGlyph(Glyph glyph) const; @@ -211,14 +206,9 @@ private: float widthForGDIGlyph(Glyph glyph) const; #endif - int m_ascent; - int m_descent; - int m_lineSpacing; - int m_lineGap; + FontMetrics m_fontMetrics; float m_maxCharWidth; float m_avgCharWidth; - float m_xHeight; - unsigned m_unitsPerEm; FontOrientation m_orientation; // This is our supported orientation according to the tables in the font. FontPlatformData will just always have the desired orientation. // This value represents what we actually support. diff --git a/Source/WebCore/platform/graphics/StringTruncator.cpp b/Source/WebCore/platform/graphics/StringTruncator.cpp index 65325f0..8468188 100644 --- a/Source/WebCore/platform/graphics/StringTruncator.cpp +++ b/Source/WebCore/platform/graphics/StringTruncator.cpp @@ -29,12 +29,12 @@ #include "config.h" #include "StringTruncator.h" -#include "CharacterNames.h" #include "Font.h" #include "TextBreakIterator.h" #include "TextRun.h" #include <wtf/Assertions.h> #include <wtf/Vector.h> +#include <wtf/unicode/CharacterNames.h> namespace WebCore { diff --git a/Source/WebCore/platform/graphics/TextRun.h b/Source/WebCore/platform/graphics/TextRun.h index dce5535..ef434bd 100644 --- a/Source/WebCore/platform/graphics/TextRun.h +++ b/Source/WebCore/platform/graphics/TextRun.h @@ -2,7 +2,7 @@ * Copyright (C) 2000 Lars Knoll (knoll@kde.org) * (C) 2000 Antti Koivisto (koivisto@kde.org) * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2003, 2006, 2007 Apple Computer, Inc. + * Copyright (C) 2003, 2006, 2007, 2011 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,12 +33,18 @@ class RenderSVGResource; class TextRun { public: - TextRun(const UChar* c, int len, bool allowTabs = false, int xpos = 0, int padding = 0, bool rtl = false, bool directionalOverride = false, + enum TrailingExpansionBehavior { + AllowTrailingExpansion, + ForbidTrailingExpansion + }; + + TextRun(const UChar* c, int len, bool allowTabs = false, int xpos = 0, int expansion = 0, TrailingExpansionBehavior trailingExpansionBehavior = AllowTrailingExpansion, bool rtl = false, bool directionalOverride = false, bool applyRunRounding = true, bool applyWordRounding = true) : m_characters(c) , m_len(len) , m_xpos(xpos) - , m_padding(padding) + , m_expansion(expansion) + , m_trailingExpansionBehavior(trailingExpansionBehavior) #if ENABLE(SVG) , m_horizontalGlyphStretch(1) #endif @@ -55,12 +61,13 @@ public: { } - TextRun(const String& s, bool allowTabs = false, int xpos = 0, int padding = 0, bool rtl = false, bool directionalOverride = false, + TextRun(const String& s, bool allowTabs = false, int xpos = 0, int expansion = 0, TrailingExpansionBehavior trailingExpansionBehavior = AllowTrailingExpansion, bool rtl = false, bool directionalOverride = false, bool applyRunRounding = true, bool applyWordRounding = true) : m_characters(s.characters()) , m_len(s.length()) , m_xpos(xpos) - , m_padding(padding) + , m_expansion(expansion) + , m_trailingExpansionBehavior(trailingExpansionBehavior) #if ENABLE(SVG) , m_horizontalGlyphStretch(1) #endif @@ -92,7 +99,8 @@ public: bool allowTabs() const { return m_allowTabs; } int xPos() const { return m_xpos; } - int padding() const { return m_padding; } + int expansion() const { return m_expansion; } + bool allowsTrailingExpansion() const { return m_trailingExpansionBehavior == AllowTrailingExpansion; } bool rtl() const { return m_rtl; } bool ltr() const { return !m_rtl; } bool directionalOverride() const { return m_directionalOverride; } @@ -121,7 +129,8 @@ private: // start of the containing block. In the case of right alignment or center alignment, left start of // the text line is not the same as left start of the containing block. int m_xpos; - int m_padding; + int m_expansion; + TrailingExpansionBehavior m_trailingExpansionBehavior; #if ENABLE(SVG) float m_horizontalGlyphStretch; #endif diff --git a/Source/WebCore/platform/graphics/TiledBackingStore.cpp b/Source/WebCore/platform/graphics/TiledBackingStore.cpp index 1d6f237..f6921ef 100644 --- a/Source/WebCore/platform/graphics/TiledBackingStore.cpp +++ b/Source/WebCore/platform/graphics/TiledBackingStore.cpp @@ -73,8 +73,8 @@ void TiledBackingStore::invalidate(const IntRect& contentsDirtyRect) { IntRect dirtyRect(mapFromContents(contentsDirtyRect)); - Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.topLeft()); - Tile::Coordinate bottomRight = tileCoordinateForPoint(dirtyRect.bottomRight()); + Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location()); + Tile::Coordinate bottomRight = tileCoordinateForPoint(IntPoint(dirtyRect.maxX(), dirtyRect.maxY())); for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { @@ -93,6 +93,8 @@ void TiledBackingStore::updateTileBuffers() if (m_contentsFrozen) return; + m_client->tiledBackingStorePaintBegin(); + Vector<IntRect> paintedArea; Vector<RefPtr<Tile> > dirtyTiles; TileMap::iterator end = m_tiles.end(); @@ -104,10 +106,10 @@ void TiledBackingStore::updateTileBuffers() paintedArea.append(mapToContents(it->second->rect())); } - if (dirtyTiles.isEmpty()) + if (dirtyTiles.isEmpty()) { + m_client->tiledBackingStorePaintEnd(paintedArea); return; - - m_client->tiledBackingStorePaintBegin(); + } // FIXME: In single threaded case, tile back buffers could be updated asynchronously // one by one and then swapped to front in one go. This would minimize the time spent @@ -132,8 +134,8 @@ void TiledBackingStore::paint(GraphicsContext* context, const IntRect& rect) IntRect dirtyRect = mapFromContents(rect); - Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.topLeft()); - Tile::Coordinate bottomRight = tileCoordinateForPoint(dirtyRect.bottomRight()); + Tile::Coordinate topLeft = tileCoordinateForPoint(dirtyRect.location()); + Tile::Coordinate bottomRight = tileCoordinateForPoint(IntPoint(dirtyRect.maxX(), dirtyRect.maxY())); for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { @@ -227,8 +229,8 @@ void TiledBackingStore::createTiles() double shortestDistance = std::numeric_limits<double>::infinity(); Vector<Tile::Coordinate> tilesToCreate; unsigned requiredTileCount = 0; - Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.topLeft()); - Tile::Coordinate bottomRight = tileCoordinateForPoint(coverRect.bottomRight()); + Tile::Coordinate topLeft = tileCoordinateForPoint(coverRect.location()); + Tile::Coordinate bottomRight = tileCoordinateForPoint(IntPoint(coverRect.maxX(), coverRect.maxY())); for (unsigned yCoordinate = topLeft.y(); yCoordinate <= bottomRight.y(); ++yCoordinate) { for (unsigned xCoordinate = topLeft.x(); xCoordinate <= bottomRight.x(); ++xCoordinate) { Tile::Coordinate currentCoordinate(xCoordinate, yCoordinate); diff --git a/Source/WebCore/platform/graphics/WidthIterator.cpp b/Source/WebCore/platform/graphics/WidthIterator.cpp index 412c86e..a1a88da 100644 --- a/Source/WebCore/platform/graphics/WidthIterator.cpp +++ b/Source/WebCore/platform/graphics/WidthIterator.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008 Holger Hans Peter Freyther * * This library is free software; you can redistribute it and/or @@ -47,6 +47,7 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const , m_end(run.length()) , m_currentCharacter(0) , m_runWidthSoFar(0) + , m_isAfterExpansion(true) , m_finalRoundingWidth(0) , m_fallbackFonts(fallbackFonts) , m_accountForGlyphBounds(accountForGlyphBounds) @@ -58,20 +59,19 @@ WidthIterator::WidthIterator(const Font* font, const TextRun& run, HashSet<const { // If the padding is non-zero, count the number of spaces in the run // and divide that by the padding for per space addition. - m_padding = m_run.padding(); - if (!m_padding) - m_padPerSpace = 0; + m_expansion = m_run.expansion(); + if (!m_expansion) + m_expansionPerOpportunity = 0; else { - int numSpaces = 0; - for (int i = 0; i < run.length(); i++) { - if (Font::treatAsSpace(m_run[i])) - numSpaces++; - } + bool isAfterExpansion = true; + unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); + if (isAfterExpansion && !m_run.allowsTrailingExpansion()) + expansionOpportunityCount--; - if (!numSpaces) - m_padPerSpace = 0; + if (!expansionOpportunityCount) + m_expansionPerOpportunity = 0; else - m_padPerSpace = m_padding / numSpaces; + m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; } } @@ -84,7 +84,7 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) const UChar* cp = m_run.data(currentCharacter); bool rtl = m_run.rtl(); - bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_padding) && !m_run.spacingDisabled(); + bool hasExtraSpacing = (m_font->letterSpacing() || m_font->wordSpacing() || m_expansion) && !m_run.spacingDisabled(); float widthSinceLastRounding = m_runWidthSoFar; m_runWidthSoFar = floorf(m_runWidthSoFar); @@ -173,26 +173,34 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) if (width && m_font->letterSpacing()) width += m_font->letterSpacing(); - if (Font::treatAsSpace(c)) { - // Account for padding. WebCore uses space padding to justify text. - // We distribute the specified padding over the available spaces in the run. - if (m_padding) { - // Use left over padding if not evenly divisible by number of spaces. - if (m_padding < m_padPerSpace) { - width += m_padding; - m_padding = 0; - } else { - float previousPadding = m_padding; - m_padding -= m_padPerSpace; - width += roundf(previousPadding) - roundf(m_padding); + static bool expandAroundIdeographs = Font::canExpandAroundIdeographsInComplexText(); + bool treatAsSpace = Font::treatAsSpace(c); + if (treatAsSpace || (expandAroundIdeographs && Font::isCJKIdeographOrSymbol(c))) { + // Distribute the run's total expansion evenly over all expansion opportunities in the run. + if (m_expansion && (m_run.allowsTrailingExpansion() || (m_run.ltr() && currentCharacter + clusterLength < static_cast<size_t>(m_run.length())) + || (m_run.rtl() && currentCharacter))) { + float previousExpansion = m_expansion; + if (!treatAsSpace && !m_isAfterExpansion) { + // Take the expansion opportunity before this ideograph. + m_expansion -= m_expansionPerOpportunity; + int expansion = roundf(previousExpansion) - roundf(m_expansion); + m_runWidthSoFar += expansion; + if (glyphBuffer) + glyphBuffer->expandLastAdvance(expansion); + previousExpansion = m_expansion; } - } + m_expansion -= m_expansionPerOpportunity; + width += roundf(previousExpansion) - roundf(m_expansion); + m_isAfterExpansion = true; + } else + m_isAfterExpansion = false; // Account for word spacing. // We apply additional space between "words" by adding width to the space character. - if (currentCharacter != 0 && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) + if (treatAsSpace && currentCharacter && !Font::treatAsSpace(cp[-1]) && m_font->wordSpacing()) width += m_font->wordSpacing(); - } + } else + m_isAfterExpansion = false; } if (m_accountForGlyphBounds) { @@ -247,9 +255,9 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) lastRoundingWidth = width - oldWidth; if (m_accountForGlyphBounds) { - m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.bottom()); + m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, bounds.maxY()); m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, bounds.y()); - m_lastGlyphOverflow = max<float>(0, bounds.right() - width); + m_lastGlyphOverflow = max<float>(0, bounds.maxX() - width); } } @@ -260,13 +268,13 @@ void WidthIterator::advance(int offset, GlyphBuffer* glyphBuffer) bool WidthIterator::advanceOneCharacter(float& width, GlyphBuffer* glyphBuffer) { - glyphBuffer->clear(); + int oldSize = glyphBuffer->size(); advance(m_currentCharacter + 1, glyphBuffer); float w = 0; - for (int i = 0; i < glyphBuffer->size(); ++i) + for (int i = oldSize; i < glyphBuffer->size(); ++i) w += glyphBuffer->advanceAt(i); width = w; - return !glyphBuffer->isEmpty(); + return glyphBuffer->size() > oldSize; } UChar32 WidthIterator::normalizeVoicingMarks(int currentCharacter) diff --git a/Source/WebCore/platform/graphics/WidthIterator.h b/Source/WebCore/platform/graphics/WidthIterator.h index 8b3c067..2b4f051 100644 --- a/Source/WebCore/platform/graphics/WidthIterator.h +++ b/Source/WebCore/platform/graphics/WidthIterator.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2008, 2011 Apple Inc. All rights reserved. * Copyright (C) 2008 Holger Hans Peter Freyther * * This library is free software; you can redistribute it and/or @@ -50,8 +50,9 @@ struct WidthIterator { unsigned m_currentCharacter; float m_runWidthSoFar; - float m_padding; - float m_padPerSpace; + float m_expansion; + float m_expansionPerOpportunity; + bool m_isAfterExpansion; float m_finalRoundingWidth; private: diff --git a/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp b/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp index 01e25e9..2b4a39e 100644 --- a/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp +++ b/Source/WebCore/platform/graphics/ca/GraphicsLayerCA.cpp @@ -1507,7 +1507,7 @@ void GraphicsLayerCA::pauseCAAnimationOnLayer(AnimatedPropertyID property, const return; // Animations on the layer are immutable, so we have to clone and modify. - RefPtr<PlatformCAAnimation> newAnim = PlatformCAAnimation::create(curAnim.get()); + RefPtr<PlatformCAAnimation> newAnim = curAnim->copy(); newAnim->setSpeed(0); newAnim->setTimeOffset(timeOffset); @@ -1646,7 +1646,7 @@ void GraphicsLayerCA::setupAnimation(PlatformCAAnimation* propertyAnim, const An float repeatCount = anim->iterationCount(); if (repeatCount == Animation::IterationCountInfinite) - repeatCount = FLT_MAX; + repeatCount = numeric_limits<float>::max(); else if (anim->direction() == Animation::AnimationDirectionAlternate) repeatCount /= 2; diff --git a/Source/WebCore/platform/graphics/ca/PlatformCAAnimation.h b/Source/WebCore/platform/graphics/ca/PlatformCAAnimation.h index 4bfce63..a8528fd 100644 --- a/Source/WebCore/platform/graphics/ca/PlatformCAAnimation.h +++ b/Source/WebCore/platform/graphics/ca/PlatformCAAnimation.h @@ -63,13 +63,14 @@ public: enum ValueFunctionType { NoValueFunction, RotateX, RotateY, RotateZ, ScaleX, ScaleY, ScaleZ, Scale, TranslateX, TranslateY, TranslateZ, Translate }; static PassRefPtr<PlatformCAAnimation> create(AnimationType, const String& keyPath); - static PassRefPtr<PlatformCAAnimation> create(PlatformAnimationRef animation); - static PassRefPtr<PlatformCAAnimation> create(const PlatformCAAnimation* animation); + static PassRefPtr<PlatformCAAnimation> create(PlatformAnimationRef); ~PlatformCAAnimation(); static bool supportsValueFunction(); + PassRefPtr<PlatformCAAnimation> copy() const; + PlatformAnimationRef platformAnimation() const; AnimationType animationType() const { return m_type; } @@ -136,8 +137,7 @@ public: protected: PlatformCAAnimation(AnimationType, const String& keyPath); - PlatformCAAnimation(PlatformAnimationRef animation); - PlatformCAAnimation(const PlatformCAAnimation* animation); + PlatformCAAnimation(PlatformAnimationRef); private: AnimationType m_type; diff --git a/Source/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm b/Source/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm index 2a00857..506bd40 100644 --- a/Source/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm +++ b/Source/WebCore/platform/graphics/ca/mac/PlatformCAAnimationMac.mm @@ -159,11 +159,6 @@ PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(PlatformAnimationRef return adoptRef(new PlatformCAAnimation(animation)); } -PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(const PlatformCAAnimation* animation) -{ - return adoptRef(new PlatformCAAnimation(animation)); -} - PlatformCAAnimation::PlatformCAAnimation(AnimationType type, const String& keyPath) : m_type(type) { @@ -187,38 +182,36 @@ PlatformCAAnimation::PlatformCAAnimation(PlatformAnimationRef animation) m_animation = static_cast<CAPropertyAnimation*>(animation); } -PlatformCAAnimation::PlatformCAAnimation(const PlatformCAAnimation* animation) +PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::copy() const { - PlatformCAAnimation* newAnimation = new PlatformCAAnimation(animation->animationType(), animation->keyPath()); - - newAnimation->setBeginTime(animation->beginTime()); - newAnimation->setDuration(animation->duration()); - newAnimation->setSpeed(animation->speed()); - newAnimation->setTimeOffset(animation->timeOffset()); - newAnimation->setRepeatCount(animation->repeatCount()); - newAnimation->setAutoreverses(animation->autoreverses()); - newAnimation->setFillMode(animation->fillMode()); - newAnimation->setRemovedOnCompletion(animation->isRemovedOnCompletion()); - newAnimation->setAdditive(animation->isAdditive()); - newAnimation->copyTimingFunctionFrom(animation); - -#if HAVE_MODERN_QUARTZCORE - newAnimation->setValueFunction(animation->valueFunction()); -#endif - - setNonZeroBeginTimeFlag(newAnimation, hasNonZeroBeginTimeFlag(animation)); + RefPtr<PlatformCAAnimation> animation = create(animationType(), keyPath()); + + animation->setBeginTime(beginTime()); + animation->setDuration(duration()); + animation->setSpeed(speed()); + animation->setTimeOffset(timeOffset()); + animation->setRepeatCount(repeatCount()); + animation->setAutoreverses(autoreverses()); + animation->setFillMode(fillMode()); + animation->setRemovedOnCompletion(isRemovedOnCompletion()); + animation->setAdditive(isAdditive()); + animation->copyTimingFunctionFrom(this); + animation->setValueFunction(valueFunction()); + + setNonZeroBeginTimeFlag(animation.get(), hasNonZeroBeginTimeFlag(this)); // Copy the specific Basic or Keyframe values - if (animation->animationType() == Keyframe) { - newAnimation->copyValuesFrom(animation); - newAnimation->copyKeyTimesFrom(animation); - newAnimation->copyTimingFunctionsFrom(animation); + if (animationType() == Keyframe) { + animation->copyValuesFrom(this); + animation->copyKeyTimesFrom(this); + animation->copyTimingFunctionsFrom(this); } else { - newAnimation->copyFromValueFrom(animation); - newAnimation->copyToValueFrom(animation); + animation->copyFromValueFrom(this); + animation->copyToValueFrom(this); } + + return animation; } - PlatformCAAnimation::~PlatformCAAnimation() { } diff --git a/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.cpp b/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.cpp index 1d27608..9dc30ea 100644 --- a/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.cpp +++ b/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.cpp @@ -28,49 +28,22 @@ #if USE(ACCELERATED_COMPOSITING) +#include "CACFLayerTreeHostClient.h" #include "LayerChangesFlusher.h" +#include "LegacyCACFLayerTreeHost.h" #include "PlatformCALayer.h" +#include "WKCACFViewLayerTreeHost.h" #include "WebCoreInstanceHandle.h" -#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <limits.h> #include <wtf/CurrentTime.h> -#include <wtf/HashMap.h> #include <wtf/OwnArrayPtr.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/StdLibExtras.h> -#ifndef NDEBUG -#define D3D_DEBUG_INFO -#endif - -#include <d3d9.h> -#include <d3dx9.h> - -using namespace std; - -#pragma comment(lib, "d3d9") -#pragma comment(lib, "d3dx9") #ifdef DEBUG_ALL #pragma comment(lib, "QuartzCore_debug") #else #pragma comment(lib, "QuartzCore") #endif -static IDirect3D9* s_d3d = 0; -static IDirect3D9* d3d() -{ - if (s_d3d) - return s_d3d; - - if (!LoadLibrary(TEXT("d3d9.dll"))) - return 0; - - s_d3d = Direct3DCreate9(D3D_SDK_VERSION); - - return s_d3d; -} - inline static CGRect winRectToCGRect(RECT rc) { return CGRectMake(rc.left, rc.top, (rc.right - rc.left), (rc.bottom - rc.top)); @@ -83,40 +56,6 @@ inline static CGRect winRectToCGRect(RECT rc, RECT relativeToRect) namespace WebCore { -static D3DPRESENT_PARAMETERS initialPresentationParameters() -{ - D3DPRESENT_PARAMETERS parameters = {0}; - parameters.Windowed = TRUE; - parameters.SwapEffect = D3DSWAPEFFECT_COPY; - parameters.BackBufferCount = 1; - parameters.BackBufferFormat = D3DFMT_A8R8G8B8; - parameters.MultiSampleType = D3DMULTISAMPLE_NONE; - - return parameters; -} - -// FIXME: <rdar://6507851> Share this code with CoreAnimation. -static bool hardwareCapabilitiesIndicateCoreAnimationSupport(const D3DCAPS9& caps) -{ - // CoreAnimation needs two or more texture units. - if (caps.MaxTextureBlendStages < 2) - return false; - - // CoreAnimation needs non-power-of-two textures. - if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) - return false; - - // CoreAnimation needs vertex shader 2.0 or greater. - if (D3DSHADER_VERSION_MAJOR(caps.VertexShaderVersion) < 2) - return false; - - // CoreAnimation needs pixel shader 2.0 or greater. - if (D3DSHADER_VERSION_MAJOR(caps.PixelShaderVersion) < 2) - return false; - - return true; -} - bool CACFLayerTreeHost::acceleratedCompositingAvailable() { static bool available; @@ -158,7 +97,7 @@ bool CACFLayerTreeHost::acceleratedCompositingAvailable() wcex.hInstance = WebCore::instanceHandle(); wcex.lpszClassName = L"CoreAnimationTesterWindowClass"; ::RegisterClassEx(&wcex); - HWND testWindow = ::CreateWindow(L"CoreAnimationTesterWindowClass", L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 0, 0, 0, 0, 0, 0); + HWND testWindow = ::CreateWindow(L"CoreAnimationTesterWindowClass", L"CoreAnimationTesterWindow", WS_POPUP, -500, -500, 20, 20, 0, 0, 0, 0); if (!testWindow) { available = false; @@ -168,6 +107,7 @@ bool CACFLayerTreeHost::acceleratedCompositingAvailable() RefPtr<CACFLayerTreeHost> host = CACFLayerTreeHost::create(); host->setWindow(testWindow); available = host->createRenderer(); + host->setWindow(0); ::DestroyWindow(testWindow); return available; @@ -177,22 +117,29 @@ PassRefPtr<CACFLayerTreeHost> CACFLayerTreeHost::create() { if (!acceleratedCompositingAvailable()) return 0; - return adoptRef(new CACFLayerTreeHost()); + RefPtr<CACFLayerTreeHost> host = WKCACFViewLayerTreeHost::create(); + if (!host) + host = LegacyCACFLayerTreeHost::create(); + host->initialize(); + return host.release(); } CACFLayerTreeHost::CACFLayerTreeHost() : m_client(0) - , m_mightBeAbleToCreateDeviceLater(true) , m_rootLayer(PlatformCALayer::create(PlatformCALayer::LayerTypeRootLayer, 0)) - , m_context(wkCACFContextCreate()) , m_window(0) - , m_renderTimer(this, &CACFLayerTreeHost::renderTimerFired) - , m_mustResetLostDeviceBeforeRendering(false) , m_shouldFlushPendingGraphicsLayerChanges(false) , m_isFlushingLayerChanges(false) +#if !ASSERT_DISABLED + , m_state(WindowNotSet) +#endif +{ +} + +void CACFLayerTreeHost::initialize() { // Point the CACFContext to this - wkCACFContextSetUserData(m_context, this); + initializeContext(this, m_rootLayer.get()); // Under the root layer, we have a clipping layer to clip the content, // that contains a scroll layer that we use for scrolling the content. @@ -212,20 +159,11 @@ CACFLayerTreeHost::CACFLayerTreeHost() m_rootLayer->setBackgroundColor(debugColor); CGColorRelease(debugColor); #endif - - if (m_context) - wkCACFContextSetLayer(m_context, m_rootLayer->platformLayer()); - -#ifndef NDEBUG - char* printTreeFlag = getenv("CA_PRINT_TREE"); - m_printTree = printTreeFlag && atoi(printTreeFlag); -#endif } CACFLayerTreeHost::~CACFLayerTreeHost() { - setWindow(0); - wkCACFContextDestroy(m_context); + ASSERT_WITH_MESSAGE(m_state != WindowSet, "Must call setWindow(0) before destroying CACFLayerTreeHost"); } void CACFLayerTreeHost::setWindow(HWND window) @@ -233,13 +171,28 @@ void CACFLayerTreeHost::setWindow(HWND window) if (window == m_window) return; +#if !ASSERT_DISABLED + switch (m_state) { + case WindowNotSet: + ASSERT_ARG(window, window); + ASSERT(!m_window); + m_state = WindowSet; + break; + case WindowSet: + ASSERT_ARG(window, !window); + ASSERT(m_window); + m_state = WindowCleared; + break; + case WindowCleared: + ASSERT_NOT_REACHED(); + break; + } +#endif + if (m_window) destroyRenderer(); m_window = window; - - if (m_window) - createRenderer(); } PlatformCALayer* CACFLayerTreeHost::rootLayer() const @@ -275,109 +228,11 @@ void CACFLayerTreeHost::layerTreeDidChange() LayerChangesFlusher::shared().flushPendingLayerChangesSoon(this); } -bool CACFLayerTreeHost::createRenderer() -{ - if (m_d3dDevice || !m_mightBeAbleToCreateDeviceLater) - return m_d3dDevice; - - m_mightBeAbleToCreateDeviceLater = false; - D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); - - if (!d3d() || !::IsWindow(m_window)) - return false; - - // D3D doesn't like to make back buffers for 0 size windows. We skirt this problem if we make the - // passed backbuffer width and height non-zero. The window will necessarily get set to a non-zero - // size eventually, and then the backbuffer size will get reset. - RECT rect; - GetClientRect(m_window, &rect); - - if (rect.left-rect.right == 0 || rect.bottom-rect.top == 0) { - parameters.BackBufferWidth = 1; - parameters.BackBufferHeight = 1; - } - - D3DCAPS9 d3dCaps; - if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))) - return false; - - DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE; - if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps) - behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; - else - behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; - - COMPtr<IDirect3DDevice9> device; - if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, m_window, behaviorFlags, ¶meters, &device))) { - // In certain situations (e.g., shortly after waking from sleep), Direct3DCreate9() will - // return an IDirect3D9 for which IDirect3D9::CreateDevice will always fail. In case we - // have one of these bad IDirect3D9s, get rid of it so we'll fetch a new one the next time - // we want to call CreateDevice. - s_d3d->Release(); - s_d3d = 0; - - // Even if we don't have a bad IDirect3D9, in certain situations (e.g., shortly after - // waking from sleep), CreateDevice will fail, but will later succeed if called again. - m_mightBeAbleToCreateDeviceLater = true; - - return false; - } - - // Now that we've created the IDirect3DDevice9 based on the capabilities we - // got from the IDirect3D9 global object, we requery the device for its - // actual capabilities. The capabilities returned by the device can - // sometimes be more complete, for example when using software vertex - // processing. - D3DCAPS9 deviceCaps; - if (FAILED(device->GetDeviceCaps(&deviceCaps))) - return false; - - if (!hardwareCapabilitiesIndicateCoreAnimationSupport(deviceCaps)) - return false; - - m_d3dDevice = device; - - initD3DGeometry(); - - wkCACFContextSetD3DDevice(m_context, m_d3dDevice.get()); - - if (IsWindow(m_window)) - m_rootLayer->setBounds(bounds()); - - return true; -} - void CACFLayerTreeHost::destroyRenderer() { - LayerChangesFlusher::shared().cancelPendingFlush(this); - - wkCACFContextSetLayer(m_context, 0); - - wkCACFContextSetD3DDevice(m_context, 0); - m_d3dDevice = 0; - if (s_d3d) - s_d3d->Release(); - - s_d3d = 0; m_rootLayer = 0; m_rootChildLayer = 0; - - m_mightBeAbleToCreateDeviceLater = true; -} - -void CACFLayerTreeHost::resize() -{ - if (!m_d3dDevice) - return; - - // Resetting the device might fail here. But that's OK, because if it does it we will attempt to - // reset the device the next time we try to render. - resetDevice(ChangedWindowSize); - - if (m_rootLayer) { - m_rootLayer->setBounds(bounds()); - wkCACFContextFlush(m_context); - } + LayerChangesFlusher::shared().cancelPendingFlush(this); } static void getDirtyRects(HWND window, Vector<CGRect>& outRects) @@ -398,7 +253,7 @@ static void getDirtyRects(HWND window, Vector<CGRect>& outRects) } DWORD dataSize = GetRegionData(region.get(), 0, 0); - OwnArrayPtr<unsigned char> regionDataBuffer(new unsigned char[dataSize]); + OwnArrayPtr<unsigned char> regionDataBuffer = adoptArrayPtr(new unsigned char[dataSize]); RGNDATA* regionData = reinterpret_cast<RGNDATA*>(regionDataBuffer.get()); if (!GetRegionData(region.get(), dataSize, regionData)) return; @@ -410,120 +265,13 @@ static void getDirtyRects(HWND window, Vector<CGRect>& outRects) outRects[i] = winRectToCGRect(*rect, clientRect); } -void CACFLayerTreeHost::renderTimerFired(Timer<CACFLayerTreeHost>*) -{ - paint(); -} - void CACFLayerTreeHost::paint() { - createRenderer(); - if (!m_d3dDevice) { - if (m_mightBeAbleToCreateDeviceLater) - renderSoon(); - return; - } - Vector<CGRect> dirtyRects; getDirtyRects(m_window, dirtyRects); render(dirtyRects); } -void CACFLayerTreeHost::render(const Vector<CGRect>& windowDirtyRects) -{ - ASSERT(m_d3dDevice); - - if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) { - // We can't reset the device right now. Try again soon. - renderSoon(); - return; - } - - // All pending animations will have been started with the flush. Fire the animationStarted calls - double currentTime = WTF::currentTime(); - double currentMediaTime = CACurrentMediaTime(); - double t = currentTime + wkCACFContextGetLastCommitTime(m_context) - currentMediaTime; - ASSERT(t <= currentTime); - - HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end(); - for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it) { - PlatformCALayerClient* owner = (*it)->owner(); - owner->platformCALayerAnimationStarted(t); - } - - m_pendingAnimatedLayers.clear(); - - CGRect bounds = this->bounds(); - - // Give the renderer some space to use. This needs to be valid until the - // wkCACFContextFinishUpdate() call below. - char space[4096]; - if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), currentMediaTime, bounds, windowDirtyRects.data(), windowDirtyRects.size())) - return; - - HRESULT err = S_OK; - CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity(); - - do { - // FIXME: don't need to clear dirty region if layer tree is opaque. - - WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context); - if (!e) - break; - - Vector<D3DRECT, 64> rects; - for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) { - D3DRECT rect; - rect.x1 = r->origin.x; - rect.x2 = rect.x1 + r->size.width; - rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height); - rect.y2 = rect.y1 + r->size.height; - - rects.append(rect); - } - wkCACFUpdateRectEnumeratorRelease(e); - - timeToNextRender = wkCACFContextGetNextUpdateTime(m_context); - - if (rects.isEmpty()) - break; - - m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0); - - m_d3dDevice->BeginScene(); - wkCACFContextRenderUpdate(m_context); - m_d3dDevice->EndScene(); - - err = m_d3dDevice->Present(0, 0, 0, 0); - - if (err == D3DERR_DEVICELOST) { - wkCACFContextAddUpdateRect(m_context, bounds); - if (!resetDevice(LostDevice)) { - // We can't reset the device right now. Try again soon. - renderSoon(); - return; - } - } - } while (err == D3DERR_DEVICELOST); - - wkCACFContextFinishUpdate(m_context); - -#ifndef NDEBUG - if (m_printTree) - m_rootLayer->printTree(); -#endif - - // If timeToNextRender is not infinity, it means animations are running, so queue up to render again - if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity()) - renderSoon(); -} - -void CACFLayerTreeHost::renderSoon() -{ - if (!m_renderTimer.isActive()) - m_renderTimer.startOneShot(0); -} - void CACFLayerTreeHost::flushPendingGraphicsLayerChangesSoon() { m_shouldFlushPendingGraphicsLayerChanges = true; @@ -545,75 +293,36 @@ void CACFLayerTreeHost::flushPendingLayerChangesNow() } // Flush changes stored up in PlatformCALayers to the context so they will be rendered. - wkCACFContextFlush(m_context); - - renderSoon(); + flushContext(); m_isFlushingLayerChanges = false; } -CGRect CACFLayerTreeHost::bounds() const +void CACFLayerTreeHost::contextDidChange() { - RECT clientRect; - GetClientRect(m_window, &clientRect); - - return winRectToCGRect(clientRect); + // All pending animations will have been started with the flush. Fire the animationStarted calls. + notifyAnimationsStarted(); } -void CACFLayerTreeHost::initD3DGeometry() +void CACFLayerTreeHost::notifyAnimationsStarted() { - ASSERT(m_d3dDevice); - - CGRect bounds = this->bounds(); - - float x0 = bounds.origin.x; - float y0 = bounds.origin.y; - float x1 = x0 + bounds.size.width; - float y1 = y0 + bounds.size.height; + double currentTime = WTF::currentTime(); + double time = currentTime + lastCommitTime() - CACurrentMediaTime(); + ASSERT(time <= currentTime); - D3DXMATRIXA16 projection; - D3DXMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f); + HashSet<RefPtr<PlatformCALayer> >::iterator end = m_pendingAnimatedLayers.end(); + for (HashSet<RefPtr<PlatformCALayer> >::iterator it = m_pendingAnimatedLayers.begin(); it != end; ++it) + (*it)->animationStarted(time); - m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection); + m_pendingAnimatedLayers.clear(); } -bool CACFLayerTreeHost::resetDevice(ResetReason reason) +CGRect CACFLayerTreeHost::bounds() const { - ASSERT(m_d3dDevice); - ASSERT(m_context); - - HRESULT hr = m_d3dDevice->TestCooperativeLevel(); - - if (hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR) { - // The device cannot be reset at this time. Try again soon. - m_mustResetLostDeviceBeforeRendering = true; - return false; - } - - m_mustResetLostDeviceBeforeRendering = false; - - if (reason == LostDevice && hr == D3D_OK) { - // The device wasn't lost after all. - return true; - } - - // We can reset the device. - - // We have to release the context's D3D resrouces whenever we reset the IDirect3DDevice9 in order to - // destroy any D3DPOOL_DEFAULT resources that Core Animation has allocated (e.g., textures used - // for mask layers). See <http://msdn.microsoft.com/en-us/library/bb174425(v=VS.85).aspx>. - wkCACFContextReleaseD3DResources(m_context); - - D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); - hr = m_d3dDevice->Reset(¶meters); - - // TestCooperativeLevel told us the device may be reset now, so we should - // not be told here that the device is lost. - ASSERT(hr != D3DERR_DEVICELOST); - - initD3DGeometry(); + RECT clientRect; + GetClientRect(m_window, &clientRect); - return true; + return winRectToCGRect(clientRect); } } diff --git a/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.h b/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.h index fc61f39..6d91a73 100644 --- a/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.h +++ b/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHost.h @@ -47,75 +47,59 @@ typedef struct CGImage* CGImageRef; namespace WebCore { +class CACFLayerTreeHostClient; class PlatformCALayer; -class CACFLayerTreeHostClient { -public: - virtual ~CACFLayerTreeHostClient() { } - virtual void flushPendingGraphicsLayerChanges() { } -}; - -// FIXME: Currently there is a CACFLayerTreeHost for each WebView and each -// has its own CARenderOGLContext and Direct3DDevice9, which is inefficient. -// (https://bugs.webkit.org/show_bug.cgi?id=31855) class CACFLayerTreeHost : public RefCounted<CACFLayerTreeHost> { friend PlatformCALayer; public: static PassRefPtr<CACFLayerTreeHost> create(); - ~CACFLayerTreeHost(); + virtual ~CACFLayerTreeHost(); static bool acceleratedCompositingAvailable(); void setClient(CACFLayerTreeHostClient* client) { m_client = client; } void setRootChildLayer(PlatformCALayer*); - void layerTreeDidChange(); void setWindow(HWND); - void paint(); - void resize(); + virtual void paint(); + virtual void resize() = 0; void flushPendingGraphicsLayerChangesSoon(); void flushPendingLayerChangesNow(); protected: - PlatformCALayer* rootLayer() const; - void addPendingAnimatedLayer(PassRefPtr<PlatformCALayer>); - -private: CACFLayerTreeHost(); - bool createRenderer(); - void destroyRenderer(); - void renderSoon(); - void renderTimerFired(Timer<CACFLayerTreeHost>*); - CGRect bounds() const; + PlatformCALayer* rootLayer() const; + HWND window() const { return m_window; } + void notifyAnimationsStarted(); - void initD3DGeometry(); + virtual bool createRenderer() = 0; + virtual void destroyRenderer(); + virtual void contextDidChange(); - // Call this when the device window has changed size or when IDirect3DDevice9::Present returns - // D3DERR_DEVICELOST. Returns true if the device was recovered, false if rendering must be - // aborted and reattempted soon. - enum ResetReason { ChangedWindowSize, LostDevice }; - bool resetDevice(ResetReason); +private: + void initialize(); + void addPendingAnimatedLayer(PassRefPtr<PlatformCALayer>); + void layerTreeDidChange(); - void render(const Vector<CGRect>& dirtyRects = Vector<CGRect>()); + virtual void flushContext() = 0; + virtual CFTimeInterval lastCommitTime() const = 0; + virtual void render(const Vector<CGRect>& dirtyRects = Vector<CGRect>()) = 0; + virtual void initializeContext(void* userData, PlatformCALayer*) = 0; CACFLayerTreeHostClient* m_client; - bool m_mightBeAbleToCreateDeviceLater; - COMPtr<IDirect3DDevice9> m_d3dDevice; RefPtr<PlatformCALayer> m_rootLayer; RefPtr<PlatformCALayer> m_rootChildLayer; - WKCACFContext* m_context; + HashSet<RefPtr<PlatformCALayer> > m_pendingAnimatedLayers; HWND m_window; - Timer<CACFLayerTreeHost> m_renderTimer; - bool m_mustResetLostDeviceBeforeRendering; bool m_shouldFlushPendingGraphicsLayerChanges; bool m_isFlushingLayerChanges; - HashSet<RefPtr<PlatformCALayer> > m_pendingAnimatedLayers; -#ifndef NDEBUG - bool m_printTree; +#if !ASSERT_DISABLED + enum { WindowNotSet, WindowSet, WindowCleared } m_state; #endif }; diff --git a/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHostClient.h b/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHostClient.h new file mode 100644 index 0000000..845f934 --- /dev/null +++ b/Source/WebCore/platform/graphics/ca/win/CACFLayerTreeHostClient.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 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 CACFLayerTreeHostClient_h +#define CACFLayerTreeHostClient_h + +#if USE(ACCELERATED_COMPOSITING) + +namespace WebCore { + +class CACFLayerTreeHostClient { +public: + virtual ~CACFLayerTreeHostClient() { } + virtual void flushPendingGraphicsLayerChanges() { } +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // CACFLayerTreeHostClient_h diff --git a/Source/WebCore/platform/graphics/ca/win/LegacyCACFLayerTreeHost.cpp b/Source/WebCore/platform/graphics/ca/win/LegacyCACFLayerTreeHost.cpp new file mode 100644 index 0000000..772244b --- /dev/null +++ b/Source/WebCore/platform/graphics/ca/win/LegacyCACFLayerTreeHost.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2011 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 "LegacyCACFLayerTreeHost.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "PlatformCALayer.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> + +#ifndef NDEBUG +#define D3D_DEBUG_INFO +#endif + +#include <d3d9.h> +#include <d3dx9.h> + +#pragma comment(lib, "d3d9") +#pragma comment(lib, "d3dx9") + +using namespace std; + +namespace WebCore { + +static IDirect3D9* s_d3d = 0; +static IDirect3D9* d3d() +{ + if (s_d3d) + return s_d3d; + + if (!LoadLibrary(TEXT("d3d9.dll"))) + return 0; + + s_d3d = Direct3DCreate9(D3D_SDK_VERSION); + + return s_d3d; +} + +static D3DPRESENT_PARAMETERS initialPresentationParameters() +{ + D3DPRESENT_PARAMETERS parameters = {0}; + parameters.Windowed = TRUE; + parameters.SwapEffect = D3DSWAPEFFECT_COPY; + parameters.BackBufferCount = 1; + parameters.BackBufferFormat = D3DFMT_A8R8G8B8; + parameters.MultiSampleType = D3DMULTISAMPLE_NONE; + + return parameters; +} + +// FIXME: <rdar://6507851> Share this code with CoreAnimation. +static bool hardwareCapabilitiesIndicateCoreAnimationSupport(const D3DCAPS9& caps) +{ + // CoreAnimation needs two or more texture units. + if (caps.MaxTextureBlendStages < 2) + return false; + + // CoreAnimation needs non-power-of-two textures. + if ((caps.TextureCaps & D3DPTEXTURECAPS_POW2) && !(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) + return false; + + // CoreAnimation needs vertex shader 2.0 or greater. + if (D3DSHADER_VERSION_MAJOR(caps.VertexShaderVersion) < 2) + return false; + + // CoreAnimation needs pixel shader 2.0 or greater. + if (D3DSHADER_VERSION_MAJOR(caps.PixelShaderVersion) < 2) + return false; + + return true; +} + +PassRefPtr<LegacyCACFLayerTreeHost> LegacyCACFLayerTreeHost::create() +{ + return adoptRef(new LegacyCACFLayerTreeHost); +} + +LegacyCACFLayerTreeHost::LegacyCACFLayerTreeHost() + : m_renderTimer(this, &LegacyCACFLayerTreeHost::renderTimerFired) + , m_context(wkCACFContextCreate()) + , m_mightBeAbleToCreateDeviceLater(true) + , m_mustResetLostDeviceBeforeRendering(false) +{ +#ifndef NDEBUG + char* printTreeFlag = getenv("CA_PRINT_TREE"); + m_printTree = printTreeFlag && atoi(printTreeFlag); +#endif +} + +LegacyCACFLayerTreeHost::~LegacyCACFLayerTreeHost() +{ + wkCACFContextDestroy(m_context); +} + +void LegacyCACFLayerTreeHost::initializeContext(void* userData, PlatformCALayer* layer) +{ + wkCACFContextSetUserData(m_context, userData); + wkCACFContextSetLayer(m_context, layer->platformLayer()); +} + +bool LegacyCACFLayerTreeHost::createRenderer() +{ + if (m_d3dDevice || !m_mightBeAbleToCreateDeviceLater) + return m_d3dDevice; + + m_mightBeAbleToCreateDeviceLater = false; + D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); + + if (!d3d() || !::IsWindow(window())) + return false; + + // D3D doesn't like to make back buffers for 0 size windows. We skirt this problem if we make the + // passed backbuffer width and height non-zero. The window will necessarily get set to a non-zero + // size eventually, and then the backbuffer size will get reset. + RECT rect; + GetClientRect(window(), &rect); + + if (rect.left-rect.right == 0 || rect.bottom-rect.top == 0) { + parameters.BackBufferWidth = 1; + parameters.BackBufferHeight = 1; + } + + D3DCAPS9 d3dCaps; + if (FAILED(d3d()->GetDeviceCaps(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3dCaps))) + return false; + + DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE; + if ((d3dCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT) && d3dCaps.VertexProcessingCaps) + behaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING; + else + behaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING; + + COMPtr<IDirect3DDevice9> device; + if (FAILED(d3d()->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, window(), behaviorFlags, ¶meters, &device))) { + // In certain situations (e.g., shortly after waking from sleep), Direct3DCreate9() will + // return an IDirect3D9 for which IDirect3D9::CreateDevice will always fail. In case we + // have one of these bad IDirect3D9s, get rid of it so we'll fetch a new one the next time + // we want to call CreateDevice. + s_d3d->Release(); + s_d3d = 0; + + // Even if we don't have a bad IDirect3D9, in certain situations (e.g., shortly after + // waking from sleep), CreateDevice will fail, but will later succeed if called again. + m_mightBeAbleToCreateDeviceLater = true; + + return false; + } + + // Now that we've created the IDirect3DDevice9 based on the capabilities we + // got from the IDirect3D9 global object, we requery the device for its + // actual capabilities. The capabilities returned by the device can + // sometimes be more complete, for example when using software vertex + // processing. + D3DCAPS9 deviceCaps; + if (FAILED(device->GetDeviceCaps(&deviceCaps))) + return false; + + if (!hardwareCapabilitiesIndicateCoreAnimationSupport(deviceCaps)) + return false; + + m_d3dDevice = device; + + initD3DGeometry(); + + wkCACFContextSetD3DDevice(m_context, m_d3dDevice.get()); + + if (IsWindow(window())) { + rootLayer()->setBounds(bounds()); + flushContext(); + } + + return true; +} + +void LegacyCACFLayerTreeHost::destroyRenderer() +{ + wkCACFContextSetLayer(m_context, 0); + + wkCACFContextSetD3DDevice(m_context, 0); + m_d3dDevice = 0; + if (s_d3d) + s_d3d->Release(); + + s_d3d = 0; + m_mightBeAbleToCreateDeviceLater = true; + + CACFLayerTreeHost::destroyRenderer(); +} + +void LegacyCACFLayerTreeHost::resize() +{ + if (!m_d3dDevice) + return; + + // Resetting the device might fail here. But that's OK, because if it does it we will attempt to + // reset the device the next time we try to render. + resetDevice(ChangedWindowSize); + + if (rootLayer()) { + rootLayer()->setBounds(bounds()); + flushContext(); + } +} + +void LegacyCACFLayerTreeHost::renderTimerFired(Timer<LegacyCACFLayerTreeHost>*) +{ + paint(); +} + +void LegacyCACFLayerTreeHost::paint() +{ + createRenderer(); + if (!m_d3dDevice) { + if (m_mightBeAbleToCreateDeviceLater) + renderSoon(); + return; + } + + CACFLayerTreeHost::paint(); +} + +void LegacyCACFLayerTreeHost::render(const Vector<CGRect>& windowDirtyRects) +{ + ASSERT(m_d3dDevice); + + if (m_mustResetLostDeviceBeforeRendering && !resetDevice(LostDevice)) { + // We can't reset the device right now. Try again soon. + renderSoon(); + return; + } + + CGRect bounds = this->bounds(); + + // Give the renderer some space to use. This needs to be valid until the + // wkCACFContextFinishUpdate() call below. + char space[4096]; + if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), CACurrentMediaTime(), bounds, windowDirtyRects.data(), windowDirtyRects.size())) + return; + + HRESULT err = S_OK; + CFTimeInterval timeToNextRender = numeric_limits<CFTimeInterval>::infinity(); + + do { + // FIXME: don't need to clear dirty region if layer tree is opaque. + + WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context); + if (!e) + break; + + Vector<D3DRECT, 64> rects; + for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) { + D3DRECT rect; + rect.x1 = r->origin.x; + rect.x2 = rect.x1 + r->size.width; + rect.y1 = bounds.origin.y + bounds.size.height - (r->origin.y + r->size.height); + rect.y2 = rect.y1 + r->size.height; + + rects.append(rect); + } + wkCACFUpdateRectEnumeratorRelease(e); + + timeToNextRender = wkCACFContextGetNextUpdateTime(m_context); + + if (rects.isEmpty()) + break; + + m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0); + + m_d3dDevice->BeginScene(); + wkCACFContextRenderUpdate(m_context); + m_d3dDevice->EndScene(); + + err = m_d3dDevice->Present(0, 0, 0, 0); + + if (err == D3DERR_DEVICELOST) { + wkCACFContextAddUpdateRect(m_context, bounds); + if (!resetDevice(LostDevice)) { + // We can't reset the device right now. Try again soon. + renderSoon(); + return; + } + } + } while (err == D3DERR_DEVICELOST); + + wkCACFContextFinishUpdate(m_context); + +#ifndef NDEBUG + if (m_printTree) + rootLayer()->printTree(); +#endif + + // If timeToNextRender is not infinity, it means animations are running, so queue up to render again + if (timeToNextRender != numeric_limits<CFTimeInterval>::infinity()) + renderSoon(); +} + +void LegacyCACFLayerTreeHost::renderSoon() +{ + if (!m_renderTimer.isActive()) + m_renderTimer.startOneShot(0); +} + +void LegacyCACFLayerTreeHost::flushContext() +{ + wkCACFContextFlush(m_context); + contextDidChange(); +} + +void LegacyCACFLayerTreeHost::contextDidChange() +{ + renderSoon(); + CACFLayerTreeHost::contextDidChange(); +} + +CFTimeInterval LegacyCACFLayerTreeHost::lastCommitTime() const +{ + return wkCACFContextGetLastCommitTime(m_context); +} + +void LegacyCACFLayerTreeHost::initD3DGeometry() +{ + ASSERT(m_d3dDevice); + + CGRect bounds = this->bounds(); + + float x0 = bounds.origin.x; + float y0 = bounds.origin.y; + float x1 = x0 + bounds.size.width; + float y1 = y0 + bounds.size.height; + + D3DXMATRIXA16 projection; + D3DXMatrixOrthoOffCenterRH(&projection, x0, x1, y0, y1, -1.0f, 1.0f); + + m_d3dDevice->SetTransform(D3DTS_PROJECTION, &projection); +} + +bool LegacyCACFLayerTreeHost::resetDevice(ResetReason reason) +{ + ASSERT(m_d3dDevice); + ASSERT(m_context); + + HRESULT hr = m_d3dDevice->TestCooperativeLevel(); + + if (hr == D3DERR_DEVICELOST || hr == D3DERR_DRIVERINTERNALERROR) { + // The device cannot be reset at this time. Try again soon. + m_mustResetLostDeviceBeforeRendering = true; + return false; + } + + m_mustResetLostDeviceBeforeRendering = false; + + if (reason == LostDevice && hr == D3D_OK) { + // The device wasn't lost after all. + return true; + } + + // We can reset the device. + + // We have to release the context's D3D resrouces whenever we reset the IDirect3DDevice9 in order to + // destroy any D3DPOOL_DEFAULT resources that Core Animation has allocated (e.g., textures used + // for mask layers). See <http://msdn.microsoft.com/en-us/library/bb174425(v=VS.85).aspx>. + wkCACFContextReleaseD3DResources(m_context); + + D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); + hr = m_d3dDevice->Reset(¶meters); + + // TestCooperativeLevel told us the device may be reset now, so we should + // not be told here that the device is lost. + ASSERT(hr != D3DERR_DEVICELOST); + + initD3DGeometry(); + + return true; +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/ca/win/LegacyCACFLayerTreeHost.h b/Source/WebCore/platform/graphics/ca/win/LegacyCACFLayerTreeHost.h new file mode 100644 index 0000000..bfa530b --- /dev/null +++ b/Source/WebCore/platform/graphics/ca/win/LegacyCACFLayerTreeHost.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 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 LegacyCACFLayerTreeHost_h +#define LegacyCACFLayerTreeHost_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CACFLayerTreeHost.h" + +namespace WebCore { + +// FIXME: Currently there is a LegacyCACFLayerTreeHost for each WebView and each +// has its own WKCACFContext and Direct3DDevice9, which is inefficient. +// (https://bugs.webkit.org/show_bug.cgi?id=31855) +class LegacyCACFLayerTreeHost : public CACFLayerTreeHost { +public: + static PassRefPtr<LegacyCACFLayerTreeHost> create(); + virtual ~LegacyCACFLayerTreeHost(); + +private: + LegacyCACFLayerTreeHost(); + + void initD3DGeometry(); + + // Call this when the device window has changed size or when IDirect3DDevice9::Present returns + // D3DERR_DEVICELOST. Returns true if the device was recovered, false if rendering must be + // aborted and reattempted soon. + enum ResetReason { ChangedWindowSize, LostDevice }; + bool resetDevice(ResetReason); + + void renderSoon(); + void renderTimerFired(Timer<LegacyCACFLayerTreeHost>*); + + virtual void initializeContext(void* userData, PlatformCALayer*); + virtual void resize(); + virtual bool createRenderer(); + virtual void destroyRenderer(); + virtual CFTimeInterval lastCommitTime() const; + virtual void flushContext(); + virtual void contextDidChange(); + virtual void paint(); + virtual void render(const Vector<CGRect>& dirtyRects = Vector<CGRect>()); + + Timer<LegacyCACFLayerTreeHost> m_renderTimer; + COMPtr<IDirect3DDevice9> m_d3dDevice; + WKCACFContext* m_context; + bool m_mightBeAbleToCreateDeviceLater; + bool m_mustResetLostDeviceBeforeRendering; + +#ifndef NDEBUG + bool m_printTree; +#endif +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // LegacyCACFLayerTreeHost_h diff --git a/Source/WebCore/platform/graphics/ca/win/PlatformCAAnimationWin.cpp b/Source/WebCore/platform/graphics/ca/win/PlatformCAAnimationWin.cpp index 228bb01..6e3011b 100644 --- a/Source/WebCore/platform/graphics/ca/win/PlatformCAAnimationWin.cpp +++ b/Source/WebCore/platform/graphics/ca/win/PlatformCAAnimationWin.cpp @@ -145,11 +145,6 @@ PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(PlatformAnimationRef return adoptRef(new PlatformCAAnimation(animation)); } -PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::create(const PlatformCAAnimation* animation) -{ - return adoptRef(new PlatformCAAnimation(animation)); -} - PlatformCAAnimation::PlatformCAAnimation(AnimationType type, const String& keyPath) : m_type(type) { @@ -176,33 +171,33 @@ PlatformCAAnimation::PlatformCAAnimation(PlatformAnimationRef animation) m_animation = animation; } -PlatformCAAnimation::PlatformCAAnimation(const PlatformCAAnimation* animation) +PassRefPtr<PlatformCAAnimation> PlatformCAAnimation::copy() const { - m_animation.adoptCF(CACFAnimationCreate((animation->animationType() == Basic) ? kCACFBasicAnimation : kCACFKeyframeAnimation)); - RetainPtr<CFStringRef> keyPath(AdoptCF, animation->keyPath().createCFString()); - CACFAnimationSetKeyPath(m_animation.get(), keyPath.get()); - - setBeginTime(animation->beginTime()); - setDuration(animation->duration()); - setSpeed(animation->speed()); - setTimeOffset(animation->timeOffset()); - setRepeatCount(animation->repeatCount()); - setAutoreverses(animation->autoreverses()); - setFillMode(animation->fillMode()); - setRemovedOnCompletion(animation->isRemovedOnCompletion()); - setAdditive(animation->isAdditive()); - copyTimingFunctionFrom(animation); - setValueFunction(animation->valueFunction()); - + RefPtr<PlatformCAAnimation> animation = create(animationType(), keyPath()); + + animation->setBeginTime(beginTime()); + animation->setDuration(duration()); + animation->setSpeed(speed()); + animation->setTimeOffset(timeOffset()); + animation->setRepeatCount(repeatCount()); + animation->setAutoreverses(autoreverses()); + animation->setFillMode(fillMode()); + animation->setRemovedOnCompletion(isRemovedOnCompletion()); + animation->setAdditive(isAdditive()); + animation->copyTimingFunctionFrom(this); + animation->setValueFunction(valueFunction()); + // Copy the specific Basic or Keyframe values - if (animation->animationType() == Keyframe) { - copyValuesFrom(animation); - copyKeyTimesFrom(animation); - copyTimingFunctionsFrom(animation); + if (animationType() == Keyframe) { + animation->copyValuesFrom(this); + animation->copyKeyTimesFrom(this); + animation->copyTimingFunctionsFrom(this); } else { - copyFromValueFrom(animation); - copyToValueFrom(animation); + animation->copyFromValueFrom(this); + animation->copyToValueFrom(this); } + + return animation; } PlatformCAAnimation::~PlatformCAAnimation() diff --git a/Source/WebCore/platform/graphics/ca/win/WKCACFViewLayerTreeHost.cpp b/Source/WebCore/platform/graphics/ca/win/WKCACFViewLayerTreeHost.cpp new file mode 100644 index 0000000..e672c2d --- /dev/null +++ b/Source/WebCore/platform/graphics/ca/win/WKCACFViewLayerTreeHost.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2011 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 "WKCACFViewLayerTreeHost.h" + +#if USE(ACCELERATED_COMPOSITING) + +#include "PlatformCALayer.h" +#include "SoftLinking.h" +#include <wtf/CurrentTime.h> + +typedef struct _CACFLayer* CACFLayerRef; + +namespace WebCore { + +#ifdef DEBUG_ALL +SOFT_LINK_DEBUG_LIBRARY(WebKitQuartzCoreAdditions) +#else +SOFT_LINK_LIBRARY(WebKitQuartzCoreAdditions) +#endif + +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewCreate, WKCACFViewRef, __cdecl, (), ()) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewSetLayer, void, __cdecl, (WKCACFViewRef view, CACFLayerRef layer), (view, layer)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewUpdate, void, __cdecl, (WKCACFViewRef view, HWND window, const CGRect* bounds), (view, window, bounds)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewCanDraw, bool, __cdecl, (WKCACFViewRef view), (view)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewDraw, void, __cdecl, (WKCACFViewRef view), (view)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewFlushContext, void, __cdecl, (WKCACFViewRef view), (view)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewInvalidateRects, void, __cdecl, (WKCACFViewRef view, const CGRect rects[], size_t count), (view, rects, count)) +typedef void (*WKCACFViewContextDidChangeCallback)(WKCACFViewRef view, void* info); +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewSetContextDidChangeCallback, void, __cdecl, (WKCACFViewRef view, WKCACFViewContextDidChangeCallback callback, void* info), (view, callback, info)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewGetLastCommitTime, CFTimeInterval, __cdecl, (WKCACFViewRef view), (view)) +SOFT_LINK(WebKitQuartzCoreAdditions, WKCACFViewSetContextUserData, void, __cdecl, (WKCACFViewRef view, void* userData), (view, userData)) + +PassRefPtr<WKCACFViewLayerTreeHost> WKCACFViewLayerTreeHost::create() +{ + if (!WebKitQuartzCoreAdditionsLibrary()) + return 0; + + return adoptRef(new WKCACFViewLayerTreeHost); +} + +WKCACFViewLayerTreeHost::WKCACFViewLayerTreeHost() + : m_view(AdoptCF, WKCACFViewCreate()) + , m_viewNeedsUpdate(true) +{ +} + +void WKCACFViewLayerTreeHost::updateViewIfNeeded() +{ + if (!m_viewNeedsUpdate) + return; + m_viewNeedsUpdate = false; + + CGRect layerBounds = rootLayer()->bounds(); + + CGRect bounds = this->bounds(); + WKCACFViewUpdate(m_view.get(), window(), &bounds); + + if (CGRectEqualToRect(layerBounds, rootLayer()->bounds())) + return; + + // Flush the context so the layer's rendered bounds will match our bounds. + flushContext(); +} + +void WKCACFViewLayerTreeHost::contextDidChangeCallback(WKCACFViewRef view, void* info) +{ + ASSERT_ARG(view, view); + ASSERT_ARG(info, info); + + WKCACFViewLayerTreeHost* host = static_cast<WKCACFViewLayerTreeHost*>(info); + ASSERT_ARG(view, view == host->m_view); + host->contextDidChange(); +} + +void WKCACFViewLayerTreeHost::contextDidChange() +{ + // Tell the WKCACFView to start rendering now that we have some contents to render. + updateViewIfNeeded(); + + CACFLayerTreeHost::contextDidChange(); +} + +void WKCACFViewLayerTreeHost::initializeContext(void* userData, PlatformCALayer* layer) +{ + WKCACFViewSetContextUserData(m_view.get(), userData); + WKCACFViewSetLayer(m_view.get(), layer->platformLayer()); + WKCACFViewSetContextDidChangeCallback(m_view.get(), contextDidChangeCallback, this); +} + +void WKCACFViewLayerTreeHost::resize() +{ + m_viewNeedsUpdate = true; +} + +bool WKCACFViewLayerTreeHost::createRenderer() +{ + updateViewIfNeeded(); + return WKCACFViewCanDraw(m_view.get()); +} + +void WKCACFViewLayerTreeHost::destroyRenderer() +{ + m_viewNeedsUpdate = true; + WKCACFViewUpdate(m_view.get(), 0, 0); + WKCACFViewSetContextUserData(m_view.get(), 0); + WKCACFViewSetLayer(m_view.get(), 0); + WKCACFViewSetContextDidChangeCallback(m_view.get(), 0, 0); + + CACFLayerTreeHost::destroyRenderer(); +} + +CFTimeInterval WKCACFViewLayerTreeHost::lastCommitTime() const +{ + return WKCACFViewGetLastCommitTime(m_view.get()); +} + +void WKCACFViewLayerTreeHost::flushContext() +{ + WKCACFViewFlushContext(m_view.get()); +} + +void WKCACFViewLayerTreeHost::paint() +{ + updateViewIfNeeded(); + CACFLayerTreeHost::paint(); +} + +void WKCACFViewLayerTreeHost::render(const Vector<CGRect>& dirtyRects) +{ + WKCACFViewInvalidateRects(m_view.get(), dirtyRects.data(), dirtyRects.size()); + WKCACFViewDraw(m_view.get()); +} + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/ca/win/WKCACFViewLayerTreeHost.h b/Source/WebCore/platform/graphics/ca/win/WKCACFViewLayerTreeHost.h new file mode 100644 index 0000000..af09f76 --- /dev/null +++ b/Source/WebCore/platform/graphics/ca/win/WKCACFViewLayerTreeHost.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2011 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 WKCACFViewLayerTreeHost_h +#define WKCACFViewLayerTreeHost_h + +#if USE(ACCELERATED_COMPOSITING) + +#include "CACFLayerTreeHost.h" + +typedef struct _WKCACFView* WKCACFViewRef; + +namespace WebCore { + +class WKCACFViewLayerTreeHost : public CACFLayerTreeHost { +public: + static PassRefPtr<WKCACFViewLayerTreeHost> create(); + +private: + WKCACFViewLayerTreeHost(); + + void updateViewIfNeeded(); + static void contextDidChangeCallback(WKCACFViewRef, void* info); + + virtual void initializeContext(void* userData, PlatformCALayer*); + virtual void resize(); + virtual bool createRenderer(); + virtual void destroyRenderer(); + virtual void flushContext(); + virtual void contextDidChange(); + virtual void paint(); + virtual void render(const Vector<CGRect>& dirtyRects = Vector<CGRect>()); + virtual CFTimeInterval lastCommitTime() const; + + RetainPtr<WKCACFViewRef> m_view; + bool m_viewNeedsUpdate; +}; + +} // namespace WebCore + +#endif // USE(ACCELERATED_COMPOSITING) + +#endif // WKCACFViewLayerTreeHost_h diff --git a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp index 013a4af..ee159a1 100644 --- a/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp +++ b/Source/WebCore/platform/graphics/cairo/CairoUtilities.cpp @@ -106,7 +106,11 @@ cairo_operator_t toCairoOperator(CompositeOperator op) case CompositeXOR: return CAIRO_OPERATOR_XOR; case CompositePlusDarker: +#if CAIRO_VERSION >= CAIRO_VERSION_ENCODE(1, 10, 0) + return CAIRO_OPERATOR_DARKEN; +#else return CAIRO_OPERATOR_SATURATE; +#endif case CompositeHighlight: // There is no Cairo equivalent for CompositeHighlight. return CAIRO_OPERATOR_OVER; diff --git a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h index 50ea00f..5807102 100644 --- a/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/cairo/FontCustomPlatformData.h @@ -24,6 +24,7 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -40,7 +41,7 @@ struct FontCustomPlatformData { public: FontCustomPlatformData(FT_Face, SharedBuffer*); ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); static bool supportsFormat(const String&); private: diff --git a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 51b5ee6..f7d6040 100644 --- a/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/Source/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -173,7 +173,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& d destx = -originx; originx = 0; } - int endx = rect.right(); + int endx = rect.maxX(); if (endx > size.width()) endx = size.width(); int numColumns = endx - originx; @@ -184,7 +184,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& d desty = -originy; originy = 0; } - int endy = rect.bottom(); + int endy = rect.maxY(); if (endy > size.height()) endy = size.height(); int numRows = endy - originy; @@ -239,9 +239,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(destx >= 0); ASSERT(destx < size.width()); ASSERT(originx >= 0); - ASSERT(originx <= sourceRect.right()); + ASSERT(originx <= sourceRect.maxX()); - int endx = destPoint.x() + sourceRect.right(); + int endx = destPoint.x() + sourceRect.maxX(); ASSERT(endx <= size.width()); int numColumns = endx - destx; @@ -251,9 +251,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(desty >= 0); ASSERT(desty < size.height()); ASSERT(originy >= 0); - ASSERT(originy <= sourceRect.bottom()); + ASSERT(originy <= sourceRect.maxY()); - int endy = destPoint.y() + sourceRect.bottom(); + int endy = destPoint.y() + sourceRect.maxY(); ASSERT(endy <= size.height()); int numRows = endy - desty; diff --git a/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp b/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp index c19bd72..187d296 100644 --- a/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp +++ b/Source/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp @@ -26,7 +26,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" #include "GraphicsContextCG.h" @@ -105,7 +105,7 @@ bool GraphicsContext3D::getImageData(Image* image, decoder.setData(image->data(), true); if (!decoder.frameCount()) return false; - decodedImage = decoder.createFrameAtIndex(0); + decodedImage.adoptCF(decoder.createFrameAtIndex(0)); cgImage = decodedImage.get(); } else cgImage = image->nativeImageForCurrentFrame(); @@ -116,6 +116,34 @@ bool GraphicsContext3D::getImageData(Image* image, size_t height = CGImageGetHeight(cgImage); if (!width || !height) return false; + + // See whether the image is using an indexed color space, and if + // so, re-render it into an RGB color space. The image re-packing + // code requires color data, not color table indices, for the + // image data. + CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage); + CGColorSpaceModel model = CGColorSpaceGetModel(colorSpace); + if (model == kCGColorSpaceModelIndexed) { + RetainPtr<CGContextRef> bitmapContext; + // FIXME: we should probably manually convert the image by indexing into + // the color table, which would allow us to avoid premultiplying the + // alpha channel. Creation of a bitmap context with an alpha channel + // doesn't seem to work unless it's premultiplied. + bitmapContext.adoptCF(CGBitmapContextCreate(0, width, height, 8, width * 4, + deviceRGBColorSpaceRef(), + kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); + if (!bitmapContext) + return false; + + CGContextSetBlendMode(bitmapContext.get(), kCGBlendModeCopy); + CGContextSetInterpolationQuality(bitmapContext.get(), kCGInterpolationNone); + CGContextDrawImage(bitmapContext.get(), CGRectMake(0, 0, width, height), cgImage); + + // Now discard the original CG image and replace it with a copy from the bitmap context. + decodedImage.adoptCF(CGBitmapContextCreateImage(bitmapContext.get())); + cgImage = decodedImage.get(); + } + size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage); size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage); if (bitsPerComponent != 8 && bitsPerComponent != 16) @@ -168,10 +196,11 @@ bool GraphicsContext3D::getImageData(Image* image, AlphaFormat alphaFormat = AlphaFormatNone; switch (CGImageGetAlphaInfo(cgImage)) { case kCGImageAlphaPremultipliedFirst: - // This path is only accessible for MacOS earlier than 10.6.4. // This is a special case for texImage2D with HTMLCanvasElement input, - // in which case image->data() should be null. - ASSERT(!image->data()); + // in which case image->data() should be null, or indexed color models, + // where we need premultiplied alpha to create the bitmap context + // successfully. + ASSERT(!image->data() || model == kCGColorSpaceModelIndexed); if (!premultiplyAlpha) neededAlphaOp = AlphaDoUnmultiply; alphaFormat = AlphaFormatFirst; @@ -254,4 +283,4 @@ void GraphicsContext3D::paintToCanvas(const unsigned char* imagePixels, int imag } // namespace WebCore -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index bcfc37b..3591479 100644 --- a/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/Source/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -35,6 +35,7 @@ #include "KURL.h" #include "Path.h" #include "Pattern.h" +#include "ShadowBlur.h" #include <CoreGraphics/CoreGraphics.h> #include <wtf/MathExtras.h> @@ -165,9 +166,9 @@ void GraphicsContext::drawRect(const IntRect& rect) setCGFillColor(context, strokeColor(), strokeColorSpace()); CGRect rects[4] = { FloatRect(rect.x(), rect.y(), rect.width(), 1), - FloatRect(rect.x(), rect.bottom() - 1, rect.width(), 1), + FloatRect(rect.x(), rect.maxY() - 1, rect.width(), 1), FloatRect(rect.x(), rect.y() + 1, 1, rect.height() - 2), - FloatRect(rect.right() - 1, rect.y() + 1, 1, rect.height() - 2) + FloatRect(rect.maxX() - 1, rect.y() + 1, 1, rect.height() - 2) }; CGContextFillRects(context, rects, 4); if (oldFillColor != strokeColor()) @@ -563,7 +564,7 @@ void GraphicsContext::fillPath(const Path& path) CGContextClip(layerContext); m_state.fillGradient->paint(layerContext); - CGContextDrawLayerAtPoint(context, CGPointMake(rect.left(), rect.top()), layer); + CGContextDrawLayerAtPoint(context, CGPointMake(rect.x(), rect.y()), layer); CGLayerRelease(layer); } else { CGContextBeginPath(context); @@ -616,6 +617,16 @@ void GraphicsContext::strokePath(const Path& path) CGContextStrokePath(context); } +static float radiusToLegacyRadius(float radius) +{ + return radius > 8 ? 8 + 4 * sqrt((radius - 8) / 2) : radius; +} + +static bool hasBlurredShadow(const GraphicsContextState& state) +{ + return state.shadowColor.isValid() && state.shadowColor.alpha() && state.shadowBlur; +} + void GraphicsContext::fillRect(const FloatRect& rect) { if (paintingDisabled()) @@ -626,11 +637,16 @@ void GraphicsContext::fillRect(const FloatRect& rect) if (m_state.fillGradient) { CGContextSaveGState(context); if (hasShadow()) { - CGContextConcatCTM(context, m_state.fillGradient->gradientSpaceTransform()); CGLayerRef layer = CGLayerCreateWithContext(context, CGSizeMake(rect.width(), rect.height()), 0); CGContextRef layerContext = CGLayerGetContext(layer); + + CGContextTranslateCTM(layerContext, -rect.x(), -rect.y()); + CGContextAddRect(layerContext, rect); + CGContextClip(layerContext); + + CGContextConcatCTM(layerContext, m_state.fillGradient->gradientSpaceTransform()); m_state.fillGradient->paint(layerContext); - CGContextDrawLayerAtPoint(context, CGPointMake(rect.left(), rect.top()), layer); + CGContextDrawLayerAtPoint(context, CGPointMake(rect.x(), rect.y()), layer); CGLayerRelease(layer); } else { CGContextClipToRect(context, rect); @@ -643,7 +659,22 @@ void GraphicsContext::fillRect(const FloatRect& rect) if (m_state.fillPattern) applyFillPattern(); + + bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet. + if (drawOwnShadow) { + float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur; + // Turn off CG shadows. + CGContextSaveGState(context); + CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0); + + ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace); + contextShadow.drawRectShadow(this, rect, RoundedIntRect::Radii()); + } + CGContextFillRect(context, rect); + + if (drawOwnShadow) + CGContextRestoreGState(context); } void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) @@ -658,7 +689,21 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorS if (oldFillColor != color || oldColorSpace != colorSpace) setCGFillColor(context, color, colorSpace); + bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet. + if (drawOwnShadow) { + float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur; + // Turn off CG shadows. + CGContextSaveGState(context); + CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0); + + ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace); + contextShadow.drawRectShadow(this, rect, RoundedIntRect::Radii()); + } + CGContextFillRect(context, rect); + + if (drawOwnShadow) + CGContextRestoreGState(context); if (oldFillColor != color || oldColorSpace != colorSpace) setCGFillColor(context, oldFillColor, oldColorSpace); @@ -678,12 +723,72 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef Path path; path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + + bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; // Don't use ShadowBlur for canvas yet. + if (drawOwnShadow) { + float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur; + + // Turn off CG shadows. + CGContextSaveGState(context); + CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0); + + ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace); + contextShadow.drawRectShadow(this, rect, RoundedIntRect::Radii(topLeft, topRight, bottomLeft, bottomRight)); + } + fillPath(path); + if (drawOwnShadow) + CGContextRestoreGState(context); + if (oldFillColor != color || oldColorSpace != colorSpace) setCGFillColor(context, oldFillColor, oldColorSpace); } +void GraphicsContext::fillRectWithRoundedHole(const IntRect& rect, const RoundedIntRect& roundedHoleRect, const Color& color, ColorSpace colorSpace) +{ + if (paintingDisabled()) + return; + + CGContextRef context = platformContext(); + + Path path; + path.addRect(rect); + + if (!roundedHoleRect.radii().isZero()) + path.addRoundedRect(roundedHoleRect.rect(), roundedHoleRect.radii().topLeft(), roundedHoleRect.radii().topRight(), roundedHoleRect.radii().bottomLeft(), roundedHoleRect.radii().bottomRight()); + else + path.addRect(roundedHoleRect.rect()); + + WindRule oldFillRule = fillRule(); + Color oldFillColor = fillColor(); + ColorSpace oldFillColorSpace = fillColorSpace(); + + setFillRule(RULE_EVENODD); + setFillColor(color, colorSpace); + + // fillRectWithRoundedHole() assumes that the edges of rect are clipped out, so we only care about shadows cast around inside the hole. + bool drawOwnShadow = hasBlurredShadow(m_state) && !m_state.shadowsIgnoreTransforms; + if (drawOwnShadow) { + float shadowBlur = m_state.shadowsUseLegacyRadius ? radiusToLegacyRadius(m_state.shadowBlur) : m_state.shadowBlur; + + // Turn off CG shadows. + CGContextSaveGState(context); + CGContextSetShadowWithColor(platformContext(), CGSizeZero, 0, 0); + + ShadowBlur contextShadow(shadowBlur, m_state.shadowOffset, m_state.shadowColor, m_state.shadowColorSpace); + contextShadow.drawInsetShadow(this, rect, roundedHoleRect.rect(), roundedHoleRect.radii()); + } + + fillPath(path); + + if (drawOwnShadow) + CGContextRestoreGState(context); + + setFillRule(oldFillRule); + setFillColor(oldFillColor, oldFillColorSpace); +} + void GraphicsContext::clip(const FloatRect& rect) { if (paintingDisabled()) @@ -722,6 +827,11 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule) CGContextClip(context); } +IntRect GraphicsContext::clipBounds() const +{ + return enclosingIntRect(CGContextGetClipBoundingBox(platformContext())); +} + void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness) { if (paintingDisabled()) @@ -766,6 +876,9 @@ void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, con { if (paintingDisabled()) return; + + // FIXME: we could avoid the shadow setup cost when we know we'll render the shadow ourselves. + CGFloat xOffset = offset.width(); CGFloat yOffset = offset.height(); CGFloat blurRadius = blur; diff --git a/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp index 295f632..ab5907e 100644 --- a/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/Source/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -267,7 +267,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& i RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); unsigned char* data = result->data(); - if (rect.x() < 0 || rect.y() < 0 || rect.right() > size.width() || rect.bottom() > size.height()) + if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height()) memset(data, 0, result->length()); int originx = rect.x(); @@ -276,7 +276,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& i destx = -originx; originx = 0; } - int endx = rect.right(); + int endx = rect.maxX(); if (endx > size.width()) endx = size.width(); int numColumns = endx - originx; @@ -287,7 +287,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& i desty = -originy; originy = 0; } - int endy = rect.bottom(); + int endy = rect.maxY(); if (endy > size.height()) endy = size.height(); int numRows = endy - originy; @@ -377,9 +377,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(destx >= 0); ASSERT(destx < size.width()); ASSERT(originx >= 0); - ASSERT(originx <= sourceRect.right()); + ASSERT(originx <= sourceRect.maxX()); - int endx = destPoint.x() + sourceRect.right(); + int endx = destPoint.x() + sourceRect.maxX(); ASSERT(endx <= size.width()); int numColumns = endx - destx; @@ -389,9 +389,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(desty >= 0); ASSERT(desty < size.height()); ASSERT(originy >= 0); - ASSERT(originy <= sourceRect.bottom()); + ASSERT(originy <= sourceRect.maxY()); - int endy = destPoint.y() + sourceRect.bottom(); + int endy = destPoint.y() + sourceRect.maxY(); ASSERT(endy <= size.height()); int numRows = endy - desty; diff --git a/Source/WebCore/platform/graphics/cg/ImageCG.cpp b/Source/WebCore/platform/graphics/cg/ImageCG.cpp index dfee96a..08f65bd 100644 --- a/Source/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/Source/WebCore/platform/graphics/cg/ImageCG.cpp @@ -204,7 +204,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F adjustedDestRect.setHeight(subimageRect.height() / yScale); image.adoptCF(CGImageCreateWithImageInRect(image.get(), subimageRect)); - if (currHeight < srcRect.bottom()) { + if (currHeight < srcRect.maxY()) { ASSERT(CGImageGetHeight(image.get()) == currHeight - CGRectIntegral(srcRect).origin.y); adjustedDestRect.setHeight(CGImageGetHeight(image.get()) / yScale); } @@ -224,7 +224,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F // Flip the coords. CGContextScaleCTM(context, 1, -1); - adjustedDestRect.setY(-adjustedDestRect.bottom()); + adjustedDestRect.setY(-adjustedDestRect.maxY()); // Adjust the color space. image = imageWithColorSpace(image.get(), styleColorSpace); diff --git a/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp index 92861fc..4598602 100644 --- a/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp +++ b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.cpp @@ -146,7 +146,10 @@ void ComplexTextController::reset(unsigned offset) // TextRun has been reached. bool ComplexTextController::nextScriptRun() { - if (!hb_utf16_script_run_next(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + // Ensure we're not pointing at the small caps buffer. + m_item.string = m_run.characters(); + + if (!hb_utf16_script_run_next(0, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) return false; // It is actually wrong to consider script runs at all in this code. @@ -184,11 +187,33 @@ float ComplexTextController::widthOfFullRun() void ComplexTextController::setupFontForScriptRun() { - const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false).fontData; + FontDataVariant fontDataVariant = AutoVariant; + // Determine if this script run needs to be converted to small caps. + // nextScriptRun() will always send us a run of the same case, because a + // case change while in small-caps mode always results in different + // FontData, so we only need to check the first character's case. + if (m_font->isSmallCaps() && u_islower(m_item.string[m_item.item.pos])) { + m_smallCapsString = String(m_run.data(m_item.item.pos), m_item.item.length); + m_smallCapsString.makeUpper(); + m_item.string = m_smallCapsString.characters(); + m_item.item.pos = 0; + fontDataVariant = SmallCapsVariant; + } + const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, fontDataVariant).fontData; const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); m_item.face = platformData.harfbuzzFace(); void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); m_item.font->userData = opaquePlatformData; + + int size = platformData.size(); + m_item.font->x_ppem = size; + m_item.font->y_ppem = size; + // x_ and y_scale are the conversion factors from font design space (fEmSize) to 1/64th of device pixels in 16.16 format. + const int devicePixelFraction = 64; + const int multiplyFor16Dot16 = 1 << 16; + int scale = devicePixelFraction * size * multiplyFor16Dot16 / platformData.emSizeInFontUnits(); + m_item.font->x_scale = scale; + m_item.font->y_scale = scale; } HB_FontRec* ComplexTextController::allocHarfbuzzFont() @@ -197,13 +222,6 @@ HB_FontRec* ComplexTextController::allocHarfbuzzFont() memset(font, 0, sizeof(HB_FontRec)); font->klass = &harfbuzzSkiaClass; font->userData = 0; - // The values which harfbuzzSkiaClass returns are already scaled to - // pixel units, so we just set all these to one to disable further - // scaling. - font->x_ppem = 1; - font->y_ppem = 1; - font->x_scale = 1; - font->y_scale = 1; return font; } @@ -369,7 +387,7 @@ const TextRun& ComplexTextController::getNormalizedTextRun(const TextRun& origin sourceText = normalizedString.getBuffer(); } - normalizedBuffer.set(new UChar[normalizedBufferLength + 1]); + normalizedBuffer = adoptArrayPtr(new UChar[normalizedBufferLength + 1]); normalizeSpacesAndMirrorChars(sourceText, originalRun.rtl(), normalizedBuffer.get(), normalizedBufferLength); diff --git a/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h index a2aea60..6a93878 100644 --- a/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h +++ b/Source/WebCore/platform/graphics/chromium/ComplexTextControllerLinux.h @@ -111,7 +111,7 @@ public: const unsigned short* logClusters() const { return m_item.log_clusters; } // return the number of code points in the current script run - const unsigned numCodePoints() const { return m_numCodePoints; } + const unsigned numCodePoints() const { return m_item.item.length; } // Return the current pixel position of the controller. const unsigned offsetX() const { return m_offsetX; } @@ -141,7 +141,6 @@ private: ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. unsigned m_offsetX; // Offset in pixels to the start of the next script run. unsigned m_pixelWidth; // Width (in px) of the current script run. - unsigned m_numCodePoints; // Code points in current script run. unsigned m_glyphsArrayCapacity; // Current size of all the Harfbuzz arrays. OwnPtr<TextRun> m_normalizedRun; @@ -155,6 +154,7 @@ private: // each word break we accumulate error. This is the // number of pixels that we are behind so far. int m_letterSpacing; // pixels to be added after each glyph. + String m_smallCapsString; // substring of m_run converted to small caps. }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp index d00faf8..a38f6bd 100644 --- a/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp @@ -343,7 +343,7 @@ void ContentLayerChromium::draw() ASSERT(sv && sv->initialized()); GraphicsContext3D* context = layerRendererContext(); GLC(context, context->activeTexture(GraphicsContext3D::TEXTURE0)); - m_contentsTexture->bindTexture(); + bindContentsTexture(); layerRenderer()->useShader(sv->contentShaderProgram()); GLC(context, context->uniform1i(sv->shaderSamplerLocation(), 0)); @@ -361,8 +361,21 @@ void ContentLayerChromium::draw() drawOpacity(), sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); } - m_contentsTexture->unreserve(); + unreserveContentsTexture(); +} + +void ContentLayerChromium::unreserveContentsTexture() +{ + if (m_contentsTexture) + m_contentsTexture->unreserve(); } +void ContentLayerChromium::bindContentsTexture() +{ + if (m_contentsTexture) + m_contentsTexture->bindTexture(); +} + + } #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h index dc1630b..3363518 100644 --- a/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h +++ b/Source/WebCore/platform/graphics/chromium/ContentLayerChromium.h @@ -50,6 +50,9 @@ public: virtual ~ContentLayerChromium(); virtual void updateContentsIfDirty(); + virtual void unreserveContentsTexture(); + virtual void bindContentsTexture(); + virtual void draw(); virtual bool drawsContent() { return m_owner && m_owner->drawsContent(); } diff --git a/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp b/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp index 2d4ca41..d956841 100644 --- a/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp @@ -36,12 +36,20 @@ #include "GraphicsContext3D.h" #include "SharedGraphicsContext3D.h" +#if ENABLE(SKIA_GPU) +#include "GrContext.h" +#endif + #if USE(ACCELERATED_COMPOSITING) #include "Canvas2DLayerChromium.h" #endif namespace WebCore { +#if ENABLE(SKIA_GPU) +extern GrContext* GetGlobalGrContext(); +#endif + struct DrawingBufferInternal { unsigned offscreenColorTexture; #if USE(ACCELERATED_COMPOSITING) @@ -72,7 +80,7 @@ DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, bool multisampleExtensionSupported, bool packedDepthStencilExtensionSupported) : m_context(context) - , m_size(-1, -1) + , m_size(size) , m_multisampleExtensionSupported(multisampleExtensionSupported) , m_packedDepthStencilExtensionSupported(packedDepthStencilExtensionSupported) , m_fbo(0) @@ -119,6 +127,8 @@ void DrawingBuffer::publishToPlatformLayer() if (m_callback) m_callback->willPublish(); + if (multisample()) + commit(); unsigned parentTexture = m_internal->platformLayer->textureId(); // FIXME: We do the copy in the canvas' (child) context so that it executes in the correct order relative to // other commands in the child context. This ensures that the parent texture always contains a complete @@ -126,6 +136,9 @@ void DrawingBuffer::publishToPlatformLayer() // happens before the compositor draws. This means we might draw stale frames sometimes. Ideally this // would insert a fence into the child command stream that the compositor could wait for. m_context->makeContextCurrent(); +#if ENABLE(SKIA_GPU) + GetGlobalGrContext()->flush(false); +#endif static_cast<Extensions3DChromium*>(m_context->getExtensions())->copyTextureToParentTextureCHROMIUM(m_colorBuffer, parentTexture); m_context->flush(); } diff --git a/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h b/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h index d120424..92fb7b3 100644 --- a/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h +++ b/Source/WebCore/platform/graphics/chromium/Extensions3DChromium.h @@ -44,8 +44,8 @@ public: virtual bool supports(const String&); virtual void ensureEnabled(const String&); virtual int getGraphicsResetStatusARB(); - virtual void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter) { } - virtual void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height) { } + virtual void blitFramebuffer(long srcX0, long srcY0, long srcX1, long srcY1, long dstX0, long dstY0, long dstX1, long dstY1, unsigned long mask, unsigned long filter); + virtual void renderbufferStorageMultisample(unsigned long target, unsigned long samples, unsigned long internalformat, unsigned long width, unsigned long height); enum { // GL_CHROMIUM_map_sub (enums inherited from GL_ARB_vertex_buffer_object) diff --git a/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp index f4c0dee..33ebc59 100644 --- a/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp +++ b/Source/WebCore/platform/graphics/chromium/FontCacheChromiumWin.cpp @@ -399,6 +399,28 @@ static int CALLBACK traitsInFamilyEnumProc(CONST LOGFONT* logFont, CONST TEXTMET return 1; } +struct GetLastResortFallbackFontProcData { + GetLastResortFallbackFontProcData(FontCache* fontCache, const FontDescription* fontDescription, wchar_t* fontName) + : m_fontCache(fontCache) + , m_fontDescription(fontDescription) + , m_fontName(fontName) + , m_fontData(0) + { + } + + FontCache* m_fontCache; + const FontDescription* m_fontDescription; + wchar_t* m_fontName; + SimpleFontData* m_fontData; +}; + +static int CALLBACK getLastResortFallbackFontProc(const LOGFONT* logFont, const TEXTMETRIC* metrics, DWORD fontType, LPARAM lParam) +{ + GetLastResortFallbackFontProcData* procData = reinterpret_cast<GetLastResortFallbackFontProcData*>(lParam); + procData->m_fontData = fontDataFromDescriptionAndLogFont(procData->m_fontCache, *procData->m_fontDescription, *logFont, procData->m_fontName); + return !procData->m_fontData; +} + void FontCache::platformInit() { // Not needed on Windows. @@ -548,6 +570,21 @@ SimpleFontData* FontCache::getLastResortFallbackFont(const FontDescription& desc return simpleFont; } + // Fall back to all the fonts installed in this PC. When a font has a + // localized name according to the system locale as well as an English name, + // both GetTextFace() and EnumFontFamilies() return the localized name. So, + // FontCache::createFontPlatformData() does not filter out the fonts + // returned by this EnumFontFamilies() call. + HDC dc = GetDC(0); + if (dc) { + GetLastResortFallbackFontProcData procData(this, &description, fallbackFontName); + EnumFontFamilies(dc, 0, getLastResortFallbackFontProc, reinterpret_cast<LPARAM>(&procData)); + ReleaseDC(0, dc); + + if (procData.m_fontData) + return procData.m_fontData; + } + ASSERT_NOT_REACHED(); return 0; } diff --git a/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp index 5da4d5a..e57a84c 100644 --- a/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp +++ b/Source/WebCore/platform/graphics/chromium/FontChromiumWin.cpp @@ -246,10 +246,11 @@ IntRect TransparencyAwareGlyphPainter::estimateTextBounds() 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()); + const FontMetrics& fontMetrics = m_font->fontMetrics(); + return IntRect(m_point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2, + m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(), + totalWidth + fontMetrics.ascent() + fontMetrics.descent(), + fontMetrics.lineSpacing()); } bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, @@ -270,7 +271,7 @@ bool TransparencyAwareGlyphPainter::drawGlyphs(int numGlyphs, // 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()); + int y = lroundf(m_point.y() - m_font->fontMetrics().ascent()); // If there is a non-blur shadow and both the fill color and shadow color // are opaque, handle without skia. @@ -354,10 +355,11 @@ IntRect TransparencyAwareUniscribePainter::estimateTextBounds() // 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()); + const FontMetrics& fontMetrics = m_font->fontMetrics(); + return IntRect(left - (fontMetrics.ascent() + fontMetrics.descent()) / 2, + m_point.y() - fontMetrics.ascent() - fontMetrics.lineGap(), + (right - left) + fontMetrics.ascent() + fontMetrics.descent(), + fontMetrics.lineSpacing()); } } // namespace @@ -367,6 +369,11 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, @@ -496,14 +503,14 @@ void Font::drawComplexText(GraphicsContext* graphicsContext, COLORREF savedTextColor = GetTextColor(hdc); SetTextColor(hdc, textColor); state.draw(graphicsContext, hdc, static_cast<int>(point.x()) + shadowOffset.width(), - static_cast<int>(point.y() - ascent()) + shadowOffset.height(), from, to); + static_cast<int>(point.y() - fontMetrics().ascent()) + shadowOffset.height(), from, to); SetTextColor(hdc, savedTextColor); } // Uniscribe counts the coordinates from the upper left, while WebKit uses // the baseline, so we have to subtract off the ascent. state.draw(graphicsContext, hdc, static_cast<int>(point.x()), - static_cast<int>(point.y() - ascent()), from, to); + static_cast<int>(point.y() - fontMetrics().ascent()), from, to); context->canvas()->endPlatformPaint(); } diff --git a/Source/WebCore/platform/graphics/chromium/FontLinux.cpp b/Source/WebCore/platform/graphics/chromium/FontLinux.cpp index 822bbbb..823dbc9 100644 --- a/Source/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/Source/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -55,6 +55,11 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + static bool isCanvasMultiLayered(SkCanvas* canvas) { SkCanvas::LayerIter layerIterator(canvas, false); @@ -204,7 +209,7 @@ void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, ComplexTextController controller(run, point.x(), this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); if (run.rtl()) { // FIXME: this causes us to shape the text twice -- once to compute the width and then again @@ -213,7 +218,7 @@ void Font::drawComplexText(GraphicsContext* gc, const TextRun& run, controller.reset(point.x() + controller.widthOfFullRun()); // We need to set the padding again because ComplexTextController layout consumed the value. // Fixing the above problem would help here too. - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); } while (controller.nextScriptRun()) { @@ -241,7 +246,7 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon ComplexTextController controller(run, 0, this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); return controller.widthOfFullRun(); } @@ -275,11 +280,11 @@ int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, ComplexTextController controller(run, 0, this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); if (run.rtl()) { // See FIXME in drawComplexText. controller.reset(controller.widthOfFullRun()); - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); } unsigned basePosition = 0; @@ -326,11 +331,11 @@ FloatRect Font::selectionRectForComplexText(const TextRun& run, ComplexTextController controller(run, 0, this); controller.setWordSpacingAdjustment(wordSpacing()); controller.setLetterSpacingAdjustment(letterSpacing()); - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); if (run.rtl()) { // See FIXME in drawComplexText. controller.reset(controller.widthOfFullRun()); - controller.setPadding(run.padding()); + controller.setPadding(run.expansion()); } // Iterate through the script runs in logical order, searching for the run covering the positions of interest. diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp index a1ea012..6f9009f 100644 --- a/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.cpp @@ -36,6 +36,7 @@ #include "PlatformBridge.h" #include "PlatformString.h" +#include "SkAdvancedTypefaceMetrics.h" #include "SkPaint.h" #include "SkTypeface.h" @@ -71,6 +72,7 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src) : m_typeface(src.m_typeface) , m_family(src.m_family) , m_textSize(src.m_textSize) + , m_emSizeInFontUnits(src.m_emSizeInFontUnits) , m_fakeBold(src.m_fakeBold) , m_fakeItalic(src.m_fakeItalic) , m_orientation(src.m_orientation) @@ -84,6 +86,7 @@ FontPlatformData::FontPlatformData(SkTypeface* tf, const char* family, float tex : m_typeface(tf) , m_family(family) , m_textSize(textSize) + , m_emSizeInFontUnits(0) , m_fakeBold(fakeBold) , m_fakeItalic(fakeItalic) , m_orientation(orientation) @@ -96,6 +99,7 @@ FontPlatformData::FontPlatformData(const FontPlatformData& src, float textSize) : m_typeface(src.m_typeface) , m_family(src.m_family) , m_textSize(textSize) + , m_emSizeInFontUnits(src.m_emSizeInFontUnits) , m_fakeBold(src.m_fakeBold) , m_fakeItalic(src.m_fakeItalic) , m_harfbuzzFace(src.m_harfbuzzFace) @@ -109,6 +113,17 @@ FontPlatformData::~FontPlatformData() SkSafeUnref(m_typeface); } +int FontPlatformData::emSizeInFontUnits() const +{ + if (m_emSizeInFontUnits) + return m_emSizeInFontUnits; + + SkAdvancedTypefaceMetrics* metrics = m_typeface->getAdvancedTypefaceMetrics(false); + m_emSizeInFontUnits = metrics->fEmSize; + metrics->unref(); + return m_emSizeInFontUnits; +} + FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) { SkRefCnt_SafeAssign(m_typeface, src.m_typeface); @@ -120,6 +135,7 @@ FontPlatformData& FontPlatformData::operator=(const FontPlatformData& src) m_harfbuzzFace = src.m_harfbuzzFace; m_orientation = src.m_orientation; m_style = src.m_style; + m_emSizeInFontUnits = src.m_emSizeInFontUnits; return *this; } diff --git a/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h index 43771d7..d9ebb61 100644 --- a/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h +++ b/Source/WebCore/platform/graphics/chromium/FontPlatformDataLinux.h @@ -63,6 +63,7 @@ public: FontPlatformData(WTF::HashTableDeletedValueType) : m_typeface(hashTableDeletedFontValue()) , m_textSize(0) + , m_emSizeInFontUnits(0) , m_fakeBold(false) , m_fakeItalic(false) { } @@ -70,6 +71,7 @@ public: FontPlatformData() : m_typeface(0) , m_textSize(0) + , m_emSizeInFontUnits(0) , m_fakeBold(false) , m_fakeItalic(false) , m_orientation(Horizontal) @@ -78,6 +80,7 @@ public: FontPlatformData(float textSize, bool fakeBold, bool fakeItalic) : m_typeface(0) , m_textSize(textSize) + , m_emSizeInFontUnits(0) , m_fakeBold(fakeBold) , m_fakeItalic(fakeItalic) , m_orientation(Horizontal) @@ -107,6 +110,7 @@ public: unsigned hash() const; float size() const { return m_textSize; } + int emSizeInFontUnits() const; FontOrientation orientation() const { return m_orientation; } @@ -153,6 +157,7 @@ private: SkTypeface* m_typeface; CString m_family; float m_textSize; + mutable int m_emSizeInFontUnits; bool m_fakeBold; bool m_fakeItalic; FontOrientation m_orientation; diff --git a/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp b/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp index 4393f97..953ee2f 100644 --- a/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp +++ b/Source/WebCore/platform/graphics/chromium/GLES2Canvas.cpp @@ -83,6 +83,22 @@ struct GLES2Canvas::State { AffineTransform m_ctm; WTF::Vector<Path> m_clippingPaths; bool m_clippingEnabled; + + // Helper function for applying the state's alpha value to the given input + // color to produce a new output color. The logic is the same as + // PlatformContextSkia::State::applyAlpha(), but the type is different. + Color applyAlpha(const Color& c) + { + int s = roundf(m_alpha * 256); + if (s >= 256) + return c; + if (s < 0) + return Color(); + + int a = (c.alpha() * s) >> 8; + return Color(c.red(), c.green(), c.blue(), a); + } + }; static inline FloatPoint operator*(const FloatPoint& f, float scale) @@ -192,7 +208,7 @@ void GLES2Canvas::fillPath(const Path& path) { m_context->applyCompositeOperator(m_state->m_compositeOp); applyClipping(m_state->m_clippingEnabled); - fillPath(path, m_state->m_fillColor); + fillPath(path, m_state->applyAlpha(m_state->m_fillColor)); } void GLES2Canvas::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) @@ -214,7 +230,7 @@ void GLES2Canvas::fillRect(const FloatRect& rect, const Color& color, ColorSpace void GLES2Canvas::fillRect(const FloatRect& rect) { - fillRect(rect, m_state->m_fillColor, ColorSpaceDeviceRGB); + fillRect(rect, m_state->applyAlpha(m_state->m_fillColor), ColorSpaceDeviceRGB); } void GLES2Canvas::setFillColor(const Color& color, ColorSpace colorSpace) @@ -322,8 +338,8 @@ void GLES2Canvas::drawTexturedRect(Texture* texture, const FloatRect& srcRect, c m_context->useQuadVertices(); m_context->setActiveTexture(GraphicsContext3D::TEXTURE0); - for (int y = tileIdxRect.y(); y <= tileIdxRect.bottom(); y++) { - for (int x = tileIdxRect.x(); x <= tileIdxRect.right(); x++) + for (int y = tileIdxRect.y(); y <= tileIdxRect.maxY(); y++) { + for (int x = tileIdxRect.x(); x <= tileIdxRect.maxX(); x++) drawTexturedRectTile(texture, tiles.tileIndex(x, y), srcRect, dstRect, transform, alpha); } } diff --git a/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp index 5e8d148..488230c 100644 --- a/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.cpp @@ -263,6 +263,17 @@ void GraphicsLayerChromium::setContentsOpaque(bool opaque) updateContentsOpaque(); } +void GraphicsLayerChromium::setMaskLayer(GraphicsLayer* maskLayer) +{ + if (maskLayer == m_maskLayer) + return; + + GraphicsLayer::setMaskLayer(maskLayer); + + LayerChromium* maskLayerChromium = m_maskLayer ? m_maskLayer->platformLayer() : 0; + m_layer->setMaskLayer(maskLayerChromium); +} + void GraphicsLayerChromium::setBackfaceVisibility(bool visible) { if (m_backfaceVisibility == visible) @@ -283,6 +294,15 @@ void GraphicsLayerChromium::setOpacity(float opacity) primaryLayer()->setOpacity(opacity); } +void GraphicsLayerChromium::setReplicatedByLayer(GraphicsLayer* layer) +{ + GraphicsLayerChromium* layerChromium = static_cast<GraphicsLayerChromium*>(layer); + GraphicsLayer::setReplicatedByLayer(layer); + LayerChromium* replicaLayer = layerChromium ? layerChromium->primaryLayer() : 0; + primaryLayer()->setReplicaLayer(replicaLayer); +} + + void GraphicsLayerChromium::setContentsNeedsDisplay() { if (m_contentsLayer) @@ -494,6 +514,7 @@ void GraphicsLayerChromium::updateAnchorPoint() { primaryLayer()->setAnchorPoint(FloatPoint(m_anchorPoint.x(), m_anchorPoint.y())); primaryLayer()->setAnchorPointZ(m_anchorPoint.z()); + updateLayerPosition(); } diff --git a/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h index 130c25c..92c61fe 100644 --- a/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h +++ b/Source/WebCore/platform/graphics/chromium/GraphicsLayerChromium.h @@ -67,6 +67,7 @@ public: virtual void setPreserves3D(bool); virtual void setMasksToBounds(bool); virtual void setDrawsContent(bool); + virtual void setMaskLayer(GraphicsLayer*); virtual void setBackgroundColor(const Color&); virtual void clearBackgroundColor(); @@ -74,6 +75,8 @@ public: virtual void setContentsOpaque(bool); virtual void setBackfaceVisibility(bool); + virtual void setReplicatedByLayer(GraphicsLayer*); + virtual void setOpacity(float); virtual void setNeedsDisplay(); diff --git a/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp b/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp index 056d8eb..26ca64e 100644 --- a/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp +++ b/Source/WebCore/platform/graphics/chromium/HarfbuzzSkia.cpp @@ -94,7 +94,7 @@ static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 n font->setupPaint(&paint); paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); - OwnArrayPtr<uint16_t> glyphs16(new uint16_t[numGlyphs]); + OwnArrayPtr<uint16_t> glyphs16 = adoptArrayPtr(new uint16_t[numGlyphs]); if (!glyphs16.get()) return; for (unsigned i = 0; i < numGlyphs; ++i) @@ -120,7 +120,7 @@ static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 font->setupPaint(&paint); paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); - OwnArrayPtr<uint16_t> glyphs16(new uint16_t[length]); + OwnArrayPtr<uint16_t> glyphs16 = adoptArrayPtr(new uint16_t[length]); if (!glyphs16.get()) return 0; int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16.get()); diff --git a/Source/WebCore/platform/graphics/chromium/IconChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/IconChromium.cpp index e958d4a..398cc3b 100644 --- a/Source/WebCore/platform/graphics/chromium/IconChromiumWin.cpp +++ b/Source/WebCore/platform/graphics/chromium/IconChromium.cpp @@ -1,10 +1,10 @@ /* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. - * + * Copyright (c) 2011, 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 @@ -31,25 +31,18 @@ #include "config.h" #include "Icon.h" -#include <windows.h> -#include <shellapi.h> - #include "GraphicsContext.h" -#include "PlatformContextSkia.h" #include "PlatformString.h" -#include "SkiaUtils.h" namespace WebCore { -Icon::Icon(const PlatformIcon& icon) +Icon::Icon(PassRefPtr<PlatformIcon> icon) : m_icon(icon) { } Icon::~Icon() { - if (m_icon) - DestroyIcon(m_icon); } void Icon::paint(GraphicsContext* context, const IntRect& rect) @@ -57,9 +50,9 @@ void Icon::paint(GraphicsContext* context, const IntRect& rect) if (context->paintingDisabled()) return; - HDC hdc = context->platformContext()->canvas()->beginPlatformPaint(); - DrawIconEx(hdc, rect.x(), rect.y(), m_icon, rect.width(), rect.height(), 0, 0, DI_NORMAL); - context->platformContext()->canvas()->endPlatformPaint(); + // An Icon doesn't know the color space of the file upload control. + // So use ColorSpaceDeviceRGB. + context->drawImage(m_icon.get(), ColorSpaceDeviceRGB, rect); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp index b7ab098..8d01d9b 100644 --- a/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/LayerChromium.cpp @@ -143,6 +143,7 @@ PassRefPtr<LayerChromium> LayerChromium::create(GraphicsLayerChromium* owner) LayerChromium::LayerChromium(GraphicsLayerChromium* owner) : m_owner(owner) , m_contentsDirty(false) + , m_maskLayer(0) , m_targetRenderSurface(0) , m_superlayer(0) , m_anchorPoint(0.5, 0.5) @@ -162,6 +163,7 @@ LayerChromium::LayerChromium(GraphicsLayerChromium* owner) , m_drawDepth(0) , m_layerRenderer(0) , m_renderSurface(0) + , m_replicaLayer(0) { } @@ -433,9 +435,7 @@ void LayerChromium::drawTexturedQuad(GraphicsContext3D* context, const Transform renderMatrix.scale3d(width, height, 1); // Apply the projection matrix before sending the transform over to the shader. - renderMatrix.multiply(projectionMatrix); - - toGLMatrix(&glMatrix[0], renderMatrix); + toGLMatrix(&glMatrix[0], projectionMatrix * renderMatrix); GLC(context, context->uniformMatrix4fv(matrixLocation, false, &glMatrix[0], 1)); @@ -457,8 +457,7 @@ void LayerChromium::drawDebugBorder() layerRenderer()->useShader(sv->borderShaderProgram()); TransformationMatrix renderMatrix = drawTransform(); renderMatrix.scale3d(bounds().width(), bounds().height(), 1); - renderMatrix.multiply(layerRenderer()->projectionMatrix()); - toGLMatrix(&glMatrix[0], renderMatrix); + toGLMatrix(&glMatrix[0], layerRenderer()->projectionMatrix() * renderMatrix); GraphicsContext3D* context = layerRendererContext(); GLC(context, context->uniformMatrix4fv(sv->borderShaderMatrixLocation(), false, &glMatrix[0], 1)); diff --git a/Source/WebCore/platform/graphics/chromium/LayerChromium.h b/Source/WebCore/platform/graphics/chromium/LayerChromium.h index a0a690f..5c7e2b1 100644 --- a/Source/WebCore/platform/graphics/chromium/LayerChromium.h +++ b/Source/WebCore/platform/graphics/chromium/LayerChromium.h @@ -112,6 +112,9 @@ public: void setName(const String& name) { m_name = name; } String name() const { return m_name; } + void setMaskLayer(LayerChromium* maskLayer) { m_maskLayer = maskLayer; } + LayerChromium* maskLayer() const { return m_maskLayer.get(); } + void setNeedsDisplay(const FloatRect& dirtyRect); void setNeedsDisplay(); const FloatRect& dirtyRect() const { return m_dirtyRect; } @@ -152,12 +155,17 @@ public: void setOwner(GraphicsLayerChromium* owner) { m_owner = owner; } + void setReplicaLayer(LayerChromium* layer) { m_replicaLayer = layer; } + LayerChromium* replicaLayer() { return m_replicaLayer; } + // Returns the rect containtaining this layer in the current view's coordinate system. const IntRect getDrawRect() const; // These methods typically need to be overwritten by derived classes. virtual bool drawsContent() { return false; } virtual void updateContentsIfDirty() { } + virtual void unreserveContentsTexture() { } + virtual void bindContentsTexture() { } virtual void draw() { } void drawDebugBorder(); @@ -222,6 +230,8 @@ protected: FloatRect m_dirtyRect; bool m_contentsDirty; + RefPtr<LayerChromium> m_maskLayer; + // Render surface this layer draws into. This is a surface that can belong // either to this layer (if m_targetRenderSurface == m_renderSurface) or // to an ancestor of this layer. The target render surface determines the @@ -298,6 +308,9 @@ private: // Hierarchical bounding rect containing the layer and its descendants. IntRect m_drawableContentRect; + // Replica layer used for reflections. + LayerChromium* m_replicaLayer; + String m_name; }; diff --git a/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp index 90eac74..f5548c9 100644 --- a/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp @@ -143,20 +143,19 @@ void LayerRendererChromium::useShader(unsigned programId) IntRect LayerRendererChromium::verticalScrollbarRect(const IntRect& visibleRect, const IntRect& contentRect) { - IntRect verticalScrollbar(IntPoint(contentRect.right(), contentRect.y()), IntSize(visibleRect.width() - contentRect.width(), visibleRect.height())); + IntRect verticalScrollbar(IntPoint(contentRect.maxX(), contentRect.y()), IntSize(visibleRect.width() - contentRect.width(), visibleRect.height())); return verticalScrollbar; } IntRect LayerRendererChromium::horizontalScrollbarRect(const IntRect& visibleRect, const IntRect& contentRect) { - IntRect horizontalScrollbar(IntPoint(contentRect.x(), contentRect.bottom()), IntSize(visibleRect.width(), visibleRect.height() - contentRect.height())); + IntRect horizontalScrollbar(IntPoint(contentRect.x(), contentRect.maxY()), IntSize(visibleRect.width(), visibleRect.height() - contentRect.height())); return horizontalScrollbar; } void LayerRendererChromium::invalidateRootLayerRect(const IntRect& dirtyRect, const IntRect& visibleRect, const IntRect& contentRect) { - if (contentRect.intersects(dirtyRect)) - m_rootLayerTiler->invalidateRect(dirtyRect); + m_rootLayerTiler->invalidateRect(dirtyRect); if (m_horizontalScrollbarTiler) { IntRect scrollbar = horizontalScrollbarRect(visibleRect, contentRect); if (dirtyRect.intersects(scrollbar)) { @@ -266,6 +265,11 @@ void LayerRendererChromium::drawLayers(const IntRect& visibleRect, const IntRect // Re-enable color writes to layers, which may be partially transparent. m_context->colorMask(true, true, true, true); + // Recheck that we still have a root layer. This may become null if + // compositing gets turned off during a paint operation. + if (!m_rootLayer) + return; + // Set the root visible/content rects --- used by subsequent drawLayers calls. m_rootVisibleRect = visibleRect; m_rootContentRect = contentRect; @@ -346,8 +350,8 @@ void LayerRendererChromium::setRootLayer(PassRefPtr<LayerChromium> layer) void LayerRendererChromium::getFramebufferPixels(void *pixels, const IntRect& rect) { - ASSERT(rect.right() <= rootLayerTextureSize().width() - && rect.bottom() <= rootLayerTextureSize().height()); + ASSERT(rect.maxX() <= rootLayerTextureSize().width() + && rect.maxY() <= rootLayerTextureSize().height()); if (!pixels) return; @@ -388,7 +392,7 @@ bool LayerRendererChromium::isLayerVisible(LayerChromium* layer, const Transform // bounds into clip space. TransformationMatrix renderMatrix = matrix; renderMatrix.scale3d(layer->bounds().width(), layer->bounds().height(), 1); - renderMatrix.multiply(m_projectionMatrix); + renderMatrix = m_projectionMatrix * renderMatrix; FloatRect layerRect(-0.5, -0.5, 1, 1); FloatRect mappedRect = renderMatrix.mapRect(layerRect); @@ -434,12 +438,12 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr // LT = Tr[l] layerLocalTransform.translate3d(position.x(), position.y(), layer->anchorPointZ()); // LT = Tr[l] * M[l] - layerLocalTransform.multLeft(layer->transform()); + layerLocalTransform.multiply(layer->transform()); // LT = Tr[l] * M[l] * Tr[c] layerLocalTransform.translate3d(centerOffsetX, centerOffsetY, -layer->anchorPointZ()); TransformationMatrix combinedTransform = parentMatrix; - combinedTransform = combinedTransform.multLeft(layerLocalTransform); + combinedTransform = combinedTransform.multiply(layerLocalTransform); FloatRect layerRect(-0.5 * layer->bounds().width(), -0.5 * layer->bounds().height(), layer->bounds().width(), layer->bounds().height()); IntRect transformedLayerRect; @@ -448,12 +452,17 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr // these conditions hold: // 1. The layer clips its descendants and its transform is not a simple translation. // 2. If the layer has opacity != 1 and does not have a preserves-3d transform style. + // 3. The layer uses a mask + // 4. The layer has a replica (used for reflections) // If a layer preserves-3d then we don't create a RenderSurface for it to avoid flattening // out its children. The opacity value of the children layers is multiplied by the opacity // of their parent. bool useSurfaceForClipping = layer->masksToBounds() && !isScaleOrTranslation(combinedTransform); bool useSurfaceForOpacity = layer->opacity() != 1 && !layer->preserves3D(); - if ((useSurfaceForClipping || useSurfaceForOpacity) && layer->descendantsDrawContent()) { + bool useSurfaceForMasking = layer->maskLayer(); + bool useSurfaceForReflection = layer->replicaLayer(); + if (((useSurfaceForClipping || useSurfaceForOpacity) && layer->descendantsDrawContent()) + || useSurfaceForMasking || useSurfaceForReflection) { RenderSurfaceChromium* renderSurface = layer->m_renderSurface.get(); if (!renderSurface) renderSurface = layer->createRenderSurface(); @@ -487,6 +496,18 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr renderSurface->m_layerList.clear(); + if (layer->maskLayer()) { + renderSurface->m_maskLayer = layer->maskLayer(); + layer->maskLayer()->setLayerRenderer(this); + layer->maskLayer()->m_targetRenderSurface = renderSurface; + } else + renderSurface->m_maskLayer = 0; + + if (layer->replicaLayer() && layer->replicaLayer()->maskLayer()) { + layer->replicaLayer()->maskLayer()->setLayerRenderer(this); + layer->replicaLayer()->maskLayer()->m_targetRenderSurface = renderSurface; + } + renderSurfaceLayerList.append(layer); } else { // DT = M[p] * LT @@ -540,7 +561,7 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr } // Apply the sublayer transform at the center of the layer. - sublayerMatrix.multLeft(layer->sublayerTransform()); + sublayerMatrix.multiply(layer->sublayerTransform()); // The origin of the sublayers is the top left corner of the layer, not the // center. The matrix passed down to the sublayers is therefore: @@ -558,16 +579,13 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr if (sublayer->m_renderSurface) { RenderSurfaceChromium* sublayerRenderSurface = sublayer->m_renderSurface.get(); - const IntRect& contentRect = sublayerRenderSurface->contentRect(); - FloatRect sublayerRect(-0.5 * contentRect.width(), -0.5 * contentRect.height(), - contentRect.width(), contentRect.height()); - layer->m_drawableContentRect.unite(enclosingIntRect(sublayerRenderSurface->m_drawTransform.mapRect(sublayerRect))); + layer->m_drawableContentRect.unite(enclosingIntRect(sublayerRenderSurface->drawableContentRect())); descendants.append(sublayer); } else layer->m_drawableContentRect.unite(sublayer->m_drawableContentRect); } - if (layer->masksToBounds()) + if (layer->masksToBounds() || useSurfaceForMasking) layer->m_drawableContentRect.intersect(transformedLayerRect); if (layer->m_renderSurface && layer != m_rootLayer) { @@ -577,9 +595,13 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr // Restrict the RenderSurface size to the portion that's visible. FloatSize centerOffsetDueToClipping; - renderSurface->m_contentRect.intersect(layer->m_scissorRect); - FloatPoint clippedSurfaceCenter = renderSurface->contentRectCenter(); - centerOffsetDueToClipping = clippedSurfaceCenter - surfaceCenter; + // Don't clip if the layer is reflected as the reflection shouldn't be + // clipped. + if (!layer->replicaLayer()) { + renderSurface->m_contentRect.intersect(layer->m_scissorRect); + FloatPoint clippedSurfaceCenter = renderSurface->contentRectCenter(); + centerOffsetDueToClipping = clippedSurfaceCenter - surfaceCenter; + } // The RenderSurface backing texture cannot exceed the maximum supported // texture size. @@ -596,6 +618,15 @@ void LayerRendererChromium::updateLayersRecursive(LayerChromium* layer, const Tr // Adjust the origin of the transform to be the center of the render surface. renderSurface->m_drawTransform = renderSurface->m_originTransform; renderSurface->m_drawTransform.translate3d(surfaceCenter.x() + centerOffsetDueToClipping.width(), surfaceCenter.y() + centerOffsetDueToClipping.height(), 0); + + // Compute the transformation matrix used to draw the replica of the render + // surface. + if (layer->replicaLayer()) { + renderSurface->m_replicaDrawTransform = renderSurface->m_originTransform; + renderSurface->m_replicaDrawTransform.translate3d(layer->replicaLayer()->position().x(), layer->replicaLayer()->position().y(), 0); + renderSurface->m_replicaDrawTransform.multiply(layer->replicaLayer()->transform()); + renderSurface->m_replicaDrawTransform.translate3d(surfaceCenter.x() - anchorPoint.x() * bounds.width(), surfaceCenter.y() - anchorPoint.y() * bounds.height(), 0); + } } // Compute the depth value of the center of the layer which will be used when @@ -682,10 +713,10 @@ void LayerRendererChromium::drawLayer(LayerChromium* layer, RenderSurfaceChromiu if (!isLayerVisible) return; - // FIXME: Need to take into account the transform of the containing - // RenderSurface here, otherwise single-sided layers that draw on - // transformed surfaces won't always be culled properly. - if (!layer->doubleSided() && layer->m_drawTransform.m33() < 0) + // FIXME: Need to take into account the commulative render surface transforms all the way from + // the default render surface in order to determine visibility. + TransformationMatrix combinedDrawMatrix = (layer->m_renderSurface ? layer->m_renderSurface->drawTransform().multiply(layer->m_drawTransform) : layer->m_drawTransform); + if (!layer->doubleSided() && combinedDrawMatrix.m33() < 0) return; if (layer->drawsContent()) { @@ -711,7 +742,7 @@ void LayerRendererChromium::setScissorToRect(const IntRect& scissorRect) // But, if rendering to offscreen texture, we reverse our sense of 'upside down'. int scissorY; if (m_currentRenderSurface == m_defaultRenderSurface && !m_compositeOffscreen) - scissorY = m_currentRenderSurface->m_contentRect.height() - (scissorRect.bottom() - m_currentRenderSurface->m_contentRect.y()); + scissorY = m_currentRenderSurface->m_contentRect.height() - (scissorRect.maxY() - m_currentRenderSurface->m_contentRect.y()); else scissorY = scissorRect.y() - m_currentRenderSurface->m_contentRect.y(); GLC(m_context.get(), m_context->scissor(scissorX, scissorY, scissorRect.width(), scissorRect.height())); @@ -737,9 +768,9 @@ bool LayerRendererChromium::checkTextureSize(const IntSize& textureSize) void LayerRendererChromium::setDrawViewportRect(const IntRect& drawRect, bool flipY) { if (flipY) - m_projectionMatrix = orthoMatrix(drawRect.x(), drawRect.right(), drawRect.bottom(), drawRect.y()); + m_projectionMatrix = orthoMatrix(drawRect.x(), drawRect.maxX(), drawRect.maxY(), drawRect.y()); else - m_projectionMatrix = orthoMatrix(drawRect.x(), drawRect.right(), drawRect.y(), drawRect.bottom()); + m_projectionMatrix = orthoMatrix(drawRect.x(), drawRect.maxX(), drawRect.y(), drawRect.maxY()); GLC(m_context.get(), m_context->viewport(0, 0, drawRect.width(), drawRect.height())); } diff --git a/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp b/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp index 32bfa0b..23cb4b3 100644 --- a/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp +++ b/Source/WebCore/platform/graphics/chromium/LayerTexture.cpp @@ -79,6 +79,7 @@ void LayerTexture::unreserve() void LayerTexture::bindTexture() { + ASSERT(m_textureManager->hasTexture(m_token)); m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_textureId); } diff --git a/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp b/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp index 6b65e66..e28c084 100644 --- a/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/LayerTilerChromium.cpp @@ -145,8 +145,8 @@ void LayerTilerChromium::contentRectToTileIndices(const IntRect& contentRect, in left = layerRect.x() / m_tileSize.width(); top = layerRect.y() / m_tileSize.height(); - right = (layerRect.right() - 1) / m_tileSize.width(); - bottom = (layerRect.bottom() - 1) / m_tileSize.height(); + right = (layerRect.maxX() - 1) / m_tileSize.width(); + bottom = (layerRect.maxY() - 1) / m_tileSize.height(); } IntRect LayerTilerChromium::contentRectToLayerRect(const IntRect& contentRect) const @@ -272,7 +272,11 @@ void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& cont // Get the contents of the updated rect. const SkBitmap& bitmap = canvas->getDevice()->accessBitmap(false); ASSERT(bitmap.width() == paintRect.width() && bitmap.height() == paintRect.height()); + if (bitmap.width() != paintRect.width() || bitmap.height() != paintRect.height()) + CRASH(); uint8_t* paintPixels = static_cast<uint8_t*>(bitmap.getPixels()); + if (!paintPixels) + CRASH(); #elif PLATFORM(CG) Vector<uint8_t> canvasPixels; int rowBytes = 4 * paintRect.width(); @@ -299,9 +303,15 @@ void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& cont #error "Need to implement for your platform." #endif + // Painting could cause compositing to get turned off, which may cause the tiler to become invalidated mid-update. + if (!m_tiles.size()) + return; + for (int j = top; j <= bottom; ++j) { for (int i = left; i <= right; ++i) { Tile* tile = m_tiles[tileIndex(i, j)].get(); + if (!tile) + CRASH(); if (!tile->dirty()) continue; @@ -320,13 +330,21 @@ void LayerTilerChromium::update(TilePaintInterface& painter, const IntRect& cont // Calculate tile-space rectangle to upload into. IntRect destRect(IntPoint(sourceRect.x() - anchor.x(), sourceRect.y() - anchor.y()), sourceRect.size()); - ASSERT(destRect.x() >= 0); - ASSERT(destRect.y() >= 0); + if (destRect.x() < 0) + CRASH(); + if (destRect.y() < 0) + CRASH(); // Offset from paint rectangle to this tile's dirty rectangle. IntPoint paintOffset(sourceRect.x() - paintRect.x(), sourceRect.y() - paintRect.y()); - ASSERT(paintOffset.x() >= 0); - ASSERT(paintOffset.y() >= 0); + if (paintOffset.x() < 0) + CRASH(); + if (paintOffset.y() < 0) + CRASH(); + if (paintOffset.x() + destRect.width() > paintRect.width()) + CRASH(); + if (paintOffset.y() + destRect.height() > paintRect.height()) + CRASH(); uint8_t* pixelSource; if (paintRect.width() == sourceRect.width() && !paintOffset.x()) @@ -357,7 +375,7 @@ void LayerTilerChromium::setLayerPosition(const IntPoint& layerPosition) void LayerTilerChromium::draw(const IntRect& contentRect) { - if (m_skipsDraw) + if (m_skipsDraw || !m_tiles.size()) return; // We reuse the shader program used by ContentLayerChromium. @@ -394,6 +412,9 @@ void LayerTilerChromium::resizeLayer(const IntSize& size) int width = (size.width() + m_tileSize.width() - 1) / m_tileSize.width(); int height = (size.height() + m_tileSize.height() - 1) / m_tileSize.height(); + if (height && (width > INT_MAX / height)) + CRASH(); + Vector<OwnPtr<Tile> > newTiles; newTiles.resize(width * height); for (int j = 0; j < m_layerTileSize.height(); ++j) @@ -409,7 +430,7 @@ void LayerTilerChromium::growLayerToContain(const IntRect& contentRect) { // Grow the tile array to contain this content rect. IntRect layerRect = contentRectToLayerRect(contentRect); - IntSize layerSize = IntSize(layerRect.right(), layerRect.bottom()); + IntSize layerSize = IntSize(layerRect.maxX(), layerRect.maxY()); IntSize newSize = layerSize.expandedTo(m_layerSize); resizeLayer(newSize); diff --git a/Source/WebCore/platform/graphics/chromium/PlatformIcon.h b/Source/WebCore/platform/graphics/chromium/PlatformIcon.h index 51613b8..b485917 100644 --- a/Source/WebCore/platform/graphics/chromium/PlatformIcon.h +++ b/Source/WebCore/platform/graphics/chromium/PlatformIcon.h @@ -31,11 +31,11 @@ #ifndef PlatformIcon_h #define PlatformIcon_h -typedef struct HICON__* HICON; - namespace WebCore { -typedef HICON PlatformIcon; +class Image; + +typedef Image PlatformIcon; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp index 696828f..b3ce9d7 100644 --- a/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.cpp @@ -38,15 +38,17 @@ namespace WebCore { RenderSurfaceChromium::SharedValues::SharedValues(GraphicsContext3D* context) : m_context(context) , m_shaderProgram(0) + , m_maskShaderProgram(0) , m_shaderSamplerLocation(-1) , m_shaderMatrixLocation(-1) , m_shaderAlphaLocation(-1) + , m_maskShaderSamplerLocation(-1) + , m_maskShaderMaskSamplerLocation(-1) + , m_maskShaderMatrixLocation(-1) + , m_maskShaderAlphaLocation(-1) , m_initialized(false) { - // The following program composites layers whose contents are the results of a previous - // render operation and therefore doesn't perform any color swizzling. It is used - // in scrolling and for compositing offscreen textures. - char renderSurfaceVertexShaderString[] = + char vertexShaderString[] = "attribute vec4 a_position; \n" "attribute vec2 a_texCoord; \n" "uniform mat4 matrix; \n" @@ -56,7 +58,7 @@ RenderSurfaceChromium::SharedValues::SharedValues(GraphicsContext3D* context) " gl_Position = matrix * a_position; \n" " v_texCoord = a_texCoord; \n" "} \n"; - char renderSurfaceFragmentShaderString[] = + char fragmentShaderString[] = "precision mediump float; \n" "varying vec2 v_texCoord; \n" "uniform sampler2D s_texture; \n" @@ -66,9 +68,22 @@ RenderSurfaceChromium::SharedValues::SharedValues(GraphicsContext3D* context) " vec4 texColor = texture2D(s_texture, v_texCoord); \n" " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" "} \n"; + char fragmentShaderWithMaskString[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "uniform sampler2D s_mask; \n" + "uniform float alpha; \n" + "void main() \n" + "{ \n" + " vec4 texColor = texture2D(s_texture, v_texCoord); \n" + " vec4 maskColor = texture2D(s_mask, v_texCoord); \n" + " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha * maskColor.w; \n" + "} \n"; - m_shaderProgram = LayerChromium::createShaderProgram(m_context, renderSurfaceVertexShaderString, renderSurfaceFragmentShaderString); - if (!m_shaderProgram) { + m_shaderProgram = LayerChromium::createShaderProgram(m_context, vertexShaderString, fragmentShaderString); + m_maskShaderProgram = LayerChromium::createShaderProgram(m_context, vertexShaderString, fragmentShaderWithMaskString); + if (!m_shaderProgram || !m_maskShaderProgram) { LOG_ERROR("RenderSurfaceChromium: Failed to create shader program"); return; } @@ -76,10 +91,24 @@ RenderSurfaceChromium::SharedValues::SharedValues(GraphicsContext3D* context) GLC(m_context, m_shaderSamplerLocation = m_context->getUniformLocation(m_shaderProgram, "s_texture")); GLC(m_context, m_shaderMatrixLocation = m_context->getUniformLocation(m_shaderProgram, "matrix")); GLC(m_context, m_shaderAlphaLocation = m_context->getUniformLocation(m_shaderProgram, "alpha")); - if (m_shaderSamplerLocation == -1 || m_shaderMatrixLocation == -1 || m_shaderAlphaLocation == -1) { - LOG_ERROR("Failed to initialize texture layer shader."); + + GLC(m_context, m_maskShaderSamplerLocation = m_context->getUniformLocation(m_maskShaderProgram, "s_texture")); + GLC(m_context, m_maskShaderMaskSamplerLocation = m_context->getUniformLocation(m_maskShaderProgram, "s_mask")); + GLC(m_context, m_maskShaderMatrixLocation = m_context->getUniformLocation(m_maskShaderProgram, "matrix")); + GLC(m_context, m_maskShaderAlphaLocation = m_context->getUniformLocation(m_maskShaderProgram, "alpha")); + + if (m_shaderSamplerLocation == -1 || m_shaderMatrixLocation == -1 || m_shaderAlphaLocation == -1 + || m_maskShaderSamplerLocation == -1 || m_maskShaderMaskSamplerLocation == -1 || m_maskShaderMatrixLocation == -1 || m_maskShaderAlphaLocation == -1) { + LOG_ERROR("Failed to initialize render surface shaders."); return; } + + GLC(m_context, m_context->useProgram(m_shaderProgram)); + GLC(m_context, m_context->uniform1i(m_shaderSamplerLocation, 0)); + GLC(m_context, m_context->useProgram(m_maskShaderProgram)); + GLC(m_context, m_context->uniform1i(m_maskShaderSamplerLocation, 0)); + GLC(m_context, m_context->uniform1i(m_maskShaderMaskSamplerLocation, 1)); + GLC(m_context, m_context->useProgram(0)); m_initialized = true; } @@ -87,10 +116,13 @@ RenderSurfaceChromium::SharedValues::~SharedValues() { if (m_shaderProgram) GLC(m_context, m_context->deleteProgram(m_shaderProgram)); + if (m_maskShaderProgram) + GLC(m_context, m_context->deleteProgram(m_maskShaderProgram)); } RenderSurfaceChromium::RenderSurfaceChromium(LayerChromium* owningLayer) : m_owningLayer(owningLayer) + , m_maskLayer(0) , m_skipsDraw(false) { } @@ -116,6 +148,17 @@ LayerRendererChromium* RenderSurfaceChromium::layerRenderer() return m_owningLayer->layerRenderer(); } +FloatRect RenderSurfaceChromium::drawableContentRect() const +{ + FloatRect localContentRect(-0.5 * m_contentRect.width(), -0.5 * m_contentRect.height(), + m_contentRect.width(), m_contentRect.height()); + FloatRect drawableContentRect = m_drawTransform.mapRect(localContentRect); + if (m_owningLayer->replicaLayer()) + drawableContentRect.unite(m_replicaDrawTransform.mapRect(localContentRect)); + + return drawableContentRect; +} + bool RenderSurfaceChromium::prepareContentsTexture() { IntSize requiredSize(m_contentRect.size()); @@ -136,24 +179,69 @@ bool RenderSurfaceChromium::prepareContentsTexture() return true; } -void RenderSurfaceChromium::draw() +void RenderSurfaceChromium::drawSurface(LayerChromium* maskLayer, const TransformationMatrix& drawTransform) { - if (m_skipsDraw || !m_contentsTexture) - return; - - m_contentsTexture->bindTexture(); + GraphicsContext3D* context3D = layerRenderer()->context(); + int shaderMatrixLocation = -1; + int shaderAlphaLocation = -1; const RenderSurfaceChromium::SharedValues* sv = layerRenderer()->renderSurfaceSharedValues(); ASSERT(sv && sv->initialized()); + bool useMask = false; + if (maskLayer && maskLayer->drawsContent()) { + maskLayer->updateContentsIfDirty(); + if (!maskLayer->bounds().isEmpty()) { + context3D->makeContextCurrent(); + layerRenderer()->useShader(sv->maskShaderProgram()); + GLC(context3D, context3D->activeTexture(GraphicsContext3D::TEXTURE0)); + m_contentsTexture->bindTexture(); + GLC(context3D, context3D->activeTexture(GraphicsContext3D::TEXTURE1)); + maskLayer->bindContentsTexture(); + GLC(context3D, context3D->activeTexture(GraphicsContext3D::TEXTURE0)); + shaderMatrixLocation = sv->maskShaderMatrixLocation(); + shaderAlphaLocation = sv->maskShaderAlphaLocation(); + useMask = true; + } + } + + if (!useMask) { + layerRenderer()->useShader(sv->shaderProgram()); + m_contentsTexture->bindTexture(); + shaderMatrixLocation = sv->shaderMatrixLocation(); + shaderAlphaLocation = sv->shaderAlphaLocation(); + } + + LayerChromium::drawTexturedQuad(layerRenderer()->context(), layerRenderer()->projectionMatrix(), drawTransform, + m_contentRect.width(), m_contentRect.height(), m_drawOpacity, + shaderMatrixLocation, shaderAlphaLocation); + + m_contentsTexture->unreserve(); + + if (maskLayer) + maskLayer->unreserveContentsTexture(); +} + +void RenderSurfaceChromium::draw() +{ + if (m_skipsDraw || !m_contentsTexture) + return; + // FIXME: By using the same RenderSurface for both the content and its reflection, + // it's currently not possible to apply a separate mask to the reflection layer + // or correctly handle opacity in reflections (opacity must be applied after drawing + // both the layer and its reflection). The solution is to introduce yet another RenderSurface + // to draw the layer and its reflection in. For now we only apply a separate reflection + // mask if the contents don't have a mask of their own. + LayerChromium* replicaMaskLayer = m_maskLayer; + if (!m_maskLayer && m_owningLayer->replicaLayer()) + replicaMaskLayer = m_owningLayer->replicaLayer()->maskLayer(); - layerRenderer()->useShader(sv->shaderProgram()); layerRenderer()->setScissorToRect(m_scissorRect); - LayerChromium::drawTexturedQuad(layerRenderer()->context(), layerRenderer()->projectionMatrix(), m_drawTransform, - m_contentRect.width(), m_contentRect.height(), m_drawOpacity, - sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + // Reflection draws before the layer. + if (m_owningLayer->replicaLayer()) + drawSurface(replicaMaskLayer, m_replicaDrawTransform); - m_contentsTexture->unreserve(); + drawSurface(m_maskLayer, m_drawTransform); } } diff --git a/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h index 689a6eb..b1f6a5c 100644 --- a/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h +++ b/Source/WebCore/platform/graphics/chromium/RenderSurfaceChromium.h @@ -55,6 +55,11 @@ public: FloatPoint contentRectCenter() const { return FloatRect(m_contentRect).center(); } IntRect contentRect() const { return m_contentRect; } + // Returns the rect that encloses the RenderSurface including any reflection. + FloatRect drawableContentRect() const; + + TransformationMatrix drawTransform() const { return m_drawTransform; } + // Stores values that are shared between instances of this class that are // associated with the same LayerRendererChromium (and hence the same GL // context). @@ -64,30 +69,44 @@ public: ~SharedValues(); unsigned shaderProgram() const { return m_shaderProgram; } + unsigned maskShaderProgram() const { return m_maskShaderProgram; } int shaderSamplerLocation() const { return m_shaderSamplerLocation; } int shaderMatrixLocation() const { return m_shaderMatrixLocation; } int shaderAlphaLocation() const { return m_shaderAlphaLocation; } + int maskShaderSamplerLocation() const { return m_maskShaderSamplerLocation; } + int maskShaderMaskSamplerLocation() const { return m_maskShaderMaskSamplerLocation; } + int maskShaderMatrixLocation() const { return m_maskShaderMatrixLocation; } + int maskShaderAlphaLocation() const { return m_maskShaderAlphaLocation; } bool initialized() const { return m_initialized; } private: GraphicsContext3D* m_context; unsigned m_shaderProgram; + unsigned m_maskShaderProgram; int m_shaderSamplerLocation; int m_shaderMatrixLocation; int m_shaderAlphaLocation; + int m_maskShaderSamplerLocation; + int m_maskShaderMaskSamplerLocation; + int m_maskShaderMatrixLocation; + int m_maskShaderAlphaLocation; bool m_initialized; }; private: LayerRendererChromium* layerRenderer(); + void drawSurface(LayerChromium* maskLayer, const TransformationMatrix& drawTransform); LayerChromium* m_owningLayer; + LayerChromium* m_maskLayer; + IntRect m_contentRect; bool m_skipsDraw; OwnPtr<LayerTexture> m_contentsTexture; float m_drawOpacity; TransformationMatrix m_drawTransform; + TransformationMatrix m_replicaDrawTransform; TransformationMatrix m_originTransform; IntRect m_scissorRect; Vector<LayerChromium*> m_layerList; diff --git a/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp b/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp index c23c586..1450c5a 100644 --- a/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp +++ b/Source/WebCore/platform/graphics/chromium/SimpleFontDataChromiumWin.cpp @@ -54,14 +54,9 @@ static inline float scaleEmToUnits(float x, int unitsPerEm) void SimpleFontData::platformInit() { if (!m_platformData.size()) { - m_ascent = 0; - m_descent = 0; - m_lineGap = 0; - m_lineSpacing = 0; + m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; - m_xHeight = 0; - m_unitsPerEm = 0; return; } @@ -82,10 +77,11 @@ void SimpleFontData::platformInit() m_avgCharWidth = textMetric.tmAveCharWidth; m_maxCharWidth = textMetric.tmMaxCharWidth; - m_ascent = textMetric.tmAscent; - m_descent = textMetric.tmDescent; - m_lineGap = textMetric.tmExternalLeading; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. + // FIXME: Access ascent/descent/lineGap with floating point precision. + float ascent = textMetric.tmAscent; + float descent = textMetric.tmDescent; + float lineGap = textMetric.tmExternalLeading; + float xHeight = ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. OUTLINETEXTMETRIC outlineTextMetric; if (GetOutlineTextMetrics(dc, sizeof(outlineTextMetric), &outlineTextMetric) > 0) { @@ -94,10 +90,14 @@ void SimpleFontData::platformInit() MAT2 identityMatrix = {{0, 1}, {0, 0}, {0, 0}, {0, 1}}; DWORD len = GetGlyphOutlineW(dc, 'x', GGO_METRICS, &glyphMetrics, 0, 0, &identityMatrix); if (len != GDI_ERROR && glyphMetrics.gmBlackBoxY > 0) - m_xHeight = static_cast<float>(glyphMetrics.gmBlackBoxY); + xHeight = static_cast<float>(glyphMetrics.gmBlackBoxY); } - m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setXHeight(xHeight); + m_fontMetrics.setLineSpacing(ascent + descent + lineGap); SelectObject(dc, oldFont); ReleaseDC(0, dc); diff --git a/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp b/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp index 355d837..9423d1e 100644 --- a/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp +++ b/Source/WebCore/platform/graphics/chromium/SimpleFontDataLinux.cpp @@ -55,14 +55,9 @@ static const size_t maxVDMXTableSize = 1024 * 1024; // 1 MB void SimpleFontData::platformInit() { if (!m_platformData.size()) { - m_ascent = 0; - m_descent = 0; - m_lineGap = 0; - m_lineSpacing = 0; + m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; - m_xHeight = 0; - m_unitsPerEm = 0; return; } @@ -88,26 +83,35 @@ void SimpleFontData::platformInit() fastFree(vdmxTable); } + float ascent; + float descent; + // Beware those who step here: This code is designed to match Win32 font // metrics *exactly*. if (isVDMXValid) { - m_ascent = vdmxAscent; - m_descent = -vdmxDescent; + ascent = vdmxAscent; + descent = -vdmxDescent; } else { SkScalar height = -metrics.fAscent + metrics.fDescent + metrics.fLeading; - m_ascent = SkScalarRound(-metrics.fAscent); - m_descent = SkScalarRound(height) - m_ascent; + ascent = SkScalarRound(-metrics.fAscent); + descent = SkScalarRound(height) - ascent; } + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + + float xHeight; if (metrics.fXHeight) - m_xHeight = metrics.fXHeight; + xHeight = metrics.fXHeight; else { // hack taken from the Windows port - m_xHeight = static_cast<float>(m_ascent) * 0.56; + xHeight = ascent * 0.56f; } - m_lineGap = SkScalarRound(metrics.fLeading); - m_lineSpacing = m_ascent + m_descent + m_lineGap; + float lineGap = SkScalarToFloat(metrics.fLeading); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setXHeight(xHeight); + m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); if (m_orientation == Vertical) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); @@ -128,7 +132,7 @@ void SimpleFontData::platformInit() if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); else { - m_avgCharWidth = m_xHeight; + m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); diff --git a/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp index ba66eae..193271d 100644 --- a/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp +++ b/Source/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -446,9 +446,9 @@ void TransparencyWin::compositeOpaqueComposite() identity.reset(); destCanvas->setMatrix(identity); - destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom()); + destRect.set(m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY()); } else - destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.right(), m_sourceRect.bottom()); + destRect.set(m_sourceRect.x(), m_sourceRect.y(), m_sourceRect.maxX(), m_sourceRect.maxY()); SkPaint paint; paint.setFilterBitmap(true); @@ -487,7 +487,7 @@ void TransparencyWin::compositeTextComposite() SkMatrix identity; identity.reset(); destCanvas->setMatrix(identity); - SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.right(), m_transformedSourceRect.bottom() }; + SkRect destRect = { m_transformedSourceRect.x(), m_transformedSourceRect.y(), m_transformedSourceRect.maxX(), m_transformedSourceRect.maxY() }; // 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. diff --git a/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp b/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp index aa18b4a..c060b43 100644 --- a/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp +++ b/Source/WebCore/platform/graphics/chromium/UniscribeHelperTextRun.cpp @@ -51,14 +51,14 @@ UniscribeHelperTextRun::UniscribeHelperTextRun(const TextRun& run, setLetterSpacing(font.letterSpacing()); setSpaceWidth(font.spaceWidth()); setWordSpacing(font.wordSpacing()); - setAscent(font.primaryFont()->ascent()); + setAscent(font.fontMetrics().ascent()); init(); - // Padding is the amount to add to make justification happen. This + // Expansion is the amount to add to make justification happen. This // should be done after Init() so all the runs are already measured. - if (run.padding() > 0) - justify(run.padding()); + if (run.expansion() > 0) + justify(run.expansion()); } UniscribeHelperTextRun::UniscribeHelperTextRun( @@ -121,7 +121,7 @@ bool UniscribeHelperTextRun::nextWinFontData( m_hfonts.append(simpleFontData->platformData().hfont()); m_scriptCaches.append(simpleFontData->platformData().scriptCache()); m_fontProperties.append(simpleFontData->platformData().scriptFontProperties()); - m_ascents.append(simpleFontData->ascent()); + m_ascents.append(simpleFontData->fontMetrics().ascent()); } *hfont = m_hfonts[m_fontIndex - 1]; diff --git a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp index 776b83f..41cd180 100644 --- a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp +++ b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp @@ -60,6 +60,7 @@ VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) , m_rgbaShaderMatrixLocation(0) , m_rgbaWidthScaleFactorLocation(0) , m_ccMatrixLocation(0) + , m_signAdjLocation(0) , m_yTextureLocation(0) , m_uTextureLocation(0) , m_vTextureLocation(0) @@ -96,14 +97,15 @@ VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) "uniform sampler2D u_texture; \n" "uniform sampler2D v_texture; \n" "uniform float alpha; \n" + "uniform float adj; \n" "uniform mat3 cc_matrix; \n" "void main() \n" "{ \n" " float y = texture2D(y_texture, v_texCoord).x; \n" - " float u = texture2D(u_texture, v_texCoord).r - .5; \n" - " float v = texture2D(v_texture, v_texCoord).r - .5; \n" + " float u = texture2D(u_texture, v_texCoord).x - adj; \n" + " float v = texture2D(v_texture, v_texCoord).x - adj; \n" " vec3 rgb = cc_matrix * vec3(y, u, v); \n" - " gl_FragColor = vec4(rgb.x, rgb.y, rgb.z, 1.0) * alpha; \n" + " gl_FragColor = vec4(rgb, float(1)) * alpha; \n" "} \n"; char rgbaFragmentShaderString[] = @@ -113,7 +115,7 @@ VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) "uniform float alpha; \n" "void main() \n" "{ \n" - " vec4 texColor = texture2D(rgba_texture, vec2(v_texCoord.x, 1.0 - v_texCoord.y)); \n" + " vec4 texColor = texture2D(rgba_texture, vec2(v_texCoord.x, float(1) - v_texCoord.y)); \n" " gl_FragColor = vec4(texColor.x, texColor.y, texColor.z, texColor.w) * alpha; \n" "} \n"; @@ -135,6 +137,7 @@ VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) m_uTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "u_texture"); m_vTextureLocation = m_context->getUniformLocation(m_yuvShaderProgram, "v_texture"); m_ccMatrixLocation = m_context->getUniformLocation(m_yuvShaderProgram, "cc_matrix"); + m_signAdjLocation = m_context->getUniformLocation(m_yuvShaderProgram, "adj"); m_yuvAlphaLocation = m_context->getUniformLocation(m_yuvShaderProgram, "alpha"); ASSERT(m_yuvShaderMatrixLocation != -1); @@ -143,6 +146,7 @@ VideoLayerChromium::SharedValues::SharedValues(GraphicsContext3D* context) ASSERT(m_uTextureLocation != -1); ASSERT(m_vTextureLocation != -1); ASSERT(m_ccMatrixLocation != -1); + ASSERT(m_signAdjLocation != -1); ASSERT(m_yuvAlphaLocation != -1); m_rgbaShaderMatrixLocation = m_context->getUniformLocation(m_rgbaShaderProgram, "matrix"); @@ -375,6 +379,12 @@ void VideoLayerChromium::drawYUV(const SharedValues* sv) GLC(context, context->uniform1i(sv->uTextureLocation(), 2)); GLC(context, context->uniform1i(sv->vTextureLocation(), 3)); + // This value of 0.5 maps to 128. It is used in the YUV to RGB conversion + // formula to turn unsigned u and v values to signed u and v values. + // This is loaded as a uniform because certain drivers have problems + // reading literal float values. + GLC(context, context->uniform1f(sv->signAdjLocation(), 0.5)); + GLC(context, context->uniformMatrix3fv(sv->ccMatrixLocation(), 0, const_cast<float*>(yuv2RGB), 1)); drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), diff --git a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h index 0992ab7..ac3bca9 100644 --- a/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h +++ b/Source/WebCore/platform/graphics/chromium/VideoLayerChromium.h @@ -70,6 +70,7 @@ public: int rgbaAlphaLocation() const { return m_rgbaAlphaLocation; } int rgbaTextureLocation() const { return m_rgbaTextureLocation; } int ccMatrixLocation() const { return m_ccMatrixLocation; } + int signAdjLocation() const { return m_signAdjLocation; } bool initialized() const { return m_initialized; } private: GraphicsContext3D* m_context; @@ -80,6 +81,7 @@ public: int m_rgbaShaderMatrixLocation; int m_rgbaWidthScaleFactorLocation; int m_ccMatrixLocation; + int m_signAdjLocation; int m_yTextureLocation; int m_uTextureLocation; int m_vTextureLocation; diff --git a/Source/WebCore/platform/graphics/cocoa/FontPlatformData.h b/Source/WebCore/platform/graphics/cocoa/FontPlatformData.h index 8cf08fb..ca38029 100644 --- a/Source/WebCore/platform/graphics/cocoa/FontPlatformData.h +++ b/Source/WebCore/platform/graphics/cocoa/FontPlatformData.h @@ -25,6 +25,7 @@ #define FontPlatformData_h #include "FontOrientation.h" +#include "FontWidthVariant.h" #include <wtf/text/StringImpl.h> #ifdef __OBJC__ @@ -59,11 +60,12 @@ inline CTFontRef toCTFontRef(NSFont *nsFont) { return reinterpret_cast<CTFontRef class FontPlatformData { public: - FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation = Horizontal) + FontPlatformData(float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation = Horizontal, FontWidthVariant widthVariant = RegularWidth) : m_syntheticBold(syntheticBold) , m_syntheticOblique(syntheticOblique) , m_orientation(orientation) , m_size(size) + , m_widthVariant(widthVariant) , m_font(0) #ifdef BUILDING_ON_TIGER , m_cgFont(0) @@ -72,13 +74,14 @@ class FontPlatformData { { } - FontPlatformData(NSFont *nsFont, float size, bool syntheticBold = false, bool syntheticOblique = false, FontOrientation = Horizontal); + FontPlatformData(NSFont*, float size, bool syntheticBold = false, bool syntheticOblique = false, FontOrientation = Horizontal, FontWidthVariant = RegularWidth); - FontPlatformData(CGFontRef cgFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation) + FontPlatformData(CGFontRef cgFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant) : m_syntheticBold(syntheticBold) , m_syntheticOblique(syntheticOblique) , m_orientation(orientation) , m_size(size) + , m_widthVariant(widthVariant) , m_font(0) , m_cgFont(cgFont) , m_isColorBitmapFont(false) @@ -96,17 +99,20 @@ class FontPlatformData { bool syntheticBold() const { return m_syntheticBold; } bool syntheticOblique() const { return m_syntheticOblique; } FontOrientation orientation() const { return m_orientation; } + FontWidthVariant widthVariant() const { return m_widthVariant; } bool m_syntheticBold; bool m_syntheticOblique; FontOrientation m_orientation; float m_size; + + FontWidthVariant m_widthVariant; unsigned hash() const { ASSERT(m_font != 0 || m_cgFont == 0); - uintptr_t hashCodes[2] = { (uintptr_t)m_font, m_orientation << 2 | m_syntheticBold << 1 | m_syntheticOblique }; + uintptr_t hashCodes[3] = { (uintptr_t)m_font, m_widthVariant, m_orientation << 2 | m_syntheticBold << 1 | m_syntheticOblique }; return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); } @@ -115,7 +121,7 @@ class FontPlatformData { bool operator==(const FontPlatformData& other) const { return m_font == other.m_font && m_syntheticBold == other.m_syntheticBold && m_syntheticOblique == other.m_syntheticOblique && - m_cgFont == other.m_cgFont && m_size == other.m_size && m_orientation == other.m_orientation; + m_cgFont == other.m_cgFont && m_size == other.m_size && m_orientation == other.m_orientation && m_widthVariant == other.m_widthVariant; } NSFont *font() const { return m_font; } diff --git a/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm b/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm index 8dacbe3..b40f698 100644 --- a/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm +++ b/Source/WebCore/platform/graphics/cocoa/FontPlatformDataCocoa.mm @@ -30,6 +30,9 @@ namespace WebCore { +// These CoreText Text Spacing feature selectors are not defined in CoreText. +enum TextSpacingCTFeatureSelector { TextSpacingProportional, TextSpacingFullWidth, TextSpacingHalfWidth, TextSpacingThirdWidth, TextSpacingQuarterWidth }; + #if PLATFORM(MAC) void FontPlatformData::loadFont(NSFont* nsFont, float, NSFont*& outNSFont, CGFontRef& cgFont) { @@ -42,10 +45,11 @@ void FontPlatformData::loadFont(NSFont* nsFont, float, NSFont*& outNSFont, CGFon } #endif // PLATFORM(MAC) -FontPlatformData::FontPlatformData(NSFont *nsFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation) +FontPlatformData::FontPlatformData(NSFont *nsFont, float size, bool syntheticBold, bool syntheticOblique, FontOrientation orientation, FontWidthVariant widthVariant) : m_syntheticBold(syntheticBold) , m_syntheticOblique(syntheticOblique) , m_size(size) + , m_widthVariant(widthVariant) , m_font(nsFont) #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) // FIXME: Chromium: The following code isn't correct for the Chromium port since the sandbox might @@ -79,6 +83,7 @@ FontPlatformData::FontPlatformData(const FontPlatformData& f) m_syntheticBold = f.m_syntheticBold; m_syntheticOblique = f.m_syntheticOblique; m_size = f.m_size; + m_widthVariant = f.m_widthVariant; m_cgFont = f.m_cgFont; m_isColorBitmapFont = f.m_isColorBitmapFont; m_orientation = f.m_orientation; @@ -99,6 +104,7 @@ const FontPlatformData& FontPlatformData::operator=(const FontPlatformData& f) m_syntheticBold = f.m_syntheticBold; m_syntheticOblique = f.m_syntheticOblique; m_size = f.m_size; + m_widthVariant = f.m_widthVariant; m_cgFont = f.m_cgFont; if (m_font == f.m_font) return *this; @@ -165,12 +171,48 @@ bool FontPlatformData::allowsLigatures() const return ![[m_font coveredCharacterSet] characterIsMember:'a']; } +inline int mapFontWidthVariantToCTFeatureSelector(FontWidthVariant variant) +{ + switch(variant) { + case RegularWidth: + return TextSpacingProportional; + + case HalfWidth: + return TextSpacingHalfWidth; + + case ThirdWidth: + return TextSpacingThirdWidth; + + case QuarterWidth: + return TextSpacingQuarterWidth; + } + + ASSERT_NOT_REACHED(); + return TextSpacingProportional; +} + CTFontRef FontPlatformData::ctFont() const { - if (m_font) - return toCTFontRef(m_font); - if (!m_CTFont) - m_CTFont.adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), m_size, 0, 0)); + if (m_widthVariant == RegularWidth) { + if (m_font) + return toCTFontRef(m_font); + if (!m_CTFont) + m_CTFont.adoptCF(CTFontCreateWithGraphicsFont(m_cgFont.get(), m_size, 0, 0)); + return m_CTFont.get(); + } + + if (!m_CTFont) { + int featureTypeValue = kTextSpacingType; + int featureSelectorValue = mapFontWidthVariantToCTFeatureSelector(m_widthVariant); + RetainPtr<CTFontRef> sourceFont(AdoptCF, CTFontCreateWithGraphicsFont(m_cgFont.get(), m_size, 0, 0)); + RetainPtr<CTFontDescriptorRef> sourceDescriptor(AdoptCF, CTFontCopyFontDescriptor(sourceFont.get())); + RetainPtr<CFNumberRef> featureType(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureTypeValue)); + RetainPtr<CFNumberRef> featureSelector(AdoptCF, CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &featureSelectorValue)); + RetainPtr<CTFontDescriptorRef> newDescriptor(AdoptCF, CTFontDescriptorCreateCopyWithFeature(sourceDescriptor.get(), featureType.get(), featureSelector.get())); + RetainPtr<CTFontRef> newFont(AdoptCF, CTFontCreateWithFontDescriptor(newDescriptor.get(), m_size, 0)); + + m_CTFont = newFont.get() ? newFont : sourceFont; + } return m_CTFont.get(); } diff --git a/Source/WebCore/platform/graphics/efl/FontEfl.cpp b/Source/WebCore/platform/graphics/efl/FontEfl.cpp index d3ca183..83cc7ff 100644 --- a/Source/WebCore/platform/graphics/efl/FontEfl.cpp +++ b/Source/WebCore/platform/graphics/efl/FontEfl.cpp @@ -50,6 +50,11 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + float Font::floatWidthForComplexText(const TextRun&, HashSet<const SimpleFontData*>*, GlyphOverflow*) const { notImplemented(); diff --git a/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp b/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp new file mode 100644 index 0000000..4c3b49b --- /dev/null +++ b/Source/WebCore/platform/graphics/filters/DistantLightSource.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> + * Copyright (C) 2005 Eric Seidel <eric@webkit.org> + * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org> + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> + * + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(FILTERS) +#include "DistantLightSource.h" + +#include "RenderTreeAsText.h" + +namespace WebCore { + +void DistantLightSource::initPaintingData(PaintingData& paintingData) +{ + float azimuth = deg2rad(m_azimuth); + float elevation = deg2rad(m_elevation); + paintingData.lightVector.setX(cosf(azimuth) * cosf(elevation)); + paintingData.lightVector.setY(sinf(azimuth) * cosf(elevation)); + paintingData.lightVector.setZ(sinf(elevation)); + paintingData.lightVectorLength = 1; +} + +void DistantLightSource::updatePaintingData(PaintingData&, int, int, float) +{ +} + +bool DistantLightSource::setAzimuth(float azimuth) +{ + if (m_azimuth == azimuth) + return false; + m_azimuth = azimuth; + return true; +} + +bool DistantLightSource::setElevation(float elevation) +{ + if (m_elevation == elevation) + return false; + m_elevation = elevation; + return true; +} + +TextStream& DistantLightSource::externalRepresentation(TextStream& ts) const +{ + ts << "[type=DISTANT-LIGHT] "; + ts << "[azimuth=\"" << azimuth() << "\"]"; + ts << "[elevation=\"" << elevation() << "\"]"; + return ts; +} + +} // namespace WebCore + +#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/DistantLightSource.h b/Source/WebCore/platform/graphics/filters/DistantLightSource.h index d5d474f..1e19c62 100644 --- a/Source/WebCore/platform/graphics/filters/DistantLightSource.h +++ b/Source/WebCore/platform/graphics/filters/DistantLightSource.h @@ -36,7 +36,9 @@ public: } float azimuth() const { return m_azimuth; } + bool setAzimuth(float); float elevation() const { return m_elevation; } + bool setElevation(float); virtual void initPaintingData(PaintingData&); virtual void updatePaintingData(PaintingData&, int x, int y, float z); diff --git a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp index a8a825a..5f9d049 100644 --- a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp +++ b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.cpp @@ -52,9 +52,12 @@ Color FEDiffuseLighting::lightingColor() const return m_lightingColor; } -void FEDiffuseLighting::setLightingColor(const Color& lightingColor) +bool FEDiffuseLighting::setLightingColor(const Color& lightingColor) { + if (m_lightingColor == lightingColor) + return false; m_lightingColor = lightingColor; + return true; } float FEDiffuseLighting::surfaceScale() const @@ -62,9 +65,12 @@ float FEDiffuseLighting::surfaceScale() const return m_surfaceScale; } -void FEDiffuseLighting::setSurfaceScale(float surfaceScale) +bool FEDiffuseLighting::setSurfaceScale(float surfaceScale) { + if (m_surfaceScale == surfaceScale) + return false; m_surfaceScale = surfaceScale; + return true; } float FEDiffuseLighting::diffuseConstant() const @@ -72,9 +78,12 @@ float FEDiffuseLighting::diffuseConstant() const return m_diffuseConstant; } -void FEDiffuseLighting::setDiffuseConstant(float diffuseConstant) +bool FEDiffuseLighting::setDiffuseConstant(float diffuseConstant) { + if (m_diffuseConstant == diffuseConstant) + return false; m_diffuseConstant = diffuseConstant; + return true; } float FEDiffuseLighting::kernelUnitLengthX() const @@ -82,9 +91,12 @@ float FEDiffuseLighting::kernelUnitLengthX() const return m_kernelUnitLengthX; } -void FEDiffuseLighting::setKernelUnitLengthX(float kernelUnitLengthX) +bool FEDiffuseLighting::setKernelUnitLengthX(float kernelUnitLengthX) { + if (m_kernelUnitLengthX == kernelUnitLengthX) + return false; m_kernelUnitLengthX = kernelUnitLengthX; + return true; } float FEDiffuseLighting::kernelUnitLengthY() const @@ -92,9 +104,12 @@ float FEDiffuseLighting::kernelUnitLengthY() const return m_kernelUnitLengthY; } -void FEDiffuseLighting::setKernelUnitLengthY(float kernelUnitLengthY) +bool FEDiffuseLighting::setKernelUnitLengthY(float kernelUnitLengthY) { + if (m_kernelUnitLengthY == kernelUnitLengthY) + return false; m_kernelUnitLengthY = kernelUnitLengthY; + return true; } const LightSource* FEDiffuseLighting::lightSource() const diff --git a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h index b58b47a..5f20651 100644 --- a/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h +++ b/Source/WebCore/platform/graphics/filters/FEDiffuseLighting.h @@ -36,19 +36,19 @@ public: virtual ~FEDiffuseLighting(); Color lightingColor() const; - void setLightingColor(const Color&); + bool setLightingColor(const Color&); float surfaceScale() const; - void setSurfaceScale(float); + bool setSurfaceScale(float); float diffuseConstant() const; - void setDiffuseConstant(float); + bool setDiffuseConstant(float); float kernelUnitLengthX() const; - void setKernelUnitLengthX(float); + bool setKernelUnitLengthX(float); float kernelUnitLengthY() const; - void setKernelUnitLengthY(float); + bool setKernelUnitLengthY(float); const LightSource* lightSource() const; void setLightSource(PassRefPtr<LightSource>); diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp index 85154b5..f07d00c 100644 --- a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp +++ b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp @@ -77,6 +77,16 @@ FilterEffect* FilterEffect::inputEffect(unsigned number) const return m_inputEffects.at(number).get(); } +void FilterEffect::clearResult() +{ + if (m_imageBufferResult) + m_imageBufferResult.clear(); + if (m_unmultipliedImageResult) + m_unmultipliedImageResult.clear(); + if (m_premultipliedImageResult) + m_premultipliedImageResult.clear(); +} + ImageBuffer* FilterEffect::asImageBuffer() { if (!hasResult()) @@ -109,7 +119,7 @@ PassRefPtr<ByteArray> FilterEffect::asPremultipliedImage(const IntRect& rect) inline void FilterEffect::copyImageBytes(ByteArray* source, ByteArray* destination, const IntRect& rect) { // Copy the necessary lines. - if (rect.x() < 0 || rect.y() < 0 || rect.bottom() > m_absolutePaintRect.width() || rect.bottom() > m_absolutePaintRect.height()) + if (rect.x() < 0 || rect.y() < 0 || rect.maxY() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height()) memset(destination->data(), 0, destination->length()); int xOrigin = rect.x(); @@ -118,7 +128,7 @@ inline void FilterEffect::copyImageBytes(ByteArray* source, ByteArray* destinati xDest = -xOrigin; xOrigin = 0; } - int xEnd = rect.right(); + int xEnd = rect.maxX(); if (xEnd > m_absolutePaintRect.width()) xEnd = m_absolutePaintRect.width(); @@ -128,7 +138,7 @@ inline void FilterEffect::copyImageBytes(ByteArray* source, ByteArray* destinati yDest = -yOrigin; yOrigin = 0; } - int yEnd = rect.bottom(); + int yEnd = rect.maxY(); if (yEnd > m_absolutePaintRect.height()) yEnd = m_absolutePaintRect.height(); diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.h b/Source/WebCore/platform/graphics/filters/FilterEffect.h index 062dd1b..2de8ac5 100644 --- a/Source/WebCore/platform/graphics/filters/FilterEffect.h +++ b/Source/WebCore/platform/graphics/filters/FilterEffect.h @@ -53,6 +53,7 @@ public: virtual ~FilterEffect(); bool hasResult() const { return m_imageBufferResult || m_unmultipliedImageResult || m_premultipliedImageResult; } + void clearResult(); ImageBuffer* asImageBuffer(); PassRefPtr<ByteArray> asUnmultipliedImage(const IntRect&); PassRefPtr<ByteArray> asPremultipliedImage(const IntRect&); diff --git a/Source/WebCore/platform/graphics/filters/LightSource.cpp b/Source/WebCore/platform/graphics/filters/LightSource.cpp index de0691e..cf262e8 100644 --- a/Source/WebCore/platform/graphics/filters/LightSource.cpp +++ b/Source/WebCore/platform/graphics/filters/LightSource.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org> + * Copyright (C) 2011 Renata Hodovan <reni@webkit.org>, University of Szeged. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -33,136 +34,80 @@ namespace WebCore { -void PointLightSource::initPaintingData(PaintingData&) +bool LightSource::setAzimuth(float azimuth) { + if (m_type == LS_DISTANT) + return static_cast<DistantLightSource*>(this)->setAzimuth(azimuth); + return false; } -void PointLightSource::updatePaintingData(PaintingData& paintingData, int x, int y, float z) +bool LightSource::setElevation(float elevation) { - paintingData.lightVector.setX(m_position.x() - x); - paintingData.lightVector.setY(m_position.y() - y); - paintingData.lightVector.setZ(m_position.z() - z); - paintingData.lightVectorLength = paintingData.lightVector.length(); + if (m_type == LS_DISTANT) + return static_cast<DistantLightSource*>(this)->setElevation(elevation); + return false; } -// spot-light edge darkening depends on an absolute treshold -// according to the SVG 1.1 SE light regression tests -static const float antiAliasTreshold = 0.016f; - -void SpotLightSource::initPaintingData(PaintingData& paintingData) +bool LightSource::setX(float x) { - paintingData.privateColorVector = paintingData.colorVector; - paintingData.directionVector.setX(m_direction.x() - m_position.x()); - paintingData.directionVector.setY(m_direction.y() - m_position.y()); - paintingData.directionVector.setZ(m_direction.z() - m_position.z()); - paintingData.directionVector.normalize(); - - if (!m_limitingConeAngle) { - paintingData.coneCutOffLimit = 0.0f; - paintingData.coneFullLight = -antiAliasTreshold; - } else { - float limitingConeAngle = m_limitingConeAngle; - if (limitingConeAngle < 0.0f) - limitingConeAngle = -limitingConeAngle; - if (limitingConeAngle > 90.0f) - limitingConeAngle = 90.0f; - paintingData.coneCutOffLimit = cosf(deg2rad(180.0f - limitingConeAngle)); - paintingData.coneFullLight = paintingData.coneCutOffLimit - antiAliasTreshold; - } - - // Optimization for common specularExponent values - if (!m_specularExponent) - paintingData.specularExponent = 0; - else if (m_specularExponent == 1.0f) - paintingData.specularExponent = 1; - else // It is neither 0.0f nor 1.0f - paintingData.specularExponent = 2; + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setX(x); + if (m_type == LS_POINT) + return static_cast<PointLightSource*>(this)->setX(x); + return false; } -void SpotLightSource::updatePaintingData(PaintingData& paintingData, int x, int y, float z) +bool LightSource::setY(float y) { - paintingData.lightVector.setX(m_position.x() - x); - paintingData.lightVector.setY(m_position.y() - y); - paintingData.lightVector.setZ(m_position.z() - z); - paintingData.lightVectorLength = paintingData.lightVector.length(); - - float cosineOfAngle = (paintingData.lightVector * paintingData.directionVector) / paintingData.lightVectorLength; - if (cosineOfAngle > paintingData.coneCutOffLimit) { - // No light is produced, scanlines are not updated - paintingData.colorVector.setX(0.0f); - paintingData.colorVector.setY(0.0f); - paintingData.colorVector.setZ(0.0f); - return; - } - - // Set the color of the pixel - float lightStrength; - switch (paintingData.specularExponent) { - case 0: - lightStrength = 1.0f; // -cosineOfAngle ^ 0 == 1 - break; - case 1: - lightStrength = -cosineOfAngle; // -cosineOfAngle ^ 1 == -cosineOfAngle - break; - default: - lightStrength = powf(-cosineOfAngle, m_specularExponent); - break; - } - - if (cosineOfAngle > paintingData.coneFullLight) - lightStrength *= (paintingData.coneCutOffLimit - cosineOfAngle) / (paintingData.coneCutOffLimit - paintingData.coneFullLight); - - if (lightStrength > 1.0f) - lightStrength = 1.0f; - - paintingData.colorVector.setX(paintingData.privateColorVector.x() * lightStrength); - paintingData.colorVector.setY(paintingData.privateColorVector.y() * lightStrength); - paintingData.colorVector.setZ(paintingData.privateColorVector.z() * lightStrength); + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setY(y); + if (m_type == LS_POINT) + return static_cast<PointLightSource*>(this)->setY(y); + return false; } -void DistantLightSource::initPaintingData(PaintingData& paintingData) +bool LightSource::setZ(float z) { - float azimuth = deg2rad(m_azimuth); - float elevation = deg2rad(m_elevation); - paintingData.lightVector.setX(cosf(azimuth) * cosf(elevation)); - paintingData.lightVector.setY(sinf(azimuth) * cosf(elevation)); - paintingData.lightVector.setZ(sinf(elevation)); - paintingData.lightVectorLength = 1; + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setZ(z); + if (m_type == LS_POINT) + return static_cast<PointLightSource*>(this)->setZ(z); + return false; } -void DistantLightSource::updatePaintingData(PaintingData&, int, int, float) +bool LightSource::setPointsAtX(float pointsAtX) { + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setPointsAtX(pointsAtX); + return false; } -static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p) +bool LightSource::setPointsAtY(float pointsAtY) { - ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z(); - return ts; + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setPointsAtY(pointsAtY); + return false; } -TextStream& PointLightSource::externalRepresentation(TextStream& ts) const +bool LightSource::setPointsAtZ(float pointsAtZ) { - ts << "[type=POINT-LIGHT] "; - ts << "[position=\"" << position() << "\"]"; - return ts; + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setPointsAtZ(pointsAtZ); + return false; } -TextStream& SpotLightSource::externalRepresentation(TextStream& ts) const +bool LightSource::setSpecularExponent(float specularExponent) { - ts << "[type=SPOT-LIGHT] "; - ts << "[position=\"" << position() << "\"]"; - ts << "[direction=\"" << direction() << "\"]"; - ts << "[specularExponent=\"" << specularExponent() << "\"]"; - ts << "[limitingConeAngle=\"" << limitingConeAngle() << "\"]"; - return ts; + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setSpecularExponent(specularExponent); + return false; } -TextStream& DistantLightSource::externalRepresentation(TextStream& ts) const +bool LightSource::setLimitingConeAngle(float limitingConeAngle) { - ts << "[type=DISTANT-LIGHT] "; - ts << "[azimuth=\"" << azimuth() << "\"]"; - ts << "[elevation=\"" << elevation() << "\"]"; - return ts; + if (m_type == LS_SPOT) + return static_cast<SpotLightSource*>(this)->setLimitingConeAngle(limitingConeAngle); + return false; } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/filters/LightSource.h b/Source/WebCore/platform/graphics/filters/LightSource.h index 013e910..24c319a 100644 --- a/Source/WebCore/platform/graphics/filters/LightSource.h +++ b/Source/WebCore/platform/graphics/filters/LightSource.h @@ -74,6 +74,17 @@ public: // specified "surfaceScale" constant, which type is <number> in the SVG standard virtual void updatePaintingData(PaintingData&, int x, int y, float z) = 0; + bool setAzimuth(float); + bool setElevation(float); + bool setX(float); + bool setY(float); + bool setZ(float); + bool setPointsAtX(float); + bool setPointsAtY(float); + bool setPointsAtZ(float); + bool setSpecularExponent(float); + bool setLimitingConeAngle(float); + private: LightType m_type; }; diff --git a/Source/WebCore/platform/graphics/filters/PointLightSource.cpp b/Source/WebCore/platform/graphics/filters/PointLightSource.cpp new file mode 100644 index 0000000..207ed8e --- /dev/null +++ b/Source/WebCore/platform/graphics/filters/PointLightSource.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> + * Copyright (C) 2005 Eric Seidel <eric@webkit.org> + * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org> + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> + * + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(FILTERS) +#include "PointLightSource.h" + +#include "TextStream.h" + +namespace WebCore { + +void PointLightSource::initPaintingData(PaintingData&) +{ +} + +void PointLightSource::updatePaintingData(PaintingData& paintingData, int x, int y, float z) +{ + paintingData.lightVector.setX(m_position.x() - x); + paintingData.lightVector.setY(m_position.y() - y); + paintingData.lightVector.setZ(m_position.z() - z); + paintingData.lightVectorLength = paintingData.lightVector.length(); +} + +bool PointLightSource::setX(float x) +{ + if (m_position.x() == x) + return false; + m_position.setX(x); + return true; +} + +bool PointLightSource::setY(float y) +{ + if (m_position.y() == y) + return false; + m_position.setY(y); + return true; +} + +bool PointLightSource::setZ(float z) +{ + if (m_position.z() == z) + return false; + m_position.setZ(z); + return true; +} + +static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p) +{ + ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z(); + return ts; +} + +TextStream& PointLightSource::externalRepresentation(TextStream& ts) const +{ + ts << "[type=POINT-LIGHT] "; + ts << "[position=\"" << position() << "\"]"; + return ts; +} + +}; // namespace WebCore + +#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/PointLightSource.h b/Source/WebCore/platform/graphics/filters/PointLightSource.h index 163c829..a93bf2c 100644 --- a/Source/WebCore/platform/graphics/filters/PointLightSource.h +++ b/Source/WebCore/platform/graphics/filters/PointLightSource.h @@ -36,6 +36,9 @@ public: } const FloatPoint3D& position() const { return m_position; } + bool setX(float); + bool setY(float); + bool setZ(float); virtual void initPaintingData(PaintingData&); virtual void updatePaintingData(PaintingData&, int x, int y, float z); diff --git a/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp b/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp new file mode 100644 index 0000000..648fcae --- /dev/null +++ b/Source/WebCore/platform/graphics/filters/SpotLightSource.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com> + * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> + * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> + * Copyright (C) 2005 Eric Seidel <eric@webkit.org> + * Copyright (C) 2010 Zoltan Herczeg <zherczeg@webkit.org> + * Copyright (C) 2011 University of Szeged + * Copyright (C) 2011 Renata Hodovan <reni@webkit.org> + * + * 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 UNIVERSITY OF SZEGED ``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 UNIVERSITY OF SZEGED OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(FILTERS) +#include "SpotLightSource.h" + +#include "TextStream.h" + +namespace WebCore { + +// spot-light edge darkening depends on an absolute treshold +// according to the SVG 1.1 SE light regression tests +static const float antiAliasTreshold = 0.016f; + +void SpotLightSource::initPaintingData(PaintingData& paintingData) +{ + paintingData.privateColorVector = paintingData.colorVector; + paintingData.directionVector.setX(m_direction.x() - m_position.x()); + paintingData.directionVector.setY(m_direction.y() - m_position.y()); + paintingData.directionVector.setZ(m_direction.z() - m_position.z()); + paintingData.directionVector.normalize(); + + if (!m_limitingConeAngle) { + paintingData.coneCutOffLimit = 0.0f; + paintingData.coneFullLight = -antiAliasTreshold; + } else { + float limitingConeAngle = m_limitingConeAngle; + if (limitingConeAngle < 0.0f) + limitingConeAngle = -limitingConeAngle; + if (limitingConeAngle > 90.0f) + limitingConeAngle = 90.0f; + paintingData.coneCutOffLimit = cosf(deg2rad(180.0f - limitingConeAngle)); + paintingData.coneFullLight = paintingData.coneCutOffLimit - antiAliasTreshold; + } + + // Optimization for common specularExponent values + if (!m_specularExponent) + paintingData.specularExponent = 0; + else if (m_specularExponent == 1.0f) + paintingData.specularExponent = 1; + else // It is neither 0.0f nor 1.0f + paintingData.specularExponent = 2; +} + +void SpotLightSource::updatePaintingData(PaintingData& paintingData, int x, int y, float z) +{ + paintingData.lightVector.setX(m_position.x() - x); + paintingData.lightVector.setY(m_position.y() - y); + paintingData.lightVector.setZ(m_position.z() - z); + paintingData.lightVectorLength = paintingData.lightVector.length(); + + float cosineOfAngle = (paintingData.lightVector * paintingData.directionVector) / paintingData.lightVectorLength; + if (cosineOfAngle > paintingData.coneCutOffLimit) { + // No light is produced, scanlines are not updated + paintingData.colorVector.setX(0.0f); + paintingData.colorVector.setY(0.0f); + paintingData.colorVector.setZ(0.0f); + return; + } + + // Set the color of the pixel + float lightStrength; + switch (paintingData.specularExponent) { + case 0: + lightStrength = 1.0f; // -cosineOfAngle ^ 0 == 1 + break; + case 1: + lightStrength = -cosineOfAngle; // -cosineOfAngle ^ 1 == -cosineOfAngle + break; + default: + lightStrength = powf(-cosineOfAngle, m_specularExponent); + break; + } + + if (cosineOfAngle > paintingData.coneFullLight) + lightStrength *= (paintingData.coneCutOffLimit - cosineOfAngle) / (paintingData.coneCutOffLimit - paintingData.coneFullLight); + + if (lightStrength > 1.0f) + lightStrength = 1.0f; + + paintingData.colorVector.setX(paintingData.privateColorVector.x() * lightStrength); + paintingData.colorVector.setY(paintingData.privateColorVector.y() * lightStrength); + paintingData.colorVector.setZ(paintingData.privateColorVector.z() * lightStrength); +} + +bool SpotLightSource::setX(float x) +{ + if (m_position.x() == x) + return false; + m_position.setX(x); + return true; +} + +bool SpotLightSource::setY(float y) +{ + if (m_position.y() == y) + return false; + m_position.setY(y); + return true; +} + +bool SpotLightSource::setZ(float z) +{ + if (m_position.z() == z) + return false; + m_position.setZ(z); + return true; +} + +bool SpotLightSource::setPointsAtX(float pointsAtX) +{ + if (m_direction.x() == pointsAtX) + return false; + m_direction.setX(pointsAtX); + return true; +} + +bool SpotLightSource::setPointsAtY(float pointsAtY) +{ + if (m_direction.y() == pointsAtY) + return false; + m_direction.setY(pointsAtY); + return true; +} + +bool SpotLightSource::setPointsAtZ(float pointsAtZ) +{ + if (m_direction.z() == pointsAtZ) + return false; + m_direction.setZ(pointsAtZ); + return true; +} + +bool SpotLightSource::setSpecularExponent(float specularExponent) +{ + if (m_specularExponent == specularExponent) + return false; + m_specularExponent = specularExponent; + return true; +} + +bool SpotLightSource::setLimitingConeAngle(float limitingConeAngle) +{ + if (m_limitingConeAngle == limitingConeAngle) + return false; + m_limitingConeAngle = limitingConeAngle; + return true; +} + +static TextStream& operator<<(TextStream& ts, const FloatPoint3D& p) +{ + ts << "x=" << p.x() << " y=" << p.y() << " z=" << p.z(); + return ts; +} + +TextStream& SpotLightSource::externalRepresentation(TextStream& ts) const +{ + ts << "[type=SPOT-LIGHT] "; + ts << "[position=\"" << position() << "\"]"; + ts << "[direction=\"" << direction() << "\"]"; + ts << "[specularExponent=\"" << specularExponent() << "\"]"; + ts << "[limitingConeAngle=\"" << limitingConeAngle() << "\"]"; + return ts; +} + +}; // namespace WebCore + +#endif // ENABLE(FILTERS) diff --git a/Source/WebCore/platform/graphics/filters/SpotLightSource.h b/Source/WebCore/platform/graphics/filters/SpotLightSource.h index cd6a614..b4f1b61 100644 --- a/Source/WebCore/platform/graphics/filters/SpotLightSource.h +++ b/Source/WebCore/platform/graphics/filters/SpotLightSource.h @@ -37,10 +37,18 @@ public: } const FloatPoint3D& position() const { return m_position; } + bool setX(float); + bool setY(float); + bool setZ(float); const FloatPoint3D& direction() const { return m_direction; } + bool setPointsAtX(float); + bool setPointsAtY(float); + bool setPointsAtZ(float); float specularExponent() const { return m_specularExponent; } + bool setSpecularExponent(float); float limitingConeAngle() const { return m_limitingConeAngle; } + bool setLimitingConeAngle(float); virtual void initPaintingData(PaintingData&); virtual void updatePaintingData(PaintingData&, int x, int y, float z); diff --git a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp index c547224..841c8a3 100644 --- a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp @@ -59,7 +59,7 @@ FontCustomPlatformData::~FontCustomPlatformData() cairo_font_face_destroy(m_fontFace); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode) { return FontPlatformData(m_fontFace, size, bold, italic); } diff --git a/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp index 97fd81a..6290eeb 100644 --- a/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/SimpleFontDataFreeType.cpp @@ -50,21 +50,28 @@ void SimpleFontData::platformInit() cairo_font_extents_t font_extents; cairo_text_extents_t text_extents; cairo_scaled_font_extents(m_platformData.scaledFont(), &font_extents); - m_ascent = static_cast<int>(lroundf(font_extents.ascent)); - m_descent = static_cast<int>(lroundf(font_extents.descent)); - m_lineSpacing = static_cast<int>(lroundf(font_extents.height)); + + m_fontMetrics.setAscent(font_extents.ascent); + m_fontMetrics.setDescent(font_extents.descent); + // 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; + float lineSpacing = font_extents.height; + if (lineSpacing < font_extents.ascent + font_extents.descent) + lineSpacing = font_extents.ascent + font_extents.descent; + + m_fontMetrics.setLineSpacing(lroundf(lineSpacing)); + m_fontMetrics.setLineGap(lineSpacing - font_extents.ascent - font_extents.descent); + cairo_scaled_font_text_extents(m_platformData.scaledFont(), "x", &text_extents); - m_xHeight = text_extents.height; + m_fontMetrics.setXHeight(text_extents.height); + cairo_scaled_font_text_extents(m_platformData.scaledFont(), " ", &text_extents); m_spaceWidth = static_cast<float>(text_extents.x_advance); - m_lineGap = m_lineSpacing - m_ascent - m_descent; + m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; } diff --git a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp b/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp index dae83a2..2a83fcf 100644 --- a/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp +++ b/Source/WebCore/platform/graphics/gpu/DrawingBuffer.cpp @@ -30,7 +30,7 @@ #include "config.h" -#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(3D_CANVAS) +#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(WEBGL) #include "DrawingBuffer.h" @@ -41,10 +41,11 @@ namespace WebCore { PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size) { Extensions3D* extensions = context->getExtensions(); - bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit") && extensions->supports("GL_ANGLE_framebuffer_multisample"); + bool multisampleSupported = extensions->supports("GL_ANGLE_framebuffer_blit") && extensions->supports("GL_ANGLE_framebuffer_multisample") && extensions->supports("GL_OES_rgb8_rgba8"); if (multisampleSupported) { extensions->ensureEnabled("GL_ANGLE_framebuffer_blit"); extensions->ensureEnabled("GL_ANGLE_framebuffer_multisample"); + extensions->ensureEnabled("GL_OES_rgb8_rgba8"); } bool packedDepthStencilSupported = extensions->supports("GL_OES_packed_depth_stencil"); if (packedDepthStencilSupported) @@ -145,8 +146,6 @@ void DrawingBuffer::resizeDepthStencil(int sampleCount) void DrawingBuffer::reset(const IntSize& newSize) { - if (m_size == newSize) - return; m_size = newSize; if (!m_context) @@ -155,13 +154,15 @@ void DrawingBuffer::reset(const IntSize& newSize) m_context->makeContextCurrent(); const GraphicsContext3D::Attributes& attributes = m_context->getContextAttributes(); - unsigned long internalColorFormat, colorFormat; + unsigned long internalColorFormat, colorFormat, internalRenderbufferFormat; if (attributes.alpha) { internalColorFormat = GraphicsContext3D::RGBA; colorFormat = GraphicsContext3D::RGBA; + internalRenderbufferFormat = Extensions3D::RGBA8_OES; } else { internalColorFormat = GraphicsContext3D::RGB; colorFormat = GraphicsContext3D::RGB; + internalRenderbufferFormat = Extensions3D::RGB8_OES; } @@ -175,7 +176,7 @@ void DrawingBuffer::reset(const IntSize& newSize) m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_multisampleFBO); m_context->bindRenderbuffer(GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer); - m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalColorFormat, m_size.width(), m_size.height()); + m_context->getExtensions()->renderbufferStorageMultisample(GraphicsContext3D::RENDERBUFFER, sampleCount, internalRenderbufferFormat, m_size.width(), m_size.height()); m_context->framebufferRenderbuffer(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::RENDERBUFFER, m_multisampleColorBuffer); resizeDepthStencil(sampleCount); if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { @@ -192,7 +193,8 @@ void DrawingBuffer::reset(const IntSize& newSize) m_context->texImage2DResourceSafe(GraphicsContext3D::TEXTURE_2D, 0, internalColorFormat, m_size.width(), m_size.height(), 0, colorFormat, GraphicsContext3D::UNSIGNED_BYTE); m_context->framebufferTexture2D(GraphicsContext3D::FRAMEBUFFER, GraphicsContext3D::COLOR_ATTACHMENT0, GraphicsContext3D::TEXTURE_2D, m_colorBuffer, 0); m_context->bindTexture(GraphicsContext3D::TEXTURE_2D, 0); - resizeDepthStencil(0); + if (!multisample()) + resizeDepthStencil(0); if (m_context->checkFramebufferStatus(GraphicsContext3D::FRAMEBUFFER) != GraphicsContext3D::FRAMEBUFFER_COMPLETE) { // Cleanup clear(); diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnPathCache.cpp b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathCache.cpp new file mode 100644 index 0000000..35f15e5 --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathCache.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2011 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 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 "LoopBlinnPathCache.h" + +namespace WebCore { + +LoopBlinnPathCache::LoopBlinnPathCache() +{ +} + +LoopBlinnPathCache::~LoopBlinnPathCache() +{ +} + +void LoopBlinnPathCache::addVertex(float x, float y, + float k, float l, float m) +{ + m_vertices.append(x); + m_vertices.append(y); + m_texcoords.append(k); + m_texcoords.append(l); + m_texcoords.append(m); +} + +void LoopBlinnPathCache::clear() +{ + m_vertices.clear(); + m_texcoords.clear(); + m_interiorVertices.clear(); +#ifdef LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + m_interiorEdgeVertices.clear(); +#endif // LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES +} + +void LoopBlinnPathCache::addInteriorVertex(float x, float y) +{ + m_interiorVertices.append(x); + m_interiorVertices.append(y); +} + +#ifdef LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES +unsigned LoopBlinnPathCache::numberOfInteriorEdgeVertices() const +{ + return m_interiorEdgeVertices.size() / 2; +} + +const float* LoopBlinnPathCache::interiorEdgeVertices() const +{ + if (!numberOfInteriorEdgeVertices()) + return 0; + return m_interiorEdgeVertices.data(); +} + +void LoopBlinnPathCache::addInteriorEdgeVertex(float x, float y) +{ + m_interiorEdgeVertices.append(x); + m_interiorEdgeVertices.append(y); +} +#endif // LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnPathCache.h b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathCache.h new file mode 100644 index 0000000..d21d246 --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathCache.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2011 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 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 LoopBlinnPathCache_h +#define LoopBlinnPathCache_h + +#include <wtf/Noncopyable.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// A cache of the processed triangle mesh for a given path. Because these +// might be expensive to allocate (using malloc/free internally), it is +// recommended to try to reuse them when possible. + +// Uncomment the following to obtain debugging information for the edges +// facing the interior region of the mesh. +// #define LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + +class LoopBlinnPathCache { + WTF_MAKE_NONCOPYABLE(LoopBlinnPathCache); +public: + LoopBlinnPathCache(); + ~LoopBlinnPathCache(); + + unsigned numberOfVertices() const { return m_vertices.size() / 2; } + + // Get the base pointer to the vertex information. There are two + // coordinates per vertex. This pointer is valid until the cache is + // cleared or another vertex is added. Returns 0 if there are no + // vertices in the mesh. + const float* vertices() const + { + if (!numberOfVertices()) + return 0; + return m_vertices.data(); + } + + // Get the base pointer to the texture coordinate information. There + // are three coordinates per vertex. This pointer is valid until the + // cache is cleared or another vertex is added. Returns 0 if + // there are no vertices in the mesh. + const float* texcoords() const + { + if (!numberOfVertices()) + return 0; + return m_texcoords.data(); + } + + // Adds a vertex's information to the cache. The first two arguments + // are the x and y coordinates of the vertex on the plane; the last + // three arguments are the cubic texture coordinates associated with + // this vertex. + void addVertex(float x, float y, + float /*k*/, float /*l*/, float /*m*/); + + unsigned numberOfInteriorVertices() const { return m_interiorVertices.size() / 2; } + + // Base pointer to the interior vertices; two coordinates per + // vertex, which can be drawn as GL_TRIANGLES. Returns 0 if there + // are no interior vertices in the mesh. + const float* interiorVertices() const + { + if (!numberOfInteriorVertices()) + return 0; + return m_interiorVertices.data(); + } + + void addInteriorVertex(float x, float y); + + // Clears all of the stored vertex information in this cache. + void clear(); + +#ifdef LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + // The number of interior edge vertices + unsigned numberOfInteriorEdgeVertices() const; + // Base pointer to the interior vertices; two coordinates per + // vertex, which can be drawn as GL_LINES. Returns 0 if there are + // no interior edge vertices in the mesh. + const float* interiorEdgeVertices() const; + void addInteriorEdgeVertex(float x, float y); +#endif // LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + +private: + // The two-dimensional vertices of the triangle mesh. + Vector<float> m_vertices; + + // The three-dimensional cubic texture coordinates. + Vector<float> m_texcoords; + + Vector<float> m_interiorVertices; + +#ifdef LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + // The following is only for debugging + Vector<float> m_interiorEdgeVertices; +#endif // LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES +}; + +} // namespace WebCore + +#endif // LoopBlinnPathCache_h diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.cpp b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.cpp new file mode 100644 index 0000000..e84ddbf --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.cpp @@ -0,0 +1,1228 @@ +/* + * Copyright (C) 2011 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 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 "LoopBlinnPathProcessor.h" + +#include "FloatPoint.h" +#include "FloatRect.h" +#include "LoopBlinnClassifier.h" +#include "LoopBlinnConstants.h" +#include "LoopBlinnLocalTriangulator.h" +#include "LoopBlinnMathUtils.h" +#include "LoopBlinnPathCache.h" +#include "LoopBlinnTextureCoords.h" +#include "PODArena.h" +#include "PODIntervalTree.h" +#include "Path.h" +#include "internal_glu.h" +#include <algorithm> +#include <wtf/Assertions.h> +#include <wtf/FastMalloc.h> + +#if PLATFORM(SKIA) +#include "SkGeometry.h" +#include "SkPath.h" +#include "SkScalar.h" +#else +// Must port to your platform. +#endif + +namespace WebCore { + +using LoopBlinnMathUtils::XRay; +using LoopBlinnMathUtils::chopCubicAt; +using LoopBlinnMathUtils::numXRayCrossingsForCubic; +using LoopBlinnMathUtils::trianglesOverlap; +using LoopBlinnMathUtils::xRayCrossesLine; +using LoopBlinnPathProcessorImplementation::Contour; +using LoopBlinnPathProcessorImplementation::Segment; + +namespace { + +#ifndef NDEBUG +String valueToString(const FloatRect& arg) +{ + StringBuilder builder; + builder.append("[FloatRect x="); + builder.append(String::number(arg.x())); + builder.append(" y="); + builder.append(String::number(arg.y())); + builder.append(" maxX="); + builder.append(String::number(arg.maxX())); + builder.append(" maxY="); + builder.append(String::number(arg.maxY())); + builder.append("]"); + return builder.toString(); +} +#endif + +struct SweepData; + +} // anonymous namespace + +namespace LoopBlinnPathProcessorImplementation { +class Segment; +} + +#ifndef NDEBUG +// Routines needed to print the types of IntervalNodes we instantiate +// in this file. +template <> +struct ValueToString<float> { + static String string(const float& value) + { + return String::number(value); + } +}; + +template <> +struct ValueToString<SweepData*> { + static String string(SweepData* const& value) + { + return String::format("0x%p", value); + } +}; + +template <> +struct ValueToString<LoopBlinnPathProcessorImplementation::Segment*> { + static String string(LoopBlinnPathProcessorImplementation::Segment* const& value) + { + return String::format("0x%p", value); + } +}; +#endif + +namespace LoopBlinnPathProcessorImplementation { + +//---------------------------------------------------------------------- +// Segment +// + +// Describes a segment of the path: either a cubic or a line segment. +// These are stored in a doubly linked list to speed up curve +// subdivision, which occurs due to either rendering artifacts in the +// loop case or due to overlapping triangles. +class Segment { + WTF_MAKE_NONCOPYABLE(Segment); +public: + enum Kind { + Cubic, + Line + }; + + // No-argument constructor allows construction by the PODArena class. + Segment() + : m_arena(0) + , m_kind(Cubic) + , m_prev(0) + , m_next(0) + , m_contour(0) + , m_triangulator(0) + , m_markedForSubdivision(false) + { + } + + // Initializer for cubic curve segments. + void setup(PODArena* arena, + Contour* contour, + FloatPoint cp0, + FloatPoint cp1, + FloatPoint cp2, + FloatPoint cp3) + { + m_arena = arena; + m_contour = contour; + m_kind = Cubic; + m_points[0] = cp0; + m_points[1] = cp1; + m_points[2] = cp2; + m_points[3] = cp3; + computeBoundingBox(); + } + + // Initializer for line segments. + void setup(PODArena* arena, + Contour* contour, + FloatPoint p0, + FloatPoint p1) + { + m_arena = arena; + m_contour = contour; + m_kind = Line; + m_points[0] = p0; + m_points[1] = p1; + computeBoundingBox(); + } + + Kind kind() const { return m_kind; } + + // Returns the i'th control point, 0 <= i < 4. + const FloatPoint& getPoint(int i) + { + ASSERT(i >= 0 && i < 4); + return m_points[i]; + } + + Segment* next() const { return m_next; } + Segment* prev() const { return m_prev; } + + void setNext(Segment* next) { m_next = next; } + void setPrev(Segment* prev) { m_prev = prev; } + + // The contour this segment belongs to. + Contour* contour() const { return m_contour; } + + // Subdivides the current segment at the given parameter value (0 <= + // t <= 1) and replaces it with the two newly created Segments in + // the linked list, if possible. Returns a pointer to the leftmost + // Segment. + Segment* subdivide(float param) + { + FloatPoint dst[7]; + chopCubicAt(m_points, dst, param); + Segment* left = m_arena->allocateObject<Segment>(); + Segment* right = m_arena->allocateObject<Segment>(); + left->setup(m_arena, m_contour, dst[0], dst[1], dst[2], dst[3]); + right->setup(m_arena, m_contour, dst[3], dst[4], dst[5], dst[6]); + left->setNext(right); + right->setPrev(left); + // Try to set up a link between "this->prev()" and "left". + if (prev()) { + left->setPrev(prev()); + prev()->setNext(left); + } + // Try to set up a link between "this->next()" and "right". + Segment* n = next(); + if (n) { + right->setNext(n); + n->setPrev(right); + } + // Set up a link between "this" and "left"; this is only to + // provide a certain amount of continuity during forward iteration. + setNext(left); + return left; + } + + // Subdivides the current segment at the halfway point and replaces + // it with the two newly created Segments in the linked list, if + // possible. Returns a pointer to the leftmost Segment. + Segment* subdivide() { return subdivide(0.5f); } + + const FloatRect& boundingBox() const { return m_boundingBox; } + + // Computes the number of times a query line starting at the given + // point and extending to x=+infinity crosses this segment. Outgoing + // "ambiguous" argument indicates whether the query intersected an + // endpoint or tangent point of the segment, indicating that another + // query point is preferred. + int numCrossingsForXRay(const XRay& xRay, bool& ambiguous) const + { + if (m_kind == Cubic) + // Should consider caching the monotonic cubics. + return numXRayCrossingsForCubic(xRay, m_points, ambiguous); + + return xRayCrossesLine(xRay, m_points, ambiguous) ? 1 : 0; + } + + // Performs a local triangulation of the control points in this + // segment. This operation only makes sense for cubic type segments. + // texCoords may be null when the klm coordinates have not been + // computed yet. + void triangulate(LoopBlinnLocalTriangulator::InsideEdgeComputation computeInsideEdges, + const LoopBlinnTextureCoords::Result* texCoords); + + // Returns the number of control point triangles associated with + // this segment. + int numberOfTriangles() const + { + if (!m_triangulator) + return 0; + return m_triangulator->numberOfTriangles(); + } + + // Fetches the given control point triangle for this segment. + LoopBlinnLocalTriangulator::Triangle* getTriangle(int index) + { + ASSERT(m_triangulator); + return m_triangulator->getTriangle(index); + } + + // Number of vertices along the inside edge of this segment. This + // can be called either for line or cubic type segments. + int numberOfInteriorVertices() const + { + if (m_kind == Cubic) { + if (m_triangulator) + return m_triangulator->numberOfInteriorVertices(); + + return 0; + } + + return 2; + } + + // Returns the given interior vertex, 0 <= index < numberOfInteriorVertices(). + FloatPoint getInteriorVertex(int index) const + { + ASSERT(index >= 0 && index < numberOfInteriorVertices()); + if (m_kind == Cubic) { + FloatPoint res; + if (m_triangulator) { + LoopBlinnLocalTriangulator::Vertex* vertex = m_triangulator->getInteriorVertex(index); + if (vertex) + res.set(vertex->xyCoordinates().x(), vertex->xyCoordinates().y()); + } + return res; + } + + return m_points[index]; + } + + // State to assist with curve subdivision. + bool markedForSubdivision() const { return m_markedForSubdivision; } + void setMarkedForSubdivision(bool markedForSubdivision) { m_markedForSubdivision = markedForSubdivision; } + +#ifndef NDEBUG + // Suppport for printing Segments. + String toString() const + { + StringBuilder builder; + builder.append("[Segment kind="); + builder.append(kind() == Line ? "line" : "cubic"); + builder.append(" boundingBox="); + builder.append(valueToString(boundingBox())); + builder.append(" contour=0x"); + builder.append(String::format("%p", contour())); + builder.append(" markedForSubdivision="); + builder.append(markedForSubdivision() ? "true" : "false"); + builder.append("]"); + return builder.toString(); + } +#endif + + private: + // Computes the bounding box of this Segment. + void computeBoundingBox() + { + switch (m_kind) { + case Cubic: + m_boundingBox.fitToPoints(m_points[0], m_points[1], m_points[2], m_points[3]); + break; + + case Line: + m_boundingBox.fitToPoints(m_points[0], m_points[1]); + break; + } + } + + PODArena* m_arena; + Kind m_kind; + FloatPoint m_points[4]; + Segment* m_prev; + Segment* m_next; + Contour* m_contour; + FloatRect m_boundingBox; + LoopBlinnLocalTriangulator* m_triangulator; + bool m_markedForSubdivision; +}; + +//---------------------------------------------------------------------- +// Contour +// + +// Describes a closed contour of the path. +class Contour { + WTF_MAKE_NONCOPYABLE(Contour); +public: + Contour() + { + m_first = &m_sentinel; + m_first->setNext(m_first); + m_first->setPrev(m_first); + m_isOrientedCounterClockwise = true; + m_boundingBoxDirty = false; + m_fillSide = LoopBlinnConstants::RightSide; + } + + void add(Segment* segment) + { + if (m_first == &m_sentinel) { + // First element is the sentinel. Replace it with the incoming + // segment. + segment->setNext(m_first); + segment->setPrev(m_first); + m_first->setNext(segment); + m_first->setPrev(segment); + m_first = segment; + } else { + // m_first->prev() is the sentinel. + ASSERT(m_first->prev() == &m_sentinel); + Segment* last = m_sentinel.prev(); + last->setNext(segment); + segment->setPrev(last); + segment->setNext(&m_sentinel); + m_sentinel.setPrev(segment); + } + m_boundingBoxDirty = true; + } + + // Subdivides the given segment at the given parametric value. + // Returns a pointer to the first of the two portions of the + // subdivided segment. + Segment* subdivide(Segment* segment, float param) + { + Segment* left = segment->subdivide(param); + if (m_first == segment) + m_first = left; + return left; + } + + // Subdivides the given segment at the halfway point. Returns a + // pointer to the first of the two portions of the subdivided + // segment. + Segment* subdivide(Segment* segment) + { + Segment* left = segment->subdivide(); + if (m_first == segment) + m_first = left; + return left; + } + + // Returns the first segment in the contour for iteration. + Segment* begin() const { return m_first; } + + // Returns the last segment in the contour for iteration. Callers + // should not iterate over this segment. In other words: + // for (Segment* cur = contour->begin(); + // cur != contour->end(); + // cur = cur->next()) { + // // .. process cur ... + // } + Segment* end() + { + ASSERT(m_first->prev() == &m_sentinel); + return &m_sentinel; + } + + bool isOrientedCounterClockwise() const { return m_isOrientedCounterClockwise; } + void setIsOrientedCounterClockwise(bool isOrientedCounterClockwise) { m_isOrientedCounterClockwise = isOrientedCounterClockwise; } + + const FloatRect& boundingBox() + { + if (m_boundingBoxDirty) { + bool first = true; + for (Segment* cur = begin(); cur != end(); cur = cur->next()) { + if (first) + m_boundingBox = cur->boundingBox(); + else + m_boundingBox.unite(cur->boundingBox()); + first = false; + } + + m_boundingBoxDirty = false; + } + return m_boundingBox; + } + + // Returns which side of this contour is filled. + LoopBlinnConstants::FillSide fillSide() const + { + return m_fillSide; + } + + void setFillSide(LoopBlinnConstants::FillSide fillSide) + { + m_fillSide = fillSide; + } + +private: + // The start of the segment chain. The segments are kept in a + // circular doubly linked list for rapid access to the beginning and + // end. + Segment* m_first; + + // The sentinel element at the end of the chain, needed for + // reasonable iteration semantics. + Segment m_sentinel; + + bool m_isOrientedCounterClockwise; + + FloatRect m_boundingBox; + bool m_boundingBoxDirty; + + // Which side of this contour should be filled. + LoopBlinnConstants::FillSide m_fillSide; +}; + +//---------------------------------------------------------------------- +// Segment +// + +// Definition of Segment::triangulate(), which must come after +// declaration of Contour. +void Segment::triangulate(LoopBlinnLocalTriangulator::InsideEdgeComputation computeInsideEdges, + const LoopBlinnTextureCoords::Result* texCoords) +{ + ASSERT(m_kind == Cubic); + if (!m_triangulator) + m_triangulator = m_arena->allocateObject<LoopBlinnLocalTriangulator>(); + m_triangulator->reset(); + for (int i = 0; i < 4; i++) { + LoopBlinnLocalTriangulator::Vertex* vertex = m_triangulator->getVertex(i); + if (texCoords) { + vertex->set(getPoint(i).x(), + getPoint(i).y(), + texCoords->klmCoordinates[i].x(), + texCoords->klmCoordinates[i].y(), + texCoords->klmCoordinates[i].z()); + } else { + vertex->set(getPoint(i).x(), + getPoint(i).y(), + // No texture coordinates yet + 0, 0, 0); + } + } + m_triangulator->triangulate(computeInsideEdges, contour()->fillSide()); +} + +} // namespace LoopBlinnPathProcessorImplementation + +//---------------------------------------------------------------------- +// LoopBlinnPathProcessor +// + +LoopBlinnPathProcessor::LoopBlinnPathProcessor() + : m_arena(PODArena::create()) +#ifndef NDEBUG + , m_verboseLogging(false) +#endif +{ +} + +LoopBlinnPathProcessor::LoopBlinnPathProcessor(PassRefPtr<PODArena> arena) + : m_arena(arena) +#ifndef NDEBUG + , m_verboseLogging(false) +#endif +{ +} + +LoopBlinnPathProcessor::~LoopBlinnPathProcessor() +{ +} + +void LoopBlinnPathProcessor::process(const Path& path, LoopBlinnPathCache& cache) +{ + buildContours(path); + + // Run plane-sweep algorithm to determine overlaps of control point + // curves and subdivide curves appropriately. + subdivideCurves(); + + // Determine orientations of countours. Based on orientation and the + // number of curve crossings at a random point on the contour, + // determine whether to fill the left or right side of the contour. + determineSidesToFill(); + + // Classify curves, compute texture coordinates and subdivide as + // necessary to eliminate rendering artifacts. Do the final + // triangulation of the curve segments, determining the path along + // the interior of the shape. + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + if (seg->kind() == Segment::Cubic) { + LoopBlinnClassifier::Result classification = LoopBlinnClassifier::classify(seg->getPoint(0), + seg->getPoint(1), + seg->getPoint(2), + seg->getPoint(3)); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("Classification: %d", (int) classification.curveType); +#endif + LoopBlinnTextureCoords::Result texCoords = + LoopBlinnTextureCoords::compute(classification, cur->fillSide()); + if (texCoords.hasRenderingArtifact) { + // FIXME: there is a problem where the algorithm + // sometimes fails to converge when splitting at the + // subdivision parameter value. For the time being, + // split halfway. + cur->subdivide(seg); + // Next iteration will handle the newly subdivided curves + } else { + if (!texCoords.isLineOrPoint) { + seg->triangulate(LoopBlinnLocalTriangulator::ComputeInsideEdges, &texCoords); + for (int i = 0; i < seg->numberOfTriangles(); i++) { + LoopBlinnLocalTriangulator::Triangle* triangle = seg->getTriangle(i); + for (int j = 0; j < 3; j++) { + LoopBlinnLocalTriangulator::Vertex* vert = triangle->getVertex(j); + cache.addVertex(vert->xyCoordinates().x(), + vert->xyCoordinates().y(), + vert->klmCoordinates().x(), + vert->klmCoordinates().y(), + vert->klmCoordinates().z()); + } + } +#ifdef LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + // Show the end user the interior edges as well + for (int i = 1; i < seg->numberOfInteriorVertices(); i++) { + FloatPoint vert = seg->getInteriorVertex(i); + // Duplicate previous vertex to be able to draw GL_LINES + FloatPoint prev = seg->getInteriorVertex(i - 1); + cache.addInteriorEdgeVertex(prev.x(), prev.y()); + cache.addInteriorEdgeVertex(vert.x(), vert.y()); + } +#endif // LOOP_BLINN_PATH_CACHE_DEBUG_INTERIOR_EDGES + } + } + } + } + } + + // Run the interior paths through a tessellation algorithm + // supporting multiple contours. + tessellateInterior(cache); +} + +void LoopBlinnPathProcessor::buildContours(const Path& path) +{ + // Clear out the contours + m_contours.clear(); +#if PLATFORM(SKIA) + SkPath::Iter iter(*path.platformPath(), false); + SkPoint points[4]; + SkPath::Verb verb; + Contour* contour = 0; + SkPoint curPoint = { 0 }; + SkPoint moveToPoint = { 0 }; + do { + verb = iter.next(points); + if (verb != SkPath::kMove_Verb) { + if (!contour) { + contour = m_arena->allocateObject<Contour>(); + m_contours.append(contour); + } + } + switch (verb) { + case SkPath::kMove_Verb: { + contour = m_arena->allocateObject<Contour>(); + m_contours.append(contour); + curPoint = points[0]; + moveToPoint = points[0]; +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("MoveTo (%f, %f)", points[0].fX, points[0].fY); +#endif + break; + } + case SkPath::kLine_Verb: { + Segment* segment = m_arena->allocateObject<Segment>(); + if (iter.isCloseLine()) { + segment->setup(m_arena.get(), contour, curPoint, points[1]); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("CloseLineTo (%f, %f), (%f, %f)", curPoint.fX, curPoint.fY, points[1].fX, points[1].fY); +#endif + contour->add(segment); + contour = 0; + } else { + segment->setup(m_arena.get(), contour, points[0], points[1]); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("LineTo (%f, %f), (%f, %f)", points[0].fX, points[0].fY, points[1].fX, points[1].fY); +#endif + contour->add(segment); + curPoint = points[1]; + } + break; + } + case SkPath::kQuad_Verb: { + // Need to degree elevate the quadratic into a cubic + SkPoint cubic[4]; + SkConvertQuadToCubic(points, cubic); + Segment* segment = m_arena->allocateObject<Segment>(); + segment->setup(m_arena.get(), contour, + cubic[0], cubic[1], cubic[2], cubic[3]); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("Quad->CubicTo (%f, %f), (%f, %f), (%f, %f), (%f, %f)", cubic[0].fX, cubic[0].fY, cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY, cubic[3].fX, cubic[3].fY); +#endif + contour->add(segment); + curPoint = cubic[3]; + break; + } + case SkPath::kCubic_Verb: { + Segment* segment = m_arena->allocateObject<Segment>(); + segment->setup(m_arena.get(), contour, points[0], points[1], points[2], points[3]); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("CubicTo (%f, %f), (%f, %f), (%f, %f), (%f, %f)", points[0].fX, points[0].fY, points[1].fX, points[1].fY, points[2].fX, points[2].fY, points[3].fX, points[3].fY); +#endif + contour->add(segment); + curPoint = points[3]; + break; + } + case SkPath::kClose_Verb: { + Segment* segment = m_arena->allocateObject<Segment>(); + segment->setup(m_arena.get(), contour, curPoint, moveToPoint); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("Close (%f, %f) -> (%f, %f)", curPoint.fX, curPoint.fY, moveToPoint.fX, moveToPoint.fY); +#endif + contour->add(segment); + contour = 0; + } + case SkPath::kDone_Verb: + break; + } + } while (verb != SkPath::kDone_Verb); +#else // !PLATFORM(SKIA) + // Must port to your platform. + ASSERT_NOT_REACHED(); +#endif +} + +#ifndef NDEBUG +Vector<Segment*> LoopBlinnPathProcessor::allSegmentsOverlappingY(Contour* queryContour, float x, float y) +{ + Vector<Segment*> res; + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + const FloatRect& boundingBox = seg->boundingBox(); + if (boundingBox.y() <= y && y <= boundingBox.maxY()) + res.append(seg); + } + } + return res; +} +#endif + +// Uncomment this to debug the orientation computation. +// #define GPU_PATH_PROCESSOR_DEBUG_ORIENTATION + +void LoopBlinnPathProcessor::determineSidesToFill() +{ + // Loop and Blinn's algorithm can only easily emulate the even/odd + // fill rule, and only for non-intersecting curves. We can determine + // which side of each curve segment to fill based on its + // clockwise/counterclockwise orientation and how many other + // contours surround it. + + // To optimize the query of all curve segments intersecting a + // horizontal line going to x=+infinity, we build up an interval + // tree whose keys are the y extents of the segments. + PODIntervalTree<float, Segment*> tree(m_arena); + typedef PODIntervalTree<float, Segment*>::IntervalType IntervalType; + + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + determineOrientation(cur); + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + const FloatRect& boundingBox = seg->boundingBox(); + tree.add(tree.createInterval(boundingBox.y(), boundingBox.maxY(), seg)); + } + } + + // Now iterate through the contours and pick a random segment (in + // this case we use the first) and a random point on that segment. + // Find all segments from other contours which intersect this one + // and count the number of crossings a horizontal line to + // x=+infinity makes with those contours. This combined with the + // orientation of the curve tells us which side to fill -- again, + // assuming an even/odd fill rule, which is all we can easily + // handle. + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + + bool ambiguous = true; + int numCrossings = 0; + + // For each contour, attempt to find a point on the contour which, + // when we cast an XRay, does not intersect the other contours at + // an ambiguous point (the junction between two curves or at a + // tangent point). Ambiguous points make the determination of + // whether this contour is contained within another fragile. Note + // that this loop is only an approximation to the selection of a + // good casting point. We could as well evaluate a segment to + // determine a point upon it. + for (Segment* seg = cur->begin(); + ambiguous && seg != cur->end(); + seg = seg->next()) { + numCrossings = 0; + // We use a zero-sized vertical interval for the query. + Vector<IntervalType> overlaps = tree.allOverlaps(tree.createInterval(seg->getPoint(0).y(), + seg->getPoint(0).y(), + 0)); +#if defined(GPU_PATH_PROCESSOR_DEBUG_ORIENTATION) && !defined(NDEBUG) + Vector<Segment*> slowOverlaps = allSegmentsOverlappingY(cur, seg->getPoint(0).x(), seg->getPoint(0).y()); + if (overlaps.size() != slowOverlaps.size()) { + LOG_ERROR("For query point (%f, %f) on contour 0x%p:", seg->getPoint(0).x(), seg->getPoint(0).y(), cur); + LOG_ERROR(" overlaps:"); + for (size_t i = 0; i < overlaps.size(); i++) + LOG_ERROR(" %d: %s", i+1, overlaps[i].data()->toString().ascii().data()); + LOG_ERROR(" slowOverlaps:"); + for (size_t i = 0; i < slowOverlaps.size(); i++) + LOG_ERROR(" %d: %s", (i+1) slowOverlaps[i]->toString()); + LOG_ERROR("Interval tree:"); + tree.dump(); + } + ASSERT(overlaps.size() == slowOverlaps.size()); +#endif // defined(GPU_PATH_PROCESSOR_DEBUG_ORIENTATION) && !defined(NDEBUG) + for (Vector<IntervalType>::iterator iter = overlaps.begin(); iter != overlaps.end(); ++iter) { + const IntervalType& interval = *iter; + Segment* querySegment = interval.data(); + // Ignore segments coming from the same contour. + if (querySegment->contour() != cur) { + // Only perform queries that can affect the computation. + const FloatRect& boundingBox = querySegment->contour()->boundingBox(); + if (seg->getPoint(0).x() >= boundingBox.x() + && seg->getPoint(0).x() <= boundingBox.maxX()) { + numCrossings += querySegment->numCrossingsForXRay(seg->getPoint(0), + ambiguous); + if (ambiguous) { +#ifndef NDEBUG + if (m_verboseLogging) { + LOG_ERROR("Ambiguous intersection query at point (%f, %f)", seg->getPoint(0).x(), seg->getPoint(0).y()); + LOG_ERROR("Query segment: %s", querySegment->toString().ascii().data()); + } +#endif + break; // Abort iteration over overlaps. + } + } + } + } + } // for (Segment* seg = cur->begin(); ... + + cur->setFillSide((cur->isOrientedCounterClockwise() ^ (numCrossings & 1)) ? LoopBlinnConstants::LeftSide : LoopBlinnConstants::RightSide); + } +} + +void LoopBlinnPathProcessor::determineOrientation(Contour* contour) +{ + // Determine signed area of the polygon represented by the points + // along the segments. Consider this an approximation to the true + // orientation of the polygon; it probably won't handle + // self-intersecting curves correctly. + // + // There is also a pretty basic assumption here that the contour is + // closed. + float signedArea = 0; + for (Segment* seg = contour->begin(); + seg != contour->end(); + seg = seg->next()) { + int limit = (seg->kind() == Segment::Cubic) ? 4 : 2; + for (int i = 1; i < limit; i++) { + const FloatPoint& prevPoint = seg->getPoint(i - 1); + const FloatPoint& point = seg->getPoint(i); + float curArea = prevPoint.x() * point.y() - prevPoint.y() * point.x(); +#ifndef NDEBUG + if (m_verboseLogging) + LOG_ERROR("Adding to signed area (%f, %f) -> (%f, %f) = %f", prevPoint.x(), prevPoint.y(), point.x(), point.y(), curArea); +#endif + signedArea += curArea; + } + } + + if (signedArea > 0) + contour->setIsOrientedCounterClockwise(true); + else + contour->setIsOrientedCounterClockwise(false); +} + +namespace { + +//---------------------------------------------------------------------- +// Classes and typedefs needed for curve subdivision. These can't be scoped +// within the subdivideCurves() method itself, because templates then fail +// to instantiate. + +// The user data which is placed in the PODIntervalTree. +struct SweepData { + SweepData() + : triangle(0) + , segment(0) + { + } + + // The triangle this interval is associated with + LoopBlinnLocalTriangulator::Triangle* triangle; + // The segment the triangle is associated with + Segment* segment; +}; + +typedef PODIntervalTree<float, SweepData*> SweepTree; +typedef SweepTree::IntervalType SweepInterval; + +// The entry / exit events which occur at the minimum and maximum x +// coordinates of the control point triangles' bounding boxes. +// +// Note that this class requires its copy constructor and assignment +// operator since it needs to be stored in a Vector. +class SweepEvent { +public: + SweepEvent() + : m_x(0) + , m_entry(false) + , m_interval(0, 0, 0) + { + } + + // Initializes the SweepEvent. + void setup(float x, bool entry, SweepInterval interval) + { + m_x = x; + m_entry = entry; + m_interval = interval; + } + + float x() const { return m_x; } + bool entry() const { return m_entry; } + const SweepInterval& interval() const { return m_interval; } + + bool operator<(const SweepEvent& other) const + { + return m_x < other.m_x; + } + +private: + float m_x; + bool m_entry; + SweepInterval m_interval; +}; + +bool trianglesOverlap(LoopBlinnLocalTriangulator::Triangle* t0, + LoopBlinnLocalTriangulator::Triangle* t1) +{ + return trianglesOverlap(t0->getVertex(0)->xyCoordinates(), + t0->getVertex(1)->xyCoordinates(), + t0->getVertex(2)->xyCoordinates(), + t1->getVertex(0)->xyCoordinates(), + t1->getVertex(1)->xyCoordinates(), + t1->getVertex(2)->xyCoordinates()); +} + +} // anonymous namespace + +void LoopBlinnPathProcessor::subdivideCurves() +{ + // We need to determine all overlaps of all control point triangles + // (from different segments, not the same segment) and, if any + // exist, subdivide the associated curves. + // + // The plane-sweep algorithm determines all overlaps of a set of + // rectangles in the 2D plane. Our problem maps very well to this + // algorithm and significantly reduces the complexity compared to a + // naive implementation. + // + // Each bounding box of a control point triangle is converted into + // an "entry" event at its smallest X coordinate and an "exit" event + // at its largest X coordinate. Each event has an associated + // one-dimensional interval representing the Y span of the bounding + // box. We sort these events by increasing X coordinate. We then + // iterate through them. For each entry event we add the interval to + // a side interval tree, and query this tree for overlapping + // intervals. Any overlapping interval corresponds to an overlapping + // bounding box. For each exit event we remove the associated + // interval from the interval tree. + + Vector<Segment*> curSegments; + Vector<Segment*> nextSegments; + + // Start things off by considering all of the segments + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + if (seg->kind() == Segment::Cubic) { + seg->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0); + curSegments.append(seg); + } + } + } + + // Subdivide curves at most this many times + const int MaxIterations = 5; + Vector<SweepInterval> overlaps; + + for (int currentIteration = 0; currentIteration < MaxIterations; ++currentIteration) { + if (!curSegments.size()) + // Done + break; + + Vector<SweepEvent> events; + SweepTree tree(m_arena); + for (Vector<Segment*>::iterator iter = curSegments.begin(); iter != curSegments.end(); ++iter) { + Segment* seg = *iter; + ASSERT(seg->kind() == Segment::Cubic); + for (int i = 0; i < seg->numberOfTriangles(); i++) { + LoopBlinnLocalTriangulator::Triangle* triangle = seg->getTriangle(i); + FloatRect boundingBox; + boundingBox.fitToPoints(triangle->getVertex(0)->xyCoordinates(), + triangle->getVertex(1)->xyCoordinates(), + triangle->getVertex(2)->xyCoordinates()); + // Ignore zero-width triangles to avoid issues with + // coincident entry and exit events for the same triangle + if (boundingBox.maxX() > boundingBox.x()) { + SweepData* data = m_arena->allocateObject<SweepData>(); + data->triangle = triangle; + data->segment = seg; + SweepInterval interval = tree.createInterval(boundingBox.y(), boundingBox.maxY(), data); + // Add entry and exit events + SweepEvent event; + event.setup(boundingBox.x(), true, interval); + events.append(event); + event.setup(boundingBox.maxX(), false, interval); + events.append(event); + } + } + } + + // Sort events by increasing X coordinate + std::sort(events.begin(), events.end()); +#ifndef NDEBUG + for (size_t ii = 1; ii < events.size(); ++ii) + ASSERT(events[ii - 1].x() <= events[ii].x()); +#endif + + // Now iterate through the events + for (Vector<SweepEvent>::iterator iter = events.begin(); iter != events.end(); ++iter) { + SweepEvent event = *iter; + if (event.entry()) { + // See whether the associated segment has been subdivided yet + if (!event.interval().data()->segment->markedForSubdivision()) { + // Query the tree + overlaps.clear(); + tree.allOverlaps(event.interval(), overlaps); + // Now see exactly which triangles overlap this one + for (Vector<SweepInterval>::iterator iter = overlaps.begin(); iter != overlaps.end(); ++iter) { + SweepInterval overlap = *iter; + // Only pay attention to overlaps from a different Segment + if (event.interval().data()->segment != overlap.data()->segment) { + // See whether the triangles actually overlap + if (trianglesOverlap(event.interval().data()->triangle, + overlap.data()->triangle)) { + // Actually subdivide the segments. + // Each one might already have been subdivided. + Segment* seg = event.interval().data()->segment; + conditionallySubdivide(seg, nextSegments); + seg = overlap.data()->segment; + conditionallySubdivide(seg, nextSegments); + } + } + } + } + // Add this interval into the tree + tree.add(event.interval()); + } else { + // Remove this interval from the tree + tree.remove(event.interval()); + } + } + + curSegments.swap(nextSegments); + nextSegments.clear(); + } +} + +void LoopBlinnPathProcessor::conditionallySubdivide(Segment* seg, Vector<Segment*>& nextSegments) +{ + if (!seg->markedForSubdivision()) { + seg->setMarkedForSubdivision(true); + Segment* next = seg->contour()->subdivide(seg); + // Triangulate the newly subdivided segments. + next->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0); + next->next()->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0); + // Add them for the next iteration. + nextSegments.append(next); + nextSegments.append(next->next()); + } +} + +#ifndef NDEBUG +void LoopBlinnPathProcessor::subdivideCurvesSlow() +{ + // Alternate, significantly slower algorithm for curve subdivision + // for use in debugging. + Vector<Segment*> curSegments; + Vector<Segment*> nextSegments; + + // Start things off by considering all of the segments + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + if (seg->kind() == Segment::Cubic) { + seg->triangulate(LoopBlinnLocalTriangulator::DontComputeInsideEdges, 0); + curSegments.append(seg); + } + } + } + + // Subdivide curves at most this many times + const int MaxIterations = 5; + + for (int currentIteration = 0; currentIteration < MaxIterations; ++currentIteration) { + if (!curSegments.size()) + // Done + break; + + for (Vector<Segment*>::iterator iter = curSegments.begin(); iter != curSegments.end(); ++iter) { + Segment* seg = *iter; + ASSERT(seg->kind() == Segment::Cubic); + for (Vector<Segment*>::iterator iter2 = curSegments.begin(); + iter2 != curSegments.end(); + iter2++) { + Segment* seg2 = *iter2; + ASSERT(seg2->kind() == Segment::Cubic); + if (seg != seg2) { + for (int i = 0; i < seg->numberOfTriangles(); i++) { + LoopBlinnLocalTriangulator::Triangle* triangle = seg->getTriangle(i); + for (int j = 0; j < seg2->numberOfTriangles(); j++) { + LoopBlinnLocalTriangulator::Triangle* triangle2 = seg2->getTriangle(j); + if (trianglesOverlap(triangle, triangle2)) { + conditionallySubdivide(seg, nextSegments); + conditionallySubdivide(seg2, nextSegments); + } + } + } + } + } + } + + curSegments.swap(nextSegments); + nextSegments.clear(); + } +} +#endif + +namespace { + +//---------------------------------------------------------------------- +// Structures and callbacks for tessellation of the interior region of +// the contours. + +// The user data for the GLU tessellator. +struct TessellationState { + TessellationState(LoopBlinnPathCache& inputCache) + : cache(inputCache) { } + + LoopBlinnPathCache& cache; + Vector<void*> allocatedPointers; +}; + +static void vertexCallback(void* vertexData, void* data) +{ + TessellationState* state = static_cast<TessellationState*>(data); + GLdouble* location = static_cast<GLdouble*>(vertexData); + state->cache.addInteriorVertex(static_cast<float>(location[0]), + static_cast<float>(location[1])); +} + +static void combineCallback(GLdouble coords[3], void* vertexData[4], + GLfloat weight[4], void** outData, + void* polygonData) +{ + TessellationState* state = static_cast<TessellationState*>(polygonData); + GLdouble* outVertex = static_cast<GLdouble*>(fastMalloc(3 * sizeof(GLdouble))); + state->allocatedPointers.append(outVertex); + outVertex[0] = coords[0]; + outVertex[1] = coords[1]; + outVertex[2] = coords[2]; + *outData = outVertex; +} + +static void edgeFlagCallback(GLboolean) +{ + // No-op just to prevent triangle strips and fans from being passed to us. + // See the OpenGL Programming Guide, Chapter 11, "Tessellators and Quadrics". +} + +} // anonymous namespace + +void LoopBlinnPathProcessor::tessellateInterior(LoopBlinnPathCache& cache) +{ + // Because the GLU tessellator requires its input in + // double-precision format, we need to make a separate copy of the + // data. + Vector<GLdouble> vertexData; + Vector<size_t> contourEndings; + // For avoiding adding coincident vertices. + float curX = 0, curY = 0; + for (Vector<Contour*>::iterator iter = m_contours.begin(); iter != m_contours.end(); ++iter) { + Contour* cur = *iter; + bool first = true; + for (Segment* seg = cur->begin(); seg != cur->end(); seg = seg->next()) { + int numberOfInteriorVertices = seg->numberOfInteriorVertices(); + for (int i = 0; i < numberOfInteriorVertices - 1; i++) { + FloatPoint point = seg->getInteriorVertex(i); + if (first) { + first = false; + vertexData.append(point.x()); + vertexData.append(point.y()); + vertexData.append(0); + curX = point.x(); + curY = point.y(); + } else if (point.x() != curX || point.y() != curY) { + vertexData.append(point.x()); + vertexData.append(point.y()); + vertexData.append(0); + curX = point.x(); + curY = point.y(); + } + } + } + contourEndings.append(vertexData.size()); + } + // Now that we have all of the vertex data in a stable location in + // memory, call the tessellator. + GLUtesselator* tess = internal_gluNewTess(); + TessellationState state(cache); + internal_gluTessCallback(tess, GLU_TESS_VERTEX_DATA, + reinterpret_cast<GLvoid (*)()>(vertexCallback)); + internal_gluTessCallback(tess, GLU_TESS_COMBINE_DATA, + reinterpret_cast<GLvoid (*)()>(combineCallback)); + internal_gluTessCallback(tess, GLU_TESS_EDGE_FLAG, + reinterpret_cast<GLvoid (*)()>(edgeFlagCallback)); + internal_gluTessBeginPolygon(tess, &state); + internal_gluTessBeginContour(tess); + GLdouble* base = vertexData.data(); + int contourIndex = 0; + for (size_t i = 0; i < vertexData.size(); i += 3) { + if (i == contourEndings[contourIndex]) { + internal_gluTessEndContour(tess); + internal_gluTessBeginContour(tess); + ++contourIndex; + } + internal_gluTessVertex(tess, &base[i], &base[i]); + } + internal_gluTessEndContour(tess); + internal_gluTessEndPolygon(tess); + for (size_t i = 0; i < state.allocatedPointers.size(); i++) + fastFree(state.allocatedPointers[i]); + internal_gluDeleteTess(tess); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.h b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.h new file mode 100644 index 0000000..ad89bc1 --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnPathProcessor.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2011 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 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. + */ + +// The main entry point for Loop and Blinn's GPU accelerated curve +// rendering algorithm. + +#ifndef LoopBlinnPathProcessor_h +#define LoopBlinnPathProcessor_h + +#include <wtf/Noncopyable.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// We use a namespace for classes which are simply implementation +// details of the algorithm but which we need to reference from the +// class definition. +namespace LoopBlinnPathProcessorImplementation { + +class Contour; +class Segment; + +} // namespace LoopBlinnPathProcessorImplementation + +class Path; +class LoopBlinnPathCache; +class PODArena; + +// The LoopBlinnPathProcessor turns a Path (assumed to contain one or +// more closed regions) into a set of exterior and interior triangles, +// stored in the LoopBlinnPathCache. The exterior triangles have +// associated 3D texture coordinates which are used to evaluate the +// curve's inside/outside function on a per-pixel basis. The interior +// triangles are filled with 100% opacity. +// +// Note that the fill style and management of multiple layers are +// separate concerns, handled at a higher level with shaders and +// polygon offsets. +class LoopBlinnPathProcessor { +public: + LoopBlinnPathProcessor(); + explicit LoopBlinnPathProcessor(PassRefPtr<PODArena>); + ~LoopBlinnPathProcessor(); + + // Transforms the given path into a triangle mesh for rendering + // using Loop and Blinn's shader, placing the result into the given + // LoopBlinnPathCache. + void process(const Path&, LoopBlinnPathCache&); + +#ifndef NDEBUG + // Enables or disables verbose logging in debug mode. + void setVerboseLogging(bool onOrOff); +#endif + +private: + // Builds a list of contours for the given path. + void buildContours(const Path&); + + // Determines whether the left or right side of each contour should + // be filled. + void determineSidesToFill(); + + // Determines whether the given (closed) contour is oriented + // clockwise or counterclockwise. + void determineOrientation(LoopBlinnPathProcessorImplementation::Contour*); + + // Subdivides the curves so that there are no overlaps of the + // triangles associated with the curves' control points. + void subdivideCurves(); + + // Helper function used during curve subdivision. + void conditionallySubdivide(LoopBlinnPathProcessorImplementation::Segment*, + Vector<LoopBlinnPathProcessorImplementation::Segment*>& nextSegments); + + // Tessellates the interior regions of the contours. + void tessellateInterior(LoopBlinnPathCache&); + +#ifndef NDEBUG + // For debugging the orientation computation. Returns all of the + // segments overlapping the given Y coordinate. + Vector<LoopBlinnPathProcessorImplementation::Segment*> allSegmentsOverlappingY(LoopBlinnPathProcessorImplementation::Contour*, float x, float y); + + // For debugging the curve subdivision algorithm. Subdivides the + // curves using an alternate, slow (O(n^3)) algorithm. + void subdivideCurvesSlow(); +#endif + + // PODArena from which to allocate temporary objects. + RefPtr<PODArena> m_arena; + + // The contours described by the path. + Vector<LoopBlinnPathProcessorImplementation::Contour*> m_contours; + +#ifndef NDEBUG + // Whether or not to perform verbose logging in debug mode. + bool m_verboseLogging; +#endif +}; + +} // namespace WebCore + +#endif // LoopBlinnPathProcessor_h diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnShader.cpp b/Source/WebCore/platform/graphics/gpu/LoopBlinnShader.cpp new file mode 100644 index 0000000..364e6c8 --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnShader.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2011 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 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 "LoopBlinnShader.h" + +#include "GraphicsContext3D.h" + +namespace WebCore { + +LoopBlinnShader::LoopBlinnShader(GraphicsContext3D* context, unsigned program) + : Shader(context, program) +{ + m_worldViewProjectionLocation = context->getUniformLocation(program, "worldViewProjection"); + m_positionLocation = context->getAttribLocation(program, "position"); + m_klmLocation = context->getAttribLocation(program, "klm"); +} + +void LoopBlinnShader::use(unsigned vertexOffset, unsigned klmOffset, const AffineTransform& transform) +{ + m_context->useProgram(m_program); + + float matrix[16]; + affineTo4x4(transform, matrix); + m_context->uniformMatrix4fv(m_worldViewProjectionLocation, false /*transpose*/, matrix, 1 /*count*/); + + m_context->vertexAttribPointer(m_positionLocation, 2, GraphicsContext3D::FLOAT, false, 0, vertexOffset); + m_context->enableVertexAttribArray(m_positionLocation); + + if (m_klmLocation != -1) { + m_context->vertexAttribPointer(m_klmLocation, 3, GraphicsContext3D::FLOAT, false, 0, klmOffset); + m_context->enableVertexAttribArray(m_klmLocation); + } +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnShader.h b/Source/WebCore/platform/graphics/gpu/LoopBlinnShader.h new file mode 100644 index 0000000..2d24dc3 --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnShader.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2011 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 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 LoopBlinnShader_h +#define LoopBlinnShader_h + +#include "Shader.h" + +namespace WebCore { + +class GraphicsContext3D; + +class LoopBlinnShader : public Shader { +public: + enum Region { + Interior, + Exterior + }; + +protected: + LoopBlinnShader(GraphicsContext3D*, unsigned program); + + // This assumes the vertices and klm coordinates are stored in the + // same, currently bound, buffer object, contiguously and at the + // specified offsets. + void use(unsigned vertexOffset, unsigned klmOffset, const AffineTransform&); + +private: + int m_worldViewProjectionLocation; + int m_positionLocation; + int m_klmLocation; +}; + +} // namespace WebCore + +#endif // LoopBlinnShader_h diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnSolidFillShader.cpp b/Source/WebCore/platform/graphics/gpu/LoopBlinnSolidFillShader.cpp new file mode 100644 index 0000000..43a97ef --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnSolidFillShader.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 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 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 "LoopBlinnSolidFillShader.h" + +#include "GraphicsContext3D.h" + +namespace WebCore { + +PassOwnPtr<LoopBlinnSolidFillShader> LoopBlinnSolidFillShader::create(GraphicsContext3D* context, + LoopBlinnShader::Region region, + Shader::AntialiasType antialiasType) +{ + VertexType type = (region == Interior) ? LoopBlinnInterior : LoopBlinnExterior; + unsigned program = loadProgram(context, + generateVertex(type, SolidFill), + generateFragment(type, SolidFill, antialiasType)); + if (!program) + return 0; + return new LoopBlinnSolidFillShader(context, program); +} + +LoopBlinnSolidFillShader::LoopBlinnSolidFillShader(GraphicsContext3D* context, unsigned program) + : LoopBlinnShader(context, program) +{ + m_colorLocation = context->getUniformLocation(program, "color"); +} + +void LoopBlinnSolidFillShader::use(unsigned vertexOffset, unsigned klmOffset, const AffineTransform& transform, const Color& color) +{ + LoopBlinnShader::use(vertexOffset, klmOffset, transform); + + float rgba[4]; + color.getRGBA(rgba[0], rgba[1], rgba[2], rgba[3]); + m_context->uniform4f(m_colorLocation, rgba[0] * rgba[3], rgba[1] * rgba[3], rgba[2] * rgba[3], rgba[3]); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/graphics/gpu/LoopBlinnSolidFillShader.h b/Source/WebCore/platform/graphics/gpu/LoopBlinnSolidFillShader.h new file mode 100644 index 0000000..36312a2 --- /dev/null +++ b/Source/WebCore/platform/graphics/gpu/LoopBlinnSolidFillShader.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 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 LoopBlinnSolidFillShader_h +#define LoopBlinnSolidFillShader_h + +#include "LoopBlinnShader.h" + +namespace WebCore { + +class GraphicsContext3D; + +class LoopBlinnSolidFillShader : public LoopBlinnShader { +public: + static PassOwnPtr<LoopBlinnSolidFillShader> create(GraphicsContext3D*, Region, AntialiasType); + + // This assumes the vertices and klm coordinates are stored in the + // same, currently bound, buffer object, contiguously and at the + // specified offsets. + void use(unsigned vertexOffset, unsigned klmOffset, const AffineTransform&, const Color&); + +private: + LoopBlinnSolidFillShader(GraphicsContext3D*, unsigned program); + + int m_colorLocation; +}; + +} // namespace WebCore + +#endif // LoopBlinnSolidFillShader_h diff --git a/Source/WebCore/platform/graphics/gpu/Shader.cpp b/Source/WebCore/platform/graphics/gpu/Shader.cpp index 6978322..1b9bfd5 100644 --- a/Source/WebCore/platform/graphics/gpu/Shader.cpp +++ b/Source/WebCore/platform/graphics/gpu/Shader.cpp @@ -38,6 +38,7 @@ #include "GraphicsContext3D.h" #include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> namespace WebCore { @@ -56,14 +57,34 @@ void Shader::affineTo3x3(const AffineTransform& transform, float mat[9]) } // static -unsigned Shader::loadShader(GraphicsContext3D* context, unsigned type, const char* shaderSource) +void Shader::affineTo4x4(const AffineTransform& transform, float mat[16]) +{ + mat[0] = transform.a(); + mat[1] = transform.b(); + mat[2] = 0.0f; + mat[3] = 0.0f; + mat[4] = transform.c(); + mat[5] = transform.d(); + mat[6] = 0.0f; + mat[7] = 0.0f; + mat[8] = 0.0f; + mat[9] = 0.0f; + mat[10] = 1.0f; + mat[11] = 0.0f; + mat[12] = transform.e(); + mat[13] = transform.f(); + mat[14] = 0.0f; + mat[15] = 1.0f; +} + +// static +unsigned Shader::loadShader(GraphicsContext3D* context, unsigned type, const String& shaderSource) { unsigned shader = context->createShader(type); if (!shader) return 0; - String shaderSourceStr(shaderSource); - context->shaderSource(shader, shaderSourceStr); + context->shaderSource(shader, shaderSource); context->compileShader(shader); int compileStatus = 0; context->getShaderiv(shader, GraphicsContext3D::COMPILE_STATUS, &compileStatus); @@ -77,7 +98,7 @@ unsigned Shader::loadShader(GraphicsContext3D* context, unsigned type, const cha } // static -unsigned Shader::loadProgram(GraphicsContext3D* context, const char* vertexShaderSource, const char* fragmentShaderSource) +unsigned Shader::loadProgram(GraphicsContext3D* context, const String& vertexShaderSource, const String& fragmentShaderSource) { unsigned vertexShader = loadShader(context, GraphicsContext3D::VERTEX_SHADER, vertexShaderSource); if (!vertexShader) @@ -111,6 +132,146 @@ Shader::~Shader() m_context->deleteProgram(m_program); } +// static +String Shader::generateVertex(Shader::VertexType vertexType, Shader::FillType fillType) +{ + StringBuilder builder; + switch (vertexType) { + case TwoDimensional: + builder.append( + "uniform mat3 matrix;\n" + "attribute vec3 position;\n"); + break; + case LoopBlinnInterior: + builder.append( + "uniform mat4 worldViewProjection;\n" + "attribute vec2 position;\n"); + break; + case LoopBlinnExterior: + builder.append( + "uniform mat4 worldViewProjection;\n" + "attribute vec2 position;\n" + "attribute vec3 klm;\n" + "varying vec3 v_klm;\n"); + break; + } + + if (fillType == TextureFill) { + builder.append( + "uniform mat3 texMatrix;\n" + "varying vec3 texCoord;\n"); + } + + builder.append( + "void main() {\n"); + + if (vertexType == TwoDimensional) { + builder.append( + "gl_Position = vec4(matrix * position, 1.0);\n"); + } else { + builder.append( + "gl_Position = worldViewProjection * vec4(position, 0.0, 1.0);\n"); + if (vertexType == LoopBlinnExterior) { + builder.append( + "v_klm = klm;\n"); + } + } + + if (fillType == TextureFill) { + builder.append( + "texCoord = texMatrix * position;\n"); + } + + builder.append( + "}\n"); + + return builder.toString(); } +// static +String Shader::generateFragment(Shader::VertexType vertexType, Shader::FillType fillType, Shader::AntialiasType antialiasType) +{ + StringBuilder builder; + builder.append( + "#ifdef GL_ES\n" + "precision mediump float;\n" + "#endif\n"); + + if (vertexType == LoopBlinnExterior) { + if (antialiasType == Antialiased) { + builder.append( + "#extension GL_OES_standard_derivatives : enable\n"); + } + builder.append( + "varying vec3 v_klm;\n"); + } + + switch (fillType) { + case SolidFill: + builder.append( + "uniform vec4 color;\n"); + break; + case TextureFill: + builder.append( + "uniform sampler2D sampler;\n" + "uniform float globalAlpha;\n" + "varying vec3 texCoord;\n"); + break; + } + + builder.append( + "void main() {\n"); + + if (vertexType != LoopBlinnExterior) { + builder.append( + "float alpha = 1.0;\n"); + } else { + if (antialiasType == Antialiased) { + builder.append( + " // Gradients\n" + " vec3 px = dFdx(v_klm);\n" + " vec3 py = dFdy(v_klm);\n" + "\n" + " // Chain rule\n" + " float k2 = v_klm.x * v_klm.x;\n" + " float c = k2 * v_klm.x - v_klm.y * v_klm.z;\n" + " float k23 = 3.0 * k2;\n" + " float cx = k23 * px.x - v_klm.z * px.y - v_klm.y * px.z;\n" + " float cy = k23 * py.x - v_klm.z * py.y - v_klm.y * py.z;\n" + "\n" + " // Signed distance\n" + " float sd = c / sqrt(cx * cx + cy * cy);\n" + "\n" + " // Linear alpha\n" + " // FIXME: figure out why this needs to be\n" + " // negated compared to the HLSL version, and also why\n" + " // we need an adjustment by +1.0 for it to look good.\n" + " // float alpha = clamp(0.5 - sd, 0.0, 1.0);\n" + " float alpha = clamp(sd + 0.5, 0.0, 1.0);\n"); + } else { + builder.append( + " float t = v_klm.x * v_klm.x * v_klm.x - v_klm.y * v_klm.z;\n" + " float alpha = clamp(sign(t), 0.0, 1.0);\n"); + } + } + + switch (fillType) { + case SolidFill: + builder.append( + "gl_FragColor = color * alpha;\n"); + break; + case TextureFill: + builder.append( + "gl_FragColor = texture2D(sampler, texCoord.xy) * alpha * globalAlpha;\n"); + break; + } + + builder.append( + "}\n"); + + return builder.toString(); +} + +} // namespace WebCore + #endif diff --git a/Source/WebCore/platform/graphics/gpu/Shader.h b/Source/WebCore/platform/graphics/gpu/Shader.h index 4f62ca9..35d1a3b 100644 --- a/Source/WebCore/platform/graphics/gpu/Shader.h +++ b/Source/WebCore/platform/graphics/gpu/Shader.h @@ -33,6 +33,7 @@ #include <wtf/Noncopyable.h> #include <wtf/PassOwnPtr.h> +#include <wtf/text/WTFString.h> namespace WebCore { @@ -42,13 +43,35 @@ class Color; class Shader { WTF_MAKE_NONCOPYABLE(Shader); +public: + enum VertexType { + TwoDimensional, + LoopBlinnInterior, + LoopBlinnExterior + }; + + enum FillType { + SolidFill, + TextureFill + }; + + // Currently only applies to the Loop-Blinn vertex type. + enum AntialiasType { + NotAntialiased, + Antialiased + }; + protected: Shader(GraphicsContext3D*, unsigned program); ~Shader(); + static String generateVertex(VertexType, FillType); + static String generateFragment(VertexType, FillType, AntialiasType); + static void affineTo3x3(const AffineTransform&, float mat[9]); - static unsigned loadShader(GraphicsContext3D*, unsigned type, const char* shaderSource); - static unsigned loadProgram(GraphicsContext3D*, const char* vertexShaderSource, const char* fragmentShaderSource); + static void affineTo4x4(const AffineTransform&, float mat[16]); + static unsigned loadShader(GraphicsContext3D*, unsigned type, const String& shaderSource); + static unsigned loadProgram(GraphicsContext3D*, const String& vertexShaderSource, const String& fragmentShaderSource); GraphicsContext3D* m_context; unsigned m_program; diff --git a/Source/WebCore/platform/graphics/gpu/SolidFillShader.cpp b/Source/WebCore/platform/graphics/gpu/SolidFillShader.cpp index 86079be..78381f0 100644 --- a/Source/WebCore/platform/graphics/gpu/SolidFillShader.cpp +++ b/Source/WebCore/platform/graphics/gpu/SolidFillShader.cpp @@ -49,23 +49,9 @@ SolidFillShader::SolidFillShader(GraphicsContext3D* context, unsigned program) PassOwnPtr<SolidFillShader> SolidFillShader::create(GraphicsContext3D* context) { - static const char* vertexShaderSource = - "uniform mat3 matrix;\n" - "uniform vec4 color;\n" - "attribute vec3 position;\n" - "void main() {\n" - " gl_Position = vec4(matrix * position, 1.0);\n" - "}\n"; - static const char* fragmentShaderSource = - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "uniform mat3 matrix;\n" - "uniform vec4 color;\n" - "void main() {\n" - " gl_FragColor = color;\n" - "}\n"; - unsigned program = loadProgram(context, vertexShaderSource, fragmentShaderSource); + unsigned program = loadProgram(context, + generateVertex(Shader::TwoDimensional, Shader::SolidFill), + generateFragment(Shader::TwoDimensional, Shader::SolidFill, Shader::NotAntialiased)); if (!program) return 0; return new SolidFillShader(context, program); diff --git a/Source/WebCore/platform/graphics/gpu/TexShader.cpp b/Source/WebCore/platform/graphics/gpu/TexShader.cpp index d7ffa17..9eb5c16 100644 --- a/Source/WebCore/platform/graphics/gpu/TexShader.cpp +++ b/Source/WebCore/platform/graphics/gpu/TexShader.cpp @@ -43,33 +43,16 @@ TexShader::TexShader(GraphicsContext3D* context, unsigned program) { m_matrixLocation = context->getUniformLocation(program, "matrix"); m_texMatrixLocation = context->getUniformLocation(program, "texMatrix"); - m_alphaLocation = context->getUniformLocation(program, "alpha"); + m_alphaLocation = context->getUniformLocation(program, "globalAlpha"); m_positionLocation = context->getAttribLocation(program, "position"); m_samplerLocation = context->getUniformLocation(program, "sampler"); } PassOwnPtr<TexShader> TexShader::create(GraphicsContext3D* context) { - static const char* vertexShaderSource = - "uniform mat3 matrix;\n" - "uniform mat3 texMatrix;\n" - "attribute vec3 position;\n" - "varying vec3 texCoord;\n" - "void main() {\n" - " texCoord = texMatrix * position;\n" - " gl_Position = vec4(matrix * position, 1.0);\n" - "}\n"; - static const char* fragmentShaderSource = - "#ifdef GL_ES\n" - "precision mediump float;\n" - "#endif\n" - "uniform sampler2D sampler;\n" - "uniform float alpha;\n" - "varying vec3 texCoord;\n" - "void main() {\n" - " gl_FragColor = texture2D(sampler, texCoord.xy)* vec4(alpha);\n" - "}\n"; - unsigned program = loadProgram(context, vertexShaderSource, fragmentShaderSource); + unsigned program = loadProgram(context, + generateVertex(Shader::TwoDimensional, Shader::TextureFill), + generateFragment(Shader::TwoDimensional, Shader::TextureFill, Shader::NotAntialiased)); if (!program) return 0; return new TexShader(context, program); diff --git a/Source/WebCore/platform/graphics/gpu/Texture.cpp b/Source/WebCore/platform/graphics/gpu/Texture.cpp index e1f8114..b1ba827 100644 --- a/Source/WebCore/platform/graphics/gpu/Texture.cpp +++ b/Source/WebCore/platform/graphics/gpu/Texture.cpp @@ -166,7 +166,7 @@ void Texture::updateSubRect(void* pixels, const IntRect& updateRect) int tempBuffSize = // Temporary buffer size is the smaller of the max texture size or the updateRectSanitized min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRectSanitized.width()) * min(m_tiles.maxTextureSize(), m_tiles.borderTexels() + updateRectSanitized.height()); - OwnArrayPtr<uint32_t> tempBuff(new uint32_t[tempBuffSize]); + OwnArrayPtr<uint32_t> tempBuff = adoptArrayPtr(new uint32_t[tempBuffSize]); for (int tile = 0; tile < m_tiles.numTiles(); tile++) { // Intersect with tile diff --git a/Source/WebCore/platform/graphics/gpu/TilingData.cpp b/Source/WebCore/platform/graphics/gpu/TilingData.cpp index a98add7..e7c4e4c 100644 --- a/Source/WebCore/platform/graphics/gpu/TilingData.cpp +++ b/Source/WebCore/platform/graphics/gpu/TilingData.cpp @@ -89,9 +89,9 @@ IntRect TilingData::tileBoundsWithBorder(int tile) const if (m_borderTexels) { int x1 = bounds.x(); - int x2 = bounds.right(); + int x2 = bounds.maxX(); int y1 = bounds.y(); - int y2 = bounds.bottom(); + int y2 = bounds.maxY(); if (tileXIndex(tile) > 0) x1--; @@ -182,8 +182,8 @@ IntRect TilingData::overlappedTileIndices(const WebCore::IntRect &srcRect) const { int x = tileXIndexFromSrcCoord(srcRect.x()); int y = tileYIndexFromSrcCoord(srcRect.y()); - int r = tileXIndexFromSrcCoord(srcRect.right()); - int b = tileYIndexFromSrcCoord(srcRect.bottom()); + int r = tileXIndexFromSrcCoord(srcRect.maxX()); + int b = tileYIndexFromSrcCoord(srcRect.maxY()); return IntRect(x, y, r - x, b - y); } diff --git a/Source/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm b/Source/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm index e6dfdb8..13c0968 100644 --- a/Source/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm +++ b/Source/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm @@ -25,7 +25,7 @@ #include "config.h" -#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(3D_CANVAS) +#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(WEBGL) #include "DrawingBuffer.h" diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index c113c69..69bdeab 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -1384,11 +1384,15 @@ static HashSet<String> mimeTypeCache() // These formats are supported by GStreamer, but not // correctly advertised. - if (g_str_equal(name, "video/x-h264") - || g_str_equal(name, "audio/x-m4a")) { + if (g_str_equal(name, "video/x-h264")) { cache.add(String("video/mp4")); + cached = true; + } + + if (g_str_equal(name, "audio/x-m4a")) { cache.add(String("audio/aac")); cache.add(String("audio/mp4")); + cache.add(String("audio/x-m4a")); cached = true; } @@ -1466,6 +1470,15 @@ static HashSet<String> mimeTypeCache() for (int index = 0; extensions[index]; index++) { if (g_str_equal(extensions[index], "m4v")) cache.add(String("video/x-m4v")); + + // Workaround for + // https://bugzilla.gnome.org/show_bug.cgi?id=640709. + // typefindfunctions <= 0.10.32 doesn't + // register the H264 typefinder correctly so + // as a workaround we check the registered + // file extensions for it. + if (g_str_equal(extensions[index], "h264")) + cache.add(String("video/mp4")); } } } diff --git a/Source/WebCore/platform/graphics/gtk/FontGtk.cpp b/Source/WebCore/platform/graphics/gtk/FontGtk.cpp index 4d6f509..216fb56 100644 --- a/Source/WebCore/platform/graphics/gtk/FontGtk.cpp +++ b/Source/WebCore/platform/graphics/gtk/FontGtk.cpp @@ -215,6 +215,11 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + static void drawGlyphsShadow(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point, PangoLayoutLine* layoutLine, PangoRegionType renderRegion) { ContextShadow* shadow = graphicsContext->contextShadow(); diff --git a/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.cpp index ce7ec46..017b1e4 100644 --- a/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.cpp +++ b/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.cpp @@ -31,7 +31,7 @@ FontCustomPlatformData::~FontCustomPlatformData() { } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode) { return FontPlatformData(size, bold, italic); } diff --git a/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.h index 86f99b2..7ffe89a 100644 --- a/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/haiku/FontCustomPlatformData.h @@ -23,6 +23,7 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include <wtf/Forward.h> namespace WebCore { @@ -38,7 +39,7 @@ namespace WebCore { static bool supportsFormat(const String&); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); }; FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); diff --git a/Source/WebCore/platform/graphics/haiku/FontHaiku.cpp b/Source/WebCore/platform/graphics/haiku/FontHaiku.cpp index 819fecb..5a1a1d0 100644 --- a/Source/WebCore/platform/graphics/haiku/FontHaiku.cpp +++ b/Source/WebCore/platform/graphics/haiku/FontHaiku.cpp @@ -65,6 +65,11 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { diff --git a/Source/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp b/Source/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp index b1e7082..4ded761 100644 --- a/Source/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp +++ b/Source/WebCore/platform/graphics/haiku/SimpleFontDataHaiku.cpp @@ -48,11 +48,11 @@ void SimpleFontData::platformInit() font_height height; font->GetHeight(&height); - m_ascent = static_cast<int>(height.ascent); - m_descent = static_cast<int>(height.descent); - m_lineSpacing = m_ascent + m_descent; - m_xHeight = height.ascent * 0.56f; // Hack taken from the win port. - m_lineGap = height.leading; + m_fontMetrics.setAscent(height.ascent); + m_fontMetrics.setDescent(height.descent); + m_fontMetrics.setXHeight(height.ascent * 0.56f); // Hack taken from the win port. + m_fontMetrics.setLineGap(height.leading); + m_fontMetrics.setLineSpacing(lroundf(height.ascent) + lroundf(height.descent) + lroundf(height.leading)); } void SimpleFontData::platformCharWidthInit() diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp b/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp index 86f6bec..1fe3d28 100644 --- a/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp +++ b/Source/WebCore/platform/graphics/mac/ComplexTextController.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,14 +25,13 @@ #include "config.h" #include "ComplexTextController.h" -#include <ApplicationServices/ApplicationServices.h> -#include "CharacterNames.h" #include "FloatSize.h" #include "Font.h" #include "TextBreakIterator.h" #include "TextRun.h" - +#include <ApplicationServices/ApplicationServices.h> #include <wtf/StdLibExtras.h> +#include <wtf/unicode/CharacterNames.h> #if defined(BUILDING_ON_LEOPARD) // Undefined when compiling agains the 10.5 SDK. @@ -71,7 +70,8 @@ ComplexTextController::ComplexTextController(const Font* font, const TextRun& ru , m_glyphInCurrentRun(0) , m_characterInCurrentGlyph(0) , m_finalRoundingWidth(0) - , m_padding(run.padding()) + , m_expansion(run.expansion()) + , m_afterExpansion(true) , m_fallbackFonts(fallbackFonts) , m_minGlyphBoundingBoxX(numeric_limits<float>::max()) , m_maxGlyphBoundingBoxX(numeric_limits<float>::min()) @@ -79,19 +79,18 @@ ComplexTextController::ComplexTextController(const Font* font, const TextRun& ru , m_maxGlyphBoundingBoxY(numeric_limits<float>::min()) , m_lastRoundingGlyph(0) { - if (!m_padding) - m_padPerSpace = 0; + if (!m_expansion) + m_expansionPerOpportunity = 0; else { - int numSpaces = 0; - for (int s = 0; s < m_run.length(); s++) { - if (Font::treatAsSpace(m_run[s])) - numSpaces++; - } + bool isAfterExpansion = true; + unsigned expansionOpportunityCount = Font::expansionOpportunityCount(m_run.characters(), m_end, m_run.ltr() ? LTR : RTL, isAfterExpansion); + if (isAfterExpansion && !m_run.allowsTrailingExpansion()) + expansionOpportunityCount--; - if (!numSpaces) - m_padPerSpace = 0; + if (!expansionOpportunityCount) + m_expansionPerOpportunity = 0; else - m_padPerSpace = m_padding / numSpaces; + m_expansionPerOpportunity = m_expansion / expansionOpportunityCount; } collectComplexTextRuns(); @@ -118,9 +117,9 @@ int ComplexTextController::offsetForPosition(float h, bool includePartialGlyphs) CFIndex hitGlyphStart = complexTextRun.indexAt(j); CFIndex hitGlyphEnd; if (m_run.ltr()) - hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.stringLength())); + hitGlyphEnd = max<CFIndex>(hitGlyphStart, j + 1 < complexTextRun.glyphCount() ? complexTextRun.indexAt(j + 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); else - hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.stringLength())); + hitGlyphEnd = max<CFIndex>(hitGlyphStart, j > 0 ? complexTextRun.indexAt(j - 1) : static_cast<CFIndex>(complexTextRun.indexEnd())); // FIXME: Instead of dividing the glyph's advance equally between the characters, this // could use the glyph's "ligature carets". However, there is no Core Text API to get the @@ -311,6 +310,7 @@ ComplexTextController::ComplexTextRun::ComplexTextRun(const SimpleFontData* font , m_characters(characters) , m_stringLocation(stringLocation) , m_stringLength(stringLength) + , m_indexEnd(stringLength) , m_isMonotonic(true) { #if USE(CORE_TEXT) && USE(ATSUI) @@ -335,7 +335,7 @@ void ComplexTextController::ComplexTextRun::setIsNonMonotonic() m_glyphEndOffsets.grow(m_glyphCount); for (size_t i = 0; i < m_glyphCount; ++i) { - CFIndex nextMappedIndex = m_stringLength; + CFIndex nextMappedIndex = m_indexEnd; for (size_t j = indexAt(i) + 1; j < m_stringLength; ++j) { if (mappedIndices[j]) { nextMappedIndex = j; @@ -370,9 +370,9 @@ void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) unsigned glyphEndOffset; if (complexTextRun.isMonotonic()) { if (ltr) - glyphEndOffset = max<unsigned>(glyphStartOffset, g + 1 < glyphCount ? static_cast<unsigned>(complexTextRun.indexAt(g + 1)) : complexTextRun.stringLength()); + glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g + 1 < glyphCount ? complexTextRun.indexAt(g + 1) : complexTextRun.indexEnd())); else - glyphEndOffset = max<unsigned>(glyphStartOffset, g > 0 ? static_cast<unsigned>(complexTextRun.indexAt(g - 1)) : complexTextRun.stringLength()); + glyphEndOffset = max<unsigned>(glyphStartOffset, static_cast<unsigned>(g > 0 ? complexTextRun.indexAt(g - 1) : complexTextRun.indexEnd())); } else glyphEndOffset = complexTextRun.endOffsetAt(g); @@ -386,7 +386,7 @@ void ComplexTextController::advance(unsigned offset, GlyphBuffer* glyphBuffer) unsigned oldCharacterInCurrentGlyph = m_characterInCurrentGlyph; m_characterInCurrentGlyph = min(m_currentCharacter - complexTextRun.stringLocation(), glyphEndOffset) - glyphStartOffset; - // FIXME: Instead of dividing the glyph's advance equially between the characters, this + // FIXME: Instead of dividing the glyph's advance equally between the characters, this // could use the glyph's "ligature carets". However, there is no Core Text API to get the // ligature carets. if (glyphStartOffset == glyphEndOffset) { @@ -421,6 +421,7 @@ void ComplexTextController::adjustGlyphsAndAdvances() { CGFloat widthSinceLastRounding = 0; size_t runCount = m_complexTextRuns.size(); + bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_expansion) && !m_run.spacingDisabled(); for (size_t r = 0; r < runCount; ++r) { ComplexTextRun& complexTextRun = *m_complexTextRuns[r]; unsigned glyphCount = complexTextRun.glyphCount(); @@ -433,7 +434,6 @@ void ComplexTextController::adjustGlyphsAndAdvances() const UChar* cp = complexTextRun.characters(); CGFloat roundedSpaceWidth = roundCGFloat(fontData->spaceWidth()); bool roundsAdvances = !m_font.isPrinterFont() && fontData->platformData().roundsGlyphAdvances(); - bool hasExtraSpacing = (m_font.letterSpacing() || m_font.wordSpacing() || m_padding) && !m_run.spacingDisabled(); CGPoint glyphOrigin = CGPointZero; CFIndex lastCharacterIndex = m_run.ltr() ? numeric_limits<CFIndex>::min() : numeric_limits<CFIndex>::max(); bool isMonotonic = true; @@ -489,25 +489,29 @@ void ComplexTextController::adjustGlyphsAndAdvances() advance.width += m_font.letterSpacing(); // Handle justification and word-spacing. - if (treatAsSpace) { - // Account for padding. WebCore uses space padding to justify text. - // We distribute the specified padding over the available spaces in the run. - if (m_padding) { - // Use leftover padding if not evenly divisible by number of spaces. - if (m_padding < m_padPerSpace) { - advance.width += m_padding; - m_padding = 0; - } else { - float previousPadding = m_padding; - m_padding -= m_padPerSpace; - advance.width += roundf(previousPadding) - roundf(m_padding); + if (treatAsSpace || Font::isCJKIdeographOrSymbol(ch)) { + // Distribute the run's total expansion evenly over all expansion opportunities in the run. + if (m_expansion && (!lastGlyph || m_run.allowsTrailingExpansion())) { + float previousExpansion = m_expansion; + if (!treatAsSpace && !m_afterExpansion) { + // Take the expansion opportunity before this ideograph. + m_expansion -= m_expansionPerOpportunity; + int expansion = roundf(previousExpansion) - roundf(m_expansion); + m_totalWidth += expansion; + m_adjustedAdvances.last().width += expansion; + previousExpansion = m_expansion; } - } + m_expansion -= m_expansionPerOpportunity; + advance.width += roundf(previousExpansion) - roundf(m_expansion); + m_afterExpansion = true; + } else + m_afterExpansion = false; // Account for word-spacing. - if (characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) + if (treatAsSpace && characterIndex > 0 && !Font::treatAsSpace(*m_run.data(characterIndex - 1)) && m_font.wordSpacing()) advance.width += m_font.wordSpacing(); - } + } else + m_afterExpansion = false; } // Deal with the float/integer impedance mismatch between CG and WebCore. "Words" (characters @@ -549,9 +553,9 @@ void ComplexTextController::adjustGlyphsAndAdvances() FloatRect glyphBounds = fontData->boundsForGlyph(glyph); glyphBounds.move(glyphOrigin.x, glyphOrigin.y); m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); - m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.right()); + m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); - m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.bottom()); + m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); glyphOrigin.x += advance.width; glyphOrigin.y += advance.height; diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextController.h b/Source/WebCore/platform/graphics/mac/ComplexTextController.h index 9cf80a6..63f93a2 100644 --- a/Source/WebCore/platform/graphics/mac/ComplexTextController.h +++ b/Source/WebCore/platform/graphics/mac/ComplexTextController.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2008, 2009, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -72,9 +72,9 @@ private: class ComplexTextRun : public RefCounted<ComplexTextRun> { public: #if USE(CORE_TEXT) - static PassRefPtr<ComplexTextRun> create(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) + static PassRefPtr<ComplexTextRun> create(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, CFRange runRange) { - return adoptRef(new ComplexTextRun(ctRun, fontData, characters, stringLocation, stringLength)); + return adoptRef(new ComplexTextRun(ctRun, fontData, characters, stringLocation, stringLength, runRange)); } #endif #if USE(ATSUI) @@ -94,6 +94,7 @@ private: unsigned stringLocation() const { return m_stringLocation; } size_t stringLength() const { return m_stringLength; } ALWAYS_INLINE CFIndex indexAt(size_t i) const; + CFIndex indexEnd() const { return m_indexEnd; } CFIndex endOffsetAt(size_t i) const { ASSERT(!m_isMonotonic); return m_glyphEndOffsets[i]; } const CGGlyph* glyphs() const { return m_glyphs; } const CGSize* advances() const { return m_advances; } @@ -102,7 +103,7 @@ private: private: #if USE(CORE_TEXT) - ComplexTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength); + ComplexTextRun(CTRunRef, const SimpleFontData*, const UChar* characters, unsigned stringLocation, size_t stringLength, CFRange runRange); void createTextRunFromFontDataCoreText(bool ltr); #endif #if USE(ATSUI) @@ -133,6 +134,7 @@ private: #if USE(ATSUI) Vector<CFIndex, 64> m_atsuiIndices; #endif + CFIndex m_indexEnd; Vector<CFIndex, 64> m_glyphEndOffsets; Vector<CGGlyph, 64> m_glyphsVector; const CGGlyph* m_glyphs; @@ -175,8 +177,9 @@ private: unsigned m_glyphInCurrentRun; unsigned m_characterInCurrentGlyph; float m_finalRoundingWidth; - float m_padding; - float m_padPerSpace; + float m_expansion; + float m_expansionPerOpportunity; + bool m_afterExpansion; HashSet<const SimpleFontData*>* m_fallbackFonts; diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp b/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp index 9c2ab6b..b367fdf 100644 --- a/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp +++ b/Source/WebCore/platform/graphics/mac/ComplexTextControllerATSUI.cpp @@ -25,10 +25,10 @@ #if USE(ATSUI) -#include "CharacterNames.h" #include "Font.h" #include "ShapeArabic.h" #include "TextRun.h" +#include <wtf/unicode/CharacterNames.h> #ifdef __LP64__ // ATSUTextInserted() is SPI in 64-bit. @@ -146,6 +146,7 @@ ComplexTextController::ComplexTextRun::ComplexTextRun(ATSUTextLayout atsuTextLay , m_characters(characters) , m_stringLocation(stringLocation) , m_stringLength(stringLength) + , m_indexEnd(stringLength) , m_directionalOverride(directionalOverride) , m_isMonotonic(true) { diff --git a/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp b/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp index 07fb153..239113f 100644 --- a/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp +++ b/Source/WebCore/platform/graphics/mac/ComplexTextControllerCoreText.cpp @@ -42,12 +42,13 @@ extern const CFStringRef kCTTypesetterOptionForcedEmbeddingLevel; namespace WebCore { -ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength) +ComplexTextController::ComplexTextRun::ComplexTextRun(CTRunRef ctRun, const SimpleFontData* fontData, const UChar* characters, unsigned stringLocation, size_t stringLength, CFRange runRange) : m_coreTextRun(ctRun) , m_fontData(fontData) , m_characters(characters) , m_stringLocation(stringLocation) , m_stringLength(stringLength) + , m_indexEnd(runRange.location + runRange.length) , m_isMonotonic(true) { m_glyphCount = CTRunGetGlyphCount(m_coreTextRun.get()); @@ -165,7 +166,8 @@ void ComplexTextController::collectComplexTextRunsForCharactersCoreText(const UC for (CFIndex r = 0; r < runCount; r++) { CTRunRef ctRun = static_cast<CTRunRef>(CFArrayGetValueAtIndex(runArray, r)); ASSERT(CFGetTypeID(ctRun) == CTRunGetTypeID()); - m_complexTextRuns.append(ComplexTextRun::create(ctRun, fontData, cp, stringLocation, length)); + CFRange runRange = CTRunGetStringRange(ctRun); + m_complexTextRuns.append(ComplexTextRun::create(ctRun, fontData, cp, stringLocation, length, runRange)); } } diff --git a/Source/WebCore/platform/graphics/mac/FontCacheMac.mm b/Source/WebCore/platform/graphics/mac/FontCacheMac.mm index 068bd8e..c254906 100644 --- a/Source/WebCore/platform/graphics/mac/FontCacheMac.mm +++ b/Source/WebCore/platform/graphics/mac/FontCacheMac.mm @@ -211,7 +211,7 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD bool syntheticBold = isAppKitFontWeightBold(weight) && !isAppKitFontWeightBold(actualWeight); bool syntheticOblique = (traits & NSFontItalicTrait) && !(actualTraits & NSFontItalicTrait); - return new FontPlatformData(platformFont, size, syntheticBold, syntheticOblique, fontDescription.orientation()); + return new FontPlatformData(platformFont, size, syntheticBold, syntheticOblique, fontDescription.orientation(), fontDescription.widthVariant()); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp b/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp index 02bac9c..865051d 100644 --- a/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp +++ b/Source/WebCore/platform/graphics/mac/FontComplexTextMac.cpp @@ -111,8 +111,8 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon { ComplexTextController controller(this, run, true, fallbackFonts); if (glyphOverflow) { - glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - ascent()); - glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - descent()); + glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - fontMetrics().ascent()); + glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - fontMetrics().descent()); glyphOverflow->left = max<int>(0, ceilf(-controller.minGlyphBoundingBoxX())); glyphOverflow->right = max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.totalWidth())); } diff --git a/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp index d04d0e4..f2bc33d 100644 --- a/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp +++ b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.cpp @@ -38,9 +38,9 @@ FontCustomPlatformData::~FontCustomPlatformData() CGFontRelease(m_cgFont); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant widthVariant, FontRenderingMode) { - return FontPlatformData(m_cgFont, size, bold, italic, orientation); + return FontPlatformData(m_cgFont, size, bold, italic, orientation, widthVariant); } FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) diff --git a/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h index 7043d7e..c7ae1ca 100644 --- a/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/mac/FontCustomPlatformData.h @@ -23,6 +23,7 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include <CoreFoundation/CFBase.h> #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -47,7 +48,7 @@ public: ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); static bool supportsFormat(const String&); diff --git a/Source/WebCore/platform/graphics/mac/FontMac.mm b/Source/WebCore/platform/graphics/mac/FontMac.mm index 8519667..acd7562 100644 --- a/Source/WebCore/platform/graphics/mac/FontMac.mm +++ b/Source/WebCore/platform/graphics/mac/FontMac.mm @@ -47,6 +47,11 @@ bool Font::canReturnFallbackFontsForComplexText() return true; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return true; +} + static void showGlyphsWithAdvances(const SimpleFontData* font, CGContextRef context, const CGGlyph* glyphs, const CGSize* advances, size_t count) { const FontPlatformData& platformData = font->platformData(); @@ -60,8 +65,8 @@ static void showGlyphsWithAdvances(const SimpleFontData* font, CGContextRef cont savedMatrix = CGContextGetTextMatrix(context); CGAffineTransform runMatrix = CGAffineTransformConcat(savedMatrix, rotateLeftTransform); // Move start point to put glyphs into original region. - runMatrix.tx = savedMatrix.tx + font->ascent(); - runMatrix.ty = savedMatrix.ty + font->descent(); + runMatrix.tx = savedMatrix.tx + font->fontMetrics().ascent(); + runMatrix.ty = savedMatrix.ty + font->fontMetrics().descent(); CGContextSetTextMatrix(context, runMatrix); } diff --git a/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp b/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp index 5388c24..8b04ffa 100644 --- a/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp +++ b/Source/WebCore/platform/graphics/mac/GlyphPageTreeNodeMac.cpp @@ -39,8 +39,8 @@ namespace WebCore { #ifndef BUILDING_ON_TIGER static bool shouldUseCoreText(UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { - if (fontData->orientation() == Vertical && !fontData->isBrokenIdeographFont()) { - // Ideographs don't have a vertical variant. + if (fontData->platformData().widthVariant() != RegularWidth || (fontData->orientation() == Vertical && !fontData->isBrokenIdeographFont())) { + // Ideographs don't have a vertical variant or width variants. for (unsigned i = 0; i < bufferLength; ++i) { if (!Font::isCJKIdeograph(buffer[i])) return true; diff --git a/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm b/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm index 21eb59d..aaa250b 100644 --- a/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm +++ b/Source/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm @@ -25,7 +25,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" @@ -245,6 +245,10 @@ bool GraphicsContext3D::isGLES2Compliant() const return false; } +void GraphicsContext3D::setContextLostCallback(PassOwnPtr<ContextLostCallback>) +{ +} + } -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm b/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm index 2361f6a..8b1fb92 100644 --- a/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm +++ b/Source/WebCore/platform/graphics/mac/MediaPlayerPrivateQTKit.mm @@ -616,7 +616,7 @@ QTTime MediaPlayerPrivateQTKit::createQTTime(float time) const if (!metaDataAvailable()) return QTMakeTime(0, 600); long timeScale = [[m_qtMovie.get() attributeForKey:QTMovieTimeScaleAttribute] longValue]; - return QTMakeTime(time * timeScale, timeScale); + return QTMakeTime(lroundf(time * timeScale), timeScale); } void MediaPlayerPrivateQTKit::resumeLoad() diff --git a/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm b/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm index 92585c6..3094498 100644 --- a/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm +++ b/Source/WebCore/platform/graphics/mac/SimpleFontDataMac.mm @@ -218,19 +218,20 @@ void SimpleFontData::platformInit() int iAscent; int iDescent; int iLineGap; + unsigned unitsPerEm; #ifdef BUILDING_ON_TIGER - wkGetFontMetrics(m_platformData.cgFont(), &iAscent, &iDescent, &iLineGap, &m_unitsPerEm); + wkGetFontMetrics(m_platformData.cgFont(), &iAscent, &iDescent, &iLineGap, &unitsPerEm); #else iAscent = CGFontGetAscent(m_platformData.cgFont()); iDescent = CGFontGetDescent(m_platformData.cgFont()); iLineGap = CGFontGetLeading(m_platformData.cgFont()); - m_unitsPerEm = CGFontGetUnitsPerEm(m_platformData.cgFont()); + unitsPerEm = CGFontGetUnitsPerEm(m_platformData.cgFont()); #endif float pointSize = m_platformData.m_size; - float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize; - float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize; - float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize; + float ascent = scaleEmToUnits(iAscent, unitsPerEm) * pointSize; + float descent = -scaleEmToUnits(iDescent, unitsPerEm) * pointSize; + float lineGap = scaleEmToUnits(iLineGap, unitsPerEm) * pointSize; // We need to adjust Times, Helvetica, and Courier to closely match the // vertical metrics of their Microsoft counterparts that are the de facto @@ -239,25 +240,23 @@ void SimpleFontData::platformInit() // and add it to the ascent. NSString *familyName = [m_platformData.font() familyName]; if ([familyName isEqualToString:@"Times"] || [familyName isEqualToString:@"Helvetica"] || [familyName isEqualToString:@"Courier"]) - fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); + ascent += floorf(((ascent + descent) * 0.15f) + 0.5f); else if ([familyName isEqualToString:@"Geeza Pro"]) { // Geeza Pro has glyphs that draw slightly above the ascent or far below the descent. Adjust // those vertical metrics to better match reality, so that diacritics at the bottom of one line // do not overlap diacritics at the top of the next line. - fAscent *= 1.08f; - fDescent *= 2.f; + ascent *= 1.08f; + descent *= 2.f; } - m_ascent = lroundf(fAscent); - m_descent = lroundf(fDescent); - m_lineGap = lroundf(fLineGap); - m_lineSpacing = m_ascent + m_descent + m_lineGap; - + // Compute and store line spacing, before the line metrics hacks are applied. + m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); + // Hack Hiragino line metrics to allow room for marked text underlines. // <rdar://problem/5386183> - if (m_descent < 3 && m_lineGap >= 3 && [familyName hasPrefix:@"Hiragino"]) { - m_lineGap -= 3 - m_descent; - m_descent = 3; + if (descent < 3 && lineGap >= 3 && [familyName hasPrefix:@"Hiragino"]) { + lineGap -= 3 - descent; + descent = 3; } if (m_orientation == Vertical) { @@ -278,6 +277,8 @@ void SimpleFontData::platformInit() m_orientation = Horizontal; } + float xHeight; + // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font. // Unfortunately, NSFont will round this for us so we don't quite get the right value. GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); @@ -288,21 +289,27 @@ void SimpleFontData::platformInit() // and web pages that foolishly use this metric for width will be laid out // poorly if we return an accurate height. Classic case is Times 13 point, // which has an "x" that is 7x6 pixels. - m_xHeight = static_cast<float>(max(CGRectGetMaxX(xBox), -CGRectGetMinY(xBox))); + xHeight = static_cast<float>(max(CGRectGetMaxX(xBox), -CGRectGetMinY(xBox))); } else { #ifndef BUILDING_ON_TIGER - m_xHeight = static_cast<float>(CGFontGetXHeight(m_platformData.cgFont())) / m_unitsPerEm; + xHeight = static_cast<float>(CGFontGetXHeight(m_platformData.cgFont())) / unitsPerEm; #else - m_xHeight = m_platformData.font() ? [m_platformData.font() xHeight] : 0; + xHeight = m_platformData.font() ? [m_platformData.font() xHeight] : 0; #endif // CGFontGetXHeight() returns a wrong value for "Apple Symbols" font (a float close to 0, but not strictly 0). - // The following code makes a guess for m_xHeight in that case. + // The following code makes a guess for xHeight in that case. // The int cast is a workaround for the "almost" zero value returned by CGFontGetXHeight(). - if (!static_cast<int>(m_xHeight) && fAscent) - m_xHeight = 2 * fAscent / 3; + if (!static_cast<int>(xHeight) && ascent) + xHeight = 2 * ascent / 3; } + + m_fontMetrics.setUnitsPerEm(unitsPerEm); + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setXHeight(xHeight); } - + static CFDataRef copyFontTableForTag(FontPlatformData& platformData, FourCharCode tableName) { #ifdef BUILDING_ON_TIGER @@ -337,7 +344,7 @@ void SimpleFontData::platformCharWidthInit() if (os2Table && CFDataGetLength(os2Table.get()) >= 4) { const UInt8* os2 = CFDataGetBytePtr(os2Table.get()); SInt16 os2AvgCharWidth = os2[2] * 256 + os2[3]; - m_avgCharWidth = scaleEmToUnits(os2AvgCharWidth, m_unitsPerEm) * m_platformData.m_size; + m_avgCharWidth = scaleEmToUnits(os2AvgCharWidth, m_fontMetrics.unitsPerEm()) * m_platformData.m_size; } RetainPtr<CFDataRef> headTable(AdoptCF, copyFontTableForTag(m_platformData, 'head')); @@ -348,7 +355,7 @@ void SimpleFontData::platformCharWidthInit() SInt16 xMin = static_cast<SInt16>(uxMin); SInt16 xMax = static_cast<SInt16>(uxMax); float diff = static_cast<float>(xMax - xMin); - m_maxCharWidth = scaleEmToUnits(diff, m_unitsPerEm) * m_platformData.m_size; + m_maxCharWidth = scaleEmToUnits(diff, m_fontMetrics.unitsPerEm()) * m_platformData.m_size; } // Fallback to a cross-platform estimate, which will populate these values if they are non-positive. @@ -387,7 +394,7 @@ SimpleFontData* SimpleFontData::scaledFontData(const FontDescription& fontDescri BEGIN_BLOCK_OBJC_EXCEPTIONS; float size = m_platformData.size() * scaleFactor; - FontPlatformData scaledFontData([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toSize:size], size); + FontPlatformData scaledFontData([[NSFontManager sharedFontManager] convertFont:m_platformData.font() toSize:size], size, false, false, m_platformData.orientation()); // AppKit resets the type information (screen/printer) when you convert a font to a different size. // We have to fix up the font that we're handed back. @@ -470,13 +477,13 @@ FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const #ifndef BUILDING_ON_TIGER boundingBox = CTFontGetBoundingRectsForGlyphs(m_platformData.ctFont(), orientation() == Vertical ? kCTFontVerticalOrientation : kCTFontHorizontalOrientation, &glyph, 0, 1); - boundingBox.setY(-boundingBox.bottom()); + boundingBox.setY(-boundingBox.maxY()); #else // FIXME: Custom fonts don't have NSFonts, so this function doesn't compute correct bounds for these on Tiger. if (!m_platformData.font()) return boundingBox; boundingBox = [m_platformData.font() boundingRectForGlyph:glyph]; - boundingBox.setY(-boundingBox.bottom()); + boundingBox.setY(-boundingBox.maxY()); #endif if (m_syntheticBoldOffset) boundingBox.setWidth(boundingBox.width() + m_syntheticBoldOffset); diff --git a/Source/WebCore/platform/graphics/mac/WebGLLayer.mm b/Source/WebCore/platform/graphics/mac/WebGLLayer.mm index c24181b..d1007b9 100644 --- a/Source/WebCore/platform/graphics/mac/WebGLLayer.mm +++ b/Source/WebCore/platform/graphics/mac/WebGLLayer.mm @@ -26,7 +26,7 @@ #include "config.h" #if USE(ACCELERATED_COMPOSITING) -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #import "WebGLLayer.h" @@ -163,5 +163,5 @@ static void freeData(void *, const void *data, size_t /* size */) @end -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) #endif // USE(ACCELERATED_COMPOSITING) diff --git a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp index 3eb5196..df45147 100644 --- a/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp +++ b/Source/WebCore/platform/graphics/opengl/Extensions3DOpenGL.cpp @@ -25,7 +25,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "Extensions3DOpenGL.h" @@ -79,6 +79,10 @@ bool Extensions3DOpenGL::supports(const String& name) if (name == "GL_ANGLE_framebuffer_multisample") return m_availableExtensions.contains("GL_EXT_framebuffer_multisample"); + // Desktop GL always supports GL_OES_rgb8_rgba8. + if (name == "GL_OES_rgb8_rgba8") + return true; + // If GL_ARB_texture_float is available then we report GL_OES_texture_float and // GL_OES_texture_half_float as available. if (name == "GL_OES_texture_float" || name == "GL_OES_texture_half_float") @@ -125,4 +129,4 @@ void Extensions3DOpenGL::renderbufferStorageMultisample(unsigned long target, un } // namespace WebCore -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp index 7c103f3..c224e20 100644 --- a/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp +++ b/Source/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp @@ -25,7 +25,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" @@ -83,7 +83,7 @@ void GraphicsContext3D::paintRenderingResultsToCanvas(CanvasRenderingContext* co int rowBytes = m_currentWidth * 4; int totalBytes = rowBytes * m_currentHeight; - OwnArrayPtr<unsigned char> pixels(new unsigned char[totalBytes]); + OwnArrayPtr<unsigned char> pixels = adoptArrayPtr(new unsigned char[totalBytes]); if (!pixels) return; @@ -1437,28 +1437,6 @@ void GraphicsContext3D::deleteTexture(Platform3DObject texture) glDeleteTextures(1, &texture); } -uint32_t GraphicsContext3D::sizeInBytes(GC3Denum type) -{ - switch (type) { - case GL_BYTE: - return sizeof(GLbyte); - case GL_UNSIGNED_BYTE: - return sizeof(GLubyte); - case GL_SHORT: - return sizeof(GLshort); - case GL_UNSIGNED_SHORT: - return sizeof(GLushort); - case GL_INT: - return sizeof(GLint); - case GL_UNSIGNED_INT: - return sizeof(GLuint); - case GL_FLOAT: - return sizeof(GLfloat); - default: - return 0; - } -} - void GraphicsContext3D::synthesizeGLError(GC3Denum error) { m_syntheticErrors.add(error); @@ -1473,4 +1451,4 @@ Extensions3D* GraphicsContext3D::getExtensions() } -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/opengl/TextureMapperGL.cpp b/Source/WebCore/platform/graphics/opengl/TextureMapperGL.cpp index 03f9b7c..2e2082d 100644 --- a/Source/WebCore/platform/graphics/opengl/TextureMapperGL.cpp +++ b/Source/WebCore/platform/graphics/opengl/TextureMapperGL.cpp @@ -390,7 +390,7 @@ void TextureMapperGL::drawTexture(const BitmapTexture& texture, const IntRect& t const GLfloat unitRect[] = {0, 0, 1, 0, 1, 1, 0, 1}; GL_CMD(glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, unitRect)) - TransformationMatrix matrix = TransformationMatrix(data().projectionMatrix).multLeft(modelViewMatrix).multLeft(TransformationMatrix( + TransformationMatrix matrix = TransformationMatrix(data().projectionMatrix).multiply(modelViewMatrix).multiply(TransformationMatrix( targetRect.width(), 0, 0, 0, 0, targetRect.height(), 0, 0, 0, 0, 1, 0, @@ -606,7 +606,7 @@ void TextureMapperGL::paintToTarget(const BitmapTexture& aSurface, const IntSize const BitmapTextureGL& surface = static_cast<const BitmapTextureGL&>(aSurface); // Create the model-view-projection matrix to display on screen. - TransformationMatrix matrix = createProjectionMatrix(surfaceSize, true).multLeft(transform).multLeft( + TransformationMatrix matrix = createProjectionMatrix(surfaceSize, true).multiply(transform).multiply( TransformationMatrix( surface.m_actualSize.width(), 0, 0, 0, 0, surface.m_actualSize.height(), 0, 0, diff --git a/Source/WebCore/platform/graphics/pango/FontCustomPlatformDataPango.cpp b/Source/WebCore/platform/graphics/pango/FontCustomPlatformDataPango.cpp index a158689..f9d36d3 100644 --- a/Source/WebCore/platform/graphics/pango/FontCustomPlatformDataPango.cpp +++ b/Source/WebCore/platform/graphics/pango/FontCustomPlatformDataPango.cpp @@ -30,7 +30,7 @@ FontCustomPlatformData::~FontCustomPlatformData() { } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode) { return FontPlatformData(m_fontFace, size, bold, italic); } diff --git a/Source/WebCore/platform/graphics/pango/SimpleFontDataPango.cpp b/Source/WebCore/platform/graphics/pango/SimpleFontDataPango.cpp index d0bf836..3fe15b3 100644 --- a/Source/WebCore/platform/graphics/pango/SimpleFontDataPango.cpp +++ b/Source/WebCore/platform/graphics/pango/SimpleFontDataPango.cpp @@ -49,21 +49,27 @@ void SimpleFontData::platformInit() cairo_font_extents_t font_extents; cairo_text_extents_t text_extents; cairo_scaled_font_extents(m_platformData.m_scaledFont, &font_extents); - m_ascent = static_cast<int>(lroundf(font_extents.ascent)); - m_descent = static_cast<int>(lroundf(font_extents.descent)); - m_lineSpacing = static_cast<int>(lroundf(font_extents.height)); + + m_fontMetrics.setAscent(font_extents.ascent); + m_fontMetrics.setDescent(font_extents.descent); + // 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; + float lineSpacing = font_extents.height; + if (lineSpacing < font_extents.ascent + font_extents.descent) + lineSpacing = font_extents.ascent + font_extents.descent; + m_fontMetrics.setLineSpacing(lroundf(lineSpacing)); + m_fontMetrics.setLineGap(lineSpacing - font_extents.ascent - font_extents.descent); + cairo_scaled_font_text_extents(m_platformData.m_scaledFont, "x", &text_extents); - m_xHeight = text_extents.height; + m_fontMetrics.setXHeight(text_extents.height); + cairo_scaled_font_text_extents(m_platformData.m_scaledFont, " ", &text_extents); - m_spaceWidth = static_cast<float>(text_extents.x_advance); - m_lineGap = m_lineSpacing - m_ascent - m_descent; + m_spaceWidth = static_cast<float>(text_extents.x_advance); + m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; } diff --git a/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp b/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp index 37d6b44..9eb31a7 100644 --- a/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp +++ b/Source/WebCore/platform/graphics/qt/ContextShadowQt.cpp @@ -57,7 +57,7 @@ private: ShadowBuffer::ShadowBuffer(QObject* parent) : QObject(parent) - , timerId(0) + , timerId(-1) { } @@ -89,7 +89,8 @@ QImage* ShadowBuffer::scratchImage(const QSize& size) void ShadowBuffer::schedulePurge() { static const double BufferPurgeDelay = 2; // seconds - killTimer(timerId); + if (timerId >= 0) + killTimer(timerId); timerId = startTimer(BufferPurgeDelay * 1000); } diff --git a/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp b/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp index cd28f0e..5238d46 100644 --- a/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp +++ b/Source/WebCore/platform/graphics/qt/Extensions3DQt.cpp @@ -25,7 +25,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "Extensions3DQt.h" @@ -58,4 +58,4 @@ int Extensions3DQt::getGraphicsResetStatusARB() } // namespace WebCore -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h index 54fa679..e8441d2 100644 --- a/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/qt/FontCustomPlatformData.h @@ -24,6 +24,7 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include <wtf/FastAllocBase.h> #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -42,7 +43,7 @@ public: // for use with QFontDatabase::addApplicationFont/removeApplicationFont int m_handle; - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); static bool supportsFormat(const String&); }; diff --git a/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp b/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp index e2f009b..ec8747d 100644 --- a/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp +++ b/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp @@ -34,7 +34,7 @@ FontCustomPlatformData::~FontCustomPlatformData() QFontDatabase::removeApplicationFont(m_handle); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode) { QFont font; font.setFamily(QFontDatabase::applicationFontFamilies(m_handle)[0]); diff --git a/Source/WebCore/platform/graphics/qt/FontQt.cpp b/Source/WebCore/platform/graphics/qt/FontQt.cpp index 778a13f..646cd0e 100644 --- a/Source/WebCore/platform/graphics/qt/FontQt.cpp +++ b/Source/WebCore/platform/graphics/qt/FontQt.cpp @@ -61,14 +61,14 @@ static const QString fromRawDataWithoutRef(const String& string, int start = 0, static QTextLine setupLayout(QTextLayout* layout, const TextRun& style) { int flags = style.rtl() ? Qt::TextForceRightToLeft : Qt::TextForceLeftToRight; - if (style.padding()) + if (style.expansion()) flags |= Qt::TextJustificationForced; layout->setFlags(flags); layout->beginLayout(); QTextLine line = layout->createLine(); line.setLineWidth(INT_MAX/256); - if (style.padding()) - line.setLineWidth(line.naturalTextWidth() + style.padding()); + if (style.expansion()) + line.setLineWidth(line.naturalTextWidth() + style.expansion()); layout->endLayout(); return line; } @@ -107,7 +107,7 @@ static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const Float textStrokePen = QPen(QColor(ctx->strokeColor()), ctx->strokeThickness()); } - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); QPointF pt(point.x(), point.y()); @@ -196,7 +196,7 @@ static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const Float p->save(); p->setPen(ctxShadow->m_color); p->translate(ctxShadow->offset()); - p->drawText(pt, string, flags, run.padding()); + p->drawText(pt, string, flags, run.expansion()); p->restore(); } else { QFontMetrics fm(font); @@ -210,7 +210,7 @@ static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const Float // Since it will be blurred anyway, we don't care about render hints. shadowPainter->setFont(p->font()); shadowPainter->setPen(ctxShadow->m_color); - shadowPainter->drawText(pt, string, flags, run.padding()); + shadowPainter->drawText(pt, string, flags, run.expansion()); ctxShadow->endShadowLayer(ctx); } } @@ -243,7 +243,7 @@ static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const Float if (ctx->textDrawingMode() & TextModeFill) { QPen previousPen = p->pen(); p->setPen(textFillPen); - p->drawText(pt, string, flags, run.padding()); + p->drawText(pt, string, flags, run.expansion()); p->setPen(previousPen); } } @@ -299,7 +299,7 @@ float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer if (!run.length()) return 0; - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); int w = QFontMetrics(font()).width(string, -1, Qt::TextBypassShaping); @@ -308,7 +308,7 @@ float Font::floatWidthForSimpleText(const TextRun& run, GlyphBuffer* glyphBuffer if (treatAsSpace(run[0])) w -= m_wordSpacing; - return w + run.padding(); + return w + run.expansion(); #else Q_ASSERT(false); return 0; @@ -324,9 +324,9 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon return 0; if (run.length() == 1 && treatAsSpace(run[0])) - return QFontMetrics(font()).width(space) + run.padding(); + return QFontMetrics(font()).width(space) + run.expansion(); - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); int w = QFontMetrics(font()).width(string); @@ -334,13 +334,13 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon if (treatAsSpace(run[0])) w -= m_wordSpacing; - return w + run.padding(); + return w + run.expansion(); } int Font::offsetForPositionForSimpleText(const TextRun& run, float position, bool includePartialGlyphs) const { #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); QFontMetrics fm(font()); @@ -367,7 +367,7 @@ int Font::offsetForPositionForSimpleText(const TextRun& run, float position, boo int Font::offsetForPositionForComplexText(const TextRun& run, float position, bool) const { - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); QTextLayout layout(string, font()); @@ -378,7 +378,7 @@ int Font::offsetForPositionForComplexText(const TextRun& run, float position, bo FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const { #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString wholeText = fromRawDataWithoutRef(sanitized); QString selectedText = fromRawDataWithoutRef(sanitized, from, qMin(to - from, wholeText.length() - from)); @@ -394,7 +394,7 @@ FloatRect Font::selectionRectForSimpleText(const TextRun& run, const FloatPoint& FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& pt, int h, int from, int to) const { - String sanitized = Font::normalizeSpaces(String(run.characters(), run.length())); + String sanitized = Font::normalizeSpaces(run.characters(), run.length()); QString string = fromRawDataWithoutRef(sanitized); QTextLayout layout(string, font()); @@ -413,6 +413,11 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + bool Font::primaryFontHasGlyphForCharacter(UChar32) const { notImplemented(); diff --git a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp index 295212c..8b87f5f 100644 --- a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp +++ b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp @@ -34,7 +34,7 @@ #include <wtf/UnusedParam.h> #include <wtf/text/CString.h> -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) namespace WebCore { @@ -1611,28 +1611,6 @@ void GraphicsContext3D::deleteTexture(Platform3DObject texture) glDeleteTextures(1, &texture); } -unsigned int GraphicsContext3D::sizeInBytes(GC3Denum type) -{ - switch (type) { - case GraphicsContext3D::BYTE: - return sizeof(GLbyte); - case GraphicsContext3D::UNSIGNED_BYTE: - return sizeof(GLubyte); - case GraphicsContext3D::SHORT: - return sizeof(GLshort); - case GraphicsContext3D::UNSIGNED_SHORT: - return sizeof(GLushort); - case GraphicsContext3D::INT: - return sizeof(GLint); - case GraphicsContext3D::UNSIGNED_INT: - return sizeof(GLuint); - case GraphicsContext3D::FLOAT: - return sizeof(GLfloat); - default: - return 0; - } -} - void GraphicsContext3D::synthesizeGLError(GC3Denum error) { m_internal->m_syntheticErrors.add(error); @@ -1669,6 +1647,10 @@ bool GraphicsContext3D::getImageData(Image* image, format, type, neededAlphaOp, outputVector.data()); } +void GraphicsContext3D::setContextLostCallback(PassOwnPtr<ContextLostCallback>) +{ +} + } -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 4dabe09..bf2826c 100644 --- a/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/Source/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -62,14 +62,11 @@ #include <QPolygonF> #include <QStack> #include <QVector> - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif +#include <wtf/MathExtras.h> namespace WebCore { -QPainter::CompositionMode GraphicsContext::toQtCompositionMode(CompositeOperator op) +static inline QPainter::CompositionMode toQtCompositionMode(CompositeOperator op) { switch (op) { case CompositeClear: @@ -231,8 +228,15 @@ GraphicsContextPlatformPrivate::GraphicsContextPlatformPrivate(QPainter* p, cons if (!painter) return; +#if OS(SYMBIAN) + if (painter->paintEngine()->type() == QPaintEngine::OpenVG) + antiAliasingForRectsAndLines = true; + else + antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing); +#else // Use the default the QPainter was constructed with. antiAliasingForRectsAndLines = painter->testRenderHint(QPainter::Antialiasing); +#endif // Used for default image interpolation quality. initialSmoothPixmapTransformHint = painter->testRenderHint(QPainter::SmoothPixmapTransform); @@ -784,7 +788,7 @@ void GraphicsContext::clipPath(const Path& path, WindRule clipRule) p->setClipPath(platformPath, Qt::IntersectClip); } -void drawFocusRingForPath(QPainter* p, const QPainterPath& path, int width, const Color& color, bool antiAliasing) +void drawFocusRingForPath(QPainter* p, const QPainterPath& path, const Color& color, bool antiAliasing) { const bool antiAlias = p->testRenderHint(QPainter::Antialiasing); p->setRenderHint(QPainter::Antialiasing, antiAliasing); @@ -794,9 +798,8 @@ void drawFocusRingForPath(QPainter* p, const QPainterPath& path, int width, cons QPen nPen = p->pen(); nPen.setColor(color); - nPen.setWidth(width); p->setBrush(Qt::NoBrush); - nPen.setStyle(Qt::SolidLine); + nPen.setStyle(Qt::DotLine); p->strokePath(path, nPen); p->setBrush(oldBrush); @@ -805,14 +808,14 @@ void drawFocusRingForPath(QPainter* p, const QPainterPath& path, int width, cons p->setRenderHint(QPainter::Antialiasing, antiAlias); } -void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) +void GraphicsContext::drawFocusRing(const Path& path, int /* width */, int offset, const Color& color) { // FIXME: Use 'offset' for something? http://webkit.org/b/49909 if (paintingDisabled() || !color.isValid()) return; - drawFocusRingForPath(m_data->p(), path.platformPath(), width, color, m_data->antiAliasingForRectsAndLines); + drawFocusRingForPath(m_data->p(), path.platformPath(), color, m_data->antiAliasingForRectsAndLines); } /** @@ -840,8 +843,7 @@ void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int tmpPath.addRoundedRect(rect, radius, radius); path = path.united(tmpPath); } - - drawFocusRingForPath(m_data->p(), path, width, color, m_data->antiAliasingForRectsAndLines); + drawFocusRingForPath(m_data->p(), path, color, m_data->antiAliasingForRectsAndLines); } void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool) @@ -896,7 +898,7 @@ FloatRect GraphicsContext::roundToDevicePixels(const FloatRect& frect) qreal deviceScaleY = sqrtf(deviceTransform.m21() * deviceTransform.m21() + deviceTransform.m22() * deviceTransform.m22()); QPoint deviceOrigin(frect.x() * deviceScaleX, frect.y() * deviceScaleY); - QPoint deviceLowerRight(frect.right() * deviceScaleX, frect.bottom() * deviceScaleY); + QPoint deviceLowerRight(frect.maxX() * deviceScaleX, frect.maxY() * deviceScaleY); // Don't let the height or width round to 0 unless either was originally 0 if (deviceOrigin.y() == deviceLowerRight.y() && frect.height()) @@ -1135,7 +1137,7 @@ void GraphicsContext::rotate(float radians) if (paintingDisabled()) return; - m_data->p()->rotate(180 / M_PI*radians); + m_data->p()->rotate(rad2deg(qreal(radians))); } void GraphicsContext::scale(const FloatSize& s) diff --git a/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index 75fb427..0d7aa45 100644 --- a/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -305,7 +305,7 @@ public: } } m_state; -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) const GraphicsContext3D* m_gc3D; #endif @@ -335,7 +335,7 @@ GraphicsLayerQtImpl::GraphicsLayerQtImpl(GraphicsLayerQt* newLayer) #if ENABLE(TILED_BACKING_STORE) , m_tiledBackingStore(0) #endif -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) , m_gc3D(0) #endif { @@ -529,12 +529,12 @@ void GraphicsLayerQtImpl::updateTransform() // have to maintain that ourselves for 3D. localTransform .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z()) - .multLeft(m_baseTransform) + .multiply(m_baseTransform) .translate3d(-originX, -originY, -m_state.anchorPoint.z()); // This is the actual 3D transform of this item, with the ancestors' transform baked in. m_transformRelativeToRootLayer = TransformationMatrix(parent ? parent->m_transformRelativeToRootLayer : TransformationMatrix()) - .multLeft(localTransform); + .multiply(localTransform); // Now we have enough information to determine if the layer is facing backwards. if (!m_state.backfaceVisibility && m_transformRelativeToRootLayer.inverse().m33() < 0) { @@ -562,7 +562,7 @@ void GraphicsLayerQtImpl::updateTransform() if (!m_state.childrenTransform.isIdentity()) { m_transformRelativeToRootLayer .translate(m_size.width() / 2, m_size.height() /2) - .multLeft(m_state.childrenTransform) + .multiply(m_state.childrenTransform) .translate(-m_size.width() / 2, -m_size.height() /2); } @@ -647,7 +647,7 @@ void GraphicsLayerQtImpl::paint(QPainter* painter, const QStyleOptionGraphicsIte case MediaContentType: // we don't need to paint anything: we have a QGraphicsItem from the media element break; -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) case Canvas3DContentType: m_gc3D->paint(painter, option->rect); break; @@ -791,7 +791,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform setFlag(ItemHasNoContents, !m_layer->drawsContent()); break; -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) case Canvas3DContentType: if (m_pendingContent.contentType != m_currentContent.contentType) update(); @@ -1250,7 +1250,7 @@ void GraphicsLayerQt::setContentsBackgroundColor(const Color& color) GraphicsLayer::setContentsBackgroundColor(color); } -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) void GraphicsLayerQt::setContentsToGraphicsContext3D(const GraphicsContext3D* ctx) { if (ctx == m_impl->m_gc3D) diff --git a/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h index 8027143..569bd8d 100644 --- a/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h +++ b/Source/WebCore/platform/graphics/qt/GraphicsLayerQt.h @@ -20,7 +20,7 @@ #ifndef GraphicsLayerQt_h #define GraphicsLayerQt_h -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" #endif #include "GraphicsLayer.h" @@ -79,7 +79,7 @@ public: virtual void setContentsNeedsDisplay(); virtual void setContentsToMedia(PlatformLayer*); virtual void setContentsBackgroundColor(const Color&); -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) virtual void setContentsToGraphicsContext3D(const GraphicsContext3D*); virtual void setGraphicsContext3DNeedsDisplay(); #endif diff --git a/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp index d1567ec..62f5c3e 100644 --- a/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/Source/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -188,7 +188,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& i RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); unsigned char* data = result->data(); - if (rect.x() < 0 || rect.y() < 0 || rect.right() > size.width() || rect.bottom() > size.height()) + if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > size.width() || rect.maxY() > size.height()) memset(data, 0, result->length()); int originx = rect.x(); @@ -197,7 +197,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& i destx = -originx; originx = 0; } - int endx = rect.right(); + int endx = rect.maxX(); if (endx > size.width()) endx = size.width(); int numColumns = endx - originx; @@ -208,7 +208,7 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const ImageBufferData& i desty = -originy; originy = 0; } - int endy = rect.bottom(); + int endy = rect.maxY(); if (endy > size.height()) endy = size.height(); int numRows = endy - originy; @@ -302,9 +302,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(destx >= 0); ASSERT(destx < size.width()); ASSERT(originx >= 0); - ASSERT(originx <= sourceRect.right()); + ASSERT(originx <= sourceRect.maxX()); - int endx = destPoint.x() + sourceRect.right(); + int endx = destPoint.x() + sourceRect.maxX(); ASSERT(endx <= size.width()); int numColumns = endx - destx; @@ -314,9 +314,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(desty >= 0); ASSERT(desty < size.height()); ASSERT(originy >= 0); - ASSERT(originy <= sourceRect.bottom()); + ASSERT(originy <= sourceRect.maxY()); - int endy = destPoint.y() + sourceRect.bottom(); + int endy = destPoint.y() + sourceRect.maxY(); ASSERT(endy <= size.height()); int numRows = endy - desty; diff --git a/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp b/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp index 71352e4..3540994 100644 --- a/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp +++ b/Source/WebCore/platform/graphics/qt/ImageDecoderQt.cpp @@ -213,7 +213,7 @@ bool ImageDecoderQt::internalHandleCurrentImage(size_t frameIndex) // now into the ImageFrame - even if the image is not ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; - buffer->setRect(m_reader->currentImageRect()); + buffer->setOriginalFrameRect(m_reader->currentImageRect()); buffer->setStatus(ImageFrame::FrameComplete); buffer->setDuration(m_reader->nextImageDelay()); buffer->setPixmap(pixmap); diff --git a/Source/WebCore/platform/graphics/qt/ImageQt.cpp b/Source/WebCore/platform/graphics/qt/ImageQt.cpp index 58f82ef..0c8ce9e 100644 --- a/Source/WebCore/platform/graphics/qt/ImageQt.cpp +++ b/Source/WebCore/platform/graphics/qt/ImageQt.cpp @@ -120,11 +120,9 @@ void Image::drawPattern(GraphicsContext* ctxt, const FloatRect& tileRect, const CompositeOperator previousOperator = ctxt->compositeOperation(); - ctxt->setCompositeOperation(op); - QPainter* p = ctxt->platformContext(); - if (!pixmap.hasAlpha() && p->compositionMode() == QPainter::CompositionMode_SourceOver) - p->setCompositionMode(QPainter::CompositionMode_Source); + ctxt->setCompositeOperation(!pixmap.hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op); + QPainter* p = ctxt->platformContext(); QTransform transform(patternTransform); // If this would draw more than one scaled tile, we scale the pixmap first and then use the result to draw. @@ -223,15 +221,8 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, return; } - QPainter* painter(ctxt->platformContext()); - - QPainter::CompositionMode compositionMode = GraphicsContext::toQtCompositionMode(op); - - if (!image->hasAlpha() && painter->compositionMode() == QPainter::CompositionMode_SourceOver) - compositionMode = QPainter::CompositionMode_Source; - - QPainter::CompositionMode lastCompositionMode = painter->compositionMode(); - painter->setCompositionMode(compositionMode); + CompositeOperator previousOperator = ctxt->compositeOperation(); + ctxt->setCompositeOperation(!image->hasAlpha() && op == CompositeSourceOver ? CompositeCopy : op); ContextShadow* shadow = ctxt->contextShadow(); if (shadow->m_type != ContextShadow::NoShadow) { @@ -243,11 +234,9 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& dst, } } - // Test using example site at - // http://www.meyerweb.com/eric/css/edge/complexspiral/demo.html - painter->drawPixmap(normalizedDst, *image, normalizedSrc); + ctxt->platformContext()->drawPixmap(normalizedDst, *image, normalizedSrc); - painter->setCompositionMode(lastCompositionMode); + ctxt->setCompositeOperation(previousOperator); if (imageObserver()) imageObserver()->didDraw(this); diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp index b881036..fab4db1 100644 --- a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivatePhonon.cpp @@ -102,7 +102,7 @@ MediaPlayerPrivatePhonon::MediaPlayerPrivatePhonon(MediaPlayer* player) // Make sure we get updates for each frame m_videoWidget->installEventFilter(this); - foreach (QWidget* widget, qFindChildren<QWidget*>(m_videoWidget)) + foreach (QWidget* widget, m_videoWidget->findChildren<QWidget*>()) widget->installEventFilter(this); connect(m_mediaObject, SIGNAL(stateChanged(Phonon::State,Phonon::State)), diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp index 0a4c0f6..caf9c2d 100644 --- a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp @@ -215,7 +215,7 @@ void MediaPlayerPrivateQt::commitLoad(const String& url) // Don't set the header if there are no cookies. // This prevents a warning from being emitted. if (!cookies.isEmpty()) - request.setHeader(QNetworkRequest::CookieHeader, qVariantFromValue(cookies)); + request.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookies)); // Set the refferer, but not when requesting insecure content from a secure page QUrl documentUrl = QUrl(QString(document->documentURI())); @@ -543,7 +543,7 @@ void MediaPlayerPrivateQt::updateStates() m_readyState = MediaPlayer::HaveCurrentData; } else if (currentStatus == QMediaPlayer::BufferedMedia || currentStatus == QMediaPlayer::EndOfMedia) { - m_networkState = MediaPlayer::Idle; + m_networkState = MediaPlayer::Loaded; m_readyState = MediaPlayer::HaveEnoughData; } else if (currentStatus == QMediaPlayer::InvalidMedia) { m_networkState = MediaPlayer::NetworkError; @@ -618,6 +618,21 @@ void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect) m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect()); } +void MediaPlayerPrivateQt::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& rect) +{ + if (context->paintingDisabled()) + return; + + if (!m_isVisible) + return; + + // Grab the painter and widget + QPainter* painter = context->platformContext(); + + // Render the video, using the item as it might not be in the scene + m_videoItem->paint(painter, 0, 0); +} + void MediaPlayerPrivateQt::repaint() { m_webCorePlayer->repaint(); diff --git a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h index 2621432..e4133db 100644 --- a/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h +++ b/Source/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h @@ -89,6 +89,8 @@ public: void setSize(const IntSize&); void paint(GraphicsContext*, const IntRect&); + // reimplemented for canvas drawImage(HTMLVideoElement) + void paintCurrentFrameInContext(GraphicsContext*, const IntRect&); bool supportsFullscreen() const { return true; } diff --git a/Source/WebCore/platform/graphics/qt/PathQt.cpp b/Source/WebCore/platform/graphics/qt/PathQt.cpp index 571b405..ad482f7 100644 --- a/Source/WebCore/platform/graphics/qt/PathQt.cpp +++ b/Source/WebCore/platform/graphics/qt/PathQt.cpp @@ -39,15 +39,9 @@ #include <QPainterPath> #include <QTransform> #include <QString> +#include <wtf/MathExtras.h> #include <wtf/OwnPtr.h> -#define _USE_MATH_DEFINES -#include <math.h> - -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - namespace WebCore { Path::Path() @@ -263,7 +257,6 @@ void Path::closeSubpath() m_path.closeSubpath(); } -#define DEGREES(t) ((t) * 180.0 / M_PI) void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool anticlockwise) { qreal xc = p.x(); @@ -280,8 +273,8 @@ void Path::addArc(const FloatPoint& p, float r, float sar, float ear, bool antic anticlockwise = !anticlockwise; //end hack - float sa = DEGREES(sar); - float ea = DEGREES(ear); + float sa = rad2deg(sar); + float ea = rad2deg(ear); double span = 0; @@ -438,6 +431,14 @@ float Path::normalAngleAtLength(float length, bool& ok) qreal percent = m_path.percentAtLength(length); qreal angle = m_path.angleAtPercent(percent); + // Normalize angle value. + // QPainterPath returns angle values with the origo being at the top left corner. + // In case of moveTo(0, 0) and addLineTo(0, 10) the angle is 270, + // while the caller expects it to be 90. + // Normalize the value by mirroring it to the x-axis. + // For more info look at pathLengthApplierFunction(). + if (angle > 0) + angle = 360 - angle; return angle; } diff --git a/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp b/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp index 47ddf02..9e43558 100644 --- a/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp +++ b/Source/WebCore/platform/graphics/qt/SimpleFontDataQt.cpp @@ -24,7 +24,7 @@ #include "config.h" #include "SimpleFontData.h" -#include <QFontMetrics> +#include <QFontMetricsF> namespace WebCore { @@ -41,25 +41,19 @@ bool SimpleFontData::containsCharacters(const UChar*, int) const void SimpleFontData::platformInit() { if (!m_platformData.size()) { - m_ascent = 0; - m_descent = 0; - m_lineGap = 0; - m_lineSpacing = 0; + m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; - m_xHeight = 0; - m_unitsPerEm = 0; return; } - QFontMetrics fm(m_platformData.font()); - - m_ascent = fm.ascent(); - m_descent = fm.descent(); - m_lineSpacing = fm.lineSpacing(); - m_xHeight = fm.xHeight(); + QFontMetricsF fm(m_platformData.font()); + m_fontMetrics.setAscent(fm.ascent()); + m_fontMetrics.setDescent(fm.descent()); + m_fontMetrics.setXHeight(fm.xHeight()); + m_fontMetrics.setLineGap(fm.leading()); + m_fontMetrics.setLineSpacing(fm.lineSpacing()); m_spaceWidth = fm.width(QLatin1Char(' ')); - m_lineGap = fm.leading(); } void SimpleFontData::platformGlyphInit() diff --git a/Source/WebCore/platform/graphics/qt/TransparencyLayer.h b/Source/WebCore/platform/graphics/qt/TransparencyLayer.h index ff9ef20..f13deb0 100644 --- a/Source/WebCore/platform/graphics/qt/TransparencyLayer.h +++ b/Source/WebCore/platform/graphics/qt/TransparencyLayer.h @@ -59,9 +59,8 @@ public: painter.setPen(p->pen()); painter.setBrush(p->brush()); painter.setTransform(p->transform(), true); - painter.setOpacity(p->opacity()); painter.setFont(p->font()); - painter.setCompositionMode(p->compositionMode()); + painter.setOpacity(1); } TransparencyLayer() diff --git a/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp b/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp index a10371f..23045ba 100644 --- a/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/FloatRectSkia.cpp @@ -43,7 +43,7 @@ FloatRect::FloatRect(const SkRect& r) FloatRect::operator SkRect() const { - SkRect rect = { x(), y(), right(), bottom() }; + SkRect rect = { x(), y(), maxX(), maxY() }; return rect; } diff --git a/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp index 0b31dfa..0e68c21 100644 --- a/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp +++ b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.cpp @@ -65,7 +65,7 @@ FontCustomPlatformData::~FontCustomPlatformData() #endif } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontRenderingMode mode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation orientation, FontWidthVariant, FontRenderingMode mode) { #if OS(WINDOWS) ASSERT(m_fontReference); @@ -114,13 +114,12 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b // not allow access from CSS. static String createUniqueFontName() { - Vector<char> fontUuid(sizeof(GUID)); - CoCreateGuid(reinterpret_cast<GUID*>(fontUuid.data())); + GUID fontUuid; + CoCreateGuid(&fontUuid); - Vector<char> fontNameVector; - base64Encode(fontUuid, fontNameVector); - ASSERT(fontNameVector.size() < LF_FACESIZE); - return String(fontNameVector.data(), fontNameVector.size()); + String fontName = base64Encode(reinterpret_cast<char*>(&fontUuid), sizeof(fontUuid)); + ASSERT(fontName.length() < LF_FACESIZE); + return fontName; } #endif diff --git a/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h index 4228b40..2dee3ab 100644 --- a/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/skia/FontCustomPlatformData.h @@ -34,6 +34,7 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -65,7 +66,7 @@ public: ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); static bool supportsFormat(const String&); diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp b/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp index c4b753b..5950c35 100644 --- a/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/GraphicsContext3DSkia.cpp @@ -26,7 +26,7 @@ #include "config.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" @@ -87,4 +87,4 @@ bool GraphicsContext3D::getImageData(Image* image, } // namespace WebCore -#endif // ENABLE(3D_CANVAS) +#endif // ENABLE(WEBGL) diff --git a/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp b/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp index 1a7112b..9f2ed32 100644 --- a/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/GraphicsContextSkia.cpp @@ -309,20 +309,6 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness platformContext()->clipPathAntiAliased(path); } -void GraphicsContext::addPath(const Path& path) -{ - if (paintingDisabled()) - return; - platformContext()->addPath(*path.platformPath()); -} - -void GraphicsContext::beginPath() -{ - if (paintingDisabled()) - return; - platformContext()->beginPath(); -} - void GraphicsContext::clearPlatformShadow() { if (paintingDisabled()) @@ -431,11 +417,7 @@ void GraphicsContext::clipPath(const Path& pathToClip, WindRule clipRule) if (platformContext()->useGPU()) platformContext()->gpuCanvas()->clipPath(pathToClip); - // FIXME: Be smarter about this. - beginPath(); - addPath(pathToClip); - - SkPath path = platformContext()->currentPathInLocalCoordinates(); + SkPath path = *pathToClip.platformPath(); if (!isPathSkiaSafe(getCTM(), path)) return; @@ -738,17 +720,13 @@ void GraphicsContext::fillPath(const Path& pathToFill) if (paintingDisabled()) return; - // FIXME: Be smarter about this. - beginPath(); - addPath(pathToFill); - if (platformContext()->useGPU() && platformContext()->canAccelerate()) { platformContext()->prepareForHardwareDraw(); platformContext()->gpuCanvas()->fillPath(pathToFill); return; } - SkPath path = platformContext()->currentPathInLocalCoordinates(); + SkPath path = *pathToFill.platformPath(); if (!isPathSkiaSafe(getCTM(), path)) return; @@ -1204,11 +1182,7 @@ void GraphicsContext::strokePath(const Path& pathToStroke) if (paintingDisabled()) return; - // FIXME: Be smarter about this. - beginPath(); - addPath(pathToStroke); - - SkPath path = platformContext()->currentPathInLocalCoordinates(); + SkPath path = *pathToStroke.platformPath(); if (!isPathSkiaSafe(getCTM(), path)) return; diff --git a/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index 2c489ef..2721523 100644 --- a/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -2,11 +2,11 @@ * Copyright (c) 2008, Google Inc. All rights reserved. * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2010 Torch Mobile (Beijing) Co. Ltd. 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 @@ -16,7 +16,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 @@ -162,12 +162,14 @@ void ImageBuffer::platformTransformColorSpace(const Vector<int>& lookUpTable) } template <Multiply multiplied> -PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, +PassRefPtr<ByteArray> getImageData(const IntRect& rect, SkDevice& srcDevice, const IntSize& size) { RefPtr<ByteArray> result = ByteArray::create(rect.width() * rect.height() * 4); - if (bitmap.config() == SkBitmap::kNo_Config) { + SkBitmap::Config srcConfig = srcDevice.accessBitmap(false).config(); + + if (srcConfig == SkBitmap::kNo_Config) { // This is an empty SkBitmap that could not be configured. ASSERT(!size.width() || !size.height()); return result.release(); @@ -177,8 +179,8 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, if (rect.x() < 0 || rect.y() < 0 - || rect.right() > size.width() - || rect.bottom() > size.height()) + || rect.maxX() > size.width() + || rect.maxY() > size.height()) memset(data, 0, result->length()); int originX = rect.x(); @@ -187,12 +189,12 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, destX = -originX; originX = 0; } - int endX = rect.right(); + int endX = rect.maxX(); if (endX > size.width()) endX = size.width(); int numColumns = endX - originX; - if (numColumns <= 0) + if (numColumns <= 0) return result.release(); int originY = rect.y(); @@ -201,38 +203,42 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, destY = -originY; originY = 0; } - int endY = rect.bottom(); + int endY = rect.maxY(); if (endY > size.height()) endY = size.height(); int numRows = endY - originY; - if (numRows <= 0) + if (numRows <= 0) return result.release(); - ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); - SkAutoLockPixels bitmapLock(bitmap); + ASSERT(srcConfig == SkBitmap::kARGB_8888_Config); unsigned destBytesPerRow = 4 * rect.width(); + + SkBitmap srcBitmap; + srcDevice.readPixels(SkIRect::MakeXYWH(originX, originY, numColumns, numRows), &srcBitmap); + unsigned char* destRow = data + destY * destBytesPerRow + destX * 4; + // Do conversion of byte order and alpha divide (if necessary) for (int y = 0; y < numRows; ++y) { - uint32_t* srcRow = bitmap.getAddr32(originX, originY + y); + SkPMColor* srcBitmapRow = srcBitmap.getAddr32(0, y); for (int x = 0; x < numColumns; ++x) { + SkPMColor srcPMColor = srcBitmapRow[x]; unsigned char* destPixel = &destRow[x * 4]; if (multiplied == Unmultiplied) { - SkColor color = srcRow[x]; - unsigned a = SkColorGetA(color); - destPixel[0] = a ? SkColorGetR(color) * 255 / a : 0; - destPixel[1] = a ? SkColorGetG(color) * 255 / a : 0; - destPixel[2] = a ? SkColorGetB(color) * 255 / a : 0; + unsigned char a = SkGetPackedA32(srcPMColor); + destPixel[0] = a ? SkGetPackedR32(srcPMColor) * 255 / a : 0; + destPixel[1] = a ? SkGetPackedG32(srcPMColor) * 255 / a : 0; + destPixel[2] = a ? SkGetPackedB32(srcPMColor) * 255 / a : 0; destPixel[3] = a; } else { // Input and output are both pre-multiplied, we just need to re-arrange the // bytes from the bitmap format to RGBA. - destPixel[0] = SkGetPackedR32(srcRow[x]); - destPixel[1] = SkGetPackedG32(srcRow[x]); - destPixel[2] = SkGetPackedB32(srcRow[x]); - destPixel[3] = SkGetPackedA32(srcRow[x]); + destPixel[0] = SkGetPackedR32(srcPMColor); + destPixel[1] = SkGetPackedG32(srcPMColor); + destPixel[2] = SkGetPackedB32(srcPMColor); + destPixel[3] = SkGetPackedA32(srcPMColor); } } destRow += destBytesPerRow; @@ -244,18 +250,18 @@ PassRefPtr<ByteArray> getImageData(const IntRect& rect, const SkBitmap& bitmap, PassRefPtr<ByteArray> ImageBuffer::getUnmultipliedImageData(const IntRect& rect) const { context()->platformContext()->syncSoftwareCanvas(); - return getImageData<Unmultiplied>(rect, *context()->platformContext()->bitmap(), m_size); + return getImageData<Unmultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size); } PassRefPtr<ByteArray> ImageBuffer::getPremultipliedImageData(const IntRect& rect) const { context()->platformContext()->syncSoftwareCanvas(); - return getImageData<Premultiplied>(rect, *context()->platformContext()->bitmap(), m_size); + return getImageData<Premultiplied>(rect, *context()->platformContext()->canvas()->getDevice(), m_size); } template <Multiply multiplied> -void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, - const SkBitmap& bitmap, const IntSize& size) +void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint, + SkDevice* dstDevice, const IntSize& size) { ASSERT(sourceRect.width() > 0); ASSERT(sourceRect.height() > 0); @@ -265,9 +271,9 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(destX >= 0); ASSERT(destX < size.width()); ASSERT(originX >= 0); - ASSERT(originX < sourceRect.right()); + ASSERT(originX < sourceRect.maxX()); - int endX = destPoint.x() + sourceRect.right(); + int endX = destPoint.x() + sourceRect.maxX(); ASSERT(endX <= size.width()); int numColumns = endX - destX; @@ -277,21 +283,33 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& ASSERT(destY >= 0); ASSERT(destY < size.height()); ASSERT(originY >= 0); - ASSERT(originY < sourceRect.bottom()); + ASSERT(originY < sourceRect.maxY()); - int endY = destPoint.y() + sourceRect.bottom(); + int endY = destPoint.y() + sourceRect.maxY(); ASSERT(endY <= size.height()); int numRows = endY - destY; - ASSERT(bitmap.config() == SkBitmap::kARGB_8888_Config); - SkAutoLockPixels bitmapLock(bitmap); - unsigned srcBytesPerRow = 4 * sourceSize.width(); - const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4; + SkBitmap deviceBitmap = dstDevice->accessBitmap(true); + SkAutoLockPixels deviceAutoLock(deviceBitmap); + // If the device's bitmap doesn't have pixels we will make a temp and call writePixels on the device. + bool temporaryBitmap = !deviceBitmap.getPixels(); + SkBitmap destBitmap; + + if (temporaryBitmap) { + destBitmap.setConfig(SkBitmap::kARGB_8888_Config, numColumns, numRows, srcBytesPerRow); + if (!destBitmap.allocPixels()) + CRASH(); + } else + deviceBitmap.extractSubset(&destBitmap, SkIRect::MakeXYWH(destX, destY, numColumns, numRows)); + + // Whether we made a temporary or not destBitmap is always configured to be written at 0,0 + SkAutoLockPixels destAutoLock(destBitmap); + const unsigned char* srcRow = source->data() + originY * srcBytesPerRow + originX * 4; for (int y = 0; y < numRows; ++y) { - uint32_t* destRow = bitmap.getAddr32(destX, destY + y); + SkPMColor* destRow = destBitmap.getAddr32(0, y); for (int x = 0; x < numColumns; ++x) { const unsigned char* srcPixel = &srcRow[x * 4]; if (multiplied == Unmultiplied) { @@ -301,22 +319,26 @@ void putImageData(ByteArray*& source, const IntSize& sourceSize, const IntRect& unsigned char b = SkMulDiv255Ceiling(srcPixel[2], alpha); destRow[x] = SkPackARGB32(alpha, r, g, b); } else - destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], - srcPixel[1], srcPixel[2]); + destRow[x] = SkPackARGB32(srcPixel[3], srcPixel[0], srcPixel[1], srcPixel[2]); } srcRow += srcBytesPerRow; } + + // If we used a temporary then write it to the device + if (temporaryBitmap) + dstDevice->writePixels(destBitmap, destX, destY); } void ImageBuffer::putUnmultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) { - context()->platformContext()->prepareForSoftwareDraw(); - putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size); + context()->platformContext()->syncSoftwareCanvas(); + putImageData<Unmultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size); } void ImageBuffer::putPremultipliedImageData(ByteArray* source, const IntSize& sourceSize, const IntRect& sourceRect, const IntPoint& destPoint) { - putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size); + context()->platformContext()->syncSoftwareCanvas(); + putImageData<Premultiplied>(source, sourceSize, sourceRect, destPoint, context()->platformContext()->canvas()->getDevice(), m_size); } String ImageBuffer::toDataURL(const String& mimeType, const double* quality) const @@ -324,14 +346,27 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con ASSERT(MIMETypeRegistry::isSupportedImageMIMETypeForEncoding(mimeType)); Vector<unsigned char> encodedImage; + SkDevice* device = context()->platformContext()->canvas()->getDevice(); + SkBitmap bitmap = device->accessBitmap(false); + + // if we can't see the pixels directly, call readPixels() to get a copy. + // this could happen if the device is backed by a GPU. + bitmap.lockPixels(); // balanced by our destructor, or explicitly if getPixels() fails + if (!bitmap.getPixels()) { + bitmap.unlockPixels(); + SkIRect bounds = SkIRect::MakeWH(device->width(), device->height()); + if (!device->readPixels(bounds, &bitmap)) + return "data:,"; + } + if (mimeType == "image/jpeg") { int compressionQuality = JPEGImageEncoder::DefaultCompressionQuality; if (quality && *quality >= 0.0 && *quality <= 1.0) compressionQuality = static_cast<int>(*quality * 100 + 0.5); - if (!JPEGImageEncoder::encode(*context()->platformContext()->bitmap(), compressionQuality, &encodedImage)) + if (!JPEGImageEncoder::encode(bitmap, compressionQuality, &encodedImage)) return "data:,"; } else { - if (!PNGImageEncoder::encode(*context()->platformContext()->bitmap(), &encodedImage)) + if (!PNGImageEncoder::encode(bitmap, &encodedImage)) return "data:,"; ASSERT(mimeType == "image/png"); } diff --git a/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp b/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp index ea138ee..0024086 100644 --- a/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/IntRectSkia.cpp @@ -37,14 +37,14 @@ namespace WebCore { IntRect::operator SkIRect() const { - SkIRect rect = { x(), y(), right(), bottom() }; + SkIRect rect = { x(), y(), maxX(), maxY() }; return rect; } IntRect::operator SkRect() const { SkRect rect; - rect.set(SkIntToScalar(x()), SkIntToScalar(y()), SkIntToScalar(right()), SkIntToScalar(bottom())); + rect.set(SkIntToScalar(x()), SkIntToScalar(y()), SkIntToScalar(maxX()), SkIntToScalar(maxY())); return rect; } diff --git a/Source/WebCore/platform/graphics/skia/PathSkia.cpp b/Source/WebCore/platform/graphics/skia/PathSkia.cpp index 89323c4..6318c21 100644 --- a/Source/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/PathSkia.cpp @@ -227,28 +227,20 @@ void Path::transform(const AffineTransform& xform) m_path->transform(xform); } -// Computes the bounding box for the stroke and style currently selected into -// the given bounding box. This also takes into account the stroke width. -static FloatRect boundingBoxForCurrentStroke(const GraphicsContext* context) -{ - SkPaint paint; - context->platformContext()->setupPaintForStroking(&paint, 0, 0); - SkPath boundingPath; - paint.getFillPath(context->platformContext()->currentPathInLocalCoordinates(), &boundingPath); - return boundingPath.getBounds(); -} - FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) { GraphicsContext* scratch = scratchContext(); scratch->save(); - scratch->beginPath(); - scratch->addPath(*this); if (applier) applier->strokeStyle(scratch); - FloatRect r = boundingBoxForCurrentStroke(scratch); + SkPaint paint; + scratch->platformContext()->setupPaintForStroking(&paint, 0, 0); + SkPath boundingPath; + paint.getFillPath(*platformPath(), &boundingPath); + + FloatRect r = boundingPath.getBounds(); scratch->restore(); return r; } diff --git a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index d852e9b..5e08b3c 100644 --- a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -51,6 +51,12 @@ #include "SkDashPathEffect.h" #include "SkShader.h" +#if ENABLE(SKIA_GPU) +#include "GrContext.h" +#include "SkGpuDevice.h" +#include "SkGpuDeviceFactory.h" +#endif + #include <wtf/MathExtras.h> #include <wtf/OwnArrayPtr.h> #include <wtf/Vector.h> @@ -62,6 +68,18 @@ namespace WebCore { +#if ENABLE(SKIA_GPU) +GrContext* GetGlobalGrContext() +{ + static GrContext* gGR; + if (!gGR) { + gGR = GrContext::CreateGLShaderContext(); + gGR->setTextureCacheLimits(512, 50 * 1024 * 1024); + } + return gGR; +} +#endif + extern bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path); // State ----------------------------------------------------------------------- @@ -266,7 +284,7 @@ void PlatformContextSkia::beginLayerClippedToImage(const FloatRect& rect, // create the resulting image. m_state->m_clip = rect; SkRect bounds = { SkFloatToScalar(rect.x()), SkFloatToScalar(rect.y()), - SkFloatToScalar(rect.right()), SkFloatToScalar(rect.bottom()) }; + SkFloatToScalar(rect.maxX()), SkFloatToScalar(rect.maxY()) }; canvas()->clipRect(bounds); canvas()->saveLayerAlpha(&bounds, 255, @@ -550,38 +568,12 @@ SkColor PlatformContextSkia::effectiveStrokeColor() const return m_state->applyAlpha(m_state->m_strokeColor); } -void PlatformContextSkia::beginPath() -{ - m_path.reset(); -} - -void PlatformContextSkia::addPath(const SkPath& path) -{ - m_path.addPath(path, m_canvas->getTotalMatrix()); -} - -SkPath PlatformContextSkia::currentPathInLocalCoordinates() const -{ - SkPath localPath = m_path; - const SkMatrix& matrix = m_canvas->getTotalMatrix(); - SkMatrix inverseMatrix; - if (!matrix.invert(&inverseMatrix)) - return SkPath(); - localPath.transform(inverseMatrix); - return localPath; -} - void PlatformContextSkia::canvasClipPath(const SkPath& path) { m_state->m_canvasClipApplied = true; m_canvas->clipPath(path); } -void PlatformContextSkia::setFillRule(SkPath::FillType fr) -{ - m_path.setFillType(fr); -} - void PlatformContextSkia::setFillShader(SkShader* fillShader) { if (fillShader) @@ -625,7 +617,11 @@ const SkBitmap* PlatformContextSkia::bitmap() const bool PlatformContextSkia::isPrinting() { +#if ENABLE(SKIA_GPU) + return true; +#else return m_canvas->getTopPlatformDevice().IsVectorial(); +#endif } void PlatformContextSkia::getImageResamplingHint(IntSize* srcSize, FloatSize* dstSize) const @@ -739,6 +735,19 @@ void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* co m_gpuCanvas = new GLES2Canvas(context, drawingBuffer, size); m_uploadTexture.clear(); drawingBuffer->setWillPublishCallback(WillPublishCallbackImpl::create(this)); + +#if ENABLE(SKIA_GPU) + m_useGPU = false; + context->makeContextCurrent(); + m_gpuCanvas->bindFramebuffer(); + + GrContext* gr = GetGlobalGrContext(); + gr->resetContext(); + SkDeviceFactory* factory = new SkGpuDeviceFactory(gr, SkGpuDevice::Current3DApiRenderTarget()); + SkDevice* device = factory->newDevice(m_canvas, SkBitmap::kARGB_8888_Config, drawingBuffer->size().width(), drawingBuffer->size().height(), false, false); + m_canvas->setDevice(device)->unref(); + m_canvas->setDeviceFactory(factory); +#endif } else { syncSoftwareCanvas(); m_uploadTexture.clear(); @@ -750,8 +759,13 @@ void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* co void PlatformContextSkia::prepareForSoftwareDraw() const { - if (!m_useGPU) + if (!m_useGPU) { +#if ENABLE(SKIA_GPU) + if (m_gpuCanvas) + m_gpuCanvas->context()->makeContextCurrent(); +#endif return; + } if (m_backingStoreState == Hardware) { // Depending on the blend mode we need to do one of a few things: @@ -804,8 +818,13 @@ void PlatformContextSkia::prepareForHardwareDraw() const void PlatformContextSkia::syncSoftwareCanvas() const { - if (!m_useGPU) + if (!m_useGPU) { +#if ENABLE(SKIA_GPU) + if (m_gpuCanvas) + m_gpuCanvas->bindFramebuffer(); +#endif return; + } if (m_backingStoreState == Hardware) readbackHardwareToSoftware(); @@ -865,7 +884,7 @@ void PlatformContextSkia::readbackHardwareToSoftware() const const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(true); SkAutoLockPixels lock(bitmap); int width = bitmap.width(), height = bitmap.height(); - OwnArrayPtr<uint32_t> buf(new uint32_t[width]); + OwnArrayPtr<uint32_t> buf = adoptArrayPtr(new uint32_t[width]); SharedGraphicsContext3D* context = m_gpuCanvas->context(); m_gpuCanvas->bindFramebuffer(); // Flips the image vertically. diff --git a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h index 0304486..d7dd6a9 100644 --- a/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/Source/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -118,7 +118,6 @@ public: void setAlpha(float); void setLineCap(SkPaint::Cap); void setLineJoin(SkPaint::Join); - void setFillRule(SkPath::FillType); void setXfermodeMode(SkXfermode::Mode); void setFillColor(SkColor); void setFillShader(SkShader*); @@ -137,10 +136,6 @@ public: float getAlpha() const; int getNormalizedAlpha() const; - void beginPath(); - void addPath(const SkPath&); - SkPath currentPathInLocalCoordinates() const; - void canvasClipPath(const SkPath&); // Returns the fill color. The returned color has it's alpha adjusted @@ -220,9 +215,6 @@ private: // mStateStack.back(). State* m_state; - // Current path in global coordinates. - SkPath m_path; - // Stores image sizes for a hint to compute image resampling modes. // Values are used in ImageSkia.cpp IntSize m_imageResamplingHintSrcSize; diff --git a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h index 85fa3ee..47a27c6 100644 --- a/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h +++ b/Source/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h @@ -26,7 +26,7 @@ #include "Image.h" #include "TextureMapperNode.h" -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) #include "GraphicsContext3D.h" #endif @@ -73,7 +73,7 @@ public: virtual void setContentsToImage(Image*); virtual void setContentsToMedia(PlatformLayer*); virtual void setContentsBackgroundColor(const Color&); -#if ENABLE(3D_CANVAS) +#if ENABLE(WEBGL) virtual void setContentsToGraphicsContext3D(const GraphicsContext3D*); virtual void setGraphicsContext3DNeedsDisplay(); #endif diff --git a/Source/WebCore/platform/graphics/texmap/TextureMapperNode.cpp b/Source/WebCore/platform/graphics/texmap/TextureMapperNode.cpp index 09051f9..bf53e61 100644 --- a/Source/WebCore/platform/graphics/texmap/TextureMapperNode.cpp +++ b/Source/WebCore/platform/graphics/texmap/TextureMapperNode.cpp @@ -318,7 +318,7 @@ void TextureMapperNode::computeLocalTransform() m_transforms.local = TransformationMatrix() .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z()) - .multLeft(m_state.transform) + .multiply(m_state.transform) .translate3d(-originX, -originY, -m_state.anchorPoint.z()); m_transforms.localDirty = false; } @@ -352,7 +352,7 @@ void TextureMapperNode::computeReplicaTransform() m_nearestSurfaceSize = nearestSurfaceSize(); if (m_layerType != TransparencyLayer) { - m_transforms.replica = TransformationMatrix(m_transforms.target).multLeft(m_state.replicaLayer->m_transforms.local); + m_transforms.replica = TransformationMatrix(m_transforms.target).multiply(m_state.replicaLayer->m_transforms.local); return; } @@ -361,7 +361,7 @@ void TextureMapperNode::computeReplicaTransform() m_transforms.replica = TransformationMatrix() .translate(originX, originY) - .multLeft(m_state.replicaLayer->m_transforms.local) + .multiply(m_state.replicaLayer->m_transforms.local) .translate(-originX, -originY); } @@ -377,7 +377,7 @@ void TextureMapperNode::computeTransformations() TextureMapperNode* parent = m_parent; computeLocalTransform(); - m_transforms.target = TransformationMatrix(parent ? parent->m_transforms.forDescendants : TransformationMatrix()).multLeft(m_transforms.local); + m_transforms.target = TransformationMatrix(parent ? parent->m_transforms.forDescendants : TransformationMatrix()).multiply(m_transforms.local); m_transforms.forDescendants = (m_layerType == ClipLayer ? TransformationMatrix() : m_transforms.target); if (m_effectTarget) @@ -408,10 +408,10 @@ void TextureMapperNode::computeTransformations() if (m_transforms.perspectiveDirty) m_transforms.perspective = TransformationMatrix() .translate(centerPoint.x(), centerPoint.y()) - .multLeft(m_state.childrenTransform) + .multiply(m_state.childrenTransform) .translate(-centerPoint.x(), -centerPoint.y()); m_transforms.perspectiveDirty = false; - m_transforms.forDescendants.multLeft(m_transforms.perspective); + m_transforms.forDescendants.multiply(m_transforms.perspective); } void TextureMapperNode::uploadTextureFromContent(TextureMapper* textureMapper, const IntRect& visibleRect, GraphicsLayer* layer) diff --git a/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp b/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp index 3f88140..a1ffa30 100644 --- a/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp +++ b/Source/WebCore/platform/graphics/transforms/AffineTransform.cpp @@ -222,14 +222,6 @@ AffineTransform& AffineTransform::translate(double tx, double ty) return *this; } -// *this = translation * *this -AffineTransform& AffineTransform::translateRight(double tx, double ty) -{ - m_transform[4] += tx; - m_transform[5] += ty; - return *this; -} - AffineTransform& AffineTransform::scaleNonUniform(double sx, double sy) { return scale(sx, sy); @@ -324,9 +316,9 @@ FloatRect AffineTransform::mapRect(const FloatRect& rect) const FloatQuad result; result.setP1(mapPoint(rect.location())); - result.setP2(mapPoint(FloatPoint(rect.right(), rect.y()))); - result.setP3(mapPoint(FloatPoint(rect.right(), rect.bottom()))); - result.setP4(mapPoint(FloatPoint(rect.x(), rect.bottom()))); + result.setP2(mapPoint(FloatPoint(rect.maxX(), rect.y()))); + result.setP3(mapPoint(FloatPoint(rect.maxX(), rect.maxY()))); + result.setP4(mapPoint(FloatPoint(rect.x(), rect.maxY()))); return result.boundingBox(); } diff --git a/Source/WebCore/platform/graphics/transforms/AffineTransform.h b/Source/WebCore/platform/graphics/transforms/AffineTransform.h index 50d0655..3e3995f 100644 --- a/Source/WebCore/platform/graphics/transforms/AffineTransform.h +++ b/Source/WebCore/platform/graphics/transforms/AffineTransform.h @@ -103,7 +103,6 @@ public: AffineTransform& rotate(double d); AffineTransform& rotateFromVector(double x, double y); AffineTransform& translate(double tx, double ty); - AffineTransform& translateRight(double tx, double ty); AffineTransform& shear(double sx, double sy); AffineTransform& flipX(); AffineTransform& flipY(); @@ -172,6 +171,11 @@ public: operator wxGraphicsMatrix() const; #endif + static AffineTransform translation(double x, double y) + { + return AffineTransform(1, 0, 0, 1, x, y); + } + private: void setMatrix(const Transform m) { diff --git a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h index 0a0aaf0..dd5dae2 100644 --- a/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h @@ -55,7 +55,7 @@ private: virtual bool apply(TransformationMatrix& transform, const IntSize&) const { - transform.multLeft(TransformationMatrix(m_matrix)); + transform.multiply(TransformationMatrix(m_matrix)); return false; } diff --git a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h index fd9b27e..6f4e725 100644 --- a/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/MatrixTransformOperation.h @@ -62,7 +62,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.multLeft(TransformationMatrix(matrix)); + transform.multiply(matrix); return false; } diff --git a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp index 9fd03a1..18bfe37 100644 --- a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp +++ b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.cpp @@ -26,7 +26,7 @@ #include "config.h" #include "PerspectiveTransformOperation.h" -#include <algorithm> +#include <wtf/MathExtras.h> using namespace std; @@ -37,22 +37,29 @@ PassRefPtr<TransformOperation> PerspectiveTransformOperation::blend(const Transf if (from && !from->isSameType(*this)) return this; - if (blendToIdentity) - return PerspectiveTransformOperation::create(m_p + (1. - m_p) * progress); + if (blendToIdentity) { + double p = m_p.calcFloatValue(1); + p = p + (1. - p) * progress; // FIXME: this seems wrong. https://bugs.webkit.org/show_bug.cgi?id=52700 + return PerspectiveTransformOperation::create(Length(clampToPositiveInteger(p), Fixed)); + } const PerspectiveTransformOperation* fromOp = static_cast<const PerspectiveTransformOperation*>(from); - double fromP = fromOp ? fromOp->m_p : 0; - double toP = m_p; + Length fromP = fromOp ? fromOp->m_p : Length(m_p.type()); + Length toP = m_p; TransformationMatrix fromT; TransformationMatrix toT; - fromT.applyPerspective(fromP); - toT.applyPerspective(toP); + fromT.applyPerspective(fromP.calcFloatValue(1)); + toT.applyPerspective(toP.calcFloatValue(1)); toT.blend(fromT, progress); TransformationMatrix::DecomposedType decomp; toT.decompose(decomp); - - return PerspectiveTransformOperation::create(decomp.perspectiveZ ? -1.0 / decomp.perspectiveZ : 0.0); + + if (decomp.perspectiveZ) { + double val = -1.0 / decomp.perspectiveZ; + return PerspectiveTransformOperation::create(Length(clampToPositiveInteger(val), Fixed)); + } + return PerspectiveTransformOperation::create(Length(0, Fixed)); } } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h index 834cc83..886d3dc 100644 --- a/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h +++ b/Source/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h @@ -26,21 +26,22 @@ #ifndef PerspectiveTransformOperation_h #define PerspectiveTransformOperation_h +#include "Length.h" #include "TransformOperation.h" namespace WebCore { class PerspectiveTransformOperation : public TransformOperation { public: - static PassRefPtr<PerspectiveTransformOperation> create(double p) + static PassRefPtr<PerspectiveTransformOperation> create(const Length& p) { return adoptRef(new PerspectiveTransformOperation(p)); } - double perspective() const { return m_p; } + Length perspective() const { return m_p; } private: - virtual bool isIdentity() const { return m_p == 0; } + virtual bool isIdentity() const { return m_p.calcFloatValue(1) == 0; } virtual OperationType getOperationType() const { return PERSPECTIVE; } virtual bool isSameType(const TransformOperation& o) const { return o.getOperationType() == PERSPECTIVE; } @@ -54,18 +55,19 @@ private: virtual bool apply(TransformationMatrix& transform, const IntSize&) const { - transform.applyPerspective(m_p); + transform.applyPerspective(m_p.calcFloatValue(1)); return false; } virtual PassRefPtr<TransformOperation> blend(const TransformOperation* from, double progress, bool blendToIdentity = false); - PerspectiveTransformOperation(double p) + PerspectiveTransformOperation(const Length& p) : m_p(p) { + ASSERT(p.isFixed()); } - double m_p; + Length m_p; }; } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp index 357a140..c7283a5 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp +++ b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp @@ -55,21 +55,17 @@ namespace WebCore { // webmasters - are to be held responsible. Basically, don't be a jerk, and remember that anything free comes // with no guarantee. -// A Note About row-major vs. column major matrixes +// A clarification about the storage of matrix elements // -// The clients of this class (CSSMatrix and SVGMatrix) assume a column-major ordering. -// That means that when the matrix is initialized with 16 values, the first 4 values -// go in the 4 rows of the first column, etc. And in the dereferencing calls, the first -// digit is the column (e.g., m23() is column 2 row 3). Because C++ uses row-major arrays -// the internal matrix is stored in row-major order, so m[2][0] means row 2, column 0. This -// has no bearing on how the matrix is viewed on the outside, since all access is done -// with function calls. But it does help make the code more clear if you know that. +// This class uses a 2 dimensional array internally to store the elements of the matrix. The first index into +// the array refers to the column that the element lies in; the second index refers to the row. // -// FIXME: Multiply calls are named for what they do in the internal, row-major world. -// multLeft is actually a multRight in a column-major world, and multiply is a multLeft -// in a column-major world. For now I've left it that way to avoid too many confusing -// changes to the code. In particular AffineTransform uses these same terms for the -// opposite operations. So we have to be VERY careful when we change them. +// In other words, this is the layout of the matrix: +// +// | m_matrix[0][0] m_matrix[1][0] m_matrix[2][0] m_matrix[3][0] | +// | m_matrix[0][1] m_matrix[1][1] m_matrix[2][1] m_matrix[3][1] | +// | m_matrix[0][2] m_matrix[1][2] m_matrix[2][2] m_matrix[3][2] | +// | m_matrix[0][3] m_matrix[1][3] m_matrix[2][3] m_matrix[3][3] | typedef double Vector4[4]; typedef double Vector3[3]; @@ -634,7 +630,7 @@ TransformationMatrix& TransformationMatrix::scaleNonUniform(double sx, double sy mat.m_matrix[0][0] = sx; mat.m_matrix[1][1] = sy; - multLeft(mat); + multiply(mat); return *this; } @@ -645,7 +641,7 @@ TransformationMatrix& TransformationMatrix::scale3d(double sx, double sy, double mat.m_matrix[1][1] = sy; mat.m_matrix[2][2] = sz; - multLeft(mat); + multiply(mat); return *this; } @@ -732,7 +728,7 @@ TransformationMatrix& TransformationMatrix::rotate3d(double x, double y, double 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); + multiply(mat); return *this; } @@ -783,7 +779,7 @@ TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, doubl 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); + rmat.multiply(mat); rx /= 2.0f; sinA = sin(rx); @@ -803,9 +799,9 @@ TransformationMatrix& TransformationMatrix::rotate3d(double rx, double ry, doubl 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); + rmat.multiply(mat); - multLeft(rmat); + multiply(rmat); return *this; } @@ -869,7 +865,7 @@ TransformationMatrix& TransformationMatrix::skew(double sx, double sy) 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); + multiply(mat); return *this; } @@ -879,7 +875,7 @@ TransformationMatrix& TransformationMatrix::applyPerspective(double p) if (p != 0) mat.m_matrix[2][3] = -1/p; - multLeft(mat); + multiply(mat); return *this; } @@ -896,7 +892,7 @@ TransformationMatrix TransformationMatrix::rectToRect(const FloatRect& from, con // // *this = mat * *this // -TransformationMatrix& TransformationMatrix::multLeft(const TransformationMatrix& mat) +TransformationMatrix& TransformationMatrix::multiply(const TransformationMatrix& mat) { Matrix4 tmp; @@ -1105,25 +1101,25 @@ void TransformationMatrix::recompose(const DecomposedType& decomp) 2 * (xz - yw), 2 * (yz + xw), 1 - 2 * (xx + yy), 0, 0, 0, 0, 1); - multLeft(rotationMatrix); + multiply(rotationMatrix); // now apply skew if (decomp.skewYZ) { TransformationMatrix tmp; tmp.setM32((float) decomp.skewYZ); - multLeft(tmp); + multiply(tmp); } if (decomp.skewXZ) { TransformationMatrix tmp; tmp.setM31((float) decomp.skewXZ); - multLeft(tmp); + multiply(tmp); } if (decomp.skewXY) { TransformationMatrix tmp; tmp.setM21((float) decomp.skewXY); - multLeft(tmp); + multiply(tmp); } // finally, apply scale diff --git a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h index c883675..fa27c0e 100644 --- a/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h +++ b/Source/WebCore/platform/graphics/transforms/TransformationMatrix.h @@ -208,11 +208,8 @@ public: void setF(double f) { m_matrix[3][1] = f; } // 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& scaleNonUniform(double sx, double sy); TransformationMatrix& scale3d(double sx, double sy, double sz); @@ -296,19 +293,18 @@ public: } bool operator!=(const TransformationMatrix& other) const { return !(*this == other); } - - // *this = *this * t (i.e., a multRight) + + // *this = *this * t TransformationMatrix& operator*=(const TransformationMatrix& t) { - *this = *this * t; - return *this; + return multiply(t); } - // result = *this * t (i.e., a multRight) + // result = *this * t TransformationMatrix operator*(const TransformationMatrix& t) const { - TransformationMatrix result = t; - result.multLeft(*this); + TransformationMatrix result = *this; + result.multiply(t); return result; } diff --git a/Source/WebCore/platform/graphics/win/FontCGWin.cpp b/Source/WebCore/platform/graphics/win/FontCGWin.cpp index 8012722..fe26c43 100644 --- a/Source/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/Source/WebCore/platform/graphics/win/FontCGWin.cpp @@ -167,8 +167,12 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData drawIntoBitmap = true; // We put slop into this rect, since glyphs can overflow the ascent/descent bounds and the left/right edges. // FIXME: Can get glyphs' optical bounds (even from CG) to get this right. - int lineGap = font->lineGap(); - textRect = IntRect(point.x() - (font->ascent() + font->descent()) / 2, point.y() - font->ascent() - lineGap, totalWidth + font->ascent() + font->descent(), font->lineSpacing()); + const FontMetrics& fontMetrics = font->fontMetrics(); + int lineGap = fontMetrics.lineGap(); + textRect = IntRect(point.x() - (fontMetrics.ascent() + fontMetrics.descent()) / 2, + point.y() - fontMetrics.ascent() - lineGap, + totalWidth + fontMetrics.ascent() + fontMetrics.descent(), + fontMetrics.lineSpacing()); bitmap.set(graphicsContext->createWindowsBitmap(textRect.size())); memset(bitmap->buffer(), 255, bitmap->bufferLength()); hdc = bitmap->hdc(); @@ -288,7 +292,7 @@ static void drawGDIGlyphs(GraphicsContext* graphicsContext, const SimpleFontData buffer[i + 2] = fillColor.red(); buffer[i + 3] = alpha; } - graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.topLeft()); + graphicsContext->drawWindowsBitmap(bitmap.get(), textRect.location()); } else graphicsContext->releaseWindowsContext(hdc, textRect, true, false); } diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp index 9cae99b..4aee6cd 100644 --- a/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.cpp @@ -59,7 +59,7 @@ FontCustomPlatformData::~FontCustomPlatformData() } } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode renderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode renderingMode) { ASSERT(m_fontReference); ASSERT(T2embedLibrary()); @@ -154,13 +154,12 @@ static unsigned long WINAPIV readEmbedProc(void* stream, void* buffer, unsigned // not allow access from CSS. static String createUniqueFontName() { - Vector<char> fontUuid(sizeof(GUID)); - CoCreateGuid(reinterpret_cast<GUID*>(fontUuid.data())); + GUID fontUuid; + CoCreateGuid(&fontUuid); - Vector<char> fontNameVector; - base64Encode(fontUuid, fontNameVector); - ASSERT(fontNameVector.size() < LF_FACESIZE); - return String(fontNameVector.data(), fontNameVector.size()); + String fontName = base64Encode(reinterpret_cast<char*>(&fontUuid), sizeof(fontUuid)); + ASSERT(fontName.length() < LF_FACESIZE); + return fontName; } FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer* buffer) diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h index de33c63..abdb356 100644 --- a/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformData.h @@ -23,6 +23,7 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include "PlatformString.h" #include <wtf/Forward.h> #include <wtf/Noncopyable.h> @@ -45,7 +46,7 @@ public: ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); static bool supportsFormat(const String&); diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp index c3decbf..fd30a6d 100644 --- a/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.cpp @@ -32,7 +32,7 @@ FontCustomPlatformData::~FontCustomPlatformData() cairo_font_face_destroy(m_fontFace); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant) { return FontPlatformData(m_fontFace, size, bold, italic); } diff --git a/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h index 9c67037..ea3ae38 100644 --- a/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h +++ b/Source/WebCore/platform/graphics/win/FontCustomPlatformDataCairo.h @@ -42,7 +42,7 @@ public: } ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth); static bool supportsFormat(const String&); diff --git a/Source/WebCore/platform/graphics/win/FontWin.cpp b/Source/WebCore/platform/graphics/win/FontWin.cpp index 2ed9eb3..47c44bc 100644 --- a/Source/WebCore/platform/graphics/win/FontWin.cpp +++ b/Source/WebCore/platform/graphics/win/FontWin.cpp @@ -45,6 +45,11 @@ bool Font::canReturnFallbackFontsForComplexText() return true; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { @@ -122,8 +127,8 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon UniscribeController controller(this, run, fallbackFonts); controller.advance(run.length()); if (glyphOverflow) { - glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - ascent()); - glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - descent()); + glyphOverflow->top = max<int>(glyphOverflow->top, ceilf(-controller.minGlyphBoundingBoxY()) - fontMetrics().ascent()); + glyphOverflow->bottom = max<int>(glyphOverflow->bottom, ceilf(controller.maxGlyphBoundingBoxY()) - fontMetrics().descent()); glyphOverflow->left = max<int>(0, ceilf(-controller.minGlyphBoundingBoxX())); glyphOverflow->right = max<int>(0, ceilf(controller.maxGlyphBoundingBoxX() - controller.runWidthSoFar())); } diff --git a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp b/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp index f1953e4..bb22024 100644 --- a/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp +++ b/Source/WebCore/platform/graphics/win/GraphicsContextWin.cpp @@ -154,7 +154,7 @@ void GraphicsContextPlatformPrivate::clip(const FloatRect& clipRect) { if (!m_hdc) return; - IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.right(), clipRect.bottom()); + IntersectClipRect(m_hdc, clipRect.x(), clipRect.y(), clipRect.maxX(), clipRect.maxY()); } void GraphicsContextPlatformPrivate::clip(const Path&) diff --git a/Source/WebCore/platform/graphics/win/IconWin.cpp b/Source/WebCore/platform/graphics/win/IconWin.cpp index 4d4d219..7e03362 100644 --- a/Source/WebCore/platform/graphics/win/IconWin.cpp +++ b/Source/WebCore/platform/graphics/win/IconWin.cpp @@ -25,7 +25,6 @@ #include "GraphicsContext.h" #include "LocalWindowsContext.h" #include "PlatformString.h" -#include <tchar.h> #include <windows.h> #if OS(WINCE) @@ -68,16 +67,16 @@ PassRefPtr<Icon> Icon::createIconForFiles(const Vector<String>& filenames) #if OS(WINCE) return 0; #else - TCHAR buffer[MAX_PATH]; - UINT length = ::GetSystemDirectory(buffer, WTF_ARRAY_LENGTH(buffer)); + WCHAR buffer[MAX_PATH]; + UINT length = ::GetSystemDirectoryW(buffer, WTF_ARRAY_LENGTH(buffer)); if (!length) return 0; - - if (_tcscat_s(buffer, TEXT("\\shell32.dll"))) + + if (wcscat_s(buffer, L"\\shell32.dll")) return 0; HICON hIcon; - if (!::ExtractIconEx(buffer, shell32MultipleFileIconIndex, 0, &hIcon, 1)) + if (!::ExtractIconExW(buffer, shell32MultipleFileIconIndex, 0, &hIcon, 1)) return 0; return adoptRef(new Icon(hIcon)); #endif diff --git a/Source/WebCore/platform/graphics/win/IntRectWin.cpp b/Source/WebCore/platform/graphics/win/IntRectWin.cpp index fe25a7f..6af6735 100644 --- a/Source/WebCore/platform/graphics/win/IntRectWin.cpp +++ b/Source/WebCore/platform/graphics/win/IntRectWin.cpp @@ -38,7 +38,7 @@ IntRect::IntRect(const RECT& r) IntRect::operator RECT() const { - RECT rect = { x(), y(), right(), bottom() }; + RECT rect = { x(), y(), maxX(), maxY() }; return rect; } diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp index 01db7f2..dd3cd32 100644 --- a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp @@ -34,6 +34,11 @@ #include <CoreGraphics/CGColor.h> #endif +#if USE(ACCELERATED_COMPOSITING) +#include "CACFLayerTreeHost.h" +#include "PlatformCALayer.h" +#endif + namespace WebCore { MediaPlayerPrivateFullscreenWindow::MediaPlayerPrivateFullscreenWindow(MediaPlayerPrivateFullscreenClient* client) @@ -47,8 +52,11 @@ MediaPlayerPrivateFullscreenWindow::MediaPlayerPrivateFullscreenWindow(MediaPlay MediaPlayerPrivateFullscreenWindow::~MediaPlayerPrivateFullscreenWindow() { - if (m_hwnd) - close(); + if (!m_hwnd) + return; + + ::DestroyWindow(m_hwnd); + ASSERT(!m_hwnd); } void MediaPlayerPrivateFullscreenWindow::createWindow(HWND parentHwnd) @@ -65,8 +73,7 @@ void MediaPlayerPrivateFullscreenWindow::createWindow(HWND parentHwnd) windowAtom = ::RegisterClassEx(&wcex); } - if (m_hwnd) - close(); + ASSERT(!m_hwnd); MONITORINFO mi = {0}; mi.cbSize = sizeof(MONITORINFO); @@ -87,12 +94,6 @@ void MediaPlayerPrivateFullscreenWindow::createWindow(HWND parentHwnd) ::SetFocus(m_hwnd); } -void MediaPlayerPrivateFullscreenWindow::close() -{ - ::DestroyWindow(m_hwnd); - ASSERT(!m_hwnd); -} - #if USE(ACCELERATED_COMPOSITING) void MediaPlayerPrivateFullscreenWindow::setRootChildLayer(PassRefPtr<PlatformCALayer> rootChild) { diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h index c1ae762..e07bbac 100644 --- a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.h @@ -26,9 +26,10 @@ #ifndef MediaPlayerPrivateFullscreenWindow_h #define MediaPlayerPrivateFullscreenWindow_h +#include <wtf/RefPtr.h> + #if USE(ACCELERATED_COMPOSITING) -#include "CACFLayerTreeHost.h" -#include "PlatformCALayer.h" +#include "CACFLayerTreeHostClient.h" #endif typedef unsigned WPARAM; @@ -40,6 +41,11 @@ typedef unsigned int UINT; namespace WebCore { +#if USE(ACCELERATED_COMPOSITING) +class CACFLayerTreeHost; +class PlatformCALayer; +#endif + class MediaPlayerPrivateFullscreenClient { public: virtual LRESULT fullscreenClientWndProc(HWND, UINT message, WPARAM, LPARAM) = 0; @@ -53,13 +59,10 @@ public: ~MediaPlayerPrivateFullscreenWindow(); void createWindow(HWND ownerWindow); - void close(); HWND hwnd() const { return m_hwnd; } #if USE(ACCELERATED_COMPOSITING) - CACFLayerTreeHost* layerView() const { return m_layerTreeHost.get(); } - PlatformCALayer* rootChildLayer() const { return m_rootChild.get(); } void setRootChildLayer(PassRefPtr<PlatformCALayer>); #endif diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp index 0b91455..d47de2b 100644 --- a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp @@ -1059,7 +1059,7 @@ float MediaPlayerPrivateQuickTimeVisualContext::mediaTimeForTimeValue(float time if (m_readyState < MediaPlayer::HaveMetadata || !(timeScale = m_movie->timeScale())) return timeValue; - long mediaTimeValue = static_cast<long>(timeValue * timeScale); + long mediaTimeValue = lroundf(timeValue * timeScale); return static_cast<float>(mediaTimeValue) / timeScale; } diff --git a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index 431d624..07b7621 100644 --- a/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/Source/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -622,7 +622,7 @@ void MediaPlayerPrivate::paint(GraphicsContext* p, const IntRect& r) m_qtGWorld->paint(hdc, r.x(), r.y()); if (usingTempBitmap) - p->drawWindowsBitmap(bitmap.get(), r.topLeft()); + p->drawWindowsBitmap(bitmap.get(), r.location()); else p->releaseWindowsContext(hdc, r); diff --git a/Source/WebCore/platform/graphics/win/QTMovie.cpp b/Source/WebCore/platform/graphics/win/QTMovie.cpp index dfa1d36..4cd8161 100644 --- a/Source/WebCore/platform/graphics/win/QTMovie.cpp +++ b/Source/WebCore/platform/graphics/win/QTMovie.cpp @@ -33,7 +33,9 @@ #include <Movies.h> #include <QTML.h> #include <QuickTimeComponents.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/Assertions.h> +#include <wtf/MathExtras.h> #include <wtf/Noncopyable.h> #include <wtf/Vector.h> @@ -46,7 +48,6 @@ static const long subTitleTrackType = 'sbtl'; static const long mpeg4ObjectDescriptionTrackType = 'odsm'; static const long mpeg4SceneDescriptionTrackType = 'sdsm'; static const long closedCaptionDisplayPropertyID = 'disp'; -static LPCTSTR fullscreenQTMoviePointerProp = TEXT("fullscreenQTMoviePointer"); // Resizing GWorlds is slow, give them a minimum size so size of small // videos can be animated smoothly @@ -60,7 +61,7 @@ union UppParam { void* ptr; }; -static Vector<CFStringRef>* gSupportedTypes = 0; +static CFMutableArrayRef gSupportedTypes = 0; static SInt32 quickTimeVersion = 0; class QTMoviePrivate : public QTMovieTaskClient { @@ -374,10 +375,10 @@ void QTMovie::setCurrentTime(float time) const m_private->m_seeking = true; TimeScale scale = GetMovieTimeScale(m_private->m_movie); if (m_private->m_movieController) { - QTRestartAtTimeRecord restart = { time * scale , 0 }; + QTRestartAtTimeRecord restart = { lroundf(time * scale) , 0 }; MCDoAction(m_private->m_movieController, mcActionRestartAtTime, (void *)&restart); } else - SetMovieTimeValue(m_private->m_movie, TimeValue(time * scale)); + SetMovieTimeValue(m_private->m_movie, TimeValue(lroundf(time * scale))); QTMovieTask::sharedTask()->updateTaskTimer(); } @@ -747,112 +748,59 @@ long QTMovie::timeScale() const return GetMovieTimeScale(m_private->m_movie); } +static void getMIMETypeCallBack(const char* type); + static void initializeSupportedTypes() { if (gSupportedTypes) return; - gSupportedTypes = new Vector<CFStringRef>; + gSupportedTypes = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); if (quickTimeVersion < minimumQuickTimeVersion) { LOG_ERROR("QuickTime version %x detected, at least %x required. Returning empty list of supported media MIME types.", quickTimeVersion, minimumQuickTimeVersion); return; } // QuickTime doesn't have an importer for video/quicktime. Add it manually. - gSupportedTypes->append(CFSTR("video/quicktime")); - - for (int index = 0; index < 2; index++) { - ComponentDescription findCD; - - // look at all movie importers that can import in place and are installed. - findCD.componentType = MovieImportType; - findCD.componentSubType = 0; - findCD.componentManufacturer = 0; - findCD.componentFlagsMask = cmpIsMissing | movieImportSubTypeIsFileExtension | canMovieImportInPlace | dontAutoFileMovieImport; - - // look at those registered by HFS file types the first time through, by file extension the second time - findCD.componentFlags = canMovieImportInPlace | (index ? movieImportSubTypeIsFileExtension : 0); - - long componentCount = CountComponents(&findCD); - if (!componentCount) - continue; + CFArrayAppendValue(gSupportedTypes, CFSTR("video/quicktime")); + + wkGetQuickTimeMIMETypeList(getMIMETypeCallBack); +} - Component comp = 0; - while (comp = FindNextComponent(comp, &findCD)) { - // Does this component have a MIME type container? - ComponentDescription infoCD; - OSErr err = GetComponentInfo(comp, &infoCD, nil /*name*/, nil /*info*/, nil /*icon*/); - if (err) - continue; - if (!(infoCD.componentFlags & hasMovieImportMIMEList)) - continue; - QTAtomContainer mimeList = 0; - err = MovieImportGetMIMETypeList((ComponentInstance)comp, &mimeList); - if (err || !mimeList) - continue; +static void getMIMETypeCallBack(const char* type) +{ + ASSERT(type); + CFStringRef cfType = CFStringCreateWithCString(kCFAllocatorDefault, type, kCFStringEncodingMacRoman); + if (!cfType) + return; - // Grab every type from the container. - QTLockContainer(mimeList); - int typeCount = QTCountChildrenOfType(mimeList, kParentAtomIsContainer, kMimeInfoMimeTypeTag); - for (int typeIndex = 1; typeIndex <= typeCount; typeIndex++) { - QTAtom mimeTag = QTFindChildByIndex(mimeList, 0, kMimeInfoMimeTypeTag, typeIndex, 0); - if (!mimeTag) - continue; - char* atomData; - long typeLength; - if (noErr != QTGetAtomDataPtr(mimeList, mimeTag, &typeLength, &atomData)) - continue; - - char typeBuffer[256]; - if (typeLength >= sizeof(typeBuffer)) - continue; - memcpy(typeBuffer, atomData, typeLength); - typeBuffer[typeLength] = 0; - - // Only add "audio/..." and "video/..." types. - if (strncmp(typeBuffer, "audio/", 6) && strncmp(typeBuffer, "video/", 6)) - continue; - - CFStringRef cfMimeType = CFStringCreateWithCString(0, typeBuffer, kCFStringEncodingUTF8); - if (!cfMimeType) - continue; - - // Only add each type once. - bool alreadyAdded = false; - for (int addedIndex = 0; addedIndex < gSupportedTypes->size(); addedIndex++) { - CFStringRef type = gSupportedTypes->at(addedIndex); - if (kCFCompareEqualTo == CFStringCompare(cfMimeType, type, kCFCompareCaseInsensitive)) { - alreadyAdded = true; - break; - } - } - if (!alreadyAdded) - gSupportedTypes->append(cfMimeType); - else - CFRelease(cfMimeType); - } - DisposeHandle(mimeList); - } + // Filter out all non-audio or -video MIME Types, and only add each type once: + if (CFStringHasPrefix(cfType, CFSTR("audio/")) || CFStringHasPrefix(cfType, CFSTR("video/"))) { + CFRange range = CFRangeMake(0, CFArrayGetCount(gSupportedTypes)); + if (!CFArrayContainsValue(gSupportedTypes, range, cfType)) + CFArrayAppendValue(gSupportedTypes, cfType); } + + CFRelease(cfType); } unsigned QTMovie::countSupportedTypes() { initializeSupportedTypes(); - return static_cast<unsigned>(gSupportedTypes->size()); + return static_cast<unsigned>(CFArrayGetCount(gSupportedTypes)); } void QTMovie::getSupportedType(unsigned index, const UChar*& str, unsigned& len) { initializeSupportedTypes(); - ASSERT(index < gSupportedTypes->size()); + ASSERT(index < CFArrayGetCount(gSupportedTypes)); // Allocate sufficient buffer to hold any MIME type static UniChar* staticBuffer = 0; if (!staticBuffer) staticBuffer = new UniChar[32]; - CFStringRef cfstr = gSupportedTypes->at(index); + CFStringRef cfstr = (CFStringRef)CFArrayGetValueAtIndex(gSupportedTypes, index); len = CFStringGetLength(cfstr); CFRange range = { 0, len }; CFStringGetCharacters(cfstr, range, staticBuffer); diff --git a/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp b/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp index e13f732..4be1bbb 100644 --- a/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp +++ b/Source/WebCore/platform/graphics/win/QTMovieGWorld.cpp @@ -40,7 +40,7 @@ using namespace std; static const long minimumQuickTimeVersion = 0x07300000; // 7.3 -static LPCTSTR fullscreenQTMovieGWorldPointerProp = TEXT("fullscreenQTMovieGWorldPointer"); +static LPCWSTR fullscreenQTMovieGWorldPointerProp = L"fullscreenQTMovieGWorldPointer"; // Resizing GWorlds is slow, give them a minimum size so size of small // videos can be animated smoothly @@ -378,10 +378,10 @@ bool QTMovieGWorld::isDisabled() const LRESULT QTMovieGWorld::fullscreenWndProc(HWND wnd, UINT message, WPARAM wParam, LPARAM lParam) { - QTMovieGWorld* movie = static_cast<QTMovieGWorld*>(GetProp(wnd, fullscreenQTMovieGWorldPointerProp)); + QTMovieGWorld* movie = static_cast<QTMovieGWorld*>(GetPropW(wnd, fullscreenQTMovieGWorldPointerProp)); if (message == WM_DESTROY) - RemoveProp(wnd, fullscreenQTMovieGWorldPointerProp); + RemovePropW(wnd, fullscreenQTMovieGWorldPointerProp); if (!movie) return DefWindowProc(wnd, message, wParam, lParam); @@ -423,7 +423,7 @@ HWND QTMovieGWorld::enterFullscreen(QTMovieGWorldFullscreenClient* client) // Set the 'this' pointer on the HWND HWND wnd = static_cast<HWND>(GetPortNativeWindow(m_private->m_fullscreenWindow)); - SetProp(wnd, fullscreenQTMovieGWorldPointerProp, static_cast<HANDLE>(this)); + SetPropW(wnd, fullscreenQTMovieGWorldPointerProp, static_cast<HANDLE>(this)); return wnd; } diff --git a/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp b/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp index 20d42ff..30a931e 100644 --- a/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp +++ b/Source/WebCore/platform/graphics/win/SimpleFontDataCGWin.cpp @@ -29,20 +29,19 @@ #include "config.h" #include "SimpleFontData.h" -#include <winsock2.h> #include "Font.h" #include "FontCache.h" #include "FloatRect.h" #include "FontDescription.h" #include "PlatformString.h" -#include <wtf/MathExtras.h> -#include <wtf/RetainPtr.h> -#include <unicode/uchar.h> -#include <unicode/unorm.h> #include <ApplicationServices/ApplicationServices.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> #include <mlang.h> -#include <tchar.h> +#include <unicode/uchar.h> +#include <unicode/unorm.h> +#include <winsock2.h> +#include <wtf/MathExtras.h> +#include <wtf/RetainPtr.h> namespace WebCore { @@ -64,19 +63,19 @@ void SimpleFontData::platformInit() int iAscent = CGFontGetAscent(font); int iDescent = CGFontGetDescent(font); int iLineGap = CGFontGetLeading(font); - m_unitsPerEm = CGFontGetUnitsPerEm(font); + unsigned unitsPerEm = CGFontGetUnitsPerEm(font); float pointSize = m_platformData.size(); - float fAscent = scaleEmToUnits(iAscent, m_unitsPerEm) * pointSize; - float fDescent = -scaleEmToUnits(iDescent, m_unitsPerEm) * pointSize; - float fLineGap = scaleEmToUnits(iLineGap, m_unitsPerEm) * pointSize; + float fAscent = scaleEmToUnits(iAscent, unitsPerEm) * pointSize; + float fDescent = -scaleEmToUnits(iDescent, unitsPerEm) * pointSize; + float fLineGap = scaleEmToUnits(iLineGap, unitsPerEm) * pointSize; if (!isCustomFont()) { HDC dc = GetDC(0); HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); int faceLength = GetTextFace(dc, 0, 0); - Vector<TCHAR> faceName(faceLength); + Vector<WCHAR> faceName(faceLength); GetTextFace(dc, faceLength, faceName.data()); - m_isSystemFont = !_tcscmp(faceName.data(), _T("Lucida Grande")); + m_isSystemFont = !wcscmp(faceName.data(), L"Lucida Grande"); SelectObject(dc, oldFont); ReleaseDC(0, dc); @@ -88,15 +87,15 @@ void SimpleFontData::platformInit() // web standard. The AppKit adjustment of 20% is too big and is // incorrectly added to line spacing, so we use a 15% adjustment instead // and add it to the ascent. - if (!_tcscmp(faceName.data(), _T("Times")) || !_tcscmp(faceName.data(), _T("Helvetica")) || !_tcscmp(faceName.data(), _T("Courier"))) + if (!wcscmp(faceName.data(), L"Times") || !wcscmp(faceName.data(), L"Helvetica") || !wcscmp(faceName.data(), L"Courier")) fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); } } - m_ascent = lroundf(fAscent); - m_descent = lroundf(fDescent); - m_lineGap = lroundf(fLineGap); - m_lineSpacing = m_ascent + m_descent + m_lineGap; + m_fontMetrics.setAscent(fAscent); + m_fontMetrics.setDescent(fDescent); + m_fontMetrics.setLineGap(fLineGap); + m_fontMetrics.setLineSpacing(lroundf(fAscent) + lroundf(fDescent) + lroundf(fLineGap)); // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font. // Unfortunately, NSFont will round this for us so we don't quite get the right value. @@ -109,11 +108,13 @@ void SimpleFontData::platformInit() // and web pages that foolishly use this metric for width will be laid out // poorly if we return an accurate height. Classic case is Times 13 point, // which has an "x" that is 7x6 pixels. - m_xHeight = scaleEmToUnits(max(CGRectGetMaxX(xBox), CGRectGetMaxY(xBox)), m_unitsPerEm) * pointSize; + m_fontMetrics.setXHeight(scaleEmToUnits(max(CGRectGetMaxX(xBox), CGRectGetMaxY(xBox)), unitsPerEm) * pointSize); } else { int iXHeight = CGFontGetXHeight(font); - m_xHeight = scaleEmToUnits(iXHeight, m_unitsPerEm) * pointSize; + m_fontMetrics.setXHeight(scaleEmToUnits(iXHeight, unitsPerEm) * pointSize); } + + m_fontMetrics.setUnitsPerEm(unitsPerEm); } void SimpleFontData::platformCharWidthInit() @@ -133,7 +134,7 @@ FloatRect SimpleFontData::platformBoundsForGlyph(Glyph glyph) const CGRect box; CGFontGetGlyphBBoxes(m_platformData.cgFont(), &glyph, 1, &box); float pointSize = m_platformData.size(); - CGFloat scale = pointSize / unitsPerEm(); + CGFloat scale = pointSize / fontMetrics().unitsPerEm(); FloatRect boundingBox = CGRectApplyAffineTransform(box, CGAffineTransformMakeScale(scale, -scale)); if (m_syntheticBoldOffset) boundingBox.setWidth(boundingBox.width() + m_syntheticBoldOffset); diff --git a/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp b/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp index 62ea060..277a13f 100644 --- a/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp +++ b/Source/WebCore/platform/graphics/win/SimpleFontDataCairoWin.cpp @@ -37,7 +37,6 @@ #include <cairo.h> #include <cairo-win32.h> #include <mlang.h> -#include <tchar.h> #include <wtf/MathExtras.h> namespace WebCore { @@ -63,13 +62,16 @@ void SimpleFontData::platformInit() TEXTMETRIC textMetrics; GetTextMetrics(hdc, &textMetrics); - m_ascent = lroundf(textMetrics.tmAscent * metricsMultiplier); - m_descent = lroundf(textMetrics.tmDescent * metricsMultiplier); - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. - m_lineGap = lroundf(textMetrics.tmExternalLeading * metricsMultiplier); - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_avgCharWidth = lroundf(textMetrics.tmAveCharWidth * metricsMultiplier); - m_maxCharWidth = lroundf(textMetrics.tmMaxCharWidth * metricsMultiplier); + float ascent = textMetrics.tmAscent * metricsMultiplier; + float descent = textMetrics.tmDescent * metricsMultiplier; + float xHeight = ascent * 0.56f; // Best guess for xHeight for non-Truetype fonts. + float lineGap = textMetrics.tmExternalLeading * metricsMultiplier; + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); + m_avgCharWidth = textMetrics.tmAveCharWidth * metricsMultiplier; + m_maxCharWidth = textMetrics.tmMaxCharWidth * metricsMultiplier; OUTLINETEXTMETRIC metrics; if (GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics) > 0) { @@ -78,9 +80,10 @@ void SimpleFontData::platformInit() MAT2 mat = { 1, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y * metricsMultiplier; + xHeight = gm.gmptGlyphOrigin.y * metricsMultiplier; } + m_fontMetrics.setXHeight(xHeight); cairo_win32_scaled_font_done_font(scaledFont); m_isSystemFont = false; diff --git a/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp b/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp index 60afe6a..323ff73 100644 --- a/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp +++ b/Source/WebCore/platform/graphics/win/SimpleFontDataWin.cpp @@ -29,16 +29,15 @@ #include "config.h" #include "SimpleFontData.h" -#include <winsock2.h> #include "Font.h" #include "FontCache.h" #include "FloatRect.h" #include "FontDescription.h" -#include <wtf/MathExtras.h> +#include <mlang.h> #include <unicode/uchar.h> #include <unicode/unorm.h> -#include <mlang.h> -#include <tchar.h> +#include <winsock2.h> +#include <wtf/MathExtras.h> #if PLATFORM(CG) #include <ApplicationServices/ApplicationServices.h> @@ -66,14 +65,9 @@ bool SimpleFontData::shouldApplyMacAscentHack() void SimpleFontData::initGDIFont() { if (!m_platformData.size()) { - m_ascent = 0; - m_descent = 0; - m_lineGap = 0; - m_lineSpacing = 0; + m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; - m_xHeight = 0; - m_unitsPerEm = 0; return; } @@ -82,21 +76,25 @@ void SimpleFontData::initGDIFont() OUTLINETEXTMETRIC metrics; GetOutlineTextMetrics(hdc, sizeof(metrics), &metrics); TEXTMETRIC& textMetrics = metrics.otmTextMetrics; - m_ascent = textMetrics.tmAscent; - m_descent = textMetrics.tmDescent; - m_lineGap = textMetrics.tmExternalLeading; - m_lineSpacing = m_ascent + m_descent + m_lineGap; + float ascent = textMetrics.tmAscent; + float descent = textMetrics.tmDescent; + float lineGap = textMetrics.tmExternalLeading; + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); m_avgCharWidth = textMetrics.tmAveCharWidth; m_maxCharWidth = textMetrics.tmMaxCharWidth; - m_xHeight = m_ascent * 0.56f; // Best guess for xHeight if no x glyph is present. + float xHeight = ascent * 0.56f; // Best guess for xHeight if no x glyph is present. GLYPHMETRICS gm; MAT2 mat = { 1, 0, 0, 1 }; DWORD len = GetGlyphOutline(hdc, 'x', GGO_METRICS, &gm, 0, 0, &mat); if (len != GDI_ERROR && gm.gmptGlyphOrigin.y > 0) - m_xHeight = gm.gmptGlyphOrigin.y; + xHeight = gm.gmptGlyphOrigin.y; - m_unitsPerEm = metrics.otmEMSquare; + m_fontMetrics.setXHeight(xHeight); + m_fontMetrics.setUnitsPerEm(metrics.otmEMSquare); SelectObject(hdc, oldFont); ReleaseDC(0, hdc); diff --git a/Source/WebCore/platform/graphics/win/UniscribeController.cpp b/Source/WebCore/platform/graphics/win/UniscribeController.cpp index dac6c3e..ebbed51 100644 --- a/Source/WebCore/platform/graphics/win/UniscribeController.cpp +++ b/Source/WebCore/platform/graphics/win/UniscribeController.cpp @@ -49,7 +49,7 @@ UniscribeController::UniscribeController(const Font* font, const TextRun& run, H , m_end(run.length()) , m_currentCharacter(0) , m_runWidthSoFar(0) - , m_padding(run.padding()) + , m_padding(run.expansion()) , m_computingOffsetPosition(false) , m_includePartialGlyphs(false) , m_offsetX(0) @@ -394,9 +394,9 @@ bool UniscribeController::shapeAndPlaceItem(const UChar* cp, unsigned i, const S FloatRect glyphBounds = fontData->boundsForGlyph(glyph); glyphBounds.move(m_glyphOrigin.x(), m_glyphOrigin.y()); m_minGlyphBoundingBoxX = min(m_minGlyphBoundingBoxX, glyphBounds.x()); - m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.right()); + m_maxGlyphBoundingBoxX = max(m_maxGlyphBoundingBoxX, glyphBounds.maxX()); m_minGlyphBoundingBoxY = min(m_minGlyphBoundingBoxY, glyphBounds.y()); - m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.bottom()); + m_maxGlyphBoundingBoxY = max(m_maxGlyphBoundingBoxY, glyphBounds.maxY()); m_glyphOrigin.move(advance + offsetX, -offsetY); // Mutate the glyph array to contain our altered advances. diff --git a/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp b/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp index c2a178b..c8f2116 100644 --- a/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp +++ b/Source/WebCore/platform/graphics/win/WKCAImageQueue.cpp @@ -24,11 +24,11 @@ */ #include "config.h" +#include "WKCAImageQueue.h" #if USE(ACCELERATED_COMPOSITING) -#include "WKCAImageQueue.h" - +#include <CoreFoundation/CoreFoundation.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/RetainPtr.h> diff --git a/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp index f61ae8e..fb97fe1 100644 --- a/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp +++ b/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.cpp @@ -45,7 +45,7 @@ FontCustomPlatformData::~FontCustomPlatformData() g_customFontCache->unregisterFont(m_name); } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode renderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode renderingMode) { FontDescription fontDesc; fontDesc.setComputedSize(size); @@ -59,16 +59,14 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, b // not allow access from CSS. static String createUniqueFontName() { - Vector<char> fontUuid(sizeof(GUID)); + GUID fontUuid; - unsigned int* ptr = reinterpret_cast<unsigned int*>(fontUuid.data()); + unsigned int* ptr = reinterpret_cast<unsigned int*>(&fontUuid); for (int i = 0; i < sizeof(GUID) / sizeof(int) ; ++i) *(ptr + i) = static_cast<unsigned int>(randomNumber() * (std::numeric_limits<unsigned>::max() + 1.0)); - Vector<char> fontNameVector; - base64Encode(fontUuid, fontNameVector); - ASSERT(fontNameVector.size() < LF_FACESIZE); - String fontName(fontNameVector.data(), fontNameVector.size()); + String fontName = base64Encode(reinterpret_cast<char*>(&fontUuid), sizeof(fontUuid)); + ASSERT(fontName.length() < LF_FACESIZE); return fontName.replace('/', '_'); } diff --git a/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.h index 0508246..fe7ee94 100644 --- a/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/wince/FontCustomPlatformData.h @@ -23,6 +23,7 @@ #include "FontDescription.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include "PlatformString.h" #include <wtf/Noncopyable.h> @@ -47,7 +48,7 @@ namespace WebCore { ~FontCustomPlatformData(); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation fontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); static bool supportsFormat(const String&); diff --git a/Source/WebCore/platform/graphics/wince/FontWinCE.cpp b/Source/WebCore/platform/graphics/wince/FontWinCE.cpp index 5a4c8da..c3e6ce4 100644 --- a/Source/WebCore/platform/graphics/wince/FontWinCE.cpp +++ b/Source/WebCore/platform/graphics/wince/FontWinCE.cpp @@ -86,6 +86,7 @@ public: TextRunComponent::TextRunComponent(const UChar *start, int length, const TextRun& parentTextRun, const Font &font, int o) : m_textRun(start, length, parentTextRun.allowTabs(), 0, 0 + , parentTextRun.allowsTrailingExpansion() ? TextRun::AllowTrailingExpansion : TextRun::ForbidTrailingExpansion , parentTextRun.rtl() , parentTextRun.directionalOverride() , parentTextRun.applyRunRounding() @@ -112,7 +113,7 @@ static int generateComponents(TextRunComponents* components, const Font &font, c { int letterSpacing = font.letterSpacing(); int wordSpacing = font.wordSpacing(); - int padding = run.padding(); + int padding = run.expansion(); int numSpaces = 0; if (padding) { for (int i = 0; i < run.length(); i++) @@ -340,4 +341,9 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } +bool Font::canExpandAroundIdeographsInComplexText() +{ + return false; +} + } // namespace WebCore diff --git a/Source/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp b/Source/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp index 9b672d2..7b1c27b 100644 --- a/Source/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp +++ b/Source/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp @@ -23,7 +23,6 @@ #include "GraphicsContext.h" #include "AffineTransform.h" -#include "CharacterNames.h" #include "Font.h" #include "GDIExtras.h" #include "GlyphBuffer.h" @@ -33,9 +32,9 @@ #include "PlatformPathWinCE.h" #include "SharedBitmap.h" #include "SimpleFontData.h" -#include <wtf/OwnPtr.h> - #include <windows.h> +#include <wtf/OwnPtr.h> +#include <wtf/unicode/CharacterNames.h> namespace WebCore { @@ -62,7 +61,7 @@ static inline int stableRound(double d) // Unlike enclosingIntRect(), this function does strict rounding. static inline IntRect roundRect(const FloatRect& r) { - return IntRect(stableRound(r.x()), stableRound(r.y()), stableRound(r.right()) - stableRound(r.x()), stableRound(r.bottom()) - stableRound(r.y())); + return IntRect(stableRound(r.x()), stableRound(r.y()), stableRound(r.maxX()) - stableRound(r.x()), stableRound(r.maxY()) - stableRound(r.y())); } // Rotation transformation @@ -129,8 +128,8 @@ template<class Transform, class Rect, class Value> static inline Rect mapRect(co { Value x[4], y[4]; Value l, t, r, b; - r = rect.right() - 1; - b = rect.bottom() - 1; + r = rect.maxX() - 1; + b = rect.maxY() - 1; transform.map(rect.x(), rect.y(), x, y); transform.map(rect.x(), b, x + 1, y + 1); transform.map(r, b, x + 2, y + 2); @@ -503,10 +502,10 @@ TransparentLayerDC::TransparentLayerDC(GraphicsContextPlatformPrivate* data, Int m_rotation.m_postShiftX -= m_origRect.x(); m_rotation.m_postShiftY -= m_origRect.y(); - FloatPoint topLeft = m_data->m_transform.mapPoint(FloatPoint(rectBeforeTransform->topLeft())); - FloatPoint topRight(rectBeforeTransform->right() - 1, rectBeforeTransform->y()); + FloatPoint topLeft = m_data->m_transform.mapPoint(FloatPoint(rectBeforeTransform->location())); + FloatPoint topRight(rectBeforeTransform->maxX() - 1, rectBeforeTransform->y()); topRight = m_data->m_transform.mapPoint(topRight); - FloatPoint bottomLeft(rectBeforeTransform->x(), rectBeforeTransform->bottom() - 1); + FloatPoint bottomLeft(rectBeforeTransform->x(), rectBeforeTransform->maxY() - 1); bottomLeft = m_data->m_transform.mapPoint(bottomLeft); FloatSize sideTop = topRight - topLeft; FloatSize sideLeft = bottomLeft - topLeft; @@ -656,7 +655,7 @@ void GraphicsContext::drawRect(const IntRect& rect) if (trRect.height() <= 0) trRect.setHeight(1); - Rectangle(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + Rectangle(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); } SelectObject(dc, oldPen); @@ -726,7 +725,7 @@ void GraphicsContext::drawEllipse(const IntRect& rect) oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); if (brush || pen) - Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + Ellipse(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); SelectObject(dc, oldPen); SelectObject(dc, oldBrush); @@ -839,7 +838,7 @@ void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSp } HGDIOBJ oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); - Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + Ellipse(dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); SelectObject(dc, oldBrush); if (newClip) @@ -960,9 +959,9 @@ void GraphicsContext::clip(const FloatRect& rect) OwnPtr<HRGN> clipRgn(CreateRectRgn(0, 0, 0, 0)); if (GetClipRgn(m_data->m_dc, clipRgn.get()) > 0) - IntersectClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + IntersectClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); else { - clipRgn.set(CreateRectRgn(trRect.x(), trRect.y(), trRect.right(), trRect.bottom())); + clipRgn.set(CreateRectRgn(trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY())); SelectClipRgn(m_data->m_dc, clipRgn.get()); } } @@ -977,7 +976,7 @@ void GraphicsContext::clipOut(const IntRect& rect) IntRect trRect = m_data->mapRect(rect); - ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + ExcludeClipRect(m_data->m_dc, trRect.x(), trRect.y(), trRect.maxX(), trRect.maxY()); } void GraphicsContext::drawFocusRing(const Path& path, int width, int offset, const Color& color) @@ -1093,8 +1092,8 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); HGDIOBJ oldPen = SelectObject(dc, pen.get()); - int right = trRect.right() - 1; - int bottom = trRect.bottom() - 1; + int right = trRect.maxX() - 1; + int bottom = trRect.maxY() - 1; const POINT intPoints[5] = { { trRect.x(), trRect.y() }, @@ -1536,8 +1535,8 @@ void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPo float oldOpacity = m_data->m_opacity; m_data->m_opacity *= fillColor().alpha() / 255.0; - FloatRect textRect = font.selectionRectForText(run, point, font.height(), from, to); - textRect.setY(textRect.y() - font.ascent()); + FloatRect textRect = font.selectionRectForText(run, point, font.fontMetrics().height(), from, to); + textRect.setY(textRect.y() - font.fontMetrics().ascent()); IntRect trRect = enclosingIntRect(m_data->mapRect(textRect)); RECT bmpRect; AlphaPaintType alphaPaintType = mustSupportAlpha ? AlphaPaintOther : AlphaPaintNone; @@ -1546,7 +1545,7 @@ void GraphicsContext::drawText(const Font& font, const TextRun& run, const IntPo GraphicsContext gc(0); gc.setBitmap(bmp); gc.scale(FloatSize(m_data->m_transform.a(), m_data->m_transform.d())); - font.drawText(&gc, run, IntPoint(0, font.ascent()), from, to); + font.drawText(&gc, run, IntPoint(0, font.fontMetrics().ascent()), from, to); } unsigned key1; HDC memDC = bmp->getDC(&key1); @@ -1591,7 +1590,7 @@ void GraphicsContext::drawText(const SimpleFontData* fontData, const GlyphBuffer ? fontData->platformData().getScaledFontHandle(height, scaleX == scaleY ? 0 : width) : 0; - FloatPoint startPoint(point.x(), point.y() - fontData->ascent()); + FloatPoint startPoint(point.x(), point.y() - fontData->fontMetrics().ascent()); FloatPoint trPoint = m_data->mapPoint(startPoint); int y = stableRound(trPoint.y()); diff --git a/Source/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp b/Source/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp index 8534f89..8efe661 100644 --- a/Source/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp +++ b/Source/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp @@ -119,7 +119,7 @@ static inline void bezier(int segments, Vector<PathPoint>& pts, const PathPoint* static bool containsPoint(const FloatRect& r, const FloatPoint& p) { - return p.x() >= r.x() && p.y() >= r.y() && p.x() < r.right() && p.y() < r.bottom(); + return p.x() >= r.x() && p.y() >= r.y() && p.x() < r.maxX() && p.y() < r.maxY(); } static void normalizeAngle(float& angle) @@ -146,7 +146,7 @@ static void inflateRectToContainPoint(FloatRect& r, float x, float y) return; } if (x < r.x()) { - r.setWidth(r.right() - x); + r.setWidth(r.maxX() - x); r.setX(x); } else { float w = x - r.x() + 1; @@ -154,7 +154,7 @@ static void inflateRectToContainPoint(FloatRect& r, float x, float y) r.setWidth(w); } if (y < r.y()) { - r.setHeight(r.bottom() - y); + r.setHeight(r.maxY() - y); r.setY(y); } else { float h = y - r.y() + 1; @@ -740,8 +740,8 @@ void PlatformPath::addRect(const FloatRect& r) { moveTo(r.location()); - float right = r.right() - 1; - float bottom = r.bottom() - 1; + float right = r.maxX() - 1; + float bottom = r.maxY() - 1; addLineTo(FloatPoint(right, r.y())); addLineTo(FloatPoint(right, bottom)); addLineTo(FloatPoint(r.x(), bottom)); diff --git a/Source/WebCore/platform/graphics/wince/SharedBitmap.cpp b/Source/WebCore/platform/graphics/wince/SharedBitmap.cpp index 168a5e2..2bf0028 100644 --- a/Source/WebCore/platform/graphics/wince/SharedBitmap.cpp +++ b/Source/WebCore/platform/graphics/wince/SharedBitmap.cpp @@ -135,7 +135,7 @@ bool SharedBitmap::to16bit() int width = newBmpInfo.width(); int paddedWidth = newBmpInfo.paddedWidth(); int bufferSize = paddedWidth * newBmpInfo.height(); - OwnArrayPtr<unsigned> newPixelData(new unsigned[bufferSize / 2]); + OwnArrayPtr<unsigned> newPixelData = adoptArrayPtr(new unsigned[bufferSize / 2]); void* newPixels = newPixelData.get(); if (!newPixels) @@ -481,8 +481,8 @@ void SharedBitmap::drawPattern(HDC hdc, const AffineTransform& transform, const RECT dstRectWin = { stableRound(trRect.x()), stableRound(trRect.y()), - stableRound(trRect.right()), - stableRound(trRect.bottom()), + stableRound(trRect.maxX()), + stableRound(trRect.maxY()), }; if (dstRectWin.right <= dstRectWin.left || dstRectWin.bottom <= dstRectWin.top) return; @@ -497,8 +497,8 @@ void SharedBitmap::drawPattern(HDC hdc, const AffineTransform& transform, const RECT srcRectWin = { 0, 0, - stableRound(visibleDstRect.right()) - stableRound(visibleDstRect.x()), - stableRound(visibleDstRect.bottom()) - stableRound(visibleDstRect.y()) + stableRound(visibleDstRect.maxX()) - stableRound(visibleDstRect.x()), + stableRound(visibleDstRect.maxY()) - stableRound(visibleDstRect.y()) }; if (srcRectWin.right <= 0 || srcRectWin.bottom <= 0) return; diff --git a/Source/WebCore/platform/graphics/wince/SimpleFontDataWinCE.cpp b/Source/WebCore/platform/graphics/wince/SimpleFontDataWinCE.cpp index 27a021e..8abafbd 100644 --- a/Source/WebCore/platform/graphics/wince/SimpleFontDataWinCE.cpp +++ b/Source/WebCore/platform/graphics/wince/SimpleFontDataWinCE.cpp @@ -27,7 +27,6 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "config.h" #include "SimpleFontData.h" @@ -35,9 +34,8 @@ #include "Font.h" #include "FontCache.h" #include "FontDescription.h" -#include <wtf/MathExtras.h> #include <mlang.h> -#include <tchar.h> +#include <wtf/MathExtras.h> namespace WebCore { @@ -51,11 +49,14 @@ void SimpleFontData::platformInit() const TEXTMETRIC& tm = m_platformData.metrics(); m_isSystemFont = m_platformData.isSystemFont(); - m_ascent = (tm.tmAscent * m_platformData.size() + 36) / 72; - m_descent = (tm.tmDescent * m_platformData.size() + 36) / 72; - m_lineGap = (tm.tmExternalLeading * m_platformData.size() + 36) / 72; - m_lineSpacing = m_ascent + m_descent + m_lineGap; - m_xHeight = m_ascent * 0.56f; + float ascent = (tm.tmAscent * m_platformData.size() + 36) / 72.0f; + float descent = (tm.tmDescent * m_platformData.size() + 36) / 72.0f; + float lineGap = (tm.tmExternalLeading * m_platformData.size() + 36) / 72.0f; + m_fontMetrics.setAscent(ascent); + m_fontMetrics.setDescent(descent); + m_fontMetrics.setLineGap(lineGap); + m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); + m_fontMetrics.setXHeight(ascent * 0.56f); } void SimpleFontData::platformDestroy() diff --git a/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.cpp b/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.cpp index 6133372..055f0fc 100644 --- a/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.cpp +++ b/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.cpp @@ -31,7 +31,7 @@ FontCustomPlatformData::~FontCustomPlatformData() { } -FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontRenderingMode) +FontPlatformData FontCustomPlatformData::fontPlatformData(int size, bool bold, bool italic, FontOrientation, FontWidthVariant, FontRenderingMode) { return FontPlatformData(size, bold, italic); } diff --git a/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.h b/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.h index 86f99b2..c975296 100644 --- a/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.h +++ b/Source/WebCore/platform/graphics/wx/FontCustomPlatformData.h @@ -23,7 +23,9 @@ #include "FontOrientation.h" #include "FontRenderingMode.h" +#include "FontWidthVariant.h" #include <wtf/Forward.h> +#include <wtf/Noncopyable.h> namespace WebCore { @@ -38,7 +40,7 @@ namespace WebCore { static bool supportsFormat(const String&); - FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontRenderingMode = NormalRenderingMode); + FontPlatformData fontPlatformData(int size, bool bold, bool italic, FontOrientation = Horizontal, FontWidthVariant = RegularWidth, FontRenderingMode = NormalRenderingMode); }; FontCustomPlatformData* createFontCustomPlatformData(SharedBuffer*); diff --git a/Source/WebCore/platform/graphics/wx/FontPlatformData.h b/Source/WebCore/platform/graphics/wx/FontPlatformData.h index 9ae8b54..3ef0179 100644 --- a/Source/WebCore/platform/graphics/wx/FontPlatformData.h +++ b/Source/WebCore/platform/graphics/wx/FontPlatformData.h @@ -30,6 +30,7 @@ #define FontPlatformData_h #include "FontDescription.h" +#include "FontWidthVariant.h" #include "FontOrientation.h" #include "StringImpl.h" #include <wtf/Forward.h> @@ -150,6 +151,9 @@ public: FontOrientation orientation() const { return Horizontal; } // FIXME: Implement. + // We don't support this yet, so just return the default value for now. + FontWidthVariant widthVariant() const { return RegularWidth; } + #if OS(WINDOWS) bool useGDI() const; HFONT hfont() const; diff --git a/Source/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp b/Source/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp index 66c69ee..c125b7c 100644 --- a/Source/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp +++ b/Source/WebCore/platform/graphics/wx/FontPlatformDataWx.cpp @@ -126,7 +126,7 @@ unsigned FontPlatformData::computeHash() const thisFont->GetStyle(), thisFont->GetWeight(), thisFont->GetUnderlined(), - StringImpl::computeHash(thisFont->GetFaceName().utf8_str()) + WTF::StringHasher::createHash(thisFont->GetFaceName().utf8_str().data()) }; return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); diff --git a/Source/WebCore/platform/graphics/wx/FontWx.cpp b/Source/WebCore/platform/graphics/wx/FontWx.cpp index c01e249..c48f3c7 100644 --- a/Source/WebCore/platform/graphics/wx/FontWx.cpp +++ b/Source/WebCore/platform/graphics/wx/FontWx.cpp @@ -32,6 +32,7 @@ #include "IntRect.h" #include "NotImplemented.h" #include "SimpleFontData.h" +#include "TextRun.h" #if OS(WINDOWS) #include "UniscribeController.h" @@ -57,6 +58,15 @@ bool Font::canReturnFallbackFontsForComplexText() #endif } +bool Font::canExpandAroundIdeographsInComplexText() +{ +#if OS(DARWIN) + return true; +#else + return false; +#endif +} + void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, int from, int numGlyphs, const FloatPoint& point) const { diff --git a/Source/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/Source/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index f1c09c5..991be79 100644 --- a/Source/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/Source/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -515,7 +515,7 @@ void GraphicsContext::fillPath(const Path& path) #if USE(WXGC) wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); if (gc) - gc->FillPath(path.platformPath()); + gc->FillPath(*path.platformPath()); #endif } @@ -524,7 +524,7 @@ void GraphicsContext::strokePath(const Path& path) #if USE(WXGC) wxGraphicsContext* gc = m_data->context->GetGraphicsContext(); if (gc) - gc->StrokePath(path.platformPath()); + gc->StrokePath(*path.platformPath()); #endif } diff --git a/Source/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp b/Source/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp index 0e24bfc..4f24e4c 100644 --- a/Source/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp +++ b/Source/WebCore/platform/graphics/wx/SimpleFontDataWx.cpp @@ -53,12 +53,12 @@ void SimpleFontData::platformInit() wxFont *font = m_platformData.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(); + m_fontMetrics.setAscent(props.GetAscent()); + m_fontMetrics.setDescent(props.GetDescent()); + m_fontMetrics.setXHeight(props.GetXHeight()); + m_fontMetrics.setUnitsPerEm(1); // FIXME! + m_fontMetrics.setLineGap(props.GetLineGap()); + m_fontMetrics.setLineSpacing(props.GetLineSpacing()); } m_syntheticBoldOffset = 0.0f; diff --git a/Source/WebCore/platform/gtk/GtkVersioning.h b/Source/WebCore/platform/gtk/GtkVersioning.h index 7e9fcd1..70e1bbe 100644 --- a/Source/WebCore/platform/gtk/GtkVersioning.h +++ b/Source/WebCore/platform/gtk/GtkVersioning.h @@ -94,7 +94,10 @@ const gchar* gtk_menu_item_get_label(GtkMenuItem*); #define gtk_selection_data_get_length(data) (data)->length #define gtk_selection_data_get_data(data) (data)->data #define gtk_selection_data_get_target(data) (data)->target -#define gtk_adjustment_set_page_size(adj, value) (adj)->page_size = value +#define gtk_adjustment_set_page_size(adj, newValue) ((adj)->page_size = newValue) +#define gtk_adjustment_set_value(adj, newValue) ((adj)->value = newValue) +#define gtk_adjustment_set_lower(adj, newValue) ((adj)->lower = newValue) +#define gtk_adjustment_set_upper(adj, newValue) ((adj)->upper = newValue) void gtk_adjustment_configure(GtkAdjustment* adjustment, gdouble value, gdouble lower, gdouble upper, gdouble stepIncrement, gdouble pageIncrement, gdouble pageSize); diff --git a/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp b/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp index 8fc3020..9f7f14c 100644 --- a/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp +++ b/Source/WebCore/platform/gtk/MIMETypeRegistryGtk.cpp @@ -28,6 +28,9 @@ #include "config.h" #include "MIMETypeRegistry.h" +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> + namespace WebCore { struct ExtensionMap { @@ -62,6 +65,8 @@ static const ExtensionMap extensionMap [] = { String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + String s = ext.lower(); const ExtensionMap *e = extensionMap; while (e->extension) { diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk.cpp index bfe3901..1e9f159 100644 --- a/Source/WebCore/platform/gtk/RenderThemeGtk.cpp +++ b/Source/WebCore/platform/gtk/RenderThemeGtk.cpp @@ -224,15 +224,47 @@ bool RenderThemeGtk::paintTextArea(RenderObject* o, const PaintInfo& i, const In return paintTextField(o, i, r); } -static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntPoint& iconPoint) -{ +static void paintGdkPixbuf(GraphicsContext* context, const GdkPixbuf* icon, const IntRect& iconRect) +{ + IntSize iconSize(gdk_pixbuf_get_width(icon), gdk_pixbuf_get_height(icon)); + if (iconRect.size() != iconSize) { + // We could use cairo_scale() here but cairo/pixman downscale quality is quite bad. + GRefPtr<GdkPixbuf> scaledIcon = gdk_pixbuf_scale_simple(icon, iconRect.width(), iconRect.height(), + GDK_INTERP_BILINEAR); + icon = scaledIcon.get(); + } + cairo_t* cr = context->platformContext(); cairo_save(cr); - gdk_cairo_set_source_pixbuf(cr, icon, iconPoint.x(), iconPoint.y()); + gdk_cairo_set_source_pixbuf(cr, icon, iconRect.x(), iconRect.y()); cairo_paint(cr); cairo_restore(cr); } +// Defined in GTK+ (gtk/gtkiconfactory.c) +static const gint gtkIconSizeMenu = 16; +static const gint gtkIconSizeSmallToolbar = 18; +static const gint gtkIconSizeButton = 20; +static const gint gtkIconSizeLargeToolbar = 24; +static const gint gtkIconSizeDnd = 32; +static const gint gtkIconSizeDialog = 48; + +static GtkIconSize getIconSizeForPixelSize(gint pixelSize) +{ + if (pixelSize < gtkIconSizeSmallToolbar) + return GTK_ICON_SIZE_MENU; + if (pixelSize >= gtkIconSizeSmallToolbar && pixelSize < gtkIconSizeButton) + return GTK_ICON_SIZE_SMALL_TOOLBAR; + if (pixelSize >= gtkIconSizeButton && pixelSize < gtkIconSizeLargeToolbar) + return GTK_ICON_SIZE_BUTTON; + if (pixelSize >= gtkIconSizeLargeToolbar && pixelSize < gtkIconSizeDnd) + return GTK_ICON_SIZE_LARGE_TOOLBAR; + if (pixelSize >= gtkIconSizeDnd && pixelSize < gtkIconSizeDialog) + return GTK_ICON_SIZE_DND; + + return GTK_ICON_SIZE_DIALOG; +} + void RenderThemeGtk::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { adjustSearchFieldCancelButtonStyle(selector, style, e); @@ -243,57 +275,77 @@ bool RenderThemeGtk::paintSearchFieldResultsButton(RenderObject* o, const PaintI return paintSearchFieldResultsDecoration(o, i, rect); } -void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +static void adjustSearchFieldIconStyle(RenderStyle* style) { style->resetBorder(); style->resetPadding(); + // Get the icon size based on the font size. + int fontSize = style->fontSize(); + if (fontSize < gtkIconSizeMenu) { + style->setWidth(Length(fontSize, Fixed)); + style->setHeight(Length(fontSize, Fixed)); + return; + } gint width = 0, height = 0; - gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); + gtk_icon_size_lookup(getIconSizeForPixelSize(fontSize), &width, &height); style->setWidth(Length(width, Fixed)); style->setHeight(Length(height, Fixed)); } -static IntPoint centerRectVerticallyInParentInputElement(RenderObject* object, const IntRect& rect) +void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + adjustSearchFieldIconStyle(style); +} + +static IntRect centerRectVerticallyInParentInputElement(RenderObject* renderObject, const IntRect& rect) { - Node* input = object->node()->shadowAncestorNode(); // Get the renderer of <input> element. + // Get the renderer of <input> element. + Node* input = renderObject->node()->shadowAncestorNode(); if (!input->renderer()->isBox()) - return rect.topLeft(); + return IntRect(); // If possible center the y-coordinate of the rect vertically in the parent input element. // We also add one pixel here to ensure that the y coordinate is rounded up for box heights // that are even, which looks in relation to the box text. IntRect inputContentBox = toRenderBox(input->renderer())->absoluteContentBox(); - return IntPoint(rect.x(), inputContentBox.y() + (inputContentBox.height() - rect.height() + 1) / 2); + // Make sure the scaled decoration stays square and will fit in its parent's box. + int iconSize = std::min(inputContentBox.width(), std::min(inputContentBox.height(), rect.height())); + IntRect scaledRect(rect.x(), inputContentBox.y() + (inputContentBox.height() - iconSize + 1) / 2, iconSize, iconSize); + return scaledRect; } bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) { + IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); + if (iconRect.isEmpty()) + return false; + GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_FIND, gtkTextDirection(renderObject->style()->direction()), - gtkIconState(this, renderObject), GTK_ICON_SIZE_MENU); - paintGdkPixbuf(paintInfo.context, icon.get(), centerRectVerticallyInParentInputElement(renderObject, rect)); + gtkIconState(this, renderObject), + getIconSizeForPixelSize(rect.height())); + paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); return false; } void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const { - style->resetBorder(); - style->resetPadding(); - - gint width = 0, height = 0; - gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height); - style->setWidth(Length(width, Fixed)); - style->setHeight(Length(height, Fixed)); + adjustSearchFieldIconStyle(style); } bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) { + IntRect iconRect = centerRectVerticallyInParentInputElement(renderObject, rect); + if (iconRect.isEmpty()) + return false; + GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CLEAR, gtkTextDirection(renderObject->style()->direction()), - gtkIconState(this, renderObject), GTK_ICON_SIZE_MENU); - paintGdkPixbuf(paintInfo.context, icon.get(), centerRectVerticallyInParentInputElement(renderObject, rect)); + gtkIconState(this, renderObject), + getIconSizeForPixelSize(rect.height())); + paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); return false; } @@ -316,14 +368,21 @@ bool RenderThemeGtk::paintCapsLockIndicator(RenderObject* renderObject, const Pa if (paintInfo.context->paintingDisabled()) return true; + int iconSize = std::min(rect.width(), rect.height()); GRefPtr<GdkPixbuf> icon = getStockIcon(GTK_TYPE_ENTRY, GTK_STOCK_CAPS_LOCK_WARNING, gtkTextDirection(renderObject->style()->direction()), - gtkIconState(this, renderObject), GTK_ICON_SIZE_MENU); + 0, getIconSizeForPixelSize(iconSize)); + + // Only re-scale the icon when it's smaller than the minimum icon size. + if (iconSize >= gtkIconSizeMenu) + iconSize = gdk_pixbuf_get_height(icon.get()); // GTK+ locates the icon right aligned in the entry. The given rectangle is already // centered vertically by RenderTextControlSingleLine. - IntPoint iconPosition(rect.x() + rect.width() - gdk_pixbuf_get_width(icon.get()), rect.y()); - paintGdkPixbuf(paintInfo.context, icon.get(), iconPosition); + IntRect iconRect(rect.x() + rect.width() - iconSize, + rect.y() + (rect.height() - iconSize) / 2, + iconSize, iconSize); + paintGdkPixbuf(paintInfo.context, icon.get(), iconRect); return true; } @@ -421,10 +480,11 @@ bool RenderThemeGtk::paintMediaButton(RenderObject* renderObject, GraphicsContex gtkTextDirection(renderObject->style()->direction()), gtkIconState(this, renderObject), getMediaButtonIconSize(m_mediaIconSize)); - IntPoint iconPoint(rect.x() + (rect.width() - m_mediaIconSize) / 2, - rect.y() + (rect.height() - m_mediaIconSize) / 2); + IntRect iconRect(rect.x() + (rect.width() - m_mediaIconSize) / 2, + rect.y() + (rect.height() - m_mediaIconSize) / 2, + m_mediaIconSize, m_mediaIconSize); context->fillRect(FloatRect(rect), m_panelColor, ColorSpaceDeviceRGB); - paintGdkPixbuf(context, icon.get(), iconPoint); + paintGdkPixbuf(context, icon.get(), iconRect); return false; } diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk.h b/Source/WebCore/platform/gtk/RenderThemeGtk.h index 5765782..f5e03a9 100644 --- a/Source/WebCore/platform/gtk/RenderThemeGtk.h +++ b/Source/WebCore/platform/gtk/RenderThemeGtk.h @@ -31,11 +31,8 @@ #include "GRefPtr.h" #include "RenderTheme.h" -#ifdef GTK_API_VERSION_2 -#include "gtkdrawing.h" -#endif - typedef gulong GType; +typedef struct _GdkColormap GdkColormap; namespace WebCore { @@ -92,7 +89,8 @@ public: #endif #ifdef GTK_API_VERSION_2 - GtkWidget* gtkScrollbar(); + GtkWidget* gtkVScrollbar() const; + GtkWidget* gtkHScrollbar() const; static void getIndicatorMetrics(ControlPart, int& indicatorSize, int& indicatorSpacing); #else GtkStyleContext* gtkScrollbarStyle(); @@ -171,6 +169,9 @@ protected: virtual bool paintCapsLockIndicator(RenderObject*, const PaintInfo&, const IntRect&); + virtual void adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&); + private: void platformInit(); static void setTextInputBorders(RenderStyle*); @@ -195,12 +196,10 @@ private: #ifdef GTK_API_VERSION_2 void setupWidgetAndAddToContainer(GtkWidget*, GtkWidget*) const; - bool paintRenderObject(GtkThemeWidgetType, RenderObject*, GraphicsContext*, const IntRect&, int flags = 0); void refreshComboBoxChildren() const; void getComboBoxPadding(RenderStyle*, int& left, int& top, int& right, int& bottom) const; int getComboBoxSeparatorWidth() const; int comboBoxArrowSize(RenderStyle*) const; - GtkThemeParts m_themeParts; GtkWidget* gtkButton() const; GtkWidget* gtkEntry() const; @@ -216,6 +215,7 @@ private: GtkWidget* gtkComboBoxArrow() const; GtkWidget* gtkComboBoxSeparator() const; + GdkColormap* m_colormap; mutable GtkWidget* m_gtkWindow; mutable GtkWidget* m_gtkContainer; mutable GtkWidget* m_gtkButton; @@ -230,6 +230,8 @@ private: mutable GtkWidget* m_gtkComboBoxButton; mutable GtkWidget* m_gtkComboBoxArrow; mutable GtkWidget* m_gtkComboBoxSeparator; + mutable GtkWidget* m_gtkVScrollbar; + mutable GtkWidget* m_gtkHScrollbar; bool m_themePartsHaveRGBAColormap; friend class WidgetRenderingContext; #endif diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp index de4195d..534aa97 100644 --- a/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp +++ b/Source/WebCore/platform/gtk/RenderThemeGtk2.cpp @@ -41,7 +41,6 @@ #include "TextDirection.h" #include "UserAgentStyleSheets.h" #include "WidgetRenderingContext.h" -#include "gtkdrawing.h" #include <gdk/gdk.h> #include <gtk/gtk.h> @@ -50,7 +49,6 @@ namespace WebCore { // This is not a static method, because we want to avoid having GTK+ headers in RenderThemeGtk.h. extern GtkTextDirection gtkTextDirection(TextDirection); -static int mozGtkRefCount = 0; void RenderThemeGtk::platformInit() { m_themePartsHaveRGBAColormap = true; @@ -68,30 +66,18 @@ void RenderThemeGtk::platformInit() m_gtkComboBoxButton = 0; m_gtkComboBoxArrow = 0; m_gtkComboBoxSeparator = 0; + m_gtkVScrollbar = 0; + m_gtkHScrollbar = 0; - memset(&m_themeParts, 0, sizeof(GtkThemeParts)); - GdkColormap* colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default()); - if (!colormap) { + m_colormap = gdk_screen_get_rgba_colormap(gdk_screen_get_default()); + if (!m_colormap) { m_themePartsHaveRGBAColormap = false; - colormap = gdk_screen_get_default_colormap(gdk_screen_get_default()); + m_colormap = gdk_screen_get_default_colormap(gdk_screen_get_default()); } - m_themeParts.colormap = colormap; - - // Initialize the Mozilla theme drawing code. - if (!mozGtkRefCount) { - moz_gtk_init(); - moz_gtk_use_theme_parts(&m_themeParts); - } - ++mozGtkRefCount; } RenderThemeGtk::~RenderThemeGtk() { - --mozGtkRefCount; - - if (!mozGtkRefCount) - moz_gtk_shutdown(); - if (m_gtkWindow) gtk_widget_destroy(m_gtkWindow); } @@ -147,34 +133,6 @@ static GtkStateType getGtkStateType(RenderThemeGtk* theme, RenderObject* object) return GTK_STATE_NORMAL; } -bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags) -{ - // Painting is disabled so just claim to have succeeded - if (context->paintingDisabled()) - return false; - - GtkWidgetState widgetState; - widgetState.active = isPressed(renderObject); - widgetState.focused = isFocused(renderObject); - - // https://bugs.webkit.org/show_bug.cgi?id=18364 - // The Mozilla theme drawing code, only paints a button as pressed when it's pressed - // while hovered. Until we move away from the Mozila code, work-around the issue by - // forcing a pressed button into the hovered state. This ensures that buttons activated - // via the keyboard have the proper rendering. - widgetState.inHover = isHovered(renderObject) || (type == MOZ_GTK_BUTTON && isPressed(renderObject)); - - // FIXME: Disabled does not always give the correct appearance for ReadOnly - widgetState.disabled = !isEnabled(renderObject) || isReadOnlyControl(renderObject); - widgetState.isDefault = false; - widgetState.canDefault = false; - widgetState.depressed = false; - - WidgetRenderingContext widgetContext(context, rect); - return !widgetContext.paintMozillaWidget(type, &widgetState, flags, - gtkTextDirection(renderObject->style()->direction())); -} - static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, GtkWidget* widget) { // The width and height are both specified, so we shouldn't change them. @@ -608,6 +566,15 @@ bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInf } #endif +void RenderThemeGtk::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +bool RenderThemeGtk::paintInnerSpinButton(RenderObject*, const PaintInfo&, const IntRect&) +{ + return true; +} + GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) { ASSERT(widgetType == GTK_TYPE_CONTAINER || widgetType == GTK_TYPE_ENTRY); @@ -708,7 +675,7 @@ GtkWidget* RenderThemeGtk::gtkContainer() const return m_gtkContainer; m_gtkWindow = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_set_colormap(m_gtkWindow, m_themeParts.colormap); + gtk_widget_set_colormap(m_gtkWindow, m_colormap); setupWidget(m_gtkWindow); gtk_widget_set_name(m_gtkWindow, "MozillaGtkWidget"); @@ -882,9 +849,22 @@ GtkWidget* RenderThemeGtk::gtkComboBoxSeparator() const return m_gtkComboBoxSeparator; } -GtkWidget* RenderThemeGtk::gtkScrollbar() +GtkWidget* RenderThemeGtk::gtkHScrollbar() const +{ + if (m_gtkHScrollbar) + return m_gtkHScrollbar; + m_gtkHScrollbar = gtk_hscrollbar_new(0); + setupWidgetAndAddToContainer(m_gtkHScrollbar, gtkContainer()); + return m_gtkHScrollbar; +} + +GtkWidget* RenderThemeGtk::gtkVScrollbar() const { - return moz_gtk_get_scrollbar_widget(); + if (m_gtkVScrollbar) + return m_gtkVScrollbar; + m_gtkVScrollbar = gtk_vscrollbar_new(0); + setupWidgetAndAddToContainer(m_gtkVScrollbar, gtkContainer()); + return m_gtkVScrollbar; } } // namespace WebCore diff --git a/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp b/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp index 1a9f445..7fa0f04 100644 --- a/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp +++ b/Source/WebCore/platform/gtk/RenderThemeGtk3.cpp @@ -45,6 +45,8 @@ namespace WebCore { // This is the default value defined by GTK+, where it was defined as MIN_ARROW_SIZE in gtkarrow.c. static const int minArrowSize = 15; +// This is the default value defined by GTK+, where it was defined as MIN_ARROW_WIDTH in gtkspinbutton.c. +static const int minSpinButtonArrowSize = 6; typedef HashMap<GType, GRefPtr<GtkStyleContext> > StyleContextMap; static StyleContextMap& styleContextMap(); @@ -543,7 +545,7 @@ bool RenderThemeGtk::paintMenuList(RenderObject* renderObject, const PaintInfo& cairo_clip(cairoContext); gtk_render_line(separatorStyleContext, cairoContext, separatorPosition.x(), separatorPosition.y(), - separatorPosition.x(), innerRect.bottom()); + separatorPosition.x(), innerRect.maxY()); cairo_restore(cairoContext); } @@ -720,6 +722,117 @@ bool RenderThemeGtk::paintProgressBar(RenderObject* renderObject, const PaintInf } #endif +static gint spinButtonArrowSize(GtkStyleContext* context) +{ + const PangoFontDescription* fontDescription = gtk_style_context_get_font(context, static_cast<GtkStateFlags>(0)); + gint fontSize = pango_font_description_get_size(fontDescription); + gint arrowSize = max(PANGO_PIXELS(fontSize), minSpinButtonArrowSize); + + return arrowSize - arrowSize % 2; // Force even. +} + +void RenderThemeGtk::adjustInnerSpinButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); + + GtkBorder padding; + gtk_style_context_get_padding(context, static_cast<GtkStateFlags>(0), &padding); + + int width = spinButtonArrowSize(context) + padding.left + padding.right; + style->setWidth(Length(width, Fixed)); + style->setMinWidth(Length(width, Fixed)); +} + +static void paintSpinArrowButton(RenderTheme* theme, GtkStyleContext* context, RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect, GtkArrowType arrowType) +{ + ASSERT(arrowType == GTK_ARROW_UP || arrowType == GTK_ARROW_DOWN); + + gtk_style_context_save(context); + gtk_style_context_add_class(context, GTK_STYLE_CLASS_BUTTON); + + GtkTextDirection direction = gtk_style_context_get_direction(context); + guint state = static_cast<guint>(gtk_style_context_get_state(context)); + if (!(state & GTK_STATE_FLAG_INSENSITIVE)) { + if (theme->isPressed(renderObject)) { + if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartPressed(renderObject)) + || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartPressed(renderObject))) + state |= GTK_STATE_FLAG_ACTIVE; + } else if (theme->isHovered(renderObject)) { + if ((arrowType == GTK_ARROW_UP && theme->isSpinUpButtonPartHovered(renderObject)) + || (arrowType == GTK_ARROW_DOWN && !theme->isSpinUpButtonPartHovered(renderObject))) + state |= GTK_STATE_FLAG_PRELIGHT; + } + } + gtk_style_context_set_state(context, static_cast<GtkStateFlags>(state)); + + // Paint button. + IntRect buttonRect(rect); + guint junction = gtk_style_context_get_junction_sides(context); + if (arrowType == GTK_ARROW_UP) + junction |= GTK_JUNCTION_BOTTOM; + else { + junction |= GTK_JUNCTION_TOP; + buttonRect.move(0, rect.height() / 2); + } + buttonRect.setHeight(rect.height() / 2); + gtk_style_context_set_junction_sides(context, static_cast<GtkJunctionSides>(junction)); + + gtk_render_background(context, paintInfo.context->platformContext(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); + gtk_render_frame(context, paintInfo.context->platformContext(), buttonRect.x(), buttonRect.y(), buttonRect.width(), buttonRect.height()); + + // Paint arrow centered inside button. + // This code is based on gtkspinbutton.c code. + IntRect arrowRect; + gdouble angle; + if (arrowType == GTK_ARROW_UP) { + angle = 0; + arrowRect.setY(rect.y()); + arrowRect.setHeight(rect.height() / 2 - 2); + } else { + angle = G_PI; + arrowRect.setY(rect.y() + buttonRect.y()); + arrowRect.setHeight(rect.height() - arrowRect.y() - 2); + } + arrowRect.setWidth(rect.width() - 3); + if (direction == GTK_TEXT_DIR_LTR) + arrowRect.setX(rect.x() + 1); + else + arrowRect.setX(rect.x() + 2); + + gint width = arrowRect.width() / 2; + width -= width % 2 - 1; // Force odd. + gint height = (width + 1) / 2; + + arrowRect.move((arrowRect.width() - width) / 2, (arrowRect.height() - height) / 2); + gtk_render_arrow(context, paintInfo.context->platformContext(), angle, arrowRect.x(), arrowRect.y(), width); + + gtk_style_context_restore(context); +} + +bool RenderThemeGtk::paintInnerSpinButton(RenderObject* renderObject, const PaintInfo& paintInfo, const IntRect& rect) +{ + GtkStyleContext* context = getStyleContext(GTK_TYPE_SPIN_BUTTON); + gtk_style_context_save(context); + + GtkTextDirection direction = static_cast<GtkTextDirection>(gtkTextDirection(renderObject->style()->direction())); + gtk_style_context_set_direction(context, direction); + + guint flags = 0; + if (!isEnabled(renderObject) || isReadOnlyControl(renderObject)) + flags |= GTK_STATE_FLAG_INSENSITIVE; + else if (isFocused(renderObject)) + flags |= GTK_STATE_FLAG_FOCUSED; + gtk_style_context_set_state(context, static_cast<GtkStateFlags>(flags)); + gtk_style_context_remove_class(context, GTK_STYLE_CLASS_ENTRY); + + paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_UP); + paintSpinArrowButton(this, context, renderObject, paintInfo, rect, GTK_ARROW_DOWN); + + gtk_style_context_restore(context); + + return false; +} + GRefPtr<GdkPixbuf> RenderThemeGtk::getStockIcon(GType widgetType, const char* iconName, gint direction, gint state, gint iconSize) { GtkStyleContext* context = getStyleContext(widgetType); diff --git a/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp b/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp index cb9b0f8..7f7c269 100644 --- a/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp +++ b/Source/WebCore/platform/gtk/ScrollbarThemeGtk.cpp @@ -228,9 +228,10 @@ bool ScrollbarThemeGtk::paint(Scrollbar* scrollbar, GraphicsContext* graphicsCon scrollMask |= ThumbPart; } - paintScrollbarBackground(graphicsContext, scrollbar); - - if (scrollMask & TrackBGPart) + ScrollbarControlPartMask allButtons = BackButtonStartPart | BackButtonEndPart + | ForwardButtonStartPart | ForwardButtonEndPart; + if (scrollMask & TrackBGPart || scrollMask & ThumbPart || scrollMask & allButtons) + paintScrollbarBackground(graphicsContext, scrollbar); paintTrackBackground(graphicsContext, scrollbar, trackPaintRect); // Paint the back and forward buttons. diff --git a/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp b/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp index 79295c1..1ab8850 100644 --- a/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp +++ b/Source/WebCore/platform/gtk/ScrollbarThemeGtk2.cpp @@ -28,12 +28,12 @@ #ifdef GTK_API_VERSION_2 +#include "GtkVersioning.h" #include "PlatformMouseEvent.h" #include "RenderThemeGtk.h" #include "ScrollView.h" #include "Scrollbar.h" #include "WidgetRenderingContext.h" -#include "gtkdrawing.h" #include <gtk/gtk.h> namespace WebCore { @@ -46,107 +46,169 @@ static void gtkStyleSetCallback(GtkWidget* widget, GtkStyle* previous, Scrollbar ScrollbarThemeGtk::ScrollbarThemeGtk() { updateThemeProperties(); - g_signal_connect(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkScrollbar(), + g_signal_connect(static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkHScrollbar(), "style-set", G_CALLBACK(gtkStyleSetCallback), this); } void ScrollbarThemeGtk::updateThemeProperties() { - MozGtkScrollbarMetrics metrics; - moz_gtk_get_scrollbar_metrics(&metrics); - - m_thumbFatness = metrics.slider_width; - m_troughBorderWidth = metrics.trough_border; - m_stepperSize = metrics.stepper_size; - m_stepperSpacing = metrics.stepper_spacing; - m_minThumbLength = metrics.min_slider_size; - m_troughUnderSteppers = metrics.trough_under_steppers; - m_hasForwardButtonStartPart = metrics.has_secondary_forward_stepper; - m_hasBackButtonEndPart = metrics.has_secondary_backward_stepper; - + GtkWidget* scrollbar = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->gtkHScrollbar(); + gtk_widget_style_get(scrollbar, + "slider_width", &m_thumbFatness, + "trough_border", &m_troughBorderWidth, + "stepper-size", &m_stepperSize, + "trough-under-steppers", &m_troughUnderSteppers, + "has-secondary-forward-stepper", &m_hasForwardButtonStartPart, + "has-secondary-backward-stepper", &m_hasBackButtonEndPart, NULL); + m_minThumbLength = gtk_range_get_min_slider_size(GTK_RANGE(scrollbar)); updateScrollbarsFrameThickness(); } -void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +static GtkWidget* getWidgetForScrollbar(Scrollbar* scrollbar) { - GtkWidgetState state; - state.focused = FALSE; - state.isDefault = FALSE; - state.canDefault = FALSE; - state.disabled = FALSE; - state.active = FALSE; - state.inHover = FALSE; + RenderThemeGtk* theme = static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get()); + return scrollbar->orientation() == VerticalScrollbar ? theme->gtkVScrollbar() : theme->gtkHScrollbar(); +} +void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ // Paint the track background. If the trough-under-steppers property is true, this // should be the full size of the scrollbar, but if is false, it should only be the // track rect. - IntRect fullScrollbarRect = rect; + IntRect fullScrollbarRect(rect); if (m_troughUnderSteppers) fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); - GtkThemeWidgetType type = scrollbar->orientation() == VerticalScrollbar ? MOZ_GTK_SCROLLBAR_TRACK_VERTICAL : MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL; WidgetRenderingContext widgetContext(context, fullScrollbarRect); - widgetContext.paintMozillaWidget(type, &state, 0); + IntRect paintRect(IntPoint(), fullScrollbarRect.size()); + widgetContext.gtkPaintBox(paintRect, getWidgetForScrollbar(scrollbar), + GTK_STATE_ACTIVE, GTK_SHADOW_IN, "trough"); } void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) { - // This is unused by the moz_gtk_scrollecd_window_paint. - GtkWidgetState state; IntRect fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); + WidgetRenderingContext widgetContext(context, fullScrollbarRect); - widgetContext.paintMozillaWidget(MOZ_GTK_SCROLLED_WINDOW, &state, 0); + widgetContext.gtkPaintBox(fullScrollbarRect, getWidgetForScrollbar(scrollbar), + GTK_STATE_NORMAL, GTK_SHADOW_IN, "scrolled_window"); } void ScrollbarThemeGtk::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) { - GtkWidgetState state; - state.focused = FALSE; - state.isDefault = FALSE; - state.canDefault = FALSE; - state.disabled = FALSE; - state.active = scrollbar->pressedPart() == ThumbPart; - state.inHover = scrollbar->hoveredPart() == ThumbPart; - state.maxpos = scrollbar->maximum(); - state.curpos = scrollbar->currentPos(); - - GtkThemeWidgetType type = scrollbar->orientation() == VerticalScrollbar ? MOZ_GTK_SCROLLBAR_THUMB_VERTICAL : MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL; + GtkWidget* widget = getWidgetForScrollbar(scrollbar); + gboolean activateSlider; + gtk_widget_style_get(widget, "activate-slider", &activateSlider, NULL); + + GtkStateType stateType = GTK_STATE_NORMAL; + GtkShadowType shadowType = GTK_SHADOW_OUT; + if (activateSlider && scrollbar->pressedPart() == ThumbPart) { + stateType = GTK_STATE_ACTIVE; + shadowType = GTK_SHADOW_IN; + } else if (scrollbar->pressedPart() == ThumbPart || scrollbar->hoveredPart() == ThumbPart) + stateType = GTK_STATE_PRELIGHT; + + // The adjustment controls the rendering of the scrollbar thumb. If it's not set + // properly the theme may not draw the thumb borders properly. + GtkAdjustment* adjustment = gtk_range_get_adjustment(GTK_RANGE(widget)); + gtk_adjustment_set_value(adjustment, scrollbar->currentPos()); + gtk_adjustment_set_lower(adjustment, 0); + gtk_adjustment_set_upper(adjustment, scrollbar->maximum()); + + GtkOrientation orientation = GTK_ORIENTATION_HORIZONTAL; + if (scrollbar->orientation() == VerticalScrollbar) { + gtk_adjustment_set_page_size(adjustment, rect.height()); + orientation = GTK_ORIENTATION_VERTICAL; + } else + gtk_adjustment_set_page_size(adjustment, rect.width()); + WidgetRenderingContext widgetContext(context, rect); - widgetContext.paintMozillaWidget(type, &state, 0); + IntRect sliderRect(IntPoint(), rect.size()); + widgetContext.gtkPaintSlider(sliderRect, widget, stateType, shadowType, "slider", orientation); } void ScrollbarThemeGtk::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) { - int flags = 0; - if (scrollbar->orientation() == VerticalScrollbar) - flags |= MOZ_GTK_STEPPER_VERTICAL; - - if (part == ForwardButtonEndPart) - flags |= (MOZ_GTK_STEPPER_DOWN | MOZ_GTK_STEPPER_BOTTOM); - if (part == ForwardButtonStartPart) - flags |= MOZ_GTK_STEPPER_DOWN; - - GtkWidgetState state; - state.focused = TRUE; - state.isDefault = TRUE; - state.canDefault = TRUE; - state.depressed = FALSE; + // The buttons will be disabled if the thumb is as the appropriate extreme. + GtkShadowType shadowType = GTK_SHADOW_OUT; + GtkStateType stateType = GTK_STATE_INSENSITIVE; + bool pressed = (part == scrollbar->pressedPart()); if ((BackButtonStartPart == part && scrollbar->currentPos()) || (BackButtonEndPart == part && scrollbar->currentPos()) || (ForwardButtonEndPart == part && scrollbar->currentPos() != scrollbar->maximum()) || (ForwardButtonStartPart == part && scrollbar->currentPos() != scrollbar->maximum())) { - state.disabled = FALSE; - state.active = part == scrollbar->pressedPart(); - state.inHover = part == scrollbar->hoveredPart(); + stateType = GTK_STATE_NORMAL; + if (pressed) { + stateType = GTK_STATE_ACTIVE; + shadowType = GTK_SHADOW_IN; + } else if (part == scrollbar->hoveredPart()) + stateType = GTK_STATE_PRELIGHT; + } + + // Themes determine how to draw the button (which button to draw) based on the allocation + // of the widget. Where the target rect is in relation to the total widget allocation + // determines the button. + ScrollbarOrientation orientation = scrollbar->orientation(); + int buttonSize = (orientation == VerticalScrollbar) ? rect.height() : rect.width(); + int totalAllocation = buttonSize * 5; // One space for each button and one extra. + int buttonOffset = 0; + if (ForwardButtonStartPart == part) + buttonOffset = buttonSize; + else if (BackButtonEndPart == part) + buttonOffset = 3 * buttonSize; + else if (ForwardButtonEndPart == part) + buttonOffset = 4 * buttonSize; + + // Now we want the allocation to be relative to the origin of the painted rect. + GtkWidget* widget = getWidgetForScrollbar(scrollbar); + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + allocation.x = allocation.y = 0; + allocation.width = rect.width(); + allocation.height = rect.height(); + + if (orientation == VerticalScrollbar) { + allocation.height = totalAllocation; + allocation.y -= buttonOffset; } else { - state.disabled = TRUE; - state.active = FALSE; - state.inHover = FALSE; + allocation.width = totalAllocation; + allocation.x -= buttonOffset; } + gtk_widget_set_allocation(widget, &allocation); + const char* detail = orientation == VerticalScrollbar ? "vscrollbar" : "hscrollbar"; WidgetRenderingContext widgetContext(context, rect); - widgetContext.paintMozillaWidget(MOZ_GTK_SCROLLBAR_BUTTON, &state, flags); + + IntRect buttonRect(IntPoint(), rect.size()); + widgetContext.gtkPaintBox(buttonRect, widget, stateType, shadowType, detail); + + float arrowScaling; + gtk_widget_style_get(widget, "arrow-scaling", &arrowScaling, NULL); + IntSize arrowSize = rect.size(); + arrowSize.scale(arrowScaling); + IntRect arrowRect(IntPoint(buttonRect.x() + (buttonRect.width() - arrowSize.width()) / 2, + buttonRect.y() + (buttonRect.height() - arrowSize.height()) / 2), + arrowSize); + if (pressed) { + int arrowDisplacementX, arrowDisplacementY; + gtk_widget_style_get(widget, + "arrow-displacement-x", &arrowDisplacementX, + "arrow-displacement-y", &arrowDisplacementY, + NULL); + arrowRect.move(arrowDisplacementX, arrowDisplacementY); + } + + GtkArrowType arrowType = GTK_ARROW_DOWN; + if (orientation == VerticalScrollbar) { + if (part == BackButtonEndPart || part == BackButtonStartPart) + arrowType = GTK_ARROW_UP; + } else if (orientation == HorizontalScrollbar) { + arrowType = GTK_ARROW_RIGHT; + if (part == BackButtonEndPart || part == BackButtonStartPart) + arrowType = GTK_ARROW_LEFT; + } + widgetContext.gtkPaintArrow(arrowRect, widget, stateType, shadowType, arrowType, detail); } } // namespace WebCore diff --git a/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp b/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp index 51b32ea..9e640f6 100644 --- a/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp +++ b/Source/WebCore/platform/gtk/WidgetRenderingContext.cpp @@ -103,8 +103,8 @@ WidgetRenderingContext::WidgetRenderingContext(GraphicsContext* graphicsContext, width = (1 + (width >> 5)) << 5; height = (1 + (height >> 5)) << 5; - gScratchBuffer = gdk_pixmap_new(0, width, height, gdk_colormap_get_visual(theme->m_themeParts.colormap)->depth); - gdk_drawable_set_colormap(gScratchBuffer, theme->m_themeParts.colormap); + gScratchBuffer = gdk_pixmap_new(0, width, height, gdk_colormap_get_visual(theme->m_colormap)->depth); + gdk_drawable_set_colormap(gScratchBuffer, theme->m_colormap); } m_target = gScratchBuffer; @@ -143,19 +143,19 @@ WidgetRenderingContext::~WidgetRenderingContext() scheduleScratchBufferPurge(); } -bool WidgetRenderingContext::paintMozillaWidget(GtkThemeWidgetType type, GtkWidgetState* state, int flags, GtkTextDirection textDirection) -{ - // Sometimes moz_gtk_widget_paint modifies the clipping rectangle, so we must use a copy. - GdkRectangle clipRect = m_paintRect; - m_hadError = moz_gtk_widget_paint(type, m_target, &clipRect, &m_paintRect, - state, flags, textDirection) != MOZ_GTK_SUCCESS; - return !m_hadError; -} - void WidgetRenderingContext::gtkPaintBox(const IntRect& rect, GtkWidget* widget, GtkStateType stateType, GtkShadowType shadowType, const gchar* detail) { GdkRectangle paintRect = { m_paintRect.x + rect.x(), m_paintRect.y + rect.y(), rect.width(), rect.height() }; - gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &m_paintRect, + + // Some widgets also need their allocation adjusted to account for extra space. + // Right now only scrollbar buttons have significant allocations. + GtkAllocation allocation; + gtk_widget_get_allocation(widget, &allocation); + allocation.x += m_paintRect.x; + allocation.y += m_paintRect.y; + gtk_widget_set_allocation(widget, &allocation); + + gtk_paint_box(gtk_widget_get_style(widget), m_target, stateType, shadowType, &paintRect, widget, detail, paintRect.x, paintRect.y, paintRect.width, paintRect.height); } diff --git a/Source/WebCore/platform/gtk/WidgetRenderingContext.h b/Source/WebCore/platform/gtk/WidgetRenderingContext.h index e248f04..8639a98 100644 --- a/Source/WebCore/platform/gtk/WidgetRenderingContext.h +++ b/Source/WebCore/platform/gtk/WidgetRenderingContext.h @@ -25,7 +25,11 @@ #ifdef GTK_API_VERSION_2 #include "IntRect.h" -#include "gtkdrawing.h" + +// Usually this is too expensive to have in headers, but GtkStateType GtkShadowType are +// enums and cannot be forward declared. WidgetRenderingContext.h is currently only +// included in RenderThemeGtk2.cpp and ScrollbarThemeGtk2.cpp. +#include <gtk/gtk.h> namespace WebCore { @@ -37,7 +41,6 @@ public: WidgetRenderingContext(GraphicsContext*, const IntRect&); ~WidgetRenderingContext(); - bool paintMozillaWidget(GtkThemeWidgetType, GtkWidgetState*, int flags, GtkTextDirection = GTK_TEXT_DIR_NONE); void gtkPaintBox(const IntRect&, GtkWidget*, GtkStateType, GtkShadowType, const gchar*); void gtkPaintFlatBox(const IntRect&, GtkWidget*, GtkStateType, GtkShadowType, const gchar*); void gtkPaintFocus(const IntRect&, GtkWidget*, GtkStateType, const gchar*); diff --git a/Source/WebCore/platform/gtk/gtk2drawing.c b/Source/WebCore/platform/gtk/gtk2drawing.c deleted file mode 100644 index 3979b7f..0000000 --- a/Source/WebCore/platform/gtk/gtk2drawing.c +++ /dev/null @@ -1,552 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 2002 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brian Ryner <bryner@brianryner.com> (Original Author) - * Pierre Chanial <p_ch@verizon.net> - * Michael Ventnor <m.ventnor@gmail.com> - * Alp Toker <alp@nuanti.com> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/* - * This file contains painting functions for each of the gtk2 widgets. - * Adapted from the gtkdrawing.c, and gtk+2.0 source. - */ - -#ifdef GTK_API_VERSION_2 - -#undef GTK_DISABLE_DEPRECATED -#undef GDK_DISABLE_DEPRECATED - -#include <gdk/gdkprivate.h> -#include "gtkdrawing.h" -#include "GtkVersioning.h" -#include <math.h> -#include <string.h> - -#define XTHICKNESS(style) (style->xthickness) -#define YTHICKNESS(style) (style->ythickness) - -static GtkThemeParts *gParts = NULL; -static style_prop_t style_prop_func; -static gboolean is_initialized; - -void -moz_gtk_use_theme_parts(GtkThemeParts* parts) -{ - gParts = parts; -} - -/* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine - that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific - things they may want to do. */ -static void -moz_gtk_set_widget_name(GtkWidget* widget) -{ - gtk_widget_set_name(widget, "MozillaGtkWidget"); -} - -gint -moz_gtk_enable_style_props(style_prop_t styleGetProp) -{ - style_prop_func = styleGetProp; - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_window_widget() -{ - if (!gParts->protoWindow) { - gParts->protoWindow = gtk_window_new(GTK_WINDOW_POPUP); - - if (gParts->colormap) - gtk_widget_set_colormap(gParts->protoWindow, gParts->colormap); - - gtk_widget_realize(gParts->protoWindow); - moz_gtk_set_widget_name(gParts->protoWindow); - } - return MOZ_GTK_SUCCESS; -} - -static gint -setup_widget_prototype(GtkWidget* widget) -{ - ensure_window_widget(); - if (!gParts->protoLayout) { - gParts->protoLayout = gtk_fixed_new(); - gtk_container_add(GTK_CONTAINER(gParts->protoWindow), gParts->protoLayout); - } - - gtk_container_add(GTK_CONTAINER(gParts->protoLayout), widget); - gtk_widget_realize(widget); - g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_scrollbar_widget() -{ - if (!gParts->vertScrollbarWidget) { - gParts->vertScrollbarWidget = gtk_vscrollbar_new(NULL); - setup_widget_prototype(gParts->vertScrollbarWidget); - } - if (!gParts->horizScrollbarWidget) { - gParts->horizScrollbarWidget = gtk_hscrollbar_new(NULL); - setup_widget_prototype(gParts->horizScrollbarWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_scrolled_window_widget() -{ - if (!gParts->scrolledWindowWidget) { - gParts->scrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); - setup_widget_prototype(gParts->scrolledWindowWidget); - } - return MOZ_GTK_SUCCESS; -} - -static GtkStateType -ConvertGtkState(GtkWidgetState* state) -{ - if (state->disabled) - return GTK_STATE_INSENSITIVE; - else if (state->depressed) - return (state->inHover ? GTK_STATE_PRELIGHT : GTK_STATE_ACTIVE); - else if (state->inHover) - return (state->active ? GTK_STATE_ACTIVE : GTK_STATE_PRELIGHT); - else - return GTK_STATE_NORMAL; -} - -static gint -TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin) -{ - int i; - /* there are 5 gc's in each array, for each of the widget states */ - for (i = 0; i < 5; ++i) - gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin); - return MOZ_GTK_SUCCESS; -} - -static gint -TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin) -{ - TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin); - TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin); - TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin); - TSOffsetStyleGCArray(style->dark_gc, xorigin, yorigin); - TSOffsetStyleGCArray(style->mid_gc, xorigin, yorigin); - TSOffsetStyleGCArray(style->text_gc, xorigin, yorigin); - TSOffsetStyleGCArray(style->base_gc, xorigin, yorigin); - gdk_gc_set_ts_origin(style->black_gc, xorigin, yorigin); - gdk_gc_set_ts_origin(style->white_gc, xorigin, yorigin); - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_init() -{ - GtkWidgetClass *entry_class; - - is_initialized = TRUE; - - /* Add style property to GtkEntry. - * Adding the style property to the normal GtkEntry class means that it - * will work without issues inside GtkComboBox and for Spinbuttons. */ - entry_class = g_type_class_ref(GTK_TYPE_ENTRY); - gtk_widget_class_install_style_property(entry_class, - g_param_spec_boolean("honors-transparent-bg-hint", - "Transparent BG enabling flag", - "If TRUE, the theme is able to draw the GtkEntry on non-prefilled background.", - FALSE, - G_PARAM_READWRITE)); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_scrolled_window_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state) -{ - GtkStyle* style; - GtkAllocation allocation; - GtkWidget* widget; - - ensure_scrolled_window_widget(); - widget = gParts->scrolledWindowWidget; - - gtk_widget_get_allocation(widget, &allocation); - allocation.x = rect->x; - allocation.y = rect->y; - allocation.width = rect->width; - allocation.height = rect->height; - gtk_widget_set_allocation(widget, &allocation); - - style = gtk_widget_get_style(widget); - TSOffsetStyleGCs(style, rect->x - 1, rect->y - 1); - gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, - cliprect, gParts->scrolledWindowWidget, "scrolled_window", - rect->x, rect->y, rect->width, rect->height); - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_scrollbar_button_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkScrollbarButtonFlags flags, - GtkTextDirection direction) -{ - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = (state->active) ? - GTK_SHADOW_IN : GTK_SHADOW_OUT; - GdkRectangle arrow_rect; - GtkStyle* style; - GtkWidget *scrollbar; - GtkAllocation allocation; - GtkArrowType arrow_type; - gint arrow_displacement_x, arrow_displacement_y; - const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? - "vscrollbar" : "hscrollbar"; - - ensure_scrollbar_widget(); - - if (flags & MOZ_GTK_STEPPER_VERTICAL) - scrollbar = gParts->vertScrollbarWidget; - else - scrollbar = gParts->horizScrollbarWidget; - - gtk_widget_set_direction(scrollbar, direction); - - /* Some theme engines (i.e., ClearLooks) check the scrollbar's allocation - to determine where it should paint rounded corners on the buttons. - We need to trick them into drawing the buttons the way we want them. */ - - gtk_widget_get_allocation(scrollbar, &allocation); - allocation.x = rect->x; - allocation.y = rect->y; - allocation.width = rect->width; - allocation.height = rect->height; - - if (flags & MOZ_GTK_STEPPER_VERTICAL) { - allocation.height *= 5; - if (flags & MOZ_GTK_STEPPER_DOWN) { - arrow_type = GTK_ARROW_DOWN; - if (flags & MOZ_GTK_STEPPER_BOTTOM) - allocation.y -= 4 * rect->height; - else - allocation.y -= rect->height; - - } else { - arrow_type = GTK_ARROW_UP; - if (flags & MOZ_GTK_STEPPER_BOTTOM) - allocation.y -= 3 * rect->height; - } - } else { - allocation.width *= 5; - if (flags & MOZ_GTK_STEPPER_DOWN) { - arrow_type = GTK_ARROW_RIGHT; - if (flags & MOZ_GTK_STEPPER_BOTTOM) - allocation.x -= 4 * rect->width; - else - allocation.x -= rect->width; - } else { - arrow_type = GTK_ARROW_LEFT; - if (flags & MOZ_GTK_STEPPER_BOTTOM) - allocation.x -= 3 * rect->width; - } - } - - gtk_widget_set_allocation(scrollbar, &allocation); - style = gtk_widget_get_style(scrollbar); - - TSOffsetStyleGCs(style, rect->x, rect->y); - - gtk_paint_box(style, drawable, state_type, shadow_type, cliprect, - scrollbar, detail, rect->x, rect->y, - rect->width, rect->height); - - arrow_rect.width = rect->width / 2; - arrow_rect.height = rect->height / 2; - arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; - arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; - - if (state_type == GTK_STATE_ACTIVE) { - gtk_widget_style_get(scrollbar, - "arrow-displacement-x", &arrow_displacement_x, - "arrow-displacement-y", &arrow_displacement_y, - NULL); - - arrow_rect.x += arrow_displacement_x; - arrow_rect.y += arrow_displacement_y; - } - - gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, - scrollbar, detail, arrow_type, TRUE, arrow_rect.x, - arrow_rect.y, arrow_rect.width, arrow_rect.height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget, - GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) -{ - GtkStyle* style; - GtkScrollbar *scrollbar; - - ensure_scrollbar_widget(); - - if (widget == MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL) - scrollbar = GTK_SCROLLBAR(gParts->horizScrollbarWidget); - else - scrollbar = GTK_SCROLLBAR(gParts->vertScrollbarWidget); - - gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); - - style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_box(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_IN, cliprect, - GTK_WIDGET(scrollbar), "trough", rect->x, rect->y, - rect->width, rect->height); - - if (state->focused) { - gtk_paint_focus(style, drawable, GTK_STATE_ACTIVE, cliprect, - GTK_WIDGET(scrollbar), "trough", - rect->x, rect->y, rect->width, rect->height); - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, - GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) -{ - GtkStateType state_type = (state->inHover || state->active) ? - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; - GtkShadowType shadow_type = GTK_SHADOW_OUT; - GtkStyle* style; - GtkScrollbar *scrollbar; - GtkAdjustment *adj; - gboolean activate_slider; - - ensure_scrollbar_widget(); - - if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) - scrollbar = GTK_SCROLLBAR(gParts->horizScrollbarWidget); - else - scrollbar = GTK_SCROLLBAR(gParts->vertScrollbarWidget); - - gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); - - /* Make sure to set the scrollbar range before painting so that - everything is drawn properly. At least the bluecurve (and - maybe other) themes don't draw the top or bottom black line - surrounding the scrollbar if the theme thinks that it's butted - up against the scrollbar arrows. Note the increases of the - clip rect below. */ - /* Changing the cliprect is pretty bogus. This lets themes draw - outside the frame, which means we don't invalidate them - correctly. See bug 297508. But some themes do seem to need - it. So we modify the frame's overflow area to account for what - we're doing here; see nsNativeThemeGTK::GetWidgetOverflow. */ - adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar)); - - if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) { - cliprect->x -= 1; - cliprect->width += 2; - gtk_adjustment_set_page_size(adj, rect->width); - } - else { - cliprect->y -= 1; - cliprect->height += 2; - gtk_adjustment_set_page_size(adj, rect->height); - } - -#if GTK_CHECK_VERSION(2, 14, 0) - gtk_adjustment_configure(adj, - state->curpos, - 0, - state->maxpos, - gtk_adjustment_get_step_increment(adj), - gtk_adjustment_get_page_increment(adj), - gtk_adjustment_get_page_size(adj)); -#else - adj->lower = 0; - adj->value = state->curpos; - adj->upper = state->maxpos; - gtk_adjustment_changed(adj); -#endif - - style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); - - gtk_widget_style_get(GTK_WIDGET(scrollbar), "activate-slider", - &activate_slider, NULL); - - if (activate_slider && state->active) { - shadow_type = GTK_SHADOW_IN; - state_type = GTK_STATE_ACTIVE; - } - - TSOffsetStyleGCs(style, rect->x, rect->y); - - gtk_paint_slider(style, drawable, state_type, shadow_type, cliprect, - GTK_WIDGET(scrollbar), "slider", rect->x, rect->y, - rect->width, rect->height, - (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? - GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, - gint* right, gint* bottom, GtkTextDirection direction, - gboolean inhtml) -{ - GtkWidget* w; - GtkStyle *style; - - switch (widget) { - /* These widgets have no borders, since they are not containers. */ - case MOZ_GTK_SCROLLBAR_BUTTON: - case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: - case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: - case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: - case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: - *left = *top = *right = *bottom = 0; - return MOZ_GTK_SUCCESS; - default: - g_warning("Unsupported widget type: %d", widget); - return MOZ_GTK_UNKNOWN_WIDGET; - } - - style = gtk_widget_get_style(w); - *right = *left = XTHICKNESS(style); - *bottom = *top = YTHICKNESS(style); - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) -{ - ensure_scrollbar_widget(); - - gtk_widget_style_get (gParts->horizScrollbarWidget, - "slider_width", &metrics->slider_width, - "trough_border", &metrics->trough_border, - "stepper_size", &metrics->stepper_size, - "stepper_spacing", &metrics->stepper_spacing, - "trough_under_steppers", &metrics->trough_under_steppers, - "has_secondary_forward_stepper", &metrics->has_secondary_forward_stepper, - "has_secondary_backward_stepper", &metrics->has_secondary_backward_stepper, - NULL); - - metrics->min_slider_size = gtk_range_get_min_slider_size(GTK_RANGE(gParts->horizScrollbarWidget)); - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, - GdkRectangle* rect, GdkRectangle* cliprect, - GtkWidgetState* state, gint flags, - GtkTextDirection direction) -{ - switch (widget) { - case MOZ_GTK_SCROLLBAR_BUTTON: - return moz_gtk_scrollbar_button_paint(drawable, rect, cliprect, state, - (GtkScrollbarButtonFlags) flags, - direction); - break; - case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: - case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: - return moz_gtk_scrollbar_trough_paint(widget, drawable, rect, - cliprect, state, direction); - break; - case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: - case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: - return moz_gtk_scrollbar_thumb_paint(widget, drawable, rect, - cliprect, state, direction); - break; - case MOZ_GTK_SCROLLED_WINDOW: - return moz_gtk_scrolled_window_paint(drawable, rect, cliprect, state); - break; - default: - g_warning("Unknown widget type: %d", widget); - } - - return MOZ_GTK_UNKNOWN_WIDGET; -} - -GtkWidget* moz_gtk_get_scrollbar_widget(void) -{ - if (!is_initialized) - return NULL; - ensure_scrollbar_widget(); - return gParts->horizScrollbarWidget; -} - -gint -moz_gtk_shutdown() -{ - GtkWidgetClass *entry_class; - entry_class = g_type_class_peek(GTK_TYPE_ENTRY); - g_type_class_unref(entry_class); - - is_initialized = FALSE; - - return MOZ_GTK_SUCCESS; -} - -void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts) -{ - if (!parts) - return; - - if (parts->protoWindow) { - gtk_widget_destroy(parts->protoWindow); - parts->protoWindow = NULL; - } -} - -#endif // GTK_API_VERSION_2 diff --git a/Source/WebCore/platform/gtk/gtkdrawing.h b/Source/WebCore/platform/gtk/gtkdrawing.h deleted file mode 100644 index cdb343c..0000000 --- a/Source/WebCore/platform/gtk/gtkdrawing.h +++ /dev/null @@ -1,217 +0,0 @@ -/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is mozilla.org code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 2002 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Brian Ryner <bryner@brianryner.com> (Original Author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -/** - * gtkdrawing.h: GTK widget rendering utilities - * - * gtkdrawing provides an API for rendering GTK widgets in the - * current theme to a pixmap or window, without requiring an actual - * widget instantiation, similar to the Macintosh Appearance Manager - * or Windows XP's DrawThemeBackground() API. - */ - -#ifndef _GTK_DRAWING_H_ -#define _GTK_DRAWING_H_ - -#undef GTK_DISABLE_DEPRECATED - -#include <gtk/gtk.h> - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -/*** type definitions ***/ -typedef struct { - guint8 active; - guint8 focused; - guint8 inHover; - guint8 disabled; - guint8 isDefault; - guint8 canDefault; - /* The depressed state is for buttons which remain active for a longer period: - * activated toggle buttons or buttons showing a popup menu. */ - guint8 depressed; - gint32 curpos; /* curpos and maxpos are used for scrollbars */ - gint32 maxpos; -} GtkWidgetState; - -typedef struct { - gint slider_width; - gint trough_border; - gint stepper_size; - gint stepper_spacing; - gint min_slider_size; - gboolean trough_under_steppers; - gboolean has_secondary_forward_stepper; - gboolean has_secondary_backward_stepper; -} MozGtkScrollbarMetrics; - -typedef struct _GtkThemeParts { - GdkColormap* colormap; - GtkWidget* protoWindow; - GtkWidget* protoLayout; - GtkWidget* horizScrollbarWidget; - GtkWidget* vertScrollbarWidget; - GtkWidget* scrolledWindowWidget; -} GtkThemeParts; - -typedef enum { - MOZ_GTK_STEPPER_DOWN = 1 << 0, - MOZ_GTK_STEPPER_BOTTOM = 1 << 1, - MOZ_GTK_STEPPER_VERTICAL = 1 << 2 -} GtkScrollbarButtonFlags; - -/* function type for moz_gtk_enable_style_props */ -typedef gint (*style_prop_t)(GtkStyle*, const gchar*, gint); - -/*** result/error codes ***/ -#define MOZ_GTK_SUCCESS 0 -#define MOZ_GTK_UNKNOWN_WIDGET -1 -#define MOZ_GTK_UNSAFE_THEME -2 - -/*** widget type constants ***/ -typedef enum { - /* Paints a GtkButton. flags is a GtkReliefStyle. */ - MOZ_GTK_BUTTON, - /** - * Paints the button of a GtkScrollbar. flags is a GtkArrowType giving - * the arrow direction. - */ - MOZ_GTK_SCROLLBAR_BUTTON, - /* Paints the trough (track) of a GtkScrollbar. */ - MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL, - MOZ_GTK_SCROLLBAR_TRACK_VERTICAL, - /* Paints the slider (thumb) of a GtkScrollbar. */ - MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL, - MOZ_GTK_SCROLLBAR_THUMB_VERTICAL, - /* Paints the background of a scrolled window */ - MOZ_GTK_SCROLLED_WINDOW, -} GtkThemeWidgetType; - -/*** General library functions ***/ -/** - * Initializes the drawing library. You must call this function - * prior to using any other functionality. - * returns: MOZ_GTK_SUCCESS if there were no errors - * MOZ_GTK_UNSAFE_THEME if the current theme engine is known - * to crash with gtkdrawing. - */ -gint moz_gtk_init(); - -/** - * Instruct the drawing library to do all rendering based on - * the given collection of theme parts. If any members of the - * GtkThemeParts struct are NULL, they will be created lazily. - */ -void -moz_gtk_use_theme_parts(GtkThemeParts* parts); - -/** - * Enable GTK+ 1.2.9+ theme enhancements. You must provide a pointer - * to the GTK+ 1.2.9+ function "gtk_style_get_prop_experimental". - * styleGetProp: pointer to gtk_style_get_prop_experimental - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_enable_style_props(style_prop_t styleGetProp); - -/** - * Perform cleanup of the drawing library. You should call this function - * when your program exits, or you no longer need the library. - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_shutdown(); - -/** - * Destroy the widgets in the given GtkThemeParts, which should - * be destroyed before the GtkThemeParts can be freed. - */ -void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts); - -/*** Widget drawing ***/ -/** - * Paint a widget in the current theme. - * widget: a constant giving the widget to paint - * rect: the bounding rectangle for the widget - * cliprect: a clipprect rectangle for this painting operation - * state: the state of the widget. ignored for some widgets. - * flags: widget-dependant flags; see the GtkThemeWidgetType definition. - * direction: the text direction, to draw the widget correctly LTR and RTL. - */ -gint -moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, - GdkRectangle* rect, GdkRectangle* cliprect, - GtkWidgetState* state, gint flags, - GtkTextDirection direction); - -/*** Widget metrics ***/ -/** - * Get the border size of a widget - * left/right: [OUT] the widget's left/right border - * top/bottom: [OUT] the widget's top/bottom border - * direction: the text direction for the widget - * inhtml: boolean indicating whether this widget will be drawn as a HTML form control, - * in order to workaround a size issue (MOZ_GTK_BUTTON only, ignored otherwise) - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, - gint* right, gint* bottom, GtkTextDirection direction, - gboolean inhtml); -/** - * Get the desired metrics for a GtkScrollbar - * metrics: [IN] struct which will contain the metrics - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint -moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics* metrics); - -/** - * Retrieve an actual GTK scrollbar widget for style analysis. It will not - * be modified. - */ -GtkWidget* moz_gtk_get_scrollbar_widget(void); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif diff --git a/Source/WebCore/platform/haiku/MIMETypeRegistryHaiku.cpp b/Source/WebCore/platform/haiku/MIMETypeRegistryHaiku.cpp index 685827e..3cfddf9 100644 --- a/Source/WebCore/platform/haiku/MIMETypeRegistryHaiku.cpp +++ b/Source/WebCore/platform/haiku/MIMETypeRegistryHaiku.cpp @@ -31,6 +31,8 @@ #include "PlatformString.h" #include <MimeType.h> +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> #include <wtf/text/CString.h> namespace WebCore { @@ -62,6 +64,8 @@ static const ExtensionMap extensionMap[] = { String MIMETypeRegistry::getMIMETypeForExtension(const String& ext) { + ASSERT(isMainThread()); + String str = ext.lower(); // Try WebCore built-in types. diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.cpp b/Source/WebCore/platform/image-decoders/ImageDecoder.cpp index 17208b5..2aae205 100644 --- a/Source/WebCore/platform/image-decoders/ImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/ImageDecoder.cpp @@ -38,7 +38,9 @@ using namespace std; namespace WebCore { -static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset) +namespace { + +unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const SharedBuffer& sharedBuffer, unsigned offset) { unsigned bytesExtracted = 0; const char* moreData; @@ -53,55 +55,80 @@ static unsigned copyFromSharedBuffer(char* buffer, unsigned bufferLength, const return bytesExtracted; } +<<<<<<< HEAD #if !OS(ANDROID) // This method requires BMPImageDecoder, PNGImageDecoder, ICOImageDecoder and // JPEGDecoder, which aren't used on Android, and which don't all compile. // TODO: Find a better fix. +======= +bool matchesGIFSignature(char* contents) +{ + return !memcmp(contents, "GIF8", 4); +} + +bool matchesPNGSignature(char* contents) +{ + return !memcmp(contents, "\x89\x50\x4E\x47", 4); +} + +bool matchesJPEGSignature(char* contents) +{ + return !memcmp(contents, "\xFF\xD8\xFF", 3); +} + +#if USE(WEBP) +bool matchesWebPSignature(char* contents) +{ + return !memcmp(contents, "RIFF", 4) && !memcmp(contents + 8, "WEBPVP", 6); +} +#endif + +bool matchesBMPSignature(char* contents) +{ + return !memcmp(contents, "BM", 2); +} + +bool matchesICOSignature(char* contents) +{ + return !memcmp(contents, "\x00\x00\x01\x00", 4); +} + +bool matchesCURSignature(char* contents) +{ + return !memcmp(contents, "\x00\x00\x02\x00", 4); +} + +} + +>>>>>>> webkit.org at r78450 ImageDecoder* ImageDecoder::create(const SharedBuffer& data, ImageSource::AlphaOption alphaOption, ImageSource::GammaAndColorProfileOption gammaAndColorProfileOption) { - // We need at least 4 bytes to figure out what kind of image we're dealing - // with. - static const unsigned maxMarkerLength = 4; - char contents[maxMarkerLength]; - unsigned length = copyFromSharedBuffer(contents, maxMarkerLength, data, 0); - if (length < maxMarkerLength) + static const unsigned lengthOfLongestSignature = 14; // To wit: "RIFF????WEBPVP" + char contents[lengthOfLongestSignature]; + unsigned length = copyFromSharedBuffer(contents, lengthOfLongestSignature, data, 0); + if (length < lengthOfLongestSignature) return 0; - // GIFs begin with GIF8(7 or 9). - if (strncmp(contents, "GIF8", 4) == 0) + if (matchesGIFSignature(contents)) return new GIFImageDecoder(alphaOption, gammaAndColorProfileOption); - // Test for PNG. - if (!memcmp(contents, "\x89\x50\x4E\x47", 4)) + if (matchesPNGSignature(contents)) return new PNGImageDecoder(alphaOption, gammaAndColorProfileOption); - // JPEG - if (!memcmp(contents, "\xFF\xD8\xFF", 3)) + if (matchesJPEGSignature(contents)) return new JPEGImageDecoder(alphaOption, gammaAndColorProfileOption); #if USE(WEBP) - if (!memcmp(contents, "RIFF", 4)) { - static const unsigned webpExtraMarker = 6; - static const unsigned webpExtraMarkeroffset = 8; - char header[webpExtraMarker]; - unsigned length = copyFromSharedBuffer(header, webpExtraMarker, data, webpExtraMarkeroffset); - if (length >= webpExtraMarker) { - if (!memcmp(header, "WEBPVP", webpExtraMarker)) - return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption); - } - } + if (matchesWebPSignature(contents)) + return new WEBPImageDecoder(alphaOption, gammaAndColorProfileOption); #endif - // BMP - if (strncmp(contents, "BM", 2) == 0) + if (matchesBMPSignature(contents)) return new BMPImageDecoder(alphaOption, gammaAndColorProfileOption); - // ICOs always begin with a 2-byte 0 followed by a 2-byte 1. - // CURs begin with 2-byte 0 followed by 2-byte 2. - if (!memcmp(contents, "\x00\x00\x01\x00", 4) || !memcmp(contents, "\x00\x00\x02\x00", 4)) + if (matchesICOSignature(contents) || matchesCURSignature(contents)) return new ICOImageDecoder(alphaOption, gammaAndColorProfileOption); - // Give up. We don't know what the heck this is. return 0; } #endif // !OS(ANDROID) @@ -123,7 +150,7 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other) return *this; copyReferenceToBitmapData(other); - setRect(other.rect()); + setOriginalFrameRect(other.originalFrameRect()); setStatus(other.status()); setDuration(other.duration()); setDisposalMethod(other.disposalMethod()); @@ -131,7 +158,7 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other) return *this; } -void ImageFrame::clear() +void ImageFrame::clearPixelData() { m_backingStore.clear(); m_bytes = 0; @@ -142,7 +169,7 @@ void ImageFrame::clear() // later. } -void ImageFrame::zeroFill() +void ImageFrame::zeroFillPixelData() { memset(m_bytes, 0, m_size.width() * m_size.height() * sizeof(PixelData)); m_hasAlpha = true; @@ -176,8 +203,7 @@ bool ImageFrame::setSize(int newWidth, int newHeight) m_bytes = m_backingStore.data(); m_size = IntSize(newWidth, newHeight); - // Zero the image. - zeroFill(); + zeroFillPixelData(); return true; } diff --git a/Source/WebCore/platform/image-decoders/ImageDecoder.h b/Source/WebCore/platform/image-decoders/ImageDecoder.h index c581ba1..7d0d0d4 100644 --- a/Source/WebCore/platform/image-decoders/ImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/ImageDecoder.h @@ -50,9 +50,8 @@ namespace WebCore { // FIXME: Do we want better encapsulation? typedef Vector<char> ColorProfile; - // The ImageFrame object represents the decoded image data in RGBA32 - // format. This buffer is what all decoders write a single frame into. - // Frames are then instantiated for drawing by being handed this buffer. + // ImageFrame represents the decoded image data. This buffer is what all + // decoders write a single frame into. class ImageFrame { public: enum FrameStatus { FrameEmpty, FramePartial, FrameComplete }; @@ -80,19 +79,17 @@ namespace WebCore { // create a new copy of the image data, only increase the ref count. ImageFrame& operator=(const ImageFrame& other); - // Deletes the pixel data entirely; used by ImageDecoder to save memory - // when we no longer need to display a frame and only need its metadata. - void clear(); + // These do not touch other metadata, only the raw pixel data. + void clearPixelData(); + void zeroFillPixelData(); - // Zeroes the pixel data in the buffer, setting it to fully-transparent. - void zeroFill(); - - // Creates a new copy of the image data in |other|, so the two images - // can be modified independently. Returns whether the copy succeeded. + // Makes this frame have an independent copy of the provided image's + // pixel data, so that modifications in one frame are not reflected in + // the other. Returns whether the copy succeeded. bool copyBitmapData(const ImageFrame&); - // Creates a new reference to the image data in |other|. The two images - // share a common backing store. + // Makes this frame reference the provided image's pixel data, so that + // modifications in one frame are reflected in the other. void copyReferenceToBitmapData(const ImageFrame&); // Copies the pixel data at [(startX, startY), (endX, startY)) to the @@ -116,18 +113,17 @@ namespace WebCore { #endif // Allocates space for the pixel data. Must be called before any pixels - // are written. Will return true on success, false if the memory - // allocation fails. Calling this multiple times is undefined and may - // leak memory. + // are written. Must only be called once. Returns whether allocation + // succeeded. bool setSize(int newWidth, int newHeight); - // To be used by ImageSource::createFrameAtIndex(). Returns a pointer - // to the underlying native image data. This pointer will be owned by - // the BitmapImage and freed in FrameData::clear(). + // Returns a caller-owned pointer to the underlying native image data. + // (Actual use: This pointer will be owned by BitmapImage and freed in + // FrameData::clear()). NativeImagePtr asNewNativeImage() const; bool hasAlpha() const; - const IntRect& rect() const { return m_rect; } + const IntRect& originalFrameRect() const { return m_originalFrameRect; } FrameStatus status() const { return m_status; } unsigned duration() const { return m_duration; } FrameDisposalMethod disposalMethod() const { return m_disposalMethod; } @@ -135,7 +131,7 @@ namespace WebCore { void setHasAlpha(bool alpha); void setColorProfile(const ColorProfile&); - void setRect(const IntRect& r) { m_rect = r; } + void setOriginalFrameRect(const IntRect& r) { m_originalFrameRect = r; } void setStatus(FrameStatus status); void setDuration(unsigned duration) { m_duration = duration; } void setDisposalMethod(FrameDisposalMethod method) { m_disposalMethod = method; } @@ -184,7 +180,11 @@ namespace WebCore { g = static_cast<unsigned>(g * alphaPercent); b = static_cast<unsigned>(b * alphaPercent); } +<<<<<<< HEAD #if PLATFORM(ANDROID) +======= +#if PLATFORM(SKIA) +>>>>>>> webkit.org at r78450 *dest = SkPackARGB32(a, r, g, b); #else *dest = (a << 24 | r << 16 | g << 8 | b); @@ -202,35 +202,26 @@ namespace WebCore { #else NativeBackingStore m_backingStore; PixelData* m_bytes; // The memory is backed by m_backingStore. - IntSize m_size; // The size of the buffer. This should be the - // same as ImageDecoder::m_size. - bool m_hasAlpha; // Whether or not any of the pixels in the buffer - // have transparency. + IntSize m_size; + bool m_hasAlpha; ColorProfile m_colorProfile; #endif - IntRect m_rect; // The rect of the original specified frame within - // the overall buffer. This will always just be - // the entire buffer except for GIF frames whose - // original rect was smaller than the overall - // image size. - FrameStatus m_status; // Whether or not this frame is completely - // finished decoding. - unsigned m_duration; // The animation delay. - FrameDisposalMethod m_disposalMethod; // What to do with this frame's data when - // initializing the next frame. - bool m_premultiplyAlpha; // Whether to premultiply alpha into R, G, B - // channels; by default it's true. + IntRect m_originalFrameRect; // This will always just be the entire + // buffer except for GIF frames whose + // original rect was smaller than the + // overall image size. + FrameStatus m_status; + unsigned m_duration; + FrameDisposalMethod m_disposalMethod; + bool m_premultiplyAlpha; }; - // The ImageDecoder class represents a base class for specific image format - // decoders (e.g., GIF, JPG, PNG, ICO) to derive from. All decoders decode - // into RGBA32 format and the base class manages the RGBA32 frame cache. + // ImageDecoder is a base for all format-specific decoders + // (e.g. JPEGImageDecoder). This base manages the ImageFrame cache. // - // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to write - // directly to scaled output buffers by down sampling. Call - // setMaxNumPixels() to specify the biggest size that decoded images can - // have. Image decoders will deflate those images that are bigger than - // m_maxNumPixels. (Not supported by all image decoders yet) + // ENABLE(IMAGE_DECODER_DOWN_SAMPLING) allows image decoders to downsample + // at decode time. Image decoders will downsample any images larger than + // |m_maxNumPixels|. FIXME: Not yet supported by all decoders. class ImageDecoder { WTF_MAKE_NONCOPYABLE(ImageDecoder); WTF_MAKE_FAST_ALLOCATED; public: @@ -241,19 +232,15 @@ namespace WebCore { , m_sizeAvailable(false) , m_maxNumPixels(-1) , m_isAllDataReceived(false) - , m_failed(false) - { - } + , m_failed(false) { } - virtual ~ImageDecoder() {} + virtual ~ImageDecoder() { } - // Factory function to create an ImageDecoder. Ports that subclass - // ImageDecoder can provide their own implementation of this to avoid - // needing to write a dedicated setData() implementation. + // Returns a caller-owned decoder of the appropriate type. Returns 0 if + // we can't sniff a supported type from the provided data (possibly + // because there isn't enough data yet). static ImageDecoder* create(const SharedBuffer& data, ImageSource::AlphaOption, ImageSource::GammaAndColorProfileOption); - // The the filename extension usually associated with an undecoded image - // of this type. virtual String filenameExtension() const = 0; bool isAllDataReceived() const { return m_isAllDataReceived; } @@ -266,41 +253,33 @@ namespace WebCore { m_isAllDataReceived = allDataReceived; } - // Whether or not the size information has been decoded yet. This - // default implementation just returns true if the size has been set and - // we have not seen a failure. Decoders may want to override this to - // lazily decode enough of the image to get the size. + // Lazily-decodes enough of the image to get the size (if possible). + // FIXME: Right now that has to be done by each subclass; factor the + // decode call out and use it here. virtual bool isSizeAvailable() { - return !m_failed && m_sizeAvailable; + return !m_failed && m_sizeAvailable; } - // Returns the size of the image. - virtual IntSize size() const - { - return m_size; - } + virtual IntSize size() const { return m_size; } IntSize scaledSize() const { return m_scaled ? IntSize(m_scaledColumns.size(), m_scaledRows.size()) : size(); } - // Returns the size of frame |index|. This will only differ from size() - // for formats where different frames are different sizes (namely ICO, - // where each frame represents a different icon within the master file). - // Notably, this does not return different sizes for different GIF - // frames, since while these may be stored as smaller rectangles, during - // decoding they are composited to create a full-size frame. + // This will only differ from size() for ICO (where each frame is a + // different icon) or other formats where different frames are different + // sizes. This does NOT differ from size() for GIF, since decoding GIFs + // composites any smaller frames against previous frames to create full- + // size frames. virtual IntSize frameSizeAtIndex(size_t) const { return size(); } - // Called by the image decoders to set their decoded size, this also - // checks the size for validity. It will return true if the size was - // set, or false if there is an error. On error, the m_failed flag will - // be set and the caller should immediately stop decoding. + // Returns whether the size is legal (i.e. not going to result in + // overflow elsewhere). If not, marks decoding as failed. virtual bool setSize(unsigned width, unsigned height) { if (isOverSize(width, height)) @@ -310,26 +289,19 @@ namespace WebCore { return true; } - // The total number of frames for the image. Classes that support - // multiple frames will scan the image data for the answer if they need - // to (without necessarily decoding all of the individual frames). + // Lazily-decodes enough of the image to get the frame count (if + // possible), without decoding the individual frames. + // FIXME: Right now that has to be done by each subclass; factor the + // decode call out and use it here. virtual size_t frameCount() { return 1; } - // The number of repetitions to perform for an animation loop. virtual int repetitionCount() const { return cAnimationNone; } - // Called to obtain the ImageFrame full of decoded data for rendering. - // The decoder plugin will decode as much of the frame as it can before - // handing back the buffer. + // Decodes as much of the requested frame as possible, and returns an + // ImageDecoder-owned pointer. virtual ImageFrame* frameBufferAtIndex(size_t) = 0; - // Whether or not the underlying image format even supports alpha - // transparency. - virtual bool supportsAlpha() const { return true; } - void setIgnoreGammaAndColorProfile(bool flag) { m_ignoreGammaAndColorProfile = flag; } - - // Whether or not the gamma and color profile are applied. bool ignoresGammaAndColorProfile() const { return m_ignoreGammaAndColorProfile; } // Sets the "decode failure" flag. For caller convenience (since so @@ -344,13 +316,10 @@ namespace WebCore { bool failed() const { return m_failed; } - // Wipe out frames in the frame buffer cache before |clearBeforeFrame|, - // assuming this can be done without breaking decoding. Different - // decoders place different restrictions on what frames are safe to - // destroy, so this is left to them to implement. - // For convenience's sake, we provide a default (empty) implementation, - // since in practice only GIFs will ever use this. - virtual void clearFrameBufferCache(size_t clearBeforeFrame) { } + // Clears decoded pixel data from before the provided frame unless that + // data may be needed to decode future frames (e.g. due to GIF frame + // compositing). + virtual void clearFrameBufferCache(size_t) { } #if ENABLE(IMAGE_DECODER_DOWN_SAMPLING) void setMaxNumPixels(int m) { m_maxNumPixels = m; } @@ -378,8 +347,6 @@ namespace WebCore { // and return it as a (signed) int. Avoid overflow. static bool isOverSize(unsigned width, unsigned height) { - // width * height must not exceed (2 ^ 29) - 1, so that we don't - // overflow when we multiply by 4. unsigned long long total_size = static_cast<unsigned long long>(width) * static_cast<unsigned long long>(height); return total_size > ((1 << 29) - 1); diff --git a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp index 1805bc7..5c00553 100644 --- a/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp +++ b/Source/WebCore/platform/image-decoders/bmp/BMPImageReader.cpp @@ -87,7 +87,7 @@ bool BMPImageReader::decodeBMP(bool onlySize) m_buffer->setHasAlpha(false); // For BMPs, the frame always fills the entire image. - m_buffer->setRect(IntRect(IntPoint(), m_parent->size())); + m_buffer->setOriginalFrameRect(IntRect(IntPoint(), m_parent->size())); if (!m_isTopDown) m_coord.setY(m_parent->size().height() - 1); @@ -707,7 +707,7 @@ BMPImageReader::ProcessingResult BMPImageReader::processNonRLEData(bool inRLE, i } else { m_seenNonZeroAlphaPixel = true; if (m_seenZeroAlphaPixel) { - m_buffer->zeroFill(); + m_buffer->zeroFillPixelData(); m_seenZeroAlphaPixel = false; } else if (alpha != 255) m_buffer->setHasAlpha(true); diff --git a/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp b/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp index 0b90107..43d6a52 100644 --- a/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp +++ b/Source/WebCore/platform/image-decoders/cg/ImageDecoderCG.cpp @@ -72,7 +72,7 @@ bool ImageFrame::setSize(int newWidth, int newHeight) m_bytes = reinterpret_cast<PixelData*>(CFDataGetMutableBytePtr(m_backingStore.get())); m_size = IntSize(newWidth, newHeight); - zeroFill(); + zeroFillPixelData(); return true; } diff --git a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp index e6de597..7e334a9 100644 --- a/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/gif/GIFImageDecoder.cpp @@ -174,14 +174,14 @@ void GIFImageDecoder::clearFrameBufferCache(size_t clearBeforeFrame) Vector<ImageFrame>::iterator i(end); for (; (i != m_frameBufferCache.begin()) && ((i->status() == ImageFrame::FrameEmpty) || (i->disposalMethod() == ImageFrame::DisposeOverwritePrevious)); --i) { if ((i->status() == ImageFrame::FrameComplete) && (i != end)) - i->clear(); + i->clearPixelData(); } // Now |i| holds the last frame we need to preserve; clear prior frames. for (Vector<ImageFrame>::iterator j(m_frameBufferCache.begin()); j != i; ++j) { ASSERT(j->status() != ImageFrame::FramePartial); if (j->status() != ImageFrame::FrameEmpty) - j->clear(); + j->clearPixelData(); } } @@ -266,7 +266,7 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, if (!m_currentBufferSawAlpha) { // The whole frame was non-transparent, so it's possible that the entire // resulting buffer was non-transparent, and we can setHasAlpha(false). - if (buffer.rect().contains(IntRect(IntPoint(), scaledSize()))) + if (buffer.originalFrameRect().contains(IntRect(IntPoint(), scaledSize()))) buffer.setHasAlpha(false); else if (frameIndex) { // Tricky case. This frame does not have alpha only if everywhere @@ -289,7 +289,7 @@ bool GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, // The only remaining case is a DisposeOverwriteBgcolor frame. If // it had no alpha, and its rect is contained in the current frame's // rect, we know the current frame has no alpha. - if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.rect().contains(prevBuffer->rect())) + if ((prevBuffer->disposalMethod() == ImageFrame::DisposeOverwriteBgcolor) && !prevBuffer->hasAlpha() && buffer.originalFrameRect().contains(prevBuffer->originalFrameRect())) buffer.setHasAlpha(false); } } @@ -327,17 +327,17 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) IntRect frameRect(frameReader->x_offset, frameReader->y_offset, frameReader->width, frameReader->height); // Make sure the frameRect doesn't extend outside the buffer. - if (frameRect.right() > size().width()) + if (frameRect.maxX() > size().width()) frameRect.setWidth(size().width() - frameReader->x_offset); - if (frameRect.bottom() > size().height()) + if (frameRect.maxY() > size().height()) frameRect.setHeight(size().height() - frameReader->y_offset); ImageFrame* const buffer = &m_frameBufferCache[frameIndex]; int left = upperBoundScaledX(frameRect.x()); - int right = lowerBoundScaledX(frameRect.right(), left); + int right = lowerBoundScaledX(frameRect.maxX(), left); int top = upperBoundScaledY(frameRect.y()); - int bottom = lowerBoundScaledY(frameRect.bottom(), top); - buffer->setRect(IntRect(left, top, right - left, bottom - top)); + int bottom = lowerBoundScaledY(frameRect.maxY(), top); + buffer->setOriginalFrameRect(IntRect(left, top, right - left, bottom - top)); if (!frameIndex) { // This is the first frame, so we're not relying on any previous data. @@ -367,7 +367,7 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) } else { // We want to clear the previous frame to transparent, without // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->rect(); + const IntRect& prevRect = prevBuffer->originalFrameRect(); const IntSize& bufferSize = scaledSize(); if (!frameIndex || prevRect.contains(IntRect(IntPoint(), scaledSize()))) { // Clearing the first frame, or a frame the size of the whole @@ -378,8 +378,8 @@ bool GIFImageDecoder::initFrameBuffer(unsigned frameIndex) // Copy the whole previous buffer, then clear just its frame. if (!buffer->copyBitmapData(*prevBuffer)) return setFailed(); - for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { - for (int x = prevRect.x(); x < prevRect.right(); ++x) + for (int y = prevRect.y(); y < prevRect.maxY(); ++y) { + for (int x = prevRect.x(); x < prevRect.maxX(); ++x) buffer->setRGBA(x, y, 0, 0, 0, 0); } if ((prevRect.width() > 0) && (prevRect.height() > 0)) diff --git a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp index a255c25..1434c65 100644 --- a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.cpp @@ -222,6 +222,12 @@ public: // jpeglib cannot convert these to rgb, but it can convert ycck // to cmyk. m_info.out_color_space = JCS_CMYK; + + // Same as with grayscale images, we convert CMYK images to RGBA + // ones. When we keep the color profiles of these CMYK images, + // CoreGraphics will convert their colors again. So, we discard + // their color profiles to prevent color corruption. + m_decoder->setIgnoreGammaAndColorProfile(true); break; default: return m_decoder->setFailed(); @@ -462,7 +468,7 @@ bool JPEGImageDecoder::outputScanlines() buffer.setColorProfile(m_colorProfile); // For JPEGs, the frame always fills the entire image. - buffer.setRect(IntRect(IntPoint(), size())); + buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } jpeg_decompress_struct* info = m_reader->info(); diff --git a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h index 801f1ab..d095cbb 100644 --- a/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/jpeg/JPEGImageDecoder.h @@ -45,7 +45,6 @@ namespace WebCore { virtual bool isSizeAvailable(); virtual bool setSize(unsigned width, unsigned height); virtual ImageFrame* frameBufferAtIndex(size_t index); - virtual bool supportsAlpha() const { return false; } // CAUTION: setFailed() deletes |m_reader|. Be careful to avoid // accessing deleted memory, especially when calling this from inside // JPEGImageReader! diff --git a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp index 755d704..8edfe36 100644 --- a/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/png/PNGImageDecoder.cpp @@ -341,7 +341,7 @@ void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, buffer.setColorProfile(m_colorProfile); // For PNGs, the frame always fills the entire image. - buffer.setRect(IntRect(IntPoint(), size())); + buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); if (m_reader->pngPtr()->interlaced) m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height()); diff --git a/Source/WebCore/platform/image-decoders/qt/ImageFrameQt.cpp b/Source/WebCore/platform/image-decoders/qt/ImageFrameQt.cpp index 81c22cf..a3cfbf6 100644 --- a/Source/WebCore/platform/image-decoders/qt/ImageFrameQt.cpp +++ b/Source/WebCore/platform/image-decoders/qt/ImageFrameQt.cpp @@ -50,14 +50,14 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other) return *this; copyBitmapData(other); - setRect(other.rect()); + setOriginalFrameRect(other.originalFrameRect()); setStatus(other.status()); setDuration(other.duration()); setDisposalMethod(other.disposalMethod()); return *this; } -void ImageFrame::clear() +void ImageFrame::clearPixelData() { m_pixmap = QPixmap(); m_image = QImage(); @@ -68,7 +68,7 @@ void ImageFrame::clear() // other metadata out of this frame later. } -void ImageFrame::zeroFill() +void ImageFrame::zeroFillPixelData() { if (m_pixmap.isNull() && !m_image.isNull()) { m_pixmap = QPixmap(m_image.width(), m_image.height()); @@ -101,8 +101,7 @@ bool ImageFrame::setSize(int newWidth, int newHeight) if (m_pixmap.isNull()) return false; - // Zero the image. - zeroFill(); + zeroFillPixelData(); return true; } diff --git a/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp b/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp index a1c8261..c7dcc7a 100644 --- a/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp +++ b/Source/WebCore/platform/image-decoders/skia/ImageDecoderSkia.cpp @@ -51,7 +51,7 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other) // Keep the pixels locked since we will be writing directly into the // bitmap throughout this object's lifetime. m_bitmap.lockPixels(); - setRect(other.rect()); + setOriginalFrameRect(other.originalFrameRect()); setStatus(other.status()); setDuration(other.duration()); setDisposalMethod(other.disposalMethod()); @@ -59,7 +59,7 @@ ImageFrame& ImageFrame::operator=(const ImageFrame& other) return *this; } -void ImageFrame::clear() +void ImageFrame::clearPixelData() { m_bitmap.reset(); m_status = FrameEmpty; @@ -69,7 +69,7 @@ void ImageFrame::clear() // other metadata out of this frame later. } -void ImageFrame::zeroFill() +void ImageFrame::zeroFillPixelData() { m_bitmap.eraseARGB(0, 0, 0, 0); } @@ -93,8 +93,7 @@ bool ImageFrame::setSize(int newWidth, int newHeight) if (!m_bitmap.allocPixels()) return false; - // Zero the image. - zeroFill(); + zeroFillPixelData(); return true; } diff --git a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp index 8045ada..3db00f6 100644 --- a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp +++ b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp @@ -116,7 +116,7 @@ bool WEBPImageDecoder::decode(bool onlySize) } buffer.setStatus(ImageFrame::FrameComplete); buffer.setHasAlpha(false); - buffer.setRect(IntRect(IntPoint(), size())); + buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); return true; } diff --git a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h index cde1bbf..c32e047 100644 --- a/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h +++ b/Source/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h @@ -42,7 +42,6 @@ public: virtual String filenameExtension() const { return "vp8"; } virtual bool isSizeAvailable(); virtual ImageFrame* frameBufferAtIndex(size_t index); - virtual bool supportsAlpha() const { return false; } private: // Returns false in case of decoding failure. diff --git a/Source/WebCore/platform/mac/DragImageMac.mm b/Source/WebCore/platform/mac/DragImageMac.mm index f444b6e..fc58173 100644 --- a/Source/WebCore/platform/mac/DragImageMac.mm +++ b/Source/WebCore/platform/mac/DragImageMac.mm @@ -28,9 +28,16 @@ #if ENABLE(DRAG_SUPPORT) #import "CachedImage.h" +#import "Font.h" +#import "FontDescription.h" +#import "FontSelector.h" +#import "GraphicsContext.h" #import "Image.h" #import "KURL.h" #import "ResourceResponse.h" +#import "Settings.h" +#import "StringTruncator.h" +#import "TextRun.h" namespace WebCore { @@ -98,7 +105,210 @@ RetainPtr<NSImage> createDragImageIconForCachedImage(CachedImage* image) return [[NSWorkspace sharedWorkspace] iconForFileType:extension]; } + + +const float DragLabelBorderX = 4; +//Keep border_y in synch with DragController::LinkDragBorderInset +const float DragLabelBorderY = 2; +const float DragLabelRadius = 5; +const float LabelBorderYOffset = 2; + +const float MinDragLabelWidthBeforeClip = 120; +const float MaxDragLabelWidth = 320; + +const float DragLinkLabelFontsize = 11; +const float DragLinkUrlFontSize = 10; + +// FIXME - we should move all the functionality of NSString extras to WebCore + +static Font& fontFromNSFont(NSFont *font) +{ + static NSFont *currentFont; + DEFINE_STATIC_LOCAL(Font, currentRenderer, ()); + + if ([font isEqual:currentFont]) + return currentRenderer; + if (currentFont) + CFRelease(currentFont); + currentFont = font; + CFRetain(currentFont); + FontPlatformData f(font, [font pointSize]); + currentRenderer = Font(f, ![[NSGraphicsContext currentContext] isDrawingToScreen]); + return currentRenderer; +} + +static bool canUseFastRenderer(const UniChar* buffer, unsigned length) +{ + unsigned i; + for (i = 0; i < length; i++) { + UCharDirection direction = u_charDirection(buffer[i]); + if (direction == U_RIGHT_TO_LEFT || direction > U_OTHER_NEUTRAL) + return false; + } + return true; +} + +static float widthWithFont(NSString *string, NSFont *font) +{ + unsigned length = [string length]; + Vector<UniChar, 2048> buffer(length); + [string getCharacters:buffer.data()]; + + if (canUseFastRenderer(buffer.data(), length)) { + Font webCoreFont(FontPlatformData(font, [font pointSize]), ![[NSGraphicsContext currentContext] isDrawingToScreen]); + TextRun run(buffer.data(), length); + run.disableRoundingHacks(); + return webCoreFont.floatWidth(run); + } + + return [string sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, nil]].width; +} + +static inline CGFloat webkit_CGCeiling(CGFloat value) +{ + if (sizeof(value) == sizeof(float)) + return ceilf(value); + return static_cast<CGFloat>(ceil(value)); +} + +static void drawAtPoint(NSString *string, NSPoint point, NSFont *font, NSColor *textColor) +{ + unsigned length = [string length]; + Vector<UniChar, 2048> buffer(length); + + [string getCharacters:buffer.data()]; + + if (canUseFastRenderer(buffer.data(), length)) { + // The following is a half-assed attempt to match AppKit's rounding rules for drawAtPoint. + // It's probably incorrect for high DPI. + // If you change this, be sure to test all the text drawn this way in Safari, including + // the status bar, bookmarks bar, tab bar, and activity window. + point.y = webkit_CGCeiling(point.y); + + NSGraphicsContext *nsContext = [NSGraphicsContext currentContext]; + CGContextRef cgContext = static_cast<CGContextRef>([nsContext graphicsPort]); + GraphicsContext graphicsContext(cgContext); + + // Safari doesn't flip the NSGraphicsContext before calling WebKit, yet WebCore requires a flipped graphics context. + BOOL flipped = [nsContext isFlipped]; + if (!flipped) + CGContextScaleCTM(cgContext, 1, -1); + + Font webCoreFont(FontPlatformData(font, [font pointSize]), ![nsContext isDrawingToScreen], Antialiased); + TextRun run(buffer.data(), length); + run.disableRoundingHacks(); + + CGFloat red; + CGFloat green; + CGFloat blue; + CGFloat alpha; + [[textColor colorUsingColorSpaceName:NSDeviceRGBColorSpace] getRed:&red green:&green blue:&blue alpha:&alpha]; + graphicsContext.setFillColor(makeRGBA(red * 255, green * 255, blue * 255, alpha * 255), ColorSpaceDeviceRGB); + + webCoreFont.drawText(&graphicsContext, run, FloatPoint(point.x, (flipped ? point.y : (-1 * point.y)))); + + if (!flipped) + CGContextScaleCTM(cgContext, 1, -1); + } else { + // The given point is on the baseline. + if ([[NSView focusView] isFlipped]) + point.y -= [font ascender]; + else + point.y += [font descender]; + + [string drawAtPoint:point withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, textColor, NSForegroundColorAttributeName, nil]]; + } +} + +static void drawDoubledAtPoint(NSString *string, NSPoint textPoint, NSColor *topColor, NSColor *bottomColor, NSFont *font) +{ + // turn off font smoothing so translucent text draws correctly (Radar 3118455) + drawAtPoint(string, textPoint, font, bottomColor); + + textPoint.y += 1; + drawAtPoint(string, textPoint, font, topColor); +} + +DragImageRef createDragImageForLink(KURL& url, const String& title, Frame* frame) +{ + if (!frame) + return nil; + NSString *label = 0; + if (!title.isEmpty()) + label = title; + NSURL *cocoaURL = url; + NSString *urlString = [cocoaURL absoluteString]; + + BOOL drawURLString = YES; + BOOL clipURLString = NO; + BOOL clipLabelString = NO; + + if (!label) { + drawURLString = NO; + label = urlString; + } + + NSFont *labelFont = [[NSFontManager sharedFontManager] convertFont:[NSFont systemFontOfSize:DragLinkLabelFontsize] + toHaveTrait:NSBoldFontMask]; + NSFont *urlFont = [NSFont systemFontOfSize:DragLinkUrlFontSize]; + NSSize labelSize; + labelSize.width = widthWithFont(label, labelFont); + labelSize.height = [labelFont ascender] - [labelFont descender]; + if (labelSize.width > MaxDragLabelWidth){ + labelSize.width = MaxDragLabelWidth; + clipLabelString = YES; + } + + NSSize imageSize; + imageSize.width = labelSize.width + DragLabelBorderX * 2; + imageSize.height = labelSize.height + DragLabelBorderY * 2; + if (drawURLString) { + NSSize urlStringSize; + urlStringSize.width = widthWithFont(urlString, urlFont); + urlStringSize.height = [urlFont ascender] - [urlFont descender]; + imageSize.height += urlStringSize.height; + if (urlStringSize.width > MaxDragLabelWidth) { + imageSize.width = std::max(MaxDragLabelWidth + DragLabelBorderY * 2, MinDragLabelWidthBeforeClip); + clipURLString = YES; + } else + imageSize.width = std::max(labelSize.width + DragLabelBorderX * 2, urlStringSize.width + DragLabelBorderX * 2); + } + NSImage *dragImage = [[[NSImage alloc] initWithSize: imageSize] autorelease]; + [dragImage lockFocus]; + + [[NSColor colorWithDeviceRed: 0.7f green: 0.7f blue: 0.7f alpha: 0.8f] set]; + + // Drag a rectangle with rounded corners + NSBezierPath *path = [NSBezierPath bezierPath]; + [path appendBezierPathWithOvalInRect: NSMakeRect(0, 0, DragLabelRadius * 2, DragLabelRadius * 2)]; + [path appendBezierPathWithOvalInRect: NSMakeRect(0, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)]; + [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, imageSize.height - DragLabelRadius * 2, DragLabelRadius * 2, DragLabelRadius * 2)]; + [path appendBezierPathWithOvalInRect: NSMakeRect(imageSize.width - DragLabelRadius * 2, 0, DragLabelRadius * 2, DragLabelRadius * 2)]; + + [path appendBezierPathWithRect: NSMakeRect(DragLabelRadius, 0, imageSize.width - DragLabelRadius * 2, imageSize.height)]; + [path appendBezierPathWithRect: NSMakeRect(0, DragLabelRadius, DragLabelRadius + 10, imageSize.height - 2 * DragLabelRadius)]; + [path appendBezierPathWithRect: NSMakeRect(imageSize.width - DragLabelRadius - 20, DragLabelRadius, DragLabelRadius + 20, imageSize.height - 2 * DragLabelRadius)]; + [path fill]; + + NSColor *topColor = [NSColor colorWithDeviceWhite:0.0f alpha:0.75f]; + NSColor *bottomColor = [NSColor colorWithDeviceWhite:1.0f alpha:0.5f]; + if (drawURLString) { + if (clipURLString) + urlString = StringTruncator::centerTruncate(urlString, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(urlFont)); + + drawDoubledAtPoint(urlString, NSMakePoint(DragLabelBorderX, DragLabelBorderY - [urlFont descender]), topColor, bottomColor, urlFont); + } + + if (clipLabelString) + label = StringTruncator::rightTruncate(label, imageSize.width - (DragLabelBorderX * 2), fontFromNSFont(labelFont)); + drawDoubledAtPoint(label, NSMakePoint(DragLabelBorderX, imageSize.height - LabelBorderYOffset - [labelFont pointSize]), topColor, bottomColor, labelFont); + + [dragImage unlockFocus]; + + return dragImage; +} + } // namespace WebCore #endif // ENABLE(DRAG_SUPPORT) diff --git a/Source/WebCore/platform/mac/EmptyProtocolDefinitions.h b/Source/WebCore/platform/mac/EmptyProtocolDefinitions.h index b73177b..04bf236 100644 --- a/Source/WebCore/platform/mac/EmptyProtocolDefinitions.h +++ b/Source/WebCore/platform/mac/EmptyProtocolDefinitions.h @@ -23,6 +23,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef EmptyProtocolDefinitions_h +#define EmptyProtocolDefinitions_h + #if defined(__OBJC__) #define EMPTY_PROTOCOL(NAME) \ @@ -47,3 +50,5 @@ EMPTY_PROTOCOL(NSURLDownloadDelegate) #undef EMPTY_PROTOCOL #endif /* defined(__OBJC__) */ + +#endif /* EmptyProtocolDefinitions_h */ diff --git a/Source/WebCore/platform/mac/FileSystemMac.mm b/Source/WebCore/platform/mac/FileSystemMac.mm index 0df3c89..bbeb76a 100644 --- a/Source/WebCore/platform/mac/FileSystemMac.mm +++ b/Source/WebCore/platform/mac/FileSystemMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Apple Inc. All rights reserved. + * Copyright (C) 2007, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,10 +25,12 @@ * (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" #import "FileSystem.h" #import "PlatformString.h" +#import <wtf/RetainPtr.h> #import <wtf/text/CString.h> namespace WebCore { @@ -62,4 +64,25 @@ CString openTemporaryFile(const char* prefix, PlatformFileHandle& platformFileHa return CString(temporaryFilePath.data()); } +bool canExcludeFromBackup() +{ +#ifdef BUILDING_ON_TIGER + return false; +#else + return true; +#endif +} + +bool excludeFromBackup(const String& path) +{ +#ifdef BUILDING_ON_TIGER + UNUSED_PARAM(path); + return false; +#else + // It is critical to pass FALSE for excludeByPath because excluding by path requires root privileges. + CSBackupSetItemExcluded(pathAsURL(path).get(), TRUE, FALSE); + return true; +#endif +} + } // namespace WebCore diff --git a/Source/WebCore/platform/mac/MIMETypeRegistryMac.mm b/Source/WebCore/platform/mac/MIMETypeRegistryMac.mm index 82348e0..3792b5a 100644 --- a/Source/WebCore/platform/mac/MIMETypeRegistryMac.mm +++ b/Source/WebCore/platform/mac/MIMETypeRegistryMac.mm @@ -28,12 +28,15 @@ #include "MIMETypeRegistry.h" #include "WebCoreSystemInterface.h" +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> namespace WebCore { String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); return wkGetMIMETypeForExtension(ext); } diff --git a/Source/WebCore/platform/mac/PasteboardMac.mm b/Source/WebCore/platform/mac/PasteboardMac.mm index 71e4046..65180a0 100644 --- a/Source/WebCore/platform/mac/PasteboardMac.mm +++ b/Source/WebCore/platform/mac/PasteboardMac.mm @@ -27,7 +27,6 @@ #import "Pasteboard.h" #import "CachedResource.h" -#import "CharacterNames.h" #import "DOMRangeInternal.h" #import "Document.h" #import "DocumentFragment.h" @@ -49,10 +48,10 @@ #import "Text.h" #import "WebCoreNSStringExtras.h" #import "markup.h" - #import <wtf/StdLibExtras.h> #import <wtf/RetainPtr.h> #import <wtf/UnusedParam.h> +#import <wtf/unicode/CharacterNames.h> @interface NSAttributedString (AppKitSecretsIKnowAbout) - (id)_initWithDOMRange:(DOMRange *)domRange; diff --git a/Source/WebCore/platform/mac/PopupMenuMac.mm b/Source/WebCore/platform/mac/PopupMenuMac.mm index 1bf500b..e69bcb2 100644 --- a/Source/WebCore/platform/mac/PopupMenuMac.mm +++ b/Source/WebCore/platform/mac/PopupMenuMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2008, 2010, 2011 Apple Inc. All rights reserved. * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). * * This library is free software; you can redistribute it and/or @@ -75,6 +75,11 @@ void PopupMenuMac::populate() if (!client()->shouldPopOver()) [m_popup.get() addItemWithTitle:@""]; +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + TextDirection menuTextDirection = client()->menuStyle().textDirection(); + [m_popup.get() setUserInterfaceLayoutDirection:menuTextDirection == LTR ? NSUserInterfaceLayoutDirectionLeftToRight : NSUserInterfaceLayoutDirectionRightToLeft]; +#endif // !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + ASSERT(client()); int size = client()->listSize(); @@ -92,13 +97,27 @@ void PopupMenuMac::populate() } [attributes setObject:font forKey:NSFontAttributeName]; } + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + RetainPtr<NSMutableParagraphStyle> paragraphStyle(AdoptNS, [[NSParagraphStyle defaultParagraphStyle] mutableCopy]); + [paragraphStyle.get() setAlignment:menuTextDirection == LTR ? NSLeftTextAlignment : NSRightTextAlignment]; + NSWritingDirection writingDirection = style.textDirection() == LTR ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft; + [paragraphStyle.get() setBaseWritingDirection:writingDirection]; + if (style.hasTextDirectionOverride()) { + RetainPtr<NSNumber> writingDirectionValue(AdoptNS, [[NSNumber alloc] initWithInteger:writingDirection + NSTextWritingDirectionOverride]); + RetainPtr<NSArray> writingDirectionArray(AdoptNS, [[NSArray alloc] initWithObjects:writingDirectionValue.get(), nil]); + [attributes setObject:writingDirectionArray.get() forKey:NSWritingDirectionAttributeName]; + } + [attributes setObject:paragraphStyle.get() forKey:NSParagraphStyleAttributeName]; +#endif // !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) + // FIXME: Add support for styling the foreground and background colors. // FIXME: Find a way to customize text color when an item is highlighted. - NSAttributedString* string = [[NSAttributedString alloc] initWithString:client()->itemText(i) attributes:attributes]; + NSAttributedString *string = [[NSAttributedString alloc] initWithString:client()->itemText(i) attributes:attributes]; [attributes release]; [m_popup.get() addItemWithTitle:@""]; - NSMenuItem* menuItem = [m_popup.get() lastItem]; + NSMenuItem *menuItem = [m_popup.get() lastItem]; [menuItem setAttributedTitle:string]; [menuItem setEnabled:client()->itemIsEnabled(i)]; [menuItem setToolTip:client()->itemToolTip(i)]; diff --git a/Source/WebCore/platform/mac/ScrollAnimatorMac.h b/Source/WebCore/platform/mac/ScrollAnimatorMac.h index f05db40..3f7612a 100644 --- a/Source/WebCore/platform/mac/ScrollAnimatorMac.h +++ b/Source/WebCore/platform/mac/ScrollAnimatorMac.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011 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,17 +28,30 @@ #if ENABLE(SMOOTH_SCROLLING) +#include "FloatPoint.h" +#include "FloatSize.h" +#include "HeaderDetection.h" #include "ScrollAnimator.h" +#include "Timer.h" +#include "WebCoreSystemInterface.h" #include <wtf/RetainPtr.h> #ifdef __OBJC__ @class ScrollAnimationHelperDelegate; +@class ScrollbarPainterDelegate; +@class ScrollbarPainterControllerDelegate; +@class ScrollbarPainterDelegate; #else class ScrollAnimationHelperDelegate; +class ScrollbarPainterDelegate; +class ScrollbarPainterControllerDelegate; +class ScrollbarPainterDelegate; #endif namespace WebCore { +class Scrollbar; + class ScrollAnimatorMac : public ScrollAnimator { public: ScrollAnimatorMac(ScrollableArea*); @@ -47,12 +60,72 @@ public: virtual bool scroll(ScrollbarOrientation, ScrollGranularity, float step, float multiplier); virtual void scrollToOffsetWithoutAnimation(const FloatPoint&); - // Called by the ScrollAnimationHelperDelegate. - void immediateScrollToPoint(const FloatPoint& newPosition); +#if ENABLE(RUBBER_BANDING) + virtual void handleWheelEvent(PlatformWheelEvent&); +#if ENABLE(GESTURE_EVENTS) + virtual void handleGestureEvent(const PlatformGestureEvent&); +#endif +#endif + void immediateScrollToPoint(const FloatPoint& newPosition); + void immediateScrollByDeltaX(float deltaX); + void immediateScrollByDeltaY(float deltaY); + private: RetainPtr<id> m_scrollAnimationHelper; RetainPtr<ScrollAnimationHelperDelegate> m_scrollAnimationHelperDelegate; + +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + RetainPtr<WKScrollbarPainterControllerRef> m_scrollbarPainterController; + RetainPtr<ScrollbarPainterControllerDelegate> m_scrollbarPainterControllerDelegate; + RetainPtr<id> m_scrollbarPainterDelegate; +#endif + + virtual void notityPositionChanged(); + virtual void contentAreaWillPaint() const; + virtual void mouseEnteredContentArea() const; + virtual void mouseExitedContentArea() const; + virtual void mouseMovedInContentArea() const; + virtual void willStartLiveResize(); + virtual void contentsResized() const; + virtual void willEndLiveResize(); + virtual void contentAreaDidShow() const; + virtual void contentAreaDidHide() const; + + virtual void didAddVerticalScrollbar(Scrollbar*); + virtual void willRemoveVerticalScrollbar(Scrollbar*); + virtual void didAddHorizontalScrollbar(Scrollbar*); + virtual void willRemoveHorizontalScrollbar(Scrollbar*); + + float adjustScrollXPositionIfNecessary(float) const; + float adjustScrollYPositionIfNecessary(float) const; + FloatPoint adjustScrollPositionIfNecessary(const FloatPoint&) const; + +#if ENABLE(RUBBER_BANDING) + bool allowsVerticalStretching() const; + bool allowsHorizontalStretching() const; + bool pinnedInDirection(float deltaX, float deltaY); + void snapRubberBand(); + void snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*); + void smoothScrollWithEvent(PlatformWheelEvent&); + void beginScrollGesture(); + void endScrollGesture(); + + bool m_inScrollGesture; + bool m_momentumScrollInProgress; + bool m_ignoreMomentumScrolls; + CFTimeInterval m_lastMomemtumScrollTimestamp; + FloatSize m_overflowScrollDelta; + FloatSize m_stretchScrollForce; + FloatSize m_momentumVelocity; + + // Rubber band state. + CFTimeInterval m_startTime; + FloatSize m_startStretch; + FloatPoint m_origOrigin; + FloatSize m_origVelocity; + Timer<ScrollAnimatorMac> m_snapRubberBandTimer; +#endif }; } // namespace WebCore diff --git a/Source/WebCore/platform/mac/ScrollAnimatorMac.mm b/Source/WebCore/platform/mac/ScrollAnimatorMac.mm index 59b333b..c1154d5 100644 --- a/Source/WebCore/platform/mac/ScrollAnimatorMac.mm +++ b/Source/WebCore/platform/mac/ScrollAnimatorMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Apple Inc. All rights reserved. + * Copyright (C) 2010, 2011 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,10 +30,20 @@ #include "ScrollAnimatorMac.h" #include "FloatPoint.h" +#include "IntRect.h" +#include "PlatformGestureEvent.h" +#include "PlatformWheelEvent.h" +#include "ScrollView.h" #include "ScrollableArea.h" +#include "ScrollbarTheme.h" +#include "ScrollbarThemeMac.h" #include <wtf/PassOwnPtr.h> +#include <wtf/UnusedParam.h> -@interface NSObject (NSScrollAnimationHelperDetails) +using namespace WebCore; +using namespace std; + +@interface NSObject (ScrollAnimationHelperDetails) - (id)initWithDelegate:(id)delegate; - (void)_stopRun; - (BOOL)_isAnimating; @@ -44,18 +54,7 @@ { WebCore::ScrollAnimatorMac* _animator; } - - (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator; - -- (NSRect)bounds; -- (void)_immediateScrollToPoint:(NSPoint)newPosition; -- (NSSize)convertSizeToBase:(NSSize)size; -- (NSSize)convertSizeFromBase:(NSSize)size; - -- (id)superview; // Return nil. -- (id)documentView; // Return nil. -- (id)window; // Return nil. -- (void)_recursiveRecomputeToolTips; // No-op. @end static NSSize abs(NSSize size) @@ -80,14 +79,24 @@ static NSSize abs(NSSize size) return self; } +- (void)scrollAnimatorDestroyed +{ + _animator = 0; +} + - (NSRect)bounds { + if (!_animator) + return NSZeroRect; + WebCore::FloatPoint currentPosition = _animator->currentPosition(); return NSMakeRect(currentPosition.x(), currentPosition.y(), 0, 0); } - (void)_immediateScrollToPoint:(NSPoint)newPosition { + if (!_animator) + return; _animator->immediateScrollToPoint(newPosition); } @@ -101,6 +110,16 @@ static NSSize abs(NSSize size) return abs(size); } +- (NSSize)convertSizeToBacking:(NSSize)size +{ + return abs(size); +} + +- (NSSize)convertSizeFromBacking:(NSSize)size +{ + return abs(size); +} + - (id)superview { return nil; @@ -122,6 +141,307 @@ static NSSize abs(NSSize size) @end +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + +@interface ScrollbarPainterControllerDelegate : NSObject +{ + WebCore::ScrollAnimatorMac* _animator; +} +- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator; +@end + +@implementation ScrollbarPainterControllerDelegate + +- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator +{ + self = [super init]; + if (!self) + return nil; + + _animator = scrollAnimator; + return self; +} + +- (void)scrollAnimatorDestroyed +{ + _animator = 0; +} + +- (NSRect)contentAreaRectForScrollerImpPair:(id)scrollerImpPair +{ + UNUSED_PARAM(scrollerImpPair); + if (!_animator) + return NSZeroRect; + + WebCore::IntSize contentsSize = _animator->scrollableArea()->contentsSize(); + return NSMakeRect(0, 0, contentsSize.width(), contentsSize.height()); +} + +- (BOOL)inLiveResizeForScrollerImpPair:(id)scrollerImpPair +{ + UNUSED_PARAM(scrollerImpPair); + if (!_animator) + return NO; + + return _animator->scrollableArea()->inLiveResize(); +} + +- (NSPoint)mouseLocationInContentAreaForScrollerImpPair:(id)scrollerImpPair +{ + UNUSED_PARAM(scrollerImpPair); + if (!_animator) + return NSZeroPoint; + + return _animator->scrollableArea()->currentMousePosition(); +} + +- (NSPoint)scrollerImpPair:(id)scrollerImpPair convertContentPoint:(NSPoint)pointInContentArea toScrollerImp:(id)scrollerImp +{ + UNUSED_PARAM(scrollerImpPair); + if (!_animator) + return NSZeroPoint; + + WebCore::Scrollbar* scrollbar = 0; + if (wkScrollbarPainterIsHorizontal((WKScrollbarPainterRef)scrollerImp)) + scrollbar = _animator->scrollableArea()->horizontalScrollbar(); + else + scrollbar = _animator->scrollableArea()->verticalScrollbar(); + + // It is possible to have a null scrollbar here since it is possible for this delegate + // method to be called between the moment when a scrollbar has been set to 0 and the + // moment when its destructor has been called. We should probably de-couple some + // of the clean-up work in ScrollbarThemeMac::unregisterScrollbar() to avoid this + // issue. + if (!scrollbar) + return WebCore::IntPoint(); + + return scrollbar->convertFromContainingView(WebCore::IntPoint(pointInContentArea)); +} + +- (void)scrollerImpPair:(id)scrollerImpPair setContentAreaNeedsDisplayInRect:(NSRect)rect +{ + UNUSED_PARAM(scrollerImpPair); + UNUSED_PARAM(rect); +} + +- (void)scrollerImpPair:(id)scrollerImpPair updateScrollerStyleForNewRecommendedScrollerStyle:(NSScrollerStyle)newRecommendedScrollerStyle +{ + if (!_animator) + return; + + WKScrollbarPainterControllerRef painterController = (WKScrollbarPainterControllerRef)scrollerImpPair; + WebCore::ScrollbarThemeMac* macTheme = (WebCore::ScrollbarThemeMac*)WebCore::ScrollbarTheme::nativeTheme(); + + WKScrollbarPainterRef oldVerticalPainter = wkVerticalScrollbarPainterForController(painterController); + if (oldVerticalPainter) { + WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar(); + WKScrollbarPainterRef newVerticalPainter = wkMakeScrollbarReplacementPainter(oldVerticalPainter, + newRecommendedScrollerStyle, + verticalScrollbar->controlSize(), + false); + macTheme->setNewPainterForScrollbar(verticalScrollbar, newVerticalPainter); + } + + WKScrollbarPainterRef oldHorizontalPainter = wkHorizontalScrollbarPainterForController(painterController); + if (oldHorizontalPainter) { + WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar(); + WKScrollbarPainterRef newHorizontalPainter = wkMakeScrollbarReplacementPainter(oldHorizontalPainter, + newRecommendedScrollerStyle, + horizontalScrollbar->controlSize(), + true); + macTheme->setNewPainterForScrollbar(horizontalScrollbar, newHorizontalPainter); + } + + wkSetScrollbarPainterControllerStyle(painterController, newRecommendedScrollerStyle); +} + +@end + +@interface ScrollbarPartAnimation : NSAnimation +{ + RetainPtr<WKScrollbarPainterRef> _scrollerPainter; + WebCore::ScrollbarPart _part; + WebCore::ScrollAnimatorMac* _animator; + CGFloat _initialAlpha; + CGFloat _newAlpha; +} +- (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration; +@end + +@implementation ScrollbarPartAnimation + +- (id)initWithScrollbarPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part scrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration +{ + self = [super initWithDuration:duration animationCurve:NSAnimationEaseInOut]; + if (!self) + return nil; + + _scrollerPainter = scrollerPainter; + _part = part; + _animator = scrollAnimator; + _initialAlpha = _part == WebCore::ThumbPart ? wkScrollbarPainterKnobAlpha(_scrollerPainter.get()) : wkScrollbarPainterTrackAlpha(_scrollerPainter.get()); + _newAlpha = newAlpha; + + return self; +} + +- (void)setCurrentProgress:(NSAnimationProgress)progress +{ + [super setCurrentProgress:progress]; + + if (!_animator) + return; + + CGFloat currentAlpha; + if (_initialAlpha > _newAlpha) + currentAlpha = 1 - progress; + else + currentAlpha = progress; + + if (_part == WebCore::ThumbPart) + wkSetScrollbarPainterKnobAlpha(_scrollerPainter.get(), currentAlpha); + else + wkSetScrollbarPainterTrackAlpha(_scrollerPainter.get(), currentAlpha); + + // Invalidate the scrollbars so that they paint the animation + if (WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar()) + _animator->scrollableArea()->invalidateScrollbarRect(verticalScrollbar, WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height())); + if (WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar()) + _animator->scrollableArea()->invalidateScrollbarRect(horizontalScrollbar, WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height())); +} + +- (void)scrollAnimatorDestroyed +{ + [self stopAnimation]; + _animator = 0; +} + +@end + +@interface ScrollbarPainterDelegate : NSObject<NSAnimationDelegate> +{ + WebCore::ScrollAnimatorMac* _animator; + + RetainPtr<ScrollbarPartAnimation> _verticalKnobAnimation; + RetainPtr<ScrollbarPartAnimation> _horizontalKnobAnimation; + + RetainPtr<ScrollbarPartAnimation> _verticalTrackAnimation; + RetainPtr<ScrollbarPartAnimation> _horizontalTrackAnimation; +} +- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator; +@end + +@implementation ScrollbarPainterDelegate + +- (id)initWithScrollAnimator:(WebCore::ScrollAnimatorMac*)scrollAnimator +{ + self = [super init]; + if (!self) + return nil; + + _animator = scrollAnimator; + return self; +} + +- (NSRect)convertRectToBacking:(NSRect)aRect +{ + return aRect; +} + +- (NSRect)convertRectFromBacking:(NSRect)aRect +{ + return aRect; +} + +- (CALayer *)layer +{ + if (!_animator) + return nil; + if (!_animator->scrollableArea()->scrollbarWillRenderIntoCompositingLayer()) + return nil; + + // FIXME: This should attempt to return an actual layer. + static CALayer *dummyLayer = [[CALayer alloc] init]; + return dummyLayer; +} + +- (void)setUpAnimation:(RetainPtr<ScrollbarPartAnimation>&)scrollbarPartAnimation scrollerPainter:(WKScrollbarPainterRef)scrollerPainter part:(WebCore::ScrollbarPart)part animateAlphaTo:(CGFloat)newAlpha duration:(NSTimeInterval)duration +{ + // If we are currently animating, stop + if (scrollbarPartAnimation) { + [scrollbarPartAnimation.get() stopAnimation]; + scrollbarPartAnimation = nil; + } + + scrollbarPartAnimation.adoptNS([[ScrollbarPartAnimation alloc] initWithScrollbarPainter:scrollerPainter + part:part + scrollAnimator:_animator + animateAlphaTo:newAlpha + duration:duration]); + [scrollbarPartAnimation.get() setAnimationBlockingMode:NSAnimationNonblocking]; + [scrollbarPartAnimation.get() startAnimation]; +} + +- (void)scrollerImp:(id)scrollerImp animateKnobAlphaTo:(CGFloat)newKnobAlpha duration:(NSTimeInterval)duration +{ + if (!_animator) + return; + + WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp; + if (newKnobAlpha == wkScrollbarPainterKnobAlpha(scrollerPainter)) + return; + + if (wkScrollbarPainterIsHorizontal(scrollerPainter)) + [self setUpAnimation:_horizontalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration]; + else + [self setUpAnimation:_verticalKnobAnimation scrollerPainter:scrollerPainter part:WebCore::ThumbPart animateAlphaTo:newKnobAlpha duration:duration]; +} + +- (void)scrollerImp:(id)scrollerImp animateTrackAlphaTo:(CGFloat)newTrackAlpha duration:(NSTimeInterval)duration +{ + if (!_animator) + return; + + WKScrollbarPainterRef scrollerPainter = (WKScrollbarPainterRef)scrollerImp; + if (newTrackAlpha == wkScrollbarPainterTrackAlpha(scrollerPainter)) + return; + + if (wkScrollbarPainterIsHorizontal(scrollerPainter)) + [self setUpAnimation:_horizontalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration]; + else + [self setUpAnimation:_verticalTrackAnimation scrollerPainter:scrollerPainter part:WebCore::BackTrackPart animateAlphaTo:newTrackAlpha duration:duration]; +} + +- (void)scrollerImp:(id)scrollerImp overlayScrollerStateChangedTo:(NSUInteger)newOverlayScrollerState +{ + if (!_animator) + return; + + WKScrollbarPainterRef scrollbarPainter = (WKScrollbarPainterRef)scrollerImp; + wkScrollbarPainterSetOverlayState(scrollbarPainter, newOverlayScrollerState); + + if (wkScrollbarPainterIsHorizontal(scrollbarPainter)) { + WebCore::Scrollbar* horizontalScrollbar = _animator->scrollableArea()->horizontalScrollbar(); + _animator->scrollableArea()->invalidateScrollbarRect(horizontalScrollbar, WebCore::IntRect(0, 0, horizontalScrollbar->width(), horizontalScrollbar->height())); + } else { + WebCore::Scrollbar* verticalScrollbar = _animator->scrollableArea()->verticalScrollbar(); + _animator->scrollableArea()->invalidateScrollbarRect(verticalScrollbar, WebCore::IntRect(0, 0, verticalScrollbar->width(), verticalScrollbar->height())); + + } +} + +- (void)scrollAnimatorDestroyed +{ + _animator = 0; + [_verticalKnobAnimation.get() scrollAnimatorDestroyed]; + [_horizontalKnobAnimation.get() scrollAnimatorDestroyed]; + [_verticalTrackAnimation.get() scrollAnimatorDestroyed]; + [_horizontalTrackAnimation.get() scrollAnimatorDestroyed]; +} + +@end +#endif // #if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + namespace WebCore { PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea) @@ -131,13 +451,33 @@ PassOwnPtr<ScrollAnimator> ScrollAnimator::create(ScrollableArea* scrollableArea ScrollAnimatorMac::ScrollAnimatorMac(ScrollableArea* scrollableArea) : ScrollAnimator(scrollableArea) +#if ENABLE(RUBBER_BANDING) + , m_inScrollGesture(false) + , m_momentumScrollInProgress(false) + , m_ignoreMomentumScrolls(false) + , m_lastMomemtumScrollTimestamp(0) + , m_startTime(0) + , m_snapRubberBandTimer(this, &ScrollAnimatorMac::snapRubberBandTimerFired) +#endif { m_scrollAnimationHelperDelegate.adoptNS([[ScrollAnimationHelperDelegate alloc] initWithScrollAnimator:this]); m_scrollAnimationHelper.adoptNS([[NSClassFromString(@"NSScrollAnimationHelper") alloc] initWithDelegate:m_scrollAnimationHelperDelegate.get()]); + +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + m_scrollbarPainterControllerDelegate.adoptNS([[ScrollbarPainterControllerDelegate alloc] initWithScrollAnimator:this]); + m_scrollbarPainterController = wkMakeScrollbarPainterController(m_scrollbarPainterControllerDelegate.get()); + m_scrollbarPainterDelegate.adoptNS([[ScrollbarPainterDelegate alloc] initWithScrollAnimator:this]); +#endif } ScrollAnimatorMac::~ScrollAnimatorMac() { +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + [m_scrollbarPainterControllerDelegate.get() scrollAnimatorDestroyed]; + [(id)m_scrollbarPainterController.get() setDelegate:nil]; + [m_scrollbarPainterDelegate.get() scrollAnimatorDestroyed]; + [m_scrollAnimationHelperDelegate.get() scrollAnimatorDestroyed]; +#endif } bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranularity granularity, float step, float multiplier) @@ -167,16 +507,567 @@ bool ScrollAnimatorMac::scroll(ScrollbarOrientation orientation, ScrollGranulari void ScrollAnimatorMac::scrollToOffsetWithoutAnimation(const FloatPoint& offset) { [m_scrollAnimationHelper.get() _stopRun]; - ScrollAnimator::scrollToOffsetWithoutAnimation(offset); + immediateScrollToPoint(offset); +} + +float ScrollAnimatorMac::adjustScrollXPositionIfNecessary(float position) const +{ + if (!m_scrollableArea->constrainsScrollingToContentEdge()) + return position; + + return max<float>(min<float>(position, m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0); +} + +float ScrollAnimatorMac::adjustScrollYPositionIfNecessary(float position) const +{ + if (!m_scrollableArea->constrainsScrollingToContentEdge()) + return position; + + return max<float>(min<float>(position, m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0); +} + +FloatPoint ScrollAnimatorMac::adjustScrollPositionIfNecessary(const FloatPoint& position) const +{ + if (!m_scrollableArea->constrainsScrollingToContentEdge()) + return position; + + float newX = max<float>(min<float>(position.x(), m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleWidth()), 0); + float newY = max<float>(min<float>(position.y(), m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleHeight()), 0); + + return FloatPoint(newX, newY); } void ScrollAnimatorMac::immediateScrollToPoint(const FloatPoint& newPosition) { - m_currentPosX = newPosition.x(); - m_currentPosY = newPosition.y(); + FloatPoint adjustedPosition = adjustScrollPositionIfNecessary(newPosition); + + m_currentPosX = adjustedPosition.x(); + m_currentPosY = adjustedPosition.y(); notityPositionChanged(); } +void ScrollAnimatorMac::immediateScrollByDeltaX(float deltaX) +{ + m_currentPosX = adjustScrollXPositionIfNecessary(m_currentPosX + deltaX); + notityPositionChanged(); +} + +void ScrollAnimatorMac::immediateScrollByDeltaY(float deltaY) +{ + m_currentPosY = adjustScrollYPositionIfNecessary(m_currentPosY + deltaY); + notityPositionChanged(); +} + +void ScrollAnimatorMac::notityPositionChanged() +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkContentAreaScrolled(m_scrollbarPainterController.get()); +#endif + ScrollAnimator::notityPositionChanged(); +} + +void ScrollAnimatorMac::contentAreaWillPaint() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkContentAreaWillPaint(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::mouseEnteredContentArea() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkMouseEnteredContentArea(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::mouseExitedContentArea() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkMouseExitedContentArea(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::mouseMovedInContentArea() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkMouseMovedInContentArea(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::willStartLiveResize() +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkWillStartLiveResize(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::contentsResized() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkContentAreaResized(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::willEndLiveResize() +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkWillEndLiveResize(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::contentAreaDidShow() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkContentAreaDidShow(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::contentAreaDidHide() const +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + wkContentAreaDidHide(m_scrollbarPainterController.get()); +#endif +} + +void ScrollAnimatorMac::didAddVerticalScrollbar(Scrollbar* scrollbar) +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar); + wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get()); + wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, false); + if (scrollableArea()->inLiveResize()) + wkSetScrollbarPainterKnobAlpha(painter, 1); +#else + UNUSED_PARAM(scrollbar); +#endif +} + +void ScrollAnimatorMac::willRemoveVerticalScrollbar(Scrollbar* scrollbar) +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar); + wkScrollbarPainterSetDelegate(painter, nil); + wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, false); +#else + UNUSED_PARAM(scrollbar); +#endif +} + +void ScrollAnimatorMac::didAddHorizontalScrollbar(Scrollbar* scrollbar) +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar); + wkScrollbarPainterSetDelegate(painter, m_scrollbarPainterDelegate.get()); + wkSetPainterForPainterController(m_scrollbarPainterController.get(), painter, true); + if (scrollableArea()->inLiveResize()) + wkSetScrollbarPainterKnobAlpha(painter, 1); +#else + UNUSED_PARAM(scrollbar); +#endif +} + +void ScrollAnimatorMac::willRemoveHorizontalScrollbar(Scrollbar* scrollbar) +{ +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + WKScrollbarPainterRef painter = static_cast<WebCore::ScrollbarThemeMac*>(WebCore::ScrollbarTheme::nativeTheme())->painterForScrollbar(scrollbar); + wkScrollbarPainterSetDelegate(painter, nil); + wkSetPainterForPainterController(m_scrollbarPainterController.get(), nil, true); +#else + UNUSED_PARAM(scrollbar); +#endif +} + +#if ENABLE(RUBBER_BANDING) + +static const float scrollVelocityZeroingTimeout = 0.10f; +static const float rubberbandStiffness = 20; +static const float rubberbandDirectionLockStretchRatio = 1; +static const float rubberbandMinimumRequiredDeltaBeforeStretch = 10; +static const float rubberbandAmplitude = 0.31f; +static const float rubberbandPeriod = 1.6f; + +static float elasticDeltaForTimeDelta(float initialPosition, float initialVelocity, float elapsedTime) +{ + float amplitude = rubberbandAmplitude; + float period = rubberbandPeriod; + float criticalDampeningFactor = expf((-elapsedTime * rubberbandStiffness) / period); + + return (initialPosition + (-initialVelocity * elapsedTime * amplitude)) * criticalDampeningFactor; +} + +static float elasticDeltaForReboundDelta(float delta) +{ + float stiffness = std::max(rubberbandStiffness, 1.0f); + return delta / stiffness; +} + +static float reboundDeltaForElasticDelta(float delta) +{ + return delta * rubberbandStiffness; +} + +static float scrollWheelMultiplier() +{ + static float multiplier = -1; + if (multiplier < 0) { + multiplier = [[NSUserDefaults standardUserDefaults] floatForKey:@"NSScrollWheelMultiplier"]; + if (multiplier <= 0) + multiplier = 1; + } + return multiplier; +} + +void ScrollAnimatorMac::handleWheelEvent(PlatformWheelEvent& wheelEvent) +{ + if (!wheelEvent.hasPreciseScrollingDeltas()) { + ScrollAnimator::handleWheelEvent(wheelEvent); + return; + } + + wheelEvent.accept(); + + bool isMometumScrollEvent = (wheelEvent.phase() != PlatformWheelEventPhaseNone); + if (m_ignoreMomentumScrolls && (isMometumScrollEvent || m_snapRubberBandTimer.isActive())) { + if (wheelEvent.phase() == PlatformWheelEventPhaseEnded) + m_ignoreMomentumScrolls = false; + return; + } + + smoothScrollWithEvent(wheelEvent); +} + +void ScrollAnimatorMac::handleGestureEvent(const PlatformGestureEvent& gestureEvent) +{ + if (gestureEvent.type() == PlatformGestureEvent::ScrollBeginType) + beginScrollGesture(); + else + endScrollGesture(); +} + +bool ScrollAnimatorMac::pinnedInDirection(float deltaX, float deltaY) +{ + FloatSize limitDelta; + if (fabsf(deltaY) >= fabsf(deltaX)) { + if (deltaY < 0) { + // We are trying to scroll up. Make sure we are not pinned to the top + limitDelta.setHeight(m_scrollableArea->visibleContentRect().y()); + } else { + // We are trying to scroll down. Make sure we are not pinned to the bottom + limitDelta.setHeight(m_scrollableArea->contentsSize().height() - m_scrollableArea->visibleContentRect().maxY()); + } + } else if (deltaX != 0) { + if (deltaX < 0) { + // We are trying to scroll left. Make sure we are not pinned to the left + limitDelta.setWidth(m_scrollableArea->visibleContentRect().x()); + } else { + // We are trying to scroll right. Make sure we are not pinned to the right + limitDelta.setWidth(m_scrollableArea->contentsSize().width() - m_scrollableArea->visibleContentRect().maxX()); + } + } + + if ((deltaX != 0 || deltaY != 0) && (limitDelta.width() < 1 && limitDelta.height() < 1)) + return true; + return false; +} + +bool ScrollAnimatorMac::allowsVerticalStretching() const +{ + Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); + Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); + if (((vScroller && vScroller->enabled()) || (!hScroller || !hScroller->enabled()))) + return true; + + return false; +} + +bool ScrollAnimatorMac::allowsHorizontalStretching() const +{ + Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); + Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); + if (((hScroller && hScroller->enabled()) || (!vScroller || !vScroller->enabled()))) + return true; + + return false; +} + +void ScrollAnimatorMac::smoothScrollWithEvent(PlatformWheelEvent& wheelEvent) +{ + float deltaX = m_overflowScrollDelta.width(); + float deltaY = m_overflowScrollDelta.height(); + + // Reset overflow values because we may decide to remove delta at various points and put it into overflow. + m_overflowScrollDelta = FloatSize(); + + float eventCoallescedDeltaX = -wheelEvent.deltaX(); + float eventCoallescedDeltaY = -wheelEvent.deltaY(); + + deltaX += eventCoallescedDeltaX; + deltaY += eventCoallescedDeltaY; + + // Slightly prefer scrolling vertically by applying the = case to deltaY + if (fabsf(deltaY) >= fabsf(deltaX)) + deltaX = 0; + else + deltaY = 0; + + bool isVerticallyStretched = false; + bool isHorizontallyStretched = false; + bool shouldStretch = false; + + IntSize stretchAmount = m_scrollableArea->overhangAmount(); + + isHorizontallyStretched = stretchAmount.width(); + isVerticallyStretched = stretchAmount.height(); + + PlatformWheelEventPhase phase = wheelEvent.phase(); + + // If we are starting momentum scrolling then do some setup. + if (!m_momentumScrollInProgress && (phase == PlatformWheelEventPhaseBegan || phase == PlatformWheelEventPhaseChanged)) + m_momentumScrollInProgress = true; + + CFTimeInterval timeDelta = wheelEvent.timestamp() - m_lastMomemtumScrollTimestamp; + if (m_inScrollGesture || m_momentumScrollInProgress) { + if (m_lastMomemtumScrollTimestamp && timeDelta > 0 && timeDelta < scrollVelocityZeroingTimeout) { + m_momentumVelocity.setWidth(eventCoallescedDeltaX / (float)timeDelta); + m_momentumVelocity.setHeight(eventCoallescedDeltaY / (float)timeDelta); + m_lastMomemtumScrollTimestamp = wheelEvent.timestamp(); + } else { + m_lastMomemtumScrollTimestamp = wheelEvent.timestamp(); + m_momentumVelocity = FloatSize(); + } + + if (isVerticallyStretched) { + if (!isHorizontallyStretched && pinnedInDirection(deltaX, 0)) { + // Stretching only in the vertical. + if (deltaY != 0 && (fabsf(deltaX / deltaY) < rubberbandDirectionLockStretchRatio)) + deltaX = 0; + else if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) { + m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX); + deltaX = 0; + } else + m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX); + } + } else if (isHorizontallyStretched) { + // Stretching only in the horizontal. + if (pinnedInDirection(0, deltaY)) { + if (deltaX != 0 && (fabsf(deltaY / deltaX) < rubberbandDirectionLockStretchRatio)) + deltaY = 0; + else if (fabsf(deltaY) < rubberbandMinimumRequiredDeltaBeforeStretch) { + m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY); + deltaY = 0; + } else + m_overflowScrollDelta.setHeight(m_overflowScrollDelta.height() + deltaY); + } + } else { + // Not stretching at all yet. + if (pinnedInDirection(deltaX, deltaY)) { + if (fabsf(deltaY) >= fabsf(deltaX)) { + if (fabsf(deltaX) < rubberbandMinimumRequiredDeltaBeforeStretch) { + m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX); + deltaX = 0; + } else + m_overflowScrollDelta.setWidth(m_overflowScrollDelta.width() + deltaX); + } + shouldStretch = true; + } + } + } + + if (deltaX != 0 || deltaY != 0) { + if (!(shouldStretch || isVerticallyStretched || isHorizontallyStretched)) { + if (deltaY != 0) { + deltaY *= scrollWheelMultiplier(); + immediateScrollByDeltaY(deltaY); + } + if (deltaX != 0) { + deltaX *= scrollWheelMultiplier(); + immediateScrollByDeltaX(deltaX); + } + } else { + if (!allowsHorizontalStretching()) { + deltaX = 0; + eventCoallescedDeltaX = 0; + } else if ((deltaX != 0) && !isHorizontallyStretched && !pinnedInDirection(deltaX, 0)) { + deltaX *= scrollWheelMultiplier(); + + m_scrollableArea->setConstrainsScrollingToContentEdge(false); + immediateScrollByDeltaX(deltaX); + m_scrollableArea->setConstrainsScrollingToContentEdge(true); + + deltaX = 0; + } + + if (!allowsVerticalStretching()) { + deltaY = 0; + eventCoallescedDeltaY = 0; + } else if ((deltaY != 0) && !isVerticallyStretched && !pinnedInDirection(0, deltaY)) { + deltaY *= scrollWheelMultiplier(); + + m_scrollableArea->setConstrainsScrollingToContentEdge(false); + immediateScrollByDeltaY(deltaY); + m_scrollableArea->setConstrainsScrollingToContentEdge(true); + + deltaY = 0; + } + + IntSize stretchAmount = m_scrollableArea->overhangAmount(); + + if (m_momentumScrollInProgress) { + if ((pinnedInDirection(eventCoallescedDeltaX, eventCoallescedDeltaY) || (fabsf(eventCoallescedDeltaX) + fabsf(eventCoallescedDeltaY) <= 0)) && m_lastMomemtumScrollTimestamp) { + m_ignoreMomentumScrolls = true; + m_momentumScrollInProgress = false; + snapRubberBand(); + } + } + + m_stretchScrollForce.setWidth(m_stretchScrollForce.width() + deltaX); + m_stretchScrollForce.setHeight(m_stretchScrollForce.height() + deltaY); + + FloatSize dampedDelta(ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.width())), ceilf(elasticDeltaForReboundDelta(m_stretchScrollForce.height()))); + FloatPoint origOrigin = m_scrollableArea->visibleContentRect().location() - stretchAmount; + FloatPoint newOrigin = origOrigin + dampedDelta; + + if (origOrigin != newOrigin) { + m_scrollableArea->setConstrainsScrollingToContentEdge(false); + immediateScrollToPoint(newOrigin); + m_scrollableArea->setConstrainsScrollingToContentEdge(true); + } + } + } + + if (m_momentumScrollInProgress && phase == PlatformWheelEventPhaseEnded) { + m_momentumScrollInProgress = false; + m_ignoreMomentumScrolls = false; + m_lastMomemtumScrollTimestamp = 0; + } +} + +void ScrollAnimatorMac::beginScrollGesture() +{ + m_inScrollGesture = true; + m_momentumScrollInProgress = false; + m_ignoreMomentumScrolls = false; + m_lastMomemtumScrollTimestamp = 0; + m_momentumVelocity = FloatSize(); + + IntSize stretchAmount = m_scrollableArea->overhangAmount(); + m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(stretchAmount.width())); + m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(stretchAmount.height())); + + m_overflowScrollDelta = FloatSize(); + + if (m_snapRubberBandTimer.isActive()) + m_snapRubberBandTimer.stop(); +} + +void ScrollAnimatorMac::endScrollGesture() +{ + snapRubberBand(); +} + +void ScrollAnimatorMac::snapRubberBand() +{ + CFTimeInterval timeDelta = [[NSProcessInfo processInfo] systemUptime] - m_lastMomemtumScrollTimestamp; + if (m_lastMomemtumScrollTimestamp && timeDelta >= scrollVelocityZeroingTimeout) + m_momentumVelocity = FloatSize(); + + m_inScrollGesture = false; + + if (m_snapRubberBandTimer.isActive()) + return; + + m_startTime = [NSDate timeIntervalSinceReferenceDate]; + m_startStretch = FloatSize(); + m_origOrigin = FloatPoint(); + m_origVelocity = FloatSize(); + + m_snapRubberBandTimer.startRepeating(1.0/60.0); +} + +static inline float roundTowardZero(float num) +{ + return num > 0 ? ceilf(num - 0.5f) : floorf(num + 0.5f); +} + +static inline float roundToDevicePixelTowardZero(float num) +{ + float roundedNum = roundf(num); + if (fabs(num - roundedNum) < 0.125) + num = roundedNum; + + return roundTowardZero(num); +} + +void ScrollAnimatorMac::snapRubberBandTimerFired(Timer<ScrollAnimatorMac>*) +{ + if (!m_momentumScrollInProgress || m_ignoreMomentumScrolls) { + CFTimeInterval timeDelta = [NSDate timeIntervalSinceReferenceDate] - m_startTime; + + if (m_startStretch == FloatSize()) { + m_startStretch = m_scrollableArea->overhangAmount(); + if (m_startStretch == FloatSize()) { + m_snapRubberBandTimer.stop(); + m_stretchScrollForce = FloatSize(); + m_startTime = 0; + m_startStretch = FloatSize(); + m_origOrigin = FloatPoint(); + m_origVelocity = FloatSize(); + + return; + } + + m_origOrigin = m_scrollableArea->visibleContentRect().location() - m_startStretch; + m_origVelocity = m_momentumVelocity; + + // Just like normal scrolling, prefer vertical rubberbanding + if (fabsf(m_origVelocity.height()) >= fabsf(m_origVelocity.width())) + m_origVelocity.setWidth(0); + + // Don't rubber-band horizontally if it's not possible to scroll horizontally + Scrollbar* hScroller = m_scrollableArea->horizontalScrollbar(); + if (!hScroller || !hScroller->enabled()) + m_origVelocity.setWidth(0); + + // Don't rubber-band vertically if it's not possible to scroll horizontally + Scrollbar* vScroller = m_scrollableArea->verticalScrollbar(); + if (!vScroller || !vScroller->enabled()) + m_origVelocity.setHeight(0); + } + + FloatPoint delta(roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.width(), -m_origVelocity.width(), (float)timeDelta)), + roundToDevicePixelTowardZero(elasticDeltaForTimeDelta(m_startStretch.height(), -m_origVelocity.height(), (float)timeDelta))); + + if (fabs(delta.x()) >= 1 || fabs(delta.y()) >= 1) { + FloatPoint newOrigin = m_origOrigin + delta; + + m_scrollableArea->setConstrainsScrollingToContentEdge(false); + immediateScrollToPoint(newOrigin); + m_scrollableArea->setConstrainsScrollingToContentEdge(true); + + FloatSize newStretch = m_scrollableArea->overhangAmount(); + + m_stretchScrollForce.setWidth(reboundDeltaForElasticDelta(newStretch.width())); + m_stretchScrollForce.setHeight(reboundDeltaForElasticDelta(newStretch.height())); + } else { + immediateScrollToPoint(m_origOrigin); + + m_scrollableArea->didCompleteRubberBand(roundedIntSize(m_startStretch)); + + m_snapRubberBandTimer.stop(); + m_stretchScrollForce = FloatSize(); + + m_startTime = 0; + m_startStretch = FloatSize(); + m_origOrigin = FloatPoint(); + m_origVelocity = FloatSize(); + } + } else { + m_startTime = [NSDate timeIntervalSinceReferenceDate]; + m_startStretch = FloatSize(); + } +} +#endif + } // namespace WebCore #endif // ENABLE(SMOOTH_SCROLLING) diff --git a/Source/WebCore/platform/mac/ScrollViewMac.mm b/Source/WebCore/platform/mac/ScrollViewMac.mm index 93ec971..ff2e14e 100644 --- a/Source/WebCore/platform/mac/ScrollViewMac.mm +++ b/Source/WebCore/platform/mac/ScrollViewMac.mm @@ -203,10 +203,10 @@ bool ScrollView::platformIsOffscreen() const return ![platformWidget() window] || ![[platformWidget() window] isVisible]; } -void ScrollView::platformSetScrollOrigin(const IntPoint& origin, bool updatePosition) +void ScrollView::platformSetScrollOrigin(const IntPoint& origin, bool updatePositionAtAll, bool updatePositionSynchronously) { BEGIN_BLOCK_OBJC_EXCEPTIONS; - [scrollView() setScrollOrigin:origin updatePosition:updatePosition]; + [scrollView() setScrollOrigin:origin updatePositionAtAll:updatePositionAtAll immediately:updatePositionSynchronously]; END_BLOCK_OBJC_EXCEPTIONS; } diff --git a/Source/WebCore/platform/mac/ScrollbarThemeMac.h b/Source/WebCore/platform/mac/ScrollbarThemeMac.h index 8b5412d..844a088 100644 --- a/Source/WebCore/platform/mac/ScrollbarThemeMac.h +++ b/Source/WebCore/platform/mac/ScrollbarThemeMac.h @@ -26,7 +26,9 @@ #ifndef ScrollbarThemeMac_h #define ScrollbarThemeMac_h +#include "HeaderDetection.h" #include "ScrollbarThemeComposite.h" +#include "WebCoreSystemInterface.h" namespace WebCore { @@ -50,6 +52,11 @@ public: virtual void registerScrollbar(Scrollbar*); virtual void unregisterScrollbar(Scrollbar*); +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) + void setNewPainterForScrollbar(Scrollbar*, WKScrollbarPainterRef); + WKScrollbarPainterRef painterForScrollbar(Scrollbar*); +#endif + protected: virtual bool hasButtons(Scrollbar*); virtual bool hasThumb(Scrollbar*); diff --git a/Source/WebCore/platform/mac/ScrollbarThemeMac.mm b/Source/WebCore/platform/mac/ScrollbarThemeMac.mm index 032d9f3..c35dfa0 100644 --- a/Source/WebCore/platform/mac/ScrollbarThemeMac.mm +++ b/Source/WebCore/platform/mac/ScrollbarThemeMac.mm @@ -29,17 +29,13 @@ #include "ImageBuffer.h" #include "LocalCurrentGraphicsContext.h" #include "PlatformMouseEvent.h" +#include "ScrollAnimatorMac.h" #include "ScrollView.h" -#include "WebCoreSystemInterface.h" #include <Carbon/Carbon.h> #include <wtf/HashMap.h> #include <wtf/StdLibExtras.h> #include <wtf/UnusedParam.h> -#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) -#define USE_WK_SCROLLBAR_PAINTER -#endif - // FIXME: There are repainting problems due to Aqua scroll bar buttons' visual overflow. using namespace std; @@ -47,7 +43,7 @@ using namespace WebCore; namespace WebCore { -#if defined(USE_WK_SCROLLBAR_PAINTER) +#if USE(WK_SCROLLBAR_PAINTER) typedef HashMap<Scrollbar*, RetainPtr<WKScrollbarPainterRef> > ScrollbarPainterMap; #else typedef HashSet<Scrollbar*> ScrollbarPainterMap; @@ -83,7 +79,7 @@ static ScrollbarPainterMap* scrollbarMap() return; ScrollbarPainterMap::iterator end = scrollbarMap()->end(); for (ScrollbarPainterMap::iterator it = scrollbarMap()->begin(); it != end; ++it) { -#if defined(USE_WK_SCROLLBAR_PAINTER) +#if USE(WK_SCROLLBAR_PAINTER) it->first->styleChanged(); it->first->invalidate(); #else @@ -117,13 +113,15 @@ ScrollbarTheme* ScrollbarTheme::nativeTheme() } // FIXME: Get these numbers from CoreUI. -static int cScrollbarThickness[] = { 15, 11 }; static int cRealButtonLength[] = { 28, 21 }; -static int cButtonInset[] = { 14, 11 }; static int cButtonHitInset[] = { 3, 2 }; // cRealButtonLength - cButtonInset static int cButtonLength[] = { 14, 10 }; +#if !USE(WK_SCROLLBAR_PAINTER) +static int cScrollbarThickness[] = { 15, 11 }; +static int cButtonInset[] = { 14, 11 }; static int cThumbMinLength[] = { 26, 20 }; +#endif static int cOuterButtonLength[] = { 16, 14 }; // The outer button in a double button pair is a bit bigger. static int cOuterButtonOverlap = 2; @@ -131,13 +129,15 @@ static int cOuterButtonOverlap = 2; static float gInitialButtonDelay = 0.5f; static float gAutoscrollButtonDelay = 0.05f; static bool gJumpOnTrackClick = false; + +#if USE(WK_SCROLLBAR_PAINTER) +static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsNone; +#else static ScrollbarButtonsPlacement gButtonPlacement = ScrollbarButtonsDoubleEnd; +#endif static void updateArrowPlacement() { -#if defined(USE_WK_SCROLLBAR_PAINTER) - gButtonPlacement = ScrollbarButtonsNone; -#else NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"]; if ([buttonPlacement isEqualToString:@"Single"]) gButtonPlacement = ScrollbarButtonsSingle; @@ -145,16 +145,20 @@ static void updateArrowPlacement() gButtonPlacement = ScrollbarButtonsDoubleStart; else if ([buttonPlacement isEqualToString:@"DoubleBoth"]) gButtonPlacement = ScrollbarButtonsDoubleBoth; - else - gButtonPlacement = ScrollbarButtonsDoubleEnd; // The default is ScrollbarButtonsDoubleEnd. + else { +#if USE(WK_SCROLLBAR_PAINTER) + gButtonPlacement = ScrollbarButtonsNone; +#else + gButtonPlacement = ScrollbarButtonsDoubleEnd; #endif + } } void ScrollbarThemeMac::registerScrollbar(Scrollbar* scrollbar) { -#if defined(USE_WK_SCROLLBAR_PAINTER) - WKScrollbarPainterRef scrollbarPainter = wkMakeScrollbarPainter(scrollbar->controlSize(), - scrollbar->orientation() == HorizontalScrollbar); +#if USE(WK_SCROLLBAR_PAINTER) + bool isHorizontal = scrollbar->orientation() == HorizontalScrollbar; + WKScrollbarPainterRef scrollbarPainter = wkMakeScrollbarPainter(scrollbar->controlSize(), isHorizontal); scrollbarMap()->add(scrollbar, scrollbarPainter); #else scrollbarMap()->add(scrollbar); @@ -163,9 +167,22 @@ void ScrollbarThemeMac::registerScrollbar(Scrollbar* scrollbar) void ScrollbarThemeMac::unregisterScrollbar(Scrollbar* scrollbar) { + scrollbarMap()->remove(scrollbar); } +#if defined(USE_WK_SCROLLBAR_PAINTER_AND_CONTROLLER) +void ScrollbarThemeMac::setNewPainterForScrollbar(Scrollbar* scrollbar, WKScrollbarPainterRef newPainter) +{ + scrollbarMap()->set(scrollbar, newPainter); +} + +WKScrollbarPainterRef ScrollbarThemeMac::painterForScrollbar(Scrollbar* scrollbar) +{ + return scrollbarMap()->get(scrollbar).get(); +} +#endif + ScrollbarThemeMac::ScrollbarThemeMac() { static bool initialized; @@ -192,13 +209,20 @@ void ScrollbarThemeMac::preferencesChanged() int ScrollbarThemeMac::scrollbarThickness(ScrollbarControlSize controlSize) { +#if USE(WK_SCROLLBAR_PAINTER) + return wkScrollbarThickness(controlSize); +#else return cScrollbarThickness[controlSize]; +#endif } bool ScrollbarThemeMac::usesOverlayScrollbars() const { - // FIXME: This should be enabled when <rdar://problem/8492788> is resolved. +#if USE(WK_SCROLLBAR_PAINTER) + return wkScrollbarPainterUsesOverlayScrollers(); +#else return false; +#endif } double ScrollbarThemeMac::initialAutoscrollTimerDelay() @@ -218,20 +242,29 @@ ScrollbarButtonsPlacement ScrollbarThemeMac::buttonsPlacement() const bool ScrollbarThemeMac::hasButtons(Scrollbar* scrollbar) { - return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ? - scrollbar->width() : - scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]); + return scrollbar->enabled() && gButtonPlacement != ScrollbarButtonsNone + && (scrollbar->orientation() == HorizontalScrollbar + ? scrollbar->width() + : scrollbar->height()) >= 2 * (cRealButtonLength[scrollbar->controlSize()] - cButtonHitInset[scrollbar->controlSize()]); } bool ScrollbarThemeMac::hasThumb(Scrollbar* scrollbar) { + int minLengthForThumb; +#if USE(WK_SCROLLBAR_PAINTER) + minLengthForThumb = wkScrollbarMinimumTotalLengthNeededForThumb(scrollbarMap()->get(scrollbar).get()); +#else + minLengthForThumb = 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1; +#endif return scrollbar->enabled() && (scrollbar->orientation() == HorizontalScrollbar ? scrollbar->width() : - scrollbar->height()) >= 2 * cButtonInset[scrollbar->controlSize()] + cThumbMinLength[scrollbar->controlSize()] + 1; + scrollbar->height()) >= minLengthForThumb; } static IntRect buttonRepaintRect(const IntRect& buttonRect, ScrollbarOrientation orientation, ScrollbarControlSize controlSize, bool start) { + ASSERT(gButtonPlacement != ScrollbarButtonsNone); + IntRect paintRect(buttonRect); if (orientation == HorizontalScrollbar) { paintRect.setWidth(cRealButtonLength[controlSize]); @@ -359,7 +392,11 @@ IntRect ScrollbarThemeMac::trackRect(Scrollbar* scrollbar, bool painting) int ScrollbarThemeMac::minimumThumbLength(Scrollbar* scrollbar) { +#if USE(WK_SCROLLBAR_PAINTER) + return wkScrollbarMinimumThumbLength(scrollbarMap()->get(scrollbar).get()); +#else return cThumbMinLength[scrollbar->controlSize()]; +#endif } bool ScrollbarThemeMac::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt) @@ -391,15 +428,32 @@ static int scrollbarPartToHIPressedState(ScrollbarPart part) bool ScrollbarThemeMac::paint(Scrollbar* scrollbar, GraphicsContext* context, const IntRect& damageRect) { -#if defined(USE_WK_SCROLLBAR_PAINTER) +#if USE(WK_SCROLLBAR_PAINTER) + float value = 0.0f; + float totalSize = 0.0f; + + if (scrollbar->currentPos() < 0) { + // Scrolled past the top. + value = 0.0f; + totalSize = scrollbar->totalSize() - scrollbar->currentPos(); + } else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->totalSize()) { + // Scrolled past the bottom. + value = 1.0f; + totalSize = scrollbar->visibleSize() + scrollbar->currentPos(); + } else { + // Within the bounds of the scrollable area. + value = scrollbar->currentPos() / scrollbar->maximum(); + totalSize = scrollbar->totalSize(); + } + context->save(); context->clip(damageRect); context->translate(scrollbar->frameRect().x(), scrollbar->frameRect().y()); LocalCurrentGraphicsContext localContext(context); wkScrollbarPainterPaint(scrollbarMap()->get(scrollbar).get(), scrollbar->enabled(), - scrollbar->currentPos() / scrollbar->maximum(), - static_cast<CGFloat>(scrollbar->visibleSize()) / scrollbar->totalSize(), + value, + static_cast<CGFloat>(scrollbar->visibleSize()) / totalSize, scrollbar->frameRect()); context->restore(); return true; @@ -409,9 +463,27 @@ bool ScrollbarThemeMac::paint(Scrollbar* scrollbar, GraphicsContext* context, co trackInfo.version = 0; trackInfo.kind = scrollbar->controlSize() == RegularScrollbar ? kThemeMediumScrollBar : kThemeSmallScrollBar; trackInfo.bounds = scrollbar->frameRect(); + + float maximum = 0.0f; + float position = 0.0f; + if (scrollbar->currentPos() < 0) { + // Scrolled past the top. + maximum = (scrollbar->totalSize() - scrollbar->currentPos()) - scrollbar->visibleSize(); + position = 0; + } else if (scrollbar->visibleSize() + scrollbar->currentPos() > scrollbar->totalSize()) { + // Scrolled past the bottom. + maximum = scrollbar->currentPos(); + position = maximum; + } else { + // Within the bounds of the scrollable area. + maximum = scrollbar->maximum(); + position = scrollbar->currentPos(); + } + trackInfo.min = 0; - trackInfo.max = scrollbar->maximum(); - trackInfo.value = scrollbar->currentPos(); + trackInfo.max = static_cast<int>(maximum); + trackInfo.value = static_cast<int>(position); + trackInfo.trackInfo.scrollbar.viewsize = scrollbar->visibleSize(); trackInfo.attributes = 0; if (scrollbar->orientation() == HorizontalScrollbar) diff --git a/Source/WebCore/platform/mac/WebCoreSystemInterface.h b/Source/WebCore/platform/mac/WebCoreSystemInterface.h index 045864a..e6d6cf6 100644 --- a/Source/WebCore/platform/mac/WebCoreSystemInterface.h +++ b/Source/WebCore/platform/mac/WebCoreSystemInterface.h @@ -147,6 +147,8 @@ extern void (*wkSignalCFReadStreamEnd)(CFReadStreamRef stream); extern void (*wkSignalCFReadStreamError)(CFReadStreamRef stream, CFStreamError *error); extern void (*wkSignalCFReadStreamHasBytes)(CFReadStreamRef stream); extern unsigned (*wkInitializeMaximumHTTPConnectionCountPerHost)(unsigned preferredConnectionCount); +extern int (*wkGetHTTPPipeliningPriority)(NSURLRequest *); +extern void (*wkSetHTTPPipeliningPriority)(NSMutableURLRequest *, int priority); extern void (*wkSetCONNECTProxyForStream)(CFReadStreamRef, CFStringRef proxyHost, CFNumberRef proxyPort); extern void (*wkSetCONNECTProxyAuthorizationForStream)(CFReadStreamRef, CFStringRef proxyAuthorizationString); extern CFHTTPMessageRef (*wkCopyCONNECTProxyResponse)(CFReadStreamRef, CFURLRef responseURL); @@ -189,9 +191,50 @@ extern CGImageRef (*wkIOSurfaceContextCreateImage)(CGContextRef context); typedef struct __WKScrollbarPainter *WKScrollbarPainterRef; extern WKScrollbarPainterRef (*wkMakeScrollbarPainter)(int controlSize, bool isHorizontal); +extern WKScrollbarPainterRef (*wkMakeScrollbarReplacementPainter)(WKScrollbarPainterRef oldPainter, int newStyle, int controlSize, bool isHorizontal); +extern void (*wkScrollbarPainterSetDelegate)(WKScrollbarPainterRef, id scrollbarPainterDelegate); extern void (*wkScrollbarPainterPaint)(WKScrollbarPainterRef, bool enabled, double value, CGFloat proportion, CGRect frameRect); +extern int (*wkScrollbarThickness)(int controlSize); +extern int (*wkScrollbarMinimumThumbLength)(WKScrollbarPainterRef); +extern int (*wkScrollbarMinimumTotalLengthNeededForThumb)(WKScrollbarPainterRef); +extern CGFloat (*wkScrollbarPainterKnobAlpha)(WKScrollbarPainterRef); +extern void (*wkSetScrollbarPainterKnobAlpha)(WKScrollbarPainterRef, CGFloat); +extern CGFloat (*wkScrollbarPainterTrackAlpha)(WKScrollbarPainterRef); +extern void (*wkSetScrollbarPainterTrackAlpha)(WKScrollbarPainterRef, CGFloat); +extern bool (*wkScrollbarPainterIsHorizontal)(WKScrollbarPainterRef); +extern void (*wkScrollbarPainterSetOverlayState)(WKScrollbarPainterRef, int overlayScrollerState); + +typedef struct __WKScrollbarPainterController *WKScrollbarPainterControllerRef; +extern WKScrollbarPainterControllerRef (*wkMakeScrollbarPainterController)(id painterControllerDelegate); +extern void (*wkSetPainterForPainterController)(WKScrollbarPainterControllerRef, WKScrollbarPainterRef, bool isHorizontal); +extern WKScrollbarPainterRef (*wkVerticalScrollbarPainterForController)(WKScrollbarPainterControllerRef); +extern WKScrollbarPainterRef (*wkHorizontalScrollbarPainterForController)(WKScrollbarPainterControllerRef); +extern void (*wkSetScrollbarPainterControllerStyle)(WKScrollbarPainterControllerRef, int newStyle); +extern void (*wkContentAreaScrolled)(WKScrollbarPainterControllerRef); +extern void (*wkContentAreaWillPaint)(WKScrollbarPainterControllerRef); +extern void (*wkMouseEnteredContentArea)(WKScrollbarPainterControllerRef); +extern void (*wkMouseExitedContentArea)(WKScrollbarPainterControllerRef); +extern void (*wkMouseMovedInContentArea)(WKScrollbarPainterControllerRef); +extern void (*wkWillStartLiveResize)(WKScrollbarPainterControllerRef); +extern void (*wkContentAreaResized)(WKScrollbarPainterControllerRef); +extern void (*wkWillEndLiveResize)(WKScrollbarPainterControllerRef); +extern void (*wkContentAreaDidShow)(WKScrollbarPainterControllerRef); +extern void (*wkContentAreaDidHide)(WKScrollbarPainterControllerRef); + +extern bool (*wkScrollbarPainterUsesOverlayScrollers)(void); #endif +extern void (*wkUnregisterUniqueIdForElement)(id element); +extern void (*wkAccessibilityHandleFocusChanged)(void); +extern CFTypeID (*wkGetAXTextMarkerTypeID)(void); +extern CFTypeID (*wkGetAXTextMarkerRangeTypeID)(void); +extern CFTypeRef (*wkCreateAXTextMarkerRange)(CFTypeRef start, CFTypeRef end); +extern CFTypeRef (*wkCopyAXTextMarkerRangeStart)(CFTypeRef range); +extern CFTypeRef (*wkCopyAXTextMarkerRangeEnd)(CFTypeRef range); +extern CFTypeRef (*wkCreateAXTextMarker)(const void *bytes, size_t len); +extern BOOL (*wkGetBytesFromAXTextMarker)(CFTypeRef textMarker, void *bytes, size_t length); +extern AXUIElementRef (*wkCreateAXUIElementRef)(id element); + } #endif diff --git a/Source/WebCore/platform/mac/WebCoreSystemInterface.mm b/Source/WebCore/platform/mac/WebCoreSystemInterface.mm index 047827f..24bdcb1 100644 --- a/Source/WebCore/platform/mac/WebCoreSystemInterface.mm +++ b/Source/WebCore/platform/mac/WebCoreSystemInterface.mm @@ -89,6 +89,8 @@ void (*wkSetNSURLConnectionDefersCallbacks)(NSURLConnection *, BOOL); void (*wkSetNSURLRequestShouldContentSniff)(NSMutableURLRequest *, BOOL); id (*wkCreateNSURLConnectionDelegateProxy)(void); unsigned (*wkInitializeMaximumHTTPConnectionCountPerHost)(unsigned preferredConnectionCount); +int (*wkGetHTTPPipeliningPriority)(NSURLRequest *); +void (*wkSetHTTPPipeliningPriority)(NSMutableURLRequest *, int priority); void (*wkSetCONNECTProxyForStream)(CFReadStreamRef, CFStringRef proxyHost, CFNumberRef proxyPort); void (*wkSetCONNECTProxyAuthorizationForStream)(CFReadStreamRef, CFStringRef proxyAuthorizationString); CFHTTPMessageRef (*wkCopyCONNECTProxyResponse)(CFReadStreamRef, CFURLRef responseURL); @@ -127,5 +129,46 @@ CGContextRef (*wkIOSurfaceContextCreate)(IOSurfaceRef surface, unsigned width, u CGImageRef (*wkIOSurfaceContextCreateImage)(CGContextRef context); WKScrollbarPainterRef (*wkMakeScrollbarPainter)(int controlSize, bool isHorizontal); +WKScrollbarPainterRef (*wkMakeScrollbarReplacementPainter)(WKScrollbarPainterRef oldPainter, int newStyle, int controlSize, bool isHorizontal); +void (*wkScrollbarPainterSetDelegate)(WKScrollbarPainterRef, id scrollbarPainterDelegate); void (*wkScrollbarPainterPaint)(WKScrollbarPainterRef, bool enabled, double value, CGFloat proportion, CGRect frameRect); +int (*wkScrollbarThickness)(int controlSize); +int (*wkScrollbarMinimumThumbLength)(WKScrollbarPainterRef); +int (*wkScrollbarMinimumTotalLengthNeededForThumb)(WKScrollbarPainterRef); +CGFloat (*wkScrollbarPainterKnobAlpha)(WKScrollbarPainterRef); +void (*wkSetScrollbarPainterKnobAlpha)(WKScrollbarPainterRef, CGFloat); +CGFloat (*wkScrollbarPainterTrackAlpha)(WKScrollbarPainterRef); +void (*wkSetScrollbarPainterTrackAlpha)(WKScrollbarPainterRef, CGFloat); +bool (*wkScrollbarPainterIsHorizontal)(WKScrollbarPainterRef); +void (*wkScrollbarPainterSetOverlayState)(WKScrollbarPainterRef, int overlayScrollerState); + +WKScrollbarPainterControllerRef (*wkMakeScrollbarPainterController)(id painterControllerDelegate); +void (*wkSetPainterForPainterController)(WKScrollbarPainterControllerRef, WKScrollbarPainterRef, bool isHorizontal); +WKScrollbarPainterRef (*wkVerticalScrollbarPainterForController)(WKScrollbarPainterControllerRef); +WKScrollbarPainterRef (*wkHorizontalScrollbarPainterForController)(WKScrollbarPainterControllerRef); +void (*wkSetScrollbarPainterControllerStyle)(WKScrollbarPainterControllerRef, int newStyle); +void (*wkContentAreaScrolled)(WKScrollbarPainterControllerRef); +void (*wkContentAreaWillPaint)(WKScrollbarPainterControllerRef); +void (*wkMouseEnteredContentArea)(WKScrollbarPainterControllerRef); +void (*wkMouseExitedContentArea)(WKScrollbarPainterControllerRef); +void (*wkMouseMovedInContentArea)(WKScrollbarPainterControllerRef); +void (*wkWillStartLiveResize)(WKScrollbarPainterControllerRef); +void (*wkContentAreaResized)(WKScrollbarPainterControllerRef); +void (*wkWillEndLiveResize)(WKScrollbarPainterControllerRef); +void (*wkContentAreaDidShow)(WKScrollbarPainterControllerRef); +void (*wkContentAreaDidHide)(WKScrollbarPainterControllerRef); + +bool (*wkScrollbarPainterUsesOverlayScrollers)(void); #endif + +void (*wkUnregisterUniqueIdForElement)(id element); +void (*wkAccessibilityHandleFocusChanged)(void); +CFTypeID (*wkGetAXTextMarkerTypeID)(void); +CFTypeID (*wkGetAXTextMarkerRangeTypeID)(void); +CFTypeRef (*wkCreateAXTextMarkerRange)(CFTypeRef start, CFTypeRef end); +CFTypeRef (*wkCopyAXTextMarkerRangeStart)(CFTypeRef range); +CFTypeRef (*wkCopyAXTextMarkerRangeEnd)(CFTypeRef range); +CFTypeRef (*wkCreateAXTextMarker)(const void *bytes, size_t len); +BOOL (*wkGetBytesFromAXTextMarker)(CFTypeRef textMarker, void *bytes, size_t length); +AXUIElementRef (*wkCreateAXUIElementRef)(id element); + diff --git a/Source/WebCore/platform/mac/WheelEventMac.mm b/Source/WebCore/platform/mac/WheelEventMac.mm index d4fc698..74265d1 100644 --- a/Source/WebCore/platform/mac/WheelEventMac.mm +++ b/Source/WebCore/platform/mac/WheelEventMac.mm @@ -64,6 +64,7 @@ PlatformWheelEvent::PlatformWheelEvent(NSEvent* event, NSView *windowView) , m_altKey([event modifierFlags] & NSAlternateKeyMask) , m_metaKey([event modifierFlags] & NSCommandKeyMask) , m_phase(phaseForEvent(event)) + , m_timestamp([event timestamp]) { BOOL continuous; @@ -71,11 +72,13 @@ PlatformWheelEvent::PlatformWheelEvent(NSEvent* event, NSView *windowView) if (continuous) { m_wheelTicksX = m_deltaX / static_cast<float>(Scrollbar::pixelsPerLineStep()); m_wheelTicksY = m_deltaY / static_cast<float>(Scrollbar::pixelsPerLineStep()); + m_hasPreciseScrollingDeltas = true; } else { m_wheelTicksX = m_deltaX; m_wheelTicksY = m_deltaY; m_deltaX *= static_cast<float>(Scrollbar::pixelsPerLineStep()); m_deltaY *= static_cast<float>(Scrollbar::pixelsPerLineStep()); + m_hasPreciseScrollingDeltas = false; } } diff --git a/Source/WebCore/platform/mac/WidgetMac.mm b/Source/WebCore/platform/mac/WidgetMac.mm index f3c951a..96bcde2 100644 --- a/Source/WebCore/platform/mac/WidgetMac.mm +++ b/Source/WebCore/platform/mac/WidgetMac.mm @@ -252,7 +252,7 @@ void Widget::paint(GraphicsContext* p, const IntRect& r) IntRect dirtyRect = r; dirtyRect.move(-transformOrigin.x(), -transformOrigin.y()); if (![view isFlipped]) - dirtyRect.setY([view bounds].size.height - dirtyRect.bottom()); + dirtyRect.setY([view bounds].size.height - dirtyRect.maxY()); [view displayRectIgnoringOpacity:dirtyRect]; @@ -296,7 +296,7 @@ void Widget::paint(GraphicsContext* p, const IntRect& r) IntRect dirtyRect = r; dirtyRect.move(-transformOrigin.x(), -transformOrigin.y()); if (![view isFlipped]) - dirtyRect.setY([view bounds].size.height - dirtyRect.bottom()); + dirtyRect.setY([view bounds].size.height - dirtyRect.maxY()); BEGIN_BLOCK_OBJC_EXCEPTIONS; { diff --git a/Source/WebCore/platform/network/BlobRegistryImpl.cpp b/Source/WebCore/platform/network/BlobRegistryImpl.cpp index 2c4e8fa..83517f1 100644 --- a/Source/WebCore/platform/network/BlobRegistryImpl.cpp +++ b/Source/WebCore/platform/network/BlobRegistryImpl.cpp @@ -68,7 +68,9 @@ PassRefPtr<ResourceHandle> BlobRegistryImpl::createResourceHandle(const Resource if (!shouldLoadResource(request)) return 0; - return BlobResourceHandle::create(m_blobs.get(request.url().string()), request, client); + RefPtr<BlobResourceHandle> handle = BlobResourceHandle::create(m_blobs.get(request.url().string()), request, client); + handle->start(); + return handle.release(); } bool BlobRegistryImpl::loadResourceSynchronously(const ResourceRequest& request, ResourceError& error, ResourceResponse& response, Vector<char>& data) diff --git a/Source/WebCore/platform/network/BlobResourceHandle.cpp b/Source/WebCore/platform/network/BlobResourceHandle.cpp index 753052a..24c9088 100644 --- a/Source/WebCore/platform/network/BlobResourceHandle.cpp +++ b/Source/WebCore/platform/network/BlobResourceHandle.cpp @@ -138,11 +138,6 @@ void BlobResourceHandle::loadResourceSynchronously(PassRefPtr<BlobStorageData> b handle->start(); } -static void delayedStart(void* context) -{ - static_cast<BlobResourceHandle*>(context)->start(); -} - BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, const ResourceRequest& request, ResourceHandleClient* client, bool async) : ResourceHandle(request, client, false, false) , m_blobData(blobData) @@ -158,11 +153,9 @@ BlobResourceHandle::BlobResourceHandle(PassRefPtr<BlobStorageData> blobData, con , m_readItemCount(0) , m_fileOpened(false) { - if (m_async) { - // We need to take a ref. + if (m_async) m_asyncStream = client->createAsyncFileStream(this); - callOnMainThread(delayedStart, this); - } else + else m_stream = FileStream::create(); } @@ -187,10 +180,32 @@ void BlobResourceHandle::cancel() } m_aborted = true; + + ResourceHandle::cancel(); +} + +void delayedStartBlobResourceHandle(void* context) +{ + RefPtr<BlobResourceHandle> handle = adoptRef(static_cast<BlobResourceHandle*>(context)); + handle->doStart(); } void BlobResourceHandle::start() { + if (m_async) { + // Keep BlobResourceHandle alive until delayedStartBlobResourceHandle runs. + ref(); + + // Finish this async call quickly and return. + callOnMainThread(delayedStartBlobResourceHandle, this); + return; + } + + doStart(); +} + +void BlobResourceHandle::doStart() +{ // Do not continue if the request is aborted or an error occurs. if (m_aborted || m_errorCode) return; @@ -578,10 +593,23 @@ void BlobResourceHandle::notifyFail(int errorCode) client()->didFail(this, ResourceError(String(), errorCode, firstRequest().url(), String())); } +static void doNotifyFinish(void* context) +{ + BlobResourceHandle* handle = static_cast<BlobResourceHandle*>(context); + if (handle->client()) + handle->client()->didFinishLoading(handle, 0); +} + void BlobResourceHandle::notifyFinish() { - if (client()) - client()->didFinishLoading(this, 0); + if (m_async) { + // Schedule to notify the client from a standalone function because the client might dispose the handle immediately from the callback function + // while we still have BlobResourceHandle calls in the stack. + callOnMainThread(doNotifyFinish, this); + return; + } + + doNotifyFinish(this); } } // namespace WebCore diff --git a/Source/WebCore/platform/network/BlobResourceHandle.h b/Source/WebCore/platform/network/BlobResourceHandle.h index 63e8578..1e9e94a 100644 --- a/Source/WebCore/platform/network/BlobResourceHandle.h +++ b/Source/WebCore/platform/network/BlobResourceHandle.h @@ -69,9 +69,12 @@ public: int readSync(char*, int); private: + friend void delayedStartBlobResourceHandle(void*); + BlobResourceHandle(PassRefPtr<BlobStorageData>, const ResourceRequest&, ResourceHandleClient*, bool async); virtual ~BlobResourceHandle(); + void doStart(); void getSizeForNext(); void seek(); void consumeData(const char* data, int bytesRead); diff --git a/Source/WebCore/platform/network/FormData.cpp b/Source/WebCore/platform/network/FormData.cpp index 16f98ad..9784b7f 100644 --- a/Source/WebCore/platform/network/FormData.cpp +++ b/Source/WebCore/platform/network/FormData.cpp @@ -209,14 +209,16 @@ void FormData::appendKeyValuePairItems(const FormDataList& list, const TextEncod name = file->webkitRelativePath().isEmpty() ? file->name() : file->webkitRelativePath(); #else name = file->name(); -#endif - +#endif // Let the application specify a filename if it's going to generate a replacement file for the upload. - if (Page* page = document->page()) { - String generatedFileName; - shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(file->path(), generatedFileName); - if (shouldGenerateFile) - name = generatedFileName; + const String& path = file->path(); + if (!path.isEmpty()) { + if (Page* page = document->page()) { + String generatedFileName; + shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName); + if (shouldGenerateFile) + name = generatedFileName; + } } } else { // For non-file blob, use the identifier part of the URL as the name. @@ -364,7 +366,9 @@ static void encode(Encoder& encoder, const FormDataElement& element) static bool decode(Decoder& decoder, FormDataElement& element) { - uint32_t type = element.m_type; + uint32_t type; + if (!decoder.decodeUInt32(type)) + return false; switch (type) { case FormDataElement::data: { @@ -432,7 +436,7 @@ void FormData::encodeForBackForward(Encoder& encoder) const encoder.encodeBool(m_hasGeneratedFiles); - encoder.encodeBool(m_identifier); + encoder.encodeInt64(m_identifier); } PassRefPtr<FormData> FormData::decodeForBackForward(Decoder& decoder) diff --git a/Source/WebCore/platform/network/FormDataBuilder.cpp b/Source/WebCore/platform/network/FormDataBuilder.cpp index da28fc2..e973f99 100644 --- a/Source/WebCore/platform/network/FormDataBuilder.cpp +++ b/Source/WebCore/platform/network/FormDataBuilder.cpp @@ -94,10 +94,7 @@ TextEncoding FormDataBuilder::encodingFromAcceptCharset(const String& acceptChar return encoding; } - if (Frame* frame = document->frame()) - return frame->loader()->writer()->encoding(); - - return Latin1Encoding(); + return document->inputEncoding(); } Vector<char> FormDataBuilder::generateUniqueBoundaryString() diff --git a/Source/WebCore/platform/network/ProtectionSpaceHash.h b/Source/WebCore/platform/network/ProtectionSpaceHash.h index 08716b5..9934321 100644 --- a/Source/WebCore/platform/network/ProtectionSpaceHash.h +++ b/Source/WebCore/platform/network/ProtectionSpaceHash.h @@ -42,11 +42,11 @@ struct ProtectionSpaceHash { protectionSpace.realm().impl() ? protectionSpace.realm().impl()->hash() : 0 }; - unsigned codeCount = sizeof(hashCodes) / sizeof(UChar); + unsigned codeCount = sizeof(hashCodes); // Ignore realm for proxies. if (protectionSpace.isProxy()) - codeCount -= sizeof(hashCodes[0]) / sizeof(UChar); - return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), codeCount); + codeCount -= sizeof(hashCodes[0]); + return WTF::StringHasher::createBlobHash(hashCodes, codeCount); } static bool equal(const ProtectionSpace& a, const ProtectionSpace& b) { return a == b; } diff --git a/Source/WebCore/platform/network/ResourceHandle.h b/Source/WebCore/platform/network/ResourceHandle.h index bb94b59..c2a0b8e 100644 --- a/Source/WebCore/platform/network/ResourceHandle.h +++ b/Source/WebCore/platform/network/ResourceHandle.h @@ -41,7 +41,7 @@ typedef struct _SoupSession SoupSession; typedef const struct __CFData * CFDataRef; #endif -#if PLATFORM(WIN) +#if USE(WININET) typedef unsigned long DWORD; typedef unsigned long DWORD_PTR; typedef void* LPVOID; @@ -173,7 +173,7 @@ public: bool hasAuthenticationChallenge() const; void clearAuthentication(); - void cancel(); + virtual void cancel(); // The client may be 0, in which case no callbacks will be made. ResourceHandleClient* client() const; diff --git a/Source/WebCore/platform/network/ResourceHandleInternal.h b/Source/WebCore/platform/network/ResourceHandleInternal.h index ed66944..5512062 100644 --- a/Source/WebCore/platform/network/ResourceHandleInternal.h +++ b/Source/WebCore/platform/network/ResourceHandleInternal.h @@ -46,8 +46,9 @@ #endif #if USE(SOUP) -#include "soup-requester.h" #include <GRefPtr.h> +#define LIBSOUP_USE_UNSTABLE_REQUEST_API +#include <libsoup/soup-request.h> #include <libsoup/soup.h> class Frame; #endif @@ -114,6 +115,8 @@ namespace WebCore { , m_cancelled(false) , m_buffer(0) , m_total(0) + , m_bodySize(0) + , m_bodyDataSent(0) , m_idleHandler(0) , m_gotChunkHandler(0) #endif @@ -132,9 +135,6 @@ namespace WebCore { m_user = url.user(); m_pass = url.pass(); m_firstRequest.removeCredentials(); -#if USE(SOUP) - m_requester = adoptGRef(webkit_soup_requester_new()); -#endif } ~ResourceHandleInternal(); @@ -190,12 +190,13 @@ namespace WebCore { GRefPtr<SoupMessage> m_soupMessage; ResourceResponse m_response; bool m_cancelled; - GRefPtr<WebKitSoupRequest> m_soupRequest; - GRefPtr<WebKitSoupRequester> m_requester; + GRefPtr<SoupRequest> m_soupRequest; GRefPtr<GInputStream> m_inputStream; GRefPtr<GCancellable> m_cancellable; char* m_buffer; gsize m_total; + unsigned long m_bodySize; + unsigned long m_bodyDataSent; guint m_idleHandler; RefPtr<NetworkingContext> m_context; gulong m_gotChunkHandler; diff --git a/Source/WebCore/platform/network/ResourceRequestBase.cpp b/Source/WebCore/platform/network/ResourceRequestBase.cpp index ae8316a..ba58461 100644 --- a/Source/WebCore/platform/network/ResourceRequestBase.cpp +++ b/Source/WebCore/platform/network/ResourceRequestBase.cpp @@ -45,6 +45,7 @@ PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadRes request->setTimeoutInterval(data->m_timeoutInterval); request->setFirstPartyForCookies(data->m_firstPartyForCookies); request->setHTTPMethod(data->m_httpMethod); + request->setPriority(data->m_priority); request->setTargetType(data->m_targetType); request->updateResourceRequest(); @@ -78,6 +79,7 @@ PassOwnPtr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const data->m_firstPartyForCookies = firstPartyForCookies().copy(); data->m_httpMethod = httpMethod().crossThreadString(); data->m_httpHeaders = httpHeaderFields().copyData(); + data->m_priority = priority(); data->m_targetType = m_targetType; data->m_responseContentDispositionEncodingFallbackArray.reserveInitialCapacity(m_responseContentDispositionEncodingFallbackArray.size()); @@ -314,6 +316,23 @@ void ResourceRequestBase::setAllowCookies(bool allowCookies) m_platformRequestUpdated = false; } +ResourceLoadPriority ResourceRequestBase::priority() const +{ + updateResourceRequest(); + + return m_priority; +} + +void ResourceRequestBase::setPriority(ResourceLoadPriority priority) +{ + updateResourceRequest(); + + m_priority = priority; + + if (url().protocolInHTTPFamily()) + m_platformRequestUpdated = false; +} + void ResourceRequestBase::addHTTPHeaderField(const AtomicString& name, const String& value) { updateResourceRequest(); @@ -352,6 +371,9 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceReque if (a.allowCookies() != b.allowCookies()) return false; + if (a.priority() != b.priority()) + return false; + FormData* formDataA = a.httpBody(); FormData* formDataB = b.httpBody(); diff --git a/Source/WebCore/platform/network/ResourceRequestBase.h b/Source/WebCore/platform/network/ResourceRequestBase.h index dce33db..9cc9148 100644 --- a/Source/WebCore/platform/network/ResourceRequestBase.h +++ b/Source/WebCore/platform/network/ResourceRequestBase.h @@ -29,8 +29,9 @@ #define ResourceRequestBase_h #include "FormData.h" -#include "KURL.h" #include "HTTPHeaderMap.h" +#include "KURL.h" +#include "ResourceLoadPriority.h" #include <wtf/OwnPtr.h> @@ -128,6 +129,9 @@ namespace WebCore { bool allowCookies() const; void setAllowCookies(bool allowCookies); + ResourceLoadPriority priority() const; + void setPriority(ResourceLoadPriority); + bool isConditional() const; // Whether the associated ResourceHandleClient needs to be notified of @@ -157,6 +161,7 @@ namespace WebCore { , m_reportUploadProgress(false) , m_reportLoadTiming(false) , m_reportRawHeaders(false) + , m_priority(ResourceLoadPriorityLow) , m_targetType(TargetIsSubresource) { } @@ -172,6 +177,7 @@ namespace WebCore { , m_reportUploadProgress(false) , m_reportLoadTiming(false) , m_reportRawHeaders(false) + , m_priority(ResourceLoadPriorityLow) , m_targetType(TargetIsSubresource) { } @@ -197,6 +203,7 @@ namespace WebCore { bool m_reportUploadProgress; bool m_reportLoadTiming; bool m_reportRawHeaders; + ResourceLoadPriority m_priority; TargetType m_targetType; private: @@ -223,11 +230,20 @@ namespace WebCore { Vector<String> m_responseContentDispositionEncodingFallbackArray; RefPtr<FormData> m_httpBody; bool m_allowCookies; + ResourceLoadPriority m_priority; ResourceRequestBase::TargetType m_targetType; }; unsigned initializeMaximumHTTPConnectionCountPerHost(); +#if PLATFORM(CF) + bool isHTTPPipeliningEnabled(); + bool shouldForceHTTPPipeliningPriorityHigh(); +#else + inline bool isHTTPPipeliningEnabled() { return false; } + inline bool shouldForceHTTPPipeliningPriorityHigh() { return false; } +#endif + } // namespace WebCore #endif // ResourceRequestBase_h diff --git a/Source/WebCore/platform/network/cf/DNSCFNet.cpp b/Source/WebCore/platform/network/cf/DNSCFNet.cpp index 166abbf..b6f9922 100644 --- a/Source/WebCore/platform/network/cf/DNSCFNet.cpp +++ b/Source/WebCore/platform/network/cf/DNSCFNet.cpp @@ -36,6 +36,7 @@ #if PLATFORM(WIN) #include "LoaderRunLoopCF.h" +#include <CFNetwork/CFNetwork.h> #endif #if defined(BUILDING_ON_LEOPARD) diff --git a/Source/WebCore/platform/network/cf/DownloadBundle.h b/Source/WebCore/platform/network/cf/DownloadBundle.h new file mode 100644 index 0000000..cf90908 --- /dev/null +++ b/Source/WebCore/platform/network/cf/DownloadBundle.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011 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 DownloadBundle_h +#define DownloadBundle_h + +#include <wtf/Forward.h> + +typedef const struct __CFData* CFDataRef; + +namespace WebCore { +namespace DownloadBundle { + +bool appendResumeData(CFDataRef resumeData, const String& bundlePath); +CFDataRef extractResumeData(const String& bundlePath); +const String& fileExtension(); + +} // namespace DownloadBundle +} // namespace WebCore + +#endif // DownloadBundle_h diff --git a/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp index eb0ec3a..8bc8f08 100644 --- a/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp +++ b/Source/WebCore/platform/network/cf/FormDataStreamCFNet.cpp @@ -145,17 +145,8 @@ static void advanceCurrentStream(FormStreamFields *form) char* data = nextInput.m_data.releaseBuffer(); form->currentStream = CFReadStreamCreateWithBytesNoCopy(0, reinterpret_cast<const UInt8*>(data), size, kCFAllocatorNull); form->currentData = data; - } else { - CFStringRef filename = nextInput.m_filename.createCFString(); -#if PLATFORM(WIN) - CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLWindowsPathStyle, FALSE); -#else - CFURLRef fileURL = CFURLCreateWithFileSystemPath(0, filename, kCFURLPOSIXPathStyle, FALSE); -#endif - CFRelease(filename); - form->currentStream = CFReadStreamCreateWithFile(0, fileURL); - CFRelease(fileURL); - } + } else + form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(nextInput.m_filename).get()); form->remainingElements.removeLast(); // Set up the callback. diff --git a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp index 249fe43..a7cc639 100644 --- a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp +++ b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.cpp @@ -28,6 +28,7 @@ #if USE(CFNETWORK) +#include <CoreFoundation/CoreFoundation.h> #include <wtf/Threading.h> namespace WebCore { diff --git a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h index f66128c..e0d3ba4 100644 --- a/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h +++ b/Source/WebCore/platform/network/cf/LoaderRunLoopCF.h @@ -32,6 +32,8 @@ #error This code is not needed on platforms other than Windows, because main thread's CFRunLoop can be used. #endif +typedef struct __CFRunLoop* CFRunLoopRef; + namespace WebCore { CFRunLoopRef loaderRunLoop(); diff --git a/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp b/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp index 3bef808..57d714b 100644 --- a/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp +++ b/Source/WebCore/platform/network/cf/ProxyServerCFNet.cpp @@ -29,6 +29,10 @@ #include "KURL.h" #include <wtf/RetainPtr.h> +#if PLATFORM(WIN) +#include <CFNetwork/CFNetwork.h> +#endif + namespace WebCore { #if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) diff --git a/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp index 66ae5a0..52b100f 100644 --- a/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp +++ b/Source/WebCore/platform/network/cf/ResourceHandleCFNet.cpp @@ -126,12 +126,7 @@ static void setDefaultMIMEType(CFURLResponseRef response) static String encodeBasicAuthorization(const String& user, const String& password) { - CString unencodedString = (user + ":" + password).utf8(); - Vector<char> unencoded(unencodedString.length()); - std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); - Vector<char> encoded; - base64Encode(unencoded, encoded); - return String(encoded.data(), encoded.size()); + return base64Encode((user + ":" + password).utf8()); } CFURLRequestRef willSendRequest(CFURLConnectionRef conn, CFURLRequestRef cfRequest, CFURLResponseRef cfRedirectResponse, const void* clientInfo) diff --git a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp index 410a649..7a1dfd5 100644 --- a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp +++ b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.cpp @@ -26,16 +26,22 @@ #include "config.h" #include "ResourceRequestCFNet.h" -#if USE(CFNETWORK) - -#include "FormDataStreamCFNet.h" #include "ResourceRequest.h" +#if PLATFORM(MAC) +#include "WebCoreSystemInterface.h" +#endif + +#if USE(CFNETWORK) +#include "FormDataStreamCFNet.h" #include <CFNetwork/CFURLRequestPriv.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> +#endif namespace WebCore { +#if USE(CFNETWORK) + typedef void (*CFURLRequestSetContentDispositionEncodingFallbackArrayFunction)(CFMutableURLRequestRef, CFArrayRef); typedef CFArrayRef (*CFURLRequestCopyContentDispositionEncodingFallbackArrayFunction)(CFURLRequestRef); @@ -189,12 +195,43 @@ void ResourceRequest::doUpdateResourceRequest() m_httpBody = httpBodyFromRequest(m_cfRequest.get()); } +#endif // USE(CFNETWORK) + unsigned initializeMaximumHTTPConnectionCountPerHost() { static const unsigned preferredConnectionCount = 6; - return wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); + static const unsigned unlimitedConnectionCount = 10000; + + // Always set the connection count per host, even when pipelining. + unsigned maximumHTTPConnectionCountPerHost = wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); + +#if PLATFORM(MAC) + if (isHTTPPipeliningEnabled()) { + // When pipelining do not rate-limit requests sent from WebCore since CFNetwork handles that. + return unlimitedConnectionCount; + } +#endif + + return maximumHTTPConnectionCountPerHost; } -} // namespace WebCore +static inline bool readBooleanPreference(CFStringRef key) +{ + Boolean keyExistsAndHasValidFormat; + Boolean result = CFPreferencesGetAppBooleanValue(key, kCFPreferencesCurrentApplication, &keyExistsAndHasValidFormat); + return keyExistsAndHasValidFormat ? result : false; +} -#endif // USE(CFNETWORK) +bool isHTTPPipeliningEnabled() +{ + static bool isEnabled = readBooleanPreference(CFSTR("WebKitEnableHTTPPipelining")); + return isEnabled; +} + +bool shouldForceHTTPPipeliningPriorityHigh() +{ + static bool shouldForcePriorityHigh = readBooleanPreference(CFSTR("WebKitForceHTTPPipeliningPriorityHigh")); + return shouldForcePriorityHigh; +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h index 39587a4..09f4cea 100644 --- a/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h +++ b/Source/WebCore/platform/network/cf/ResourceRequestCFNet.h @@ -26,18 +26,55 @@ #ifndef ResourceRequestCFNet_h #define ResourceRequestCFNet_h -#if USE(CFNETWORK) +#include "ResourceLoadPriority.h" +#if USE(CFNETWORK) typedef const struct _CFURLRequest* CFURLRequestRef; +#endif namespace WebCore { - class ResourceRequest; +class ResourceRequest; + +#if USE(CFNETWORK) +void getResourceRequest(ResourceRequest&, CFURLRequestRef); +CFURLRequestRef cfURLRequest(const ResourceRequest&); +#endif + +inline ResourceLoadPriority mapHTTPPipeliningPriorityToResourceLoadPriority(int priority) +{ + switch (priority) { + case 0: + return ResourceLoadPriorityLow; + case 1: + return ResourceLoadPriorityMedium; + case 2: + return ResourceLoadPriorityHigh; + default: + ASSERT_NOT_REACHED(); + return ResourceLoadPriorityLowest; + } +} + +inline int mapResourceLoadPriorityToHTTPPipeliningPriority(ResourceLoadPriority priority) +{ + switch (priority) { + case ResourceLoadPriorityVeryLow: + case ResourceLoadPriorityLow: + return 0; + case ResourceLoadPriorityMedium: + return 1; + case ResourceLoadPriorityHigh: + return 2; + case ResourceLoadPriorityUnresolved: + ASSERT_NOT_REACHED(); + return 0; + } - void getResourceRequest(ResourceRequest&, CFURLRequestRef); - CFURLRequestRef cfURLRequest(const ResourceRequest&); + ASSERT_NOT_REACHED(); + return 0; } -#endif // USE(CFNETWORK) +} // namespace WebCore #endif // ResourceRequestCFNet_h diff --git a/Source/WebCore/platform/network/cf/SocketStreamHandle.h b/Source/WebCore/platform/network/cf/SocketStreamHandle.h index df1d4a3..fbda3bc 100644 --- a/Source/WebCore/platform/network/cf/SocketStreamHandle.h +++ b/Source/WebCore/platform/network/cf/SocketStreamHandle.h @@ -36,6 +36,7 @@ #include "SocketStreamHandleBase.h" #include <wtf/RetainPtr.h> +typedef struct __CFHTTPMessage* CFHTTPMessageRef; namespace WebCore { diff --git a/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp index 821b1ca..06454a7 100644 --- a/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp +++ b/Source/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp @@ -47,6 +47,7 @@ #if PLATFORM(WIN) #include "LoaderRunLoopCF.h" +#include <CFNetwork/CFNetwork.h> #include <WebKitSystemInterface/WebKitSystemInterface.h> #else #include "WebCoreSystemInterface.h" diff --git a/Source/WebCore/platform/network/chromium/ResourceRequest.cpp b/Source/WebCore/platform/network/chromium/ResourceRequest.cpp index 69591c1..519c63f 100644 --- a/Source/WebCore/platform/network/chromium/ResourceRequest.cpp +++ b/Source/WebCore/platform/network/chromium/ResourceRequest.cpp @@ -43,6 +43,7 @@ PassOwnPtr<CrossThreadResourceRequestData> ResourceRequest::doPlatformCopyData(P data->m_requestorProcessID = m_requestorProcessID; data->m_appCacheHostID = m_appCacheHostID; data->m_hasUserGesture = m_hasUserGesture; + data->m_downloadToFile = m_downloadToFile; return data; } @@ -52,6 +53,7 @@ void ResourceRequest::doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData> m_requestorProcessID = data->m_requestorProcessID; m_appCacheHostID = data->m_appCacheHostID; m_hasUserGesture = data->m_hasUserGesture; + m_downloadToFile = data->m_downloadToFile; } } // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceRequest.h b/Source/WebCore/platform/network/chromium/ResourceRequest.h index 41ad6e0..07b31eb 100644 --- a/Source/WebCore/platform/network/chromium/ResourceRequest.h +++ b/Source/WebCore/platform/network/chromium/ResourceRequest.h @@ -42,6 +42,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { } @@ -51,6 +52,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { } @@ -60,6 +62,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { setHTTPReferrer(referrer); } @@ -70,6 +73,7 @@ namespace WebCore { , m_requestorProcessID(0) , m_appCacheHostID(0) , m_hasUserGesture(false) + , m_downloadToFile(false) { } @@ -92,6 +96,10 @@ namespace WebCore { bool hasUserGesture() const { return m_hasUserGesture; } void setHasUserGesture(bool hasUserGesture) { m_hasUserGesture = hasUserGesture; } + // True if request should be downloaded to file. + bool downloadToFile() const { return m_downloadToFile; } + void setDownloadToFile(bool downloadToFile) { m_downloadToFile = downloadToFile; } + private: friend class ResourceRequestBase; @@ -105,6 +113,7 @@ namespace WebCore { int m_requestorProcessID; int m_appCacheHostID; bool m_hasUserGesture; + bool m_downloadToFile; }; struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { @@ -112,6 +121,7 @@ namespace WebCore { int m_requestorProcessID; int m_appCacheHostID; bool m_hasUserGesture; + bool m_downloadToFile; }; } // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceResponse.cpp b/Source/WebCore/platform/network/chromium/ResourceResponse.cpp index acd44d3..fc8ac62 100644 --- a/Source/WebCore/platform/network/chromium/ResourceResponse.cpp +++ b/Source/WebCore/platform/network/chromium/ResourceResponse.cpp @@ -39,6 +39,8 @@ PassOwnPtr<CrossThreadResourceResponseData> ResourceResponse::doPlatformCopyData data->m_wasAlternateProtocolAvailable = m_wasAlternateProtocolAvailable; data->m_wasFetchedViaProxy = m_wasFetchedViaProxy; data->m_responseTime = m_responseTime; + data->m_socketAddress = m_socketAddress; + data->m_downloadFilePath = m_downloadFilePath; return data; } @@ -53,6 +55,8 @@ void ResourceResponse::doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseDat m_wasAlternateProtocolAvailable = data->m_wasAlternateProtocolAvailable; m_wasFetchedViaProxy = data->m_wasFetchedViaProxy; m_responseTime = data->m_responseTime; + m_socketAddress = data->m_socketAddress; + m_downloadFilePath = data->m_downloadFilePath; } } // namespace WebCore diff --git a/Source/WebCore/platform/network/chromium/ResourceResponse.h b/Source/WebCore/platform/network/chromium/ResourceResponse.h index 5e99994..35f13d1 100644 --- a/Source/WebCore/platform/network/chromium/ResourceResponse.h +++ b/Source/WebCore/platform/network/chromium/ResourceResponse.h @@ -96,6 +96,12 @@ namespace WebCore { double responseTime() const { return m_responseTime; } void setResponseTime(double responseTime) { m_responseTime = responseTime; } + const String& socketAddress() const { return m_socketAddress; } + void setSocketAddress(const String& value) { m_socketAddress = value; } + + const String& downloadFilePath() const { return m_downloadFilePath; } + void setDownloadFilePath(const String& downloadFilePath) { m_downloadFilePath = downloadFilePath; } + private: friend class ResourceResponseBase; @@ -143,6 +149,13 @@ namespace WebCore { // The time at which the response headers were received. For cached // responses, this time could be "far" in the past. double m_responseTime; + + // Remote address of the socket which fetched this resource, for presenting + // to inquisitive users. Can be "ipv4:port", "[ipv6]:port", or empty. + String m_socketAddress; + + // The path to the downloaded file. + String m_downloadFilePath; }; struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { @@ -155,6 +168,8 @@ namespace WebCore { bool m_wasAlternateProtocolAvailable; bool m_wasFetchedViaProxy; double m_responseTime; + String m_socketAddress; + String m_downloadFilePath; }; } // namespace WebCore diff --git a/Source/WebCore/platform/network/mac/AuthenticationMac.mm b/Source/WebCore/platform/network/mac/AuthenticationMac.mm index efa42d9..a187187 100644 --- a/Source/WebCore/platform/network/mac/AuthenticationMac.mm +++ b/Source/WebCore/platform/network/mac/AuthenticationMac.mm @@ -31,6 +31,7 @@ #import "AuthenticationClient.h" #import "Credential.h" #import "ProtectionSpace.h" +#import <wtf/UnusedParam.h> #import <Foundation/NSURLAuthenticationChallenge.h> #import <Foundation/NSURLCredential.h> @@ -86,6 +87,20 @@ using namespace WebCore; m_client->receivedCancellation(core(challenge)); } +- (void)performDefaultHandlingForAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge +{ + // FIXME: <rdar://problem/8995483> Determine what, if anything, we should do here. + ASSERT_NOT_REACHED(); + UNUSED_PARAM(challenge); +} + +- (void)rejectProtectionSpaceAndContinueWithChallenge:(NSURLAuthenticationChallenge *)challenge +{ + // FIXME: <rdar://problem/8995483> Determine what, if anything, we should do here. + ASSERT_NOT_REACHED(); + UNUSED_PARAM(challenge); +} + @end namespace WebCore { diff --git a/Source/WebCore/platform/network/mac/FormDataStreamMac.mm b/Source/WebCore/platform/network/mac/FormDataStreamMac.mm index 03f4579..eb6f601 100644 --- a/Source/WebCore/platform/network/mac/FormDataStreamMac.mm +++ b/Source/WebCore/platform/network/mac/FormDataStreamMac.mm @@ -185,9 +185,7 @@ static bool advanceCurrentStream(FormStreamFields* form) } #endif const String& path = nextInput.m_shouldGenerateFile ? nextInput.m_generatedFilename : nextInput.m_filename; - RetainPtr<CFStringRef> filename(AdoptCF, path.createCFString()); - RetainPtr<CFURLRef> fileURL(AdoptCF, CFURLCreateWithFileSystemPath(0, filename.get(), kCFURLPOSIXPathStyle, FALSE)); - form->currentStream = CFReadStreamCreateWithFile(0, fileURL.get()); + form->currentStream = CFReadStreamCreateWithFile(0, pathAsURL(path).get()); if (!form->currentStream) { // The file must have been removed or become unreadable. return false; diff --git a/Source/WebCore/platform/network/mac/ResourceHandleMac.mm b/Source/WebCore/platform/network/mac/ResourceHandleMac.mm index 84b656c..2d687c0 100644 --- a/Source/WebCore/platform/network/mac/ResourceHandleMac.mm +++ b/Source/WebCore/platform/network/mac/ResourceHandleMac.mm @@ -164,12 +164,7 @@ public: #ifndef BUILDING_ON_TIGER static String encodeBasicAuthorization(const String& user, const String& password) { - CString unencodedString = (user + ":" + password).utf8(); - Vector<char> unencoded(unencodedString.length()); - std::copy(unencodedString.data(), unencodedString.data() + unencodedString.length(), unencoded.begin()); - Vector<char> encoded; - base64Encode(unencoded, encoded); - return String(encoded.data(), encoded.size()); + return base64Encode((user + ":" + password).utf8()); } #endif diff --git a/Source/WebCore/platform/network/mac/ResourceRequestMac.mm b/Source/WebCore/platform/network/mac/ResourceRequestMac.mm index f0357e5..640d237 100644 --- a/Source/WebCore/platform/network/mac/ResourceRequestMac.mm +++ b/Source/WebCore/platform/network/mac/ResourceRequestMac.mm @@ -31,6 +31,8 @@ #import "WebCoreSystemInterface.h" #import "FormDataStreamMac.h" +#import "ResourceRequestCFNet.h" +#import "WebCoreSystemInterface.h" #import <Foundation/Foundation.h> @@ -65,7 +67,12 @@ void ResourceRequest::doUpdateResourceRequest() if (NSString* method = [m_nsRequest.get() HTTPMethod]) m_httpMethod = method; m_allowCookies = [m_nsRequest.get() HTTPShouldHandleCookies]; - + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + if (isHTTPPipeliningEnabled() && !shouldForceHTTPPipeliningPriorityHigh()) + m_priority = mapHTTPPipeliningPriorityToResourceLoadPriority(wkGetHTTPPipeliningPriority(m_nsRequest.get())); +#endif + NSDictionary *headers = [m_nsRequest.get() allHTTPHeaderFields]; NSEnumerator *e = [headers keyEnumerator]; NSString *name; @@ -111,6 +118,13 @@ void ResourceRequest::doUpdatePlatformRequest() wkSupportsMultipartXMixedReplace(nsRequest); #endif +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD) + if (isHTTPPipeliningEnabled()) { + int priority = mapResourceLoadPriorityToHTTPPipeliningPriority(m_priority); + wkSetHTTPPipeliningPriority(nsRequest, shouldForceHTTPPipeliningPriorityHigh() ? 2 : priority); + } +#endif + [nsRequest setCachePolicy:(NSURLRequestCachePolicy)cachePolicy()]; if (timeoutInterval() != unspecifiedTimeoutInterval) [nsRequest setTimeoutInterval:timeoutInterval()]; @@ -154,12 +168,6 @@ void ResourceRequest::applyWebArchiveHackForMail() // Hack because Mail checks for this property to detect data / archive loads [NSURLProtocol setProperty:@"" forKey:@"WebDataRequest" inRequest:(NSMutableURLRequest *)nsURLRequest()]; } - -unsigned initializeMaximumHTTPConnectionCountPerHost() -{ - static const unsigned preferredConnectionCount = 6; - return wkInitializeMaximumHTTPConnectionCountPerHost(preferredConnectionCount); -} } // namespace WebCore diff --git a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp index e0d6e69..a98b4f4 100644 --- a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp +++ b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.cpp @@ -35,6 +35,18 @@ QtNAMThreadSafeProxy::QtNAMThreadSafeProxy(QNetworkAccessManager *manager) connect(this, SIGNAL(localSetCookiesRequested(const QUrl&, const QString&)), SLOT(localSetCookies(const QUrl&, const QString&))); connect(this, SIGNAL(localCookiesForUrlRequested(const QUrl&, bool*, QList<QNetworkCookie>*)), SLOT(localCookiesForUrl(const QUrl&, bool*, QList<QNetworkCookie>*))); connect(this, SIGNAL(localWillLoadFromCacheRequested(const QUrl&, bool*, bool*)), SLOT(localWillLoadFromCache(const QUrl&, bool*, bool*))); + connect(this, SIGNAL(hasCookieJarRequested(bool*, bool*)), SLOT(hasCookieJar(bool*, bool*))); +} + +bool QtNAMThreadSafeProxy::hasCookieJar() +{ + bool result; + bool done = false; + emit hasCookieJarRequested(&done, &result); + QMutexLocker lock(&m_resultMutex); + while (!done) + m_resultWaitCondition.wait(&m_resultMutex); + return result; } void QtNAMThreadSafeProxy::localSetCookies(const QUrl& url, const QString& cookies) @@ -69,6 +81,14 @@ void QtNAMThreadSafeProxy::localWillLoadFromCache(const QUrl& url, bool* done, b m_resultWaitCondition.wakeAll(); } +void QtNAMThreadSafeProxy::hasCookieJar(bool* done, bool* result) +{ + QMutexLocker lock(&m_resultMutex); + *result = !!m_manager->cookieJar(); + *done = true; + m_resultWaitCondition.wakeAll(); +} + QtNetworkReplyThreadSafeProxy::QtNetworkReplyThreadSafeProxy(QNetworkAccessManager *manager) : m_manager(manager) , m_reply(0) diff --git a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h index ae963cf..4906fe2 100644 --- a/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h +++ b/Source/WebCore/platform/network/qt/QtNAMThreadSafeProxy.h @@ -67,15 +67,19 @@ public: return result; } + bool hasCookieJar(); + signals: void localSetCookiesRequested(const QUrl&, const QString& cookies); void localCookiesForUrlRequested(const QUrl&, bool* done, QList<QNetworkCookie>* result); void localWillLoadFromCacheRequested(const QUrl&, bool* done, bool* result); + void hasCookieJarRequested(bool* done, bool* result); private slots: void localSetCookies(const QUrl&, const QString& cookies); void localCookiesForUrl(const QUrl&, bool* done, QList<QNetworkCookie>* result); void localWillLoadFromCache(const QUrl&, bool* done, bool* result); + void hasCookieJar(bool* done, bool* result); private: QNetworkAccessManager* m_manager; diff --git a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp index ba29622..e2c2f05 100644 --- a/Source/WebCore/platform/network/soup/CookieJarSoup.cpp +++ b/Source/WebCore/platform/network/soup/CookieJarSoup.cpp @@ -38,9 +38,7 @@ SoupCookieJar* defaultCookieJar() cookiesInitialized = true; cookieJar = soup_cookie_jar_new(); -#ifdef HAVE_LIBSOUP_2_29_90 soup_cookie_jar_set_accept_policy(cookieJar, SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY); -#endif } return cookieJar; @@ -67,18 +65,12 @@ void setCookies(Document* document, const KURL& url, const String& value) GOwnPtr<SoupURI> origin(soup_uri_new(url.string().utf8().data())); -#ifdef HAVE_LIBSOUP_2_29_90 GOwnPtr<SoupURI> firstParty(soup_uri_new(document->firstPartyForCookies().string().utf8().data())); soup_cookie_jar_set_cookie_with_first_party(jar, origin.get(), firstParty.get(), value.utf8().data()); -#else - soup_cookie_jar_set_cookie(jar, - origin.get(), - value.utf8().data()); -#endif } String cookies(const Document* /*document*/, const KURL& url) diff --git a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp index a7170fe..3b1f157 100644 --- a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -45,12 +45,14 @@ #include "ResourceHandleInternal.h" #include "ResourceResponse.h" #include "SharedBuffer.h" -#include "soup-request-http.h" #include "TextEncoding.h" #include <errno.h> #include <fcntl.h> #include <gio/gio.h> #include <glib.h> +#define LIBSOUP_USE_UNSTABLE_REQUEST_API +#include <libsoup/soup-request-http.h> +#include <libsoup/soup-requester.h> #include <libsoup/soup.h> #include <sys/stat.h> #include <sys/types.h> @@ -127,7 +129,7 @@ static void cleanupSoupRequestOperation(ResourceHandle*, bool isDestroying); static void sendRequestCallback(GObject*, GAsyncResult*, gpointer); static void readCallback(GObject*, GAsyncResult*, gpointer); static void closeCallback(GObject*, GAsyncResult*, gpointer); -static bool startGio(ResourceHandle*, KURL); +static bool startNonHTTPRequest(ResourceHandle*, KURL); ResourceHandleInternal::~ResourceHandleInternal() { @@ -145,14 +147,48 @@ ResourceHandle::~ResourceHandle() cleanupSoupRequestOperation(this, true); } +static void ensureSessionIsInitialized(SoupSession* session) +{ + // Values taken from http://stevesouders.com/ua/index.php following + // the rule "Do What Every Other Modern Browser Is Doing". They seem + // to significantly improve page loading time compared to soup's + // default values. + static const int maxConnections = 60; + static const int maxConnectionsPerHost = 6; + + if (g_object_get_data(G_OBJECT(session), "webkit-init")) + return; + + SoupCookieJar* jar = SOUP_COOKIE_JAR(soup_session_get_feature(session, SOUP_TYPE_COOKIE_JAR)); + if (!jar) + soup_session_add_feature(session, SOUP_SESSION_FEATURE(defaultCookieJar())); + else + setDefaultCookieJar(jar); + + 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_session_add_feature(session, SOUP_SESSION_FEATURE(logger)); + g_object_unref(logger); + } + + SoupRequester* requester = soup_requester_new(); + soup_session_add_feature(session, SOUP_SESSION_FEATURE(requester)); + g_object_unref(requester); + + g_object_set(session, + SOUP_SESSION_MAX_CONNS, maxConnections, + SOUP_SESSION_MAX_CONNS_PER_HOST, maxConnectionsPerHost, + NULL); + + g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef)); +} + void ResourceHandle::prepareForURL(const KURL &url) { -#ifdef HAVE_LIBSOUP_2_29_90 GOwnPtr<SoupURI> soupURI(soup_uri_new(url.prettyURL().utf8().data())); if (!soupURI) return; soup_session_prepare_for_uri(ResourceHandle::defaultSession(), soupURI.get()); -#endif } // All other kinds of redirections, except for the *304* status code @@ -206,14 +242,12 @@ static void restartedCallback(SoupMessage* msg, gpointer data) if (d->m_cancelled) return; -#ifdef HAVE_LIBSOUP_2_29_90 // Update the first party in case the base URL changed with the redirect String firstPartyString = request.firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(d->m_soupMessage.get(), firstParty.get()); } -#endif } static void contentSniffedCallback(SoupMessage*, const char*, GHashTable*, gpointer); @@ -263,7 +297,26 @@ static void gotHeadersCallback(SoupMessage* msg, gpointer data) client->didReceiveResponse(handle.get(), d->m_response); } -// This callback will not be called if the content sniffer is disabled in startHttp. +static void wroteBodyDataCallback(SoupMessage*, SoupBuffer* buffer, gpointer data) +{ + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(data); + if (!handle) + return; + + ASSERT(buffer); + ResourceHandleInternal* internal = handle->getInternal(); + internal->m_bodyDataSent += buffer->length; + + if (internal->m_cancelled) + return; + ResourceHandleClient* client = handle->client(); + if (!client) + return; + + client->didSendData(handle.get(), internal->m_bodyDataSent, internal->m_bodySize); +} + +// This callback will not be called if the content sniffer is disabled in startHTTPRequest. static void contentSniffedCallback(SoupMessage* msg, const char* sniffedType, GHashTable *params, gpointer data) { if (sniffedType) { @@ -312,136 +365,11 @@ static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) client->didReceiveData(handle.get(), chunk->data, chunk->length, false); } -static gboolean parseDataUrl(gpointer callbackData) -{ - ResourceHandle* handle = static_cast<ResourceHandle*>(callbackData); - ResourceHandleClient* client = handle->client(); - ResourceHandleInternal* d = handle->getInternal(); - if (d->m_cancelled) - return false; - - d->m_idleHandler = 0; - - ASSERT(client); - if (!client) - return false; - - String url = handle->firstRequest().url().string(); - ASSERT(url.startsWith("data:", false)); - - int index = url.find(','); - if (index == -1) { - client->cannotShowURL(handle); - return false; - } - - String mediaType = url.substring(5, index - 5); - - bool isBase64 = mediaType.endsWith(";base64", false); - if (isBase64) - mediaType = mediaType.left(mediaType.length() - 7); - - if (mediaType.isEmpty()) - mediaType = "text/plain;charset=US-ASCII"; - - String mimeType = extractMIMETypeFromMediaType(mediaType); - String charset = extractCharsetFromMediaType(mediaType); - - ASSERT(d->m_response.isNull()); - - d->m_response.setURL(handle->firstRequest().url()); - d->m_response.setMimeType(mimeType); - - // For non base64 encoded data we have to convert to UTF-16 early - // due to limitations in KURL - d->m_response.setTextEncodingName(isBase64 ? charset : "UTF-16"); - client->didReceiveResponse(handle, d->m_response); - - // The load may be cancelled, and the client may be destroyed - // by any of the client reporting calls, so we check, and bail - // out in either of those cases. - if (d->m_cancelled || !handle->client()) - return false; - - SoupSession* session = handle->defaultSession(); - GOwnPtr<GError> error; - d->m_soupRequest = adoptGRef(webkit_soup_requester_request(d->m_requester.get(), handle->firstRequest().url().string().utf8().data(), session, &error.outPtr())); - if (error) { - d->m_soupRequest = 0; - client->didFinishLoading(handle, 0); - return false; - } - - d->m_inputStream = adoptGRef(webkit_soup_request_send(d->m_soupRequest.get(), 0, &error.outPtr())); - if (error) { - d->m_inputStream = 0; - client->didFinishLoading(handle, 0); - return false; - } - - d->m_buffer = static_cast<char*>(g_slice_alloc0(READ_BUFFER_SIZE)); - d->m_total = 0; - - g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle); - // balanced by a deref() in cleanupSoupRequestOperation, which should always run - handle->ref(); - - d->m_cancellable = adoptGRef(g_cancellable_new()); - g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, - d->m_cancellable.get(), readCallback, GINT_TO_POINTER(!isBase64)); - - return false; -} - -static bool startData(ResourceHandle* handle, String urlString) -{ - ASSERT(handle); - - ResourceHandleInternal* d = handle->getInternal(); - - // If parseDataUrl is called synchronously the job is not yet effectively started - // and webkit won't never know that the data has been parsed even didFinishLoading is called. - d->m_idleHandler = g_timeout_add(0, parseDataUrl, handle); - return true; -} - static SoupSession* createSoupSession() { return soup_session_async_new(); } -// Values taken from http://stevesouders.com/ua/index.php following -// the rule "Do What Every Other Modern Browser Is Doing". They seem -// to significantly improve page loading time compared to soup's -// default values. -#define MAX_CONNECTIONS 60 -#define MAX_CONNECTIONS_PER_HOST 6 - -static void ensureSessionIsInitialized(SoupSession* session) -{ - if (g_object_get_data(G_OBJECT(session), "webkit-init")) - return; - - 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); - - 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(session, - SOUP_SESSION_MAX_CONNS, MAX_CONNECTIONS, - SOUP_SESSION_MAX_CONNS_PER_HOST, MAX_CONNECTIONS_PER_HOST, - NULL); - - g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef)); -} - static void cleanupSoupRequestOperation(ResourceHandle* handle, bool isDestroying = false) { ResourceHandleInternal* d = handle->getInternal(); @@ -495,14 +423,14 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use } GOwnPtr<GError> error; - GInputStream* in = webkit_soup_request_send_finish(d->m_soupRequest.get(), res, &error.outPtr()); + GInputStream* in = soup_request_send_finish(d->m_soupRequest.get(), res, &error.outPtr()); if (error) { SoupMessage* soupMsg = d->m_soupMessage.get(); gboolean isTransportError = d->m_soupMessage && SOUP_STATUS_IS_TRANSPORT_ERROR(soupMsg->status_code); if (isTransportError || (error->domain == G_IO_ERROR)) { - SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get()); + SoupURI* uri = soup_request_get_uri(d->m_soupRequest.get()); GOwnPtr<char> uriStr(soup_uri_to_string(uri, false)); gint errorCode = isTransportError ? static_cast<gint>(soupMsg->status_code) : error->code; const gchar* errorMsg = isTransportError ? soupMsg->reason_phrase : error->message; @@ -550,13 +478,14 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use // readCallback needs it g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle.get()); - // Ensure a response is sent for any protocols that don't explicitly support responses - // through got-headers signal or content sniffing. - // (e.g. file and GIO based protocol). - if (!handle->shouldContentSniff() && d->m_response.isNull()) { + // If not using SoupMessage we need to call didReceiveResponse now. + // (This will change later when SoupRequest supports content sniffing.) + if (!d->m_soupMessage) { d->m_response.setURL(handle->firstRequest().url()); - d->m_response.setMimeType(webkit_soup_request_get_content_type(d->m_soupRequest.get())); - d->m_response.setExpectedContentLength(webkit_soup_request_get_content_length(d->m_soupRequest.get())); + const gchar* contentType = soup_request_get_content_type(d->m_soupRequest.get()); + d->m_response.setMimeType(extractMIMETypeFromMediaType(contentType)); + d->m_response.setTextEncodingName(extractCharsetFromMediaType(contentType)); + d->m_response.setExpectedContentLength(soup_request_get_content_length(d->m_soupRequest.get())); client->didReceiveResponse(handle.get(), d->m_response); if (d->m_cancelled) { @@ -572,12 +501,56 @@ static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer use G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, 0); } -static bool startHttp(ResourceHandle* handle) +static bool addFormElementsToSoupMessage(SoupMessage* message, const char* contentType, FormData* httpBody, unsigned long& totalBodySize) +{ + size_t numElements = httpBody->elements().size(); + if (numElements < 2) { // No file upload is the most common case. + Vector<char> body; + httpBody->flatten(body); + totalBodySize = body.size(); + soup_message_set_request(message, contentType, SOUP_MEMORY_COPY, body.data(), body.size()); + return true; + } + + // We have more than one element to upload, and some may be large files, + // which we will want to mmap instead of copying into memory + soup_message_body_set_accumulate(message->request_body, FALSE); + for (size_t i = 0; i < numElements; i++) { + const FormDataElement& element = httpBody->elements()[i]; + + if (element.m_type == FormDataElement::data) { + totalBodySize += element.m_data.size(); + soup_message_body_append(message->request_body, SOUP_MEMORY_TEMPORARY, + element.m_data.data(), element.m_data.size()); + continue; + } + + // This technique is inspired by libsoup's simple-httpd test. + GOwnPtr<GError> error; + CString fileName = fileSystemRepresentation(element.m_filename); + GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); + if (error) + return false; + + gsize mappedFileSize = g_mapped_file_get_length(fileMapping); + totalBodySize += mappedFileSize; + SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping), + mappedFileSize, fileMapping, + reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); + soup_message_body_append_buffer(message->request_body, soupBuffer); + soup_buffer_free(soupBuffer); + } + + return true; +} + +static bool startHTTPRequest(ResourceHandle* handle) { ASSERT(handle); SoupSession* session = handle->defaultSession(); ensureSessionIsInitialized(session); + SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(session, SOUP_TYPE_REQUESTER)); ResourceHandleInternal* d = handle->getInternal(); @@ -587,7 +560,7 @@ static bool startHttp(ResourceHandle* handle) request.setURL(url); GOwnPtr<GError> error; - d->m_soupRequest = adoptGRef(webkit_soup_requester_request(d->m_requester.get(), url.string().utf8().data(), session, &error.outPtr())); + d->m_soupRequest = adoptGRef(soup_requester_request(requester, url.string().utf8().data(), &error.outPtr())); if (error) { d->m_soupRequest = 0; return false; @@ -595,7 +568,7 @@ static bool startHttp(ResourceHandle* handle) g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", handle); - d->m_soupMessage = adoptGRef(webkit_soup_request_http_get_message(WEBKIT_SOUP_REQUEST_HTTP(d->m_soupRequest.get()))); + d->m_soupMessage = adoptGRef(soup_request_http_get_message(SOUP_REQUEST_HTTP(d->m_soupRequest.get()))); if (!d->m_soupMessage) return false; @@ -609,65 +582,23 @@ static bool startHttp(ResourceHandle* handle) g_signal_connect(soupMessage, "restarted", G_CALLBACK(restartedCallback), handle); g_signal_connect(soupMessage, "got-headers", G_CALLBACK(gotHeadersCallback), handle); + g_signal_connect(soupMessage, "wrote-body-data", G_CALLBACK(wroteBodyDataCallback), handle); d->m_gotChunkHandler = g_signal_connect(soupMessage, "got-chunk", G_CALLBACK(gotChunkCallback), handle); -#ifdef HAVE_LIBSOUP_2_29_90 String firstPartyString = request.firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(soupMessage, firstParty.get()); } -#endif FormData* httpBody = d->m_firstRequest.httpBody(); - if (httpBody && !httpBody->isEmpty()) { - 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(soupMessage, d->m_firstRequest.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(soupMessage->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(soupMessage->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 - */ - GOwnPtr<GError> error; - CString fileName = fileSystemRepresentation(element.m_filename); - GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); - - if (error) { - g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, handle); - d->m_soupMessage.clear(); - - 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_unref)); - soup_message_body_append_buffer(soupMessage->request_body, soupBuffer); - soup_buffer_free(soupBuffer); - } - } - } + CString contentType = d->m_firstRequest.httpContentType().utf8().data(); + if (httpBody && !httpBody->isEmpty() + && !addFormElementsToSoupMessage(soupMessage, contentType.data(), httpBody, d->m_bodySize)) { + // We failed to prepare the body data, so just fail this load. + g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, handle); + d->m_soupMessage.clear(); + return false; } // balanced by a deref() in cleanupSoupRequestOperation, which should always run @@ -681,7 +612,7 @@ static bool startHttp(ResourceHandle* handle) // Send the request only if it's not been explicitely deferred. if (!d->m_defersLoading) { d->m_cancellable = adoptGRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); } return true; @@ -715,19 +646,13 @@ bool ResourceHandle::start(NetworkingContext* context) // Used to set the authentication dialog toplevel; may be NULL d->m_context = context; - if (equalIgnoringCase(protocol, "data")) - return startData(this, urlString); - if (equalIgnoringCase(protocol, "http") || equalIgnoringCase(protocol, "https")) { - if (startHttp(this)) + if (startHTTPRequest(this)) return true; } - if (equalIgnoringCase(protocol, "file") || equalIgnoringCase(protocol, "ftp") || equalIgnoringCase(protocol, "ftps")) { - // FIXME: should we be doing any other protocols here? - if (startGio(this, url)) - return true; - } + if (startNonHTTPRequest(this, url)) + return true; // Error must not be reported immediately this->scheduleFailure(InvalidURLFailure); @@ -764,7 +689,7 @@ void ResourceHandle::platformSetDefersLoading(bool defersLoading) if (!defersLoading && !d->m_cancellable && d->m_soupRequest.get()) { d->m_cancellable = adoptGRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); return; } @@ -795,11 +720,13 @@ bool ResourceHandle::willLoadFromCache(ResourceRequest&, Frame*) void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const ResourceRequest& request, StoredCredentials /*storedCredentials*/, ResourceError& error, ResourceResponse& response, Vector<char>& data) { WebCoreSynchronousLoader syncLoader(error, response, data); - // FIXME: we should use the ResourceHandle::create method here, - // but it makes us timeout in a couple of tests. See - // https://bugs.webkit.org/show_bug.cgi?id=41823 - RefPtr<ResourceHandle> handle = adoptRef(new ResourceHandle(request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/)); - handle->start(context); + RefPtr<ResourceHandle> handle = create(context, request, &syncLoader, false /*defersLoading*/, false /*shouldContentSniff*/); + if (!handle) + return; + + // If the request has already failed, do not run the main loop, or else we'll block indefinitely. + if (handle->d->m_scheduledFailureType != NoFailure) + return; syncLoader.run(); } @@ -811,18 +738,8 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer) return; ResourceHandleInternal* d = handle->getInternal(); - ResourceHandleClient* client = handle->client(); - g_input_stream_close_finish(d->m_inputStream.get(), res, 0); cleanupSoupRequestOperation(handle.get()); - - // The load may have been cancelled, the client may have been - // destroyed already. In such cases calling didFinishLoading is a - // bad idea. - if (d->m_cancelled || !client) - return; - - client->didFinishLoading(handle.get(), 0); } static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer data) @@ -844,7 +761,7 @@ static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer da gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr()); if (error) { - SoupURI* uri = webkit_soup_request_get_uri(d->m_soupRequest.get()); + SoupURI* uri = soup_request_get_uri(d->m_soupRequest.get()); GOwnPtr<char> uriStr(soup_uri_to_string(uri, false)); ResourceError resourceError(g_quark_to_string(G_IO_ERROR), error->code, uriStr.get(), error ? String::fromUTF8(error->message) : String()); @@ -854,6 +771,10 @@ static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer da } if (!bytesRead) { + // Finish the load. We do not wait for the stream to + // close. Instead we better notify WebCore as soon as possible + client->didFinishLoading(handle.get(), 0); + g_input_stream_close_async(d->m_inputStream.get(), G_PRIORITY_DEFAULT, 0, closeCallback, 0); return; @@ -881,7 +802,7 @@ static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer da d->m_cancellable.get(), readCallback, data); } -static bool startGio(ResourceHandle* handle, KURL url) +static bool startNonHTTPRequest(ResourceHandle* handle, KURL url) { ASSERT(handle); @@ -889,18 +810,14 @@ static bool startGio(ResourceHandle* handle, KURL url) return false; SoupSession* session = handle->defaultSession(); + ensureSessionIsInitialized(session); + SoupRequester* requester = SOUP_REQUESTER(soup_session_get_feature(session, SOUP_TYPE_REQUESTER)); ResourceHandleInternal* d = handle->getInternal(); - // 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.removeFragmentIdentifier(); - url.setQuery(String()); - url.removePort(); CString urlStr = url.string().utf8(); GOwnPtr<GError> error; - d->m_soupRequest = adoptGRef(webkit_soup_requester_request(d->m_requester.get(), urlStr.data(), session, &error.outPtr())); + d->m_soupRequest = adoptGRef(soup_requester_request(requester, urlStr.data(), &error.outPtr())); if (error) { d->m_soupRequest = 0; return false; @@ -912,7 +829,7 @@ static bool startGio(ResourceHandle* handle, KURL url) handle->ref(); d->m_cancellable = adoptGRef(g_cancellable_new()); - webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); return true; } diff --git a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp index d46e47b..5016bf1 100644 --- a/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceRequestSoup.cpp @@ -46,13 +46,11 @@ void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const soup_message_headers_append(soupHeaders, it->first.string().utf8().data(), it->second.utf8().data()); } -#ifdef HAVE_LIBSOUP_2_29_90 String firstPartyString = firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(soupMessage, firstParty.get()); } -#endif soup_message_set_flags(soupMessage, m_soupFlags); } @@ -71,13 +69,11 @@ SoupMessage* ResourceRequest::toSoupMessage() const soup_message_headers_append(soupHeaders, it->first.string().utf8().data(), it->second.utf8().data()); } -#ifdef HAVE_LIBSOUP_2_29_90 String firstPartyString = firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); soup_message_set_first_party(soupMessage, firstParty.get()); } -#endif soup_message_set_flags(soupMessage, m_soupFlags); @@ -104,11 +100,9 @@ void ResourceRequest::updateFromSoupMessage(SoupMessage* soupMessage) if (soupMessage->request_body->data) m_httpBody = FormData::create(soupMessage->request_body->data, soupMessage->request_body->length); -#ifdef HAVE_LIBSOUP_2_29_90 SoupURI* firstParty = soup_message_get_first_party(soupMessage); if (firstParty) m_firstPartyForCookies = soupURIToKURL(firstParty); -#endif m_soupFlags = soup_message_get_flags(soupMessage); diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandle.h b/Source/WebCore/platform/network/soup/SocketStreamHandle.h index 3168fae..c8fe3b3 100644 --- a/Source/WebCore/platform/network/soup/SocketStreamHandle.h +++ b/Source/WebCore/platform/network/soup/SocketStreamHandle.h @@ -60,7 +60,7 @@ namespace WebCore { private: GRefPtr<GSocketConnection> m_socketConnection; GRefPtr<GInputStream> m_inputStream; - GRefPtr<GOutputStream> m_outputStream; + GRefPtr<GPollableOutputStream> m_outputStream; GRefPtr<GSource> m_writeReadySource; char* m_readBuffer; void* m_id; diff --git a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp index 841e209..34382dd 100644 --- a/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/SocketStreamHandleSoup.cpp @@ -50,7 +50,7 @@ namespace WebCore { // These functions immediately call the similarly named SocketStreamHandle methods. static void connectedCallback(GSocketClient*, GAsyncResult*, void*); static void readReadyCallback(GInputStream*, GAsyncResult*, void*); -static gboolean writeReadyCallback(GSocket*, GIOCondition, void*); +static gboolean writeReadyCallback(GPollableOutputStream*, void*); // Having a list of active handles means that we do not have to worry about WebCore // reference counting in GLib callbacks. Once the handle is off the active handles list @@ -82,13 +82,12 @@ SocketStreamHandle::SocketStreamHandle(const KURL& url, SocketStreamHandleClient : SocketStreamHandleBase(url, client) , m_readBuffer(0) { - // No support for SSL sockets yet. - if (url.protocolIs("wss")) - return; - unsigned int port = url.hasPort() ? url.port() : 80; + unsigned int port = url.hasPort() ? url.port() : (url.protocolIs("wss") ? 443 : 80); m_id = activateHandle(this); GRefPtr<GSocketClient> socketClient = adoptGRef(g_socket_client_new()); + if (url.protocolIs("wss")) + g_socket_client_set_tls(socketClient.get(), TRUE); g_socket_client_connect_to_host_async(socketClient.get(), url.host().utf8().data(), port, 0, reinterpret_cast<GAsyncReadyCallback>(connectedCallback), m_id); } @@ -108,7 +107,7 @@ void SocketStreamHandle::connected(GSocketConnection* socketConnection, GError* } m_socketConnection = adoptGRef(socketConnection); - m_outputStream = g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get())); + m_outputStream = G_POLLABLE_OUTPUT_STREAM(g_io_stream_get_output_stream(G_IO_STREAM(m_socketConnection.get()))); m_inputStream = g_io_stream_get_input_stream(G_IO_STREAM(m_socketConnection.get())); m_readBuffer = new char[READ_BUFFER_SIZE]; @@ -156,14 +155,14 @@ void SocketStreamHandle::writeReady() int SocketStreamHandle::platformSend(const char* data, int length) { - if (!g_socket_condition_check(g_socket_connection_get_socket(m_socketConnection.get()), G_IO_OUT)) { + if (!g_pollable_output_stream_is_writable(m_outputStream.get())) { beginWaitingForSocketWritability(); return 0; } GOwnPtr<GError> error; - gssize written = g_output_stream_write(m_outputStream.get(), data, length, 0, &error.outPtr()); - if (error) { + gssize written = g_pollable_output_stream_write_nonblocking(m_outputStream.get(), data, length, 0, &error.outPtr()); + if (error && !g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) { m_client->didFail(this, SocketStreamError(error->code)); // FIXME: Provide a sensible error. return 0; } @@ -222,8 +221,7 @@ void SocketStreamHandle::beginWaitingForSocketWritability() if (m_writeReadySource) // Already waiting. return; - m_writeReadySource = adoptGRef(g_socket_create_source( - g_socket_connection_get_socket(m_socketConnection.get()), static_cast<GIOCondition>(G_IO_OUT), 0)); + m_writeReadySource = adoptGRef(g_pollable_output_stream_create_source(m_outputStream.get(), 0)); g_source_set_callback(m_writeReadySource.get(), reinterpret_cast<GSourceFunc>(writeReadyCallback), m_id, 0); g_source_attach(m_writeReadySource.get(), 0); } @@ -266,24 +264,13 @@ static void readReadyCallback(GInputStream* stream, GAsyncResult* result, void* handle->readBytes(bytesRead, error.get()); } -static gboolean writeReadyCallback(GSocket*, GIOCondition condition, void* id) +static gboolean writeReadyCallback(GPollableOutputStream*, void* id) { SocketStreamHandle* handle = getHandleFromId(id); if (!handle) return FALSE; - // G_IO_HUP and G_IO_ERR are are always active. See: - // http://library.gnome.org/devel/gio/stable/GSocket.html#g-socket-create-source - if (condition & G_IO_HUP) { - handle->close(); - return FALSE; - } - if (condition & G_IO_ERR) { - handle->client()->didFail(handle, SocketStreamError(0)); // FIXME: Provide a sensible error. - return FALSE; - } - if (condition & G_IO_OUT) - handle->writeReady(); + handle->writeReady(); return TRUE; } diff --git a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c b/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c deleted file mode 100644 index c14863b..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.c +++ /dev/null @@ -1,200 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2008 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "soup-directory-input-stream.h" - -#include <libsoup/soup.h> -#include <stdio.h> -#include <string.h> - -#define INIT_STRING "<html><head><title>OMG!</title></head><body><table>" -#define EXIT_STRING "</table></html>" - -G_DEFINE_TYPE (WebKitSoupDirectoryInputStream, webkit_soup_directory_input_stream, G_TYPE_INPUT_STREAM) - -static SoupBuffer * -webkit_soup_directory_input_stream_parse_info (WebKitSoupDirectoryInputStream * stream, - GFileInfo * info) -{ - SoupBuffer *buffer; - GString *string; - const char *s; - char *escaped, *path, *xml_string; - - if (!g_file_info_get_name (info)) - return NULL; - - s = g_file_info_get_display_name (info); - if (!s) { - s = g_file_info_get_name (info); - /* FIXME: convert somehow? */ - if (!g_utf8_validate (s, -1, NULL)) - return NULL; - } - string = g_string_new ("<tr>"); - - xml_string = g_markup_escape_text (s, -1); - escaped = g_uri_escape_string (g_file_info_get_name (info), NULL, FALSE); - path = g_strconcat (stream->uri, "/", escaped, NULL); - g_free (escaped); - g_string_append_printf (string, "<td><a href=\"%s\">%s</a></td>", path, xml_string); - g_free (path); - g_free (xml_string); - g_string_append (string, "</tr>"); - - buffer = soup_buffer_new (SOUP_MEMORY_TAKE, string->str, string->len); - g_string_free (string, FALSE); - - return buffer; -} - -static SoupBuffer * -webkit_soup_directory_input_stream_read_next_file (WebKitSoupDirectoryInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - GFileInfo *info; - SoupBuffer *buffer; - GError *err = NULL; - - do { - info = g_file_enumerator_next_file (stream->enumerator, cancellable, &err); - if (info == NULL) { - if (err) { - g_propagate_error (error, err); - return NULL; - } else if (!stream->done) { - stream->done = TRUE; - return soup_buffer_new (SOUP_MEMORY_STATIC, - EXIT_STRING, - sizeof (EXIT_STRING)); - } else { - return NULL; - } - } - - buffer = webkit_soup_directory_input_stream_parse_info (stream, info); - } while (buffer == NULL); - - return buffer; -} - -static gssize -webkit_soup_directory_input_stream_read (GInputStream *input, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input); - gsize total, size; - - for (total = 0; total < count; total += size) { - if (stream->buffer == NULL) { - stream->buffer = webkit_soup_directory_input_stream_read_next_file (stream, cancellable, error); - if (stream->buffer == NULL) { - /* FIXME: Is this correct or should we forward the error? */ - if (total) - g_clear_error (error); - return total; - } - } - - size = MIN (stream->buffer->length, count - total); - memcpy ((char *)buffer + total, stream->buffer->data, size); - if (size == stream->buffer->length) { - soup_buffer_free (stream->buffer); - stream->buffer = NULL; - } else { - SoupBuffer *sub = soup_buffer_new_subbuffer (stream->buffer, - size, - stream->buffer->length - size); - soup_buffer_free (stream->buffer); - stream->buffer = sub; - } - } - - return total; -} - -static gboolean -webkit_soup_directory_input_stream_close (GInputStream *input, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupDirectoryInputStream *stream = WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (input); - gboolean result; - - if (stream->buffer) { - soup_buffer_free (stream->buffer); - stream->buffer = NULL; - } - - result = g_file_enumerator_close (stream->enumerator, - cancellable, - error); - g_object_unref (stream->enumerator); - stream->enumerator = NULL; - - g_free (stream->uri); - stream->uri = NULL; - - return result; -} - -static void -webkit_soup_directory_input_stream_class_init (WebKitSoupDirectoryInputStreamClass *stream_class) -{ - GInputStreamClass *inputstream_class = G_INPUT_STREAM_CLASS (stream_class); - - inputstream_class->read_fn = webkit_soup_directory_input_stream_read; - inputstream_class->close_fn = webkit_soup_directory_input_stream_close; -} - -static void -webkit_soup_directory_input_stream_init (WebKitSoupDirectoryInputStream *stream) -{ - stream->buffer = soup_buffer_new (SOUP_MEMORY_STATIC, - INIT_STRING, - sizeof (INIT_STRING)); -} - -GInputStream * -webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator, - SoupURI *uri) -{ - GInputStream *stream; - - g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL); - g_return_val_if_fail (uri != NULL, NULL); - - stream = g_object_new (WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, NULL); - - WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->enumerator = g_object_ref (enumerator); - WEBKIT_SOUP_DIRECTORY_INPUT_STREAM (stream)->uri = soup_uri_to_string (uri, FALSE); - - return stream; -} - diff --git a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h b/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h deleted file mode 100644 index 0c5b0be..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-directory-input-stream.h +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2010 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H 1 - -#include <gio/gio.h> -#include <libsoup/soup-types.h> -#include <libsoup/soup-message-body.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM (webkit_soup_directory_input_stream_get_type ()) -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStream)) -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass)) -#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM)) -#define WEBKIT_IS_SOUP_DIRECTORY_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM)) -#define WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_DIRECTORY_INPUT_STREAM, WebKitSoupDirectoryInputStreamClass)) - -typedef struct _WebKitSoupDirectoryInputStream WebKitSoupDirectoryInputStream; -typedef struct _WebKitSoupDirectoryInputStreamClass WebKitSoupDirectoryInputStreamClass; - -struct _WebKitSoupDirectoryInputStream { - GInputStream parent; - - GFileEnumerator *enumerator; - char *uri; - SoupBuffer *buffer; - gboolean done; -}; - -struct _WebKitSoupDirectoryInputStreamClass { - GInputStreamClass parent_class; -}; - -GType webkit_soup_directory_input_stream_get_type (void); - -GInputStream *webkit_soup_directory_input_stream_new (GFileEnumerator *enumerator, - SoupURI *uri); - - -G_END_DECLS - -#endif /* WEBKIT_SOUP_DIRECTORY_INPUT_STREAM_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c deleted file mode 100644 index 2a5d995..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.c +++ /dev/null @@ -1,922 +0,0 @@ -/* soup-input-stream.c, based on gsocketinputstream.c - * - * Copyright (C) 2006-2007 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include <config.h> - -#include <string.h> - -#include <glib.h> -#include <gio/gio.h> - -#include <libsoup/soup.h> - -#include "soup-http-input-stream.h" - -static void webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface); - -G_DEFINE_TYPE_WITH_CODE (WebKitSoupHTTPInputStream, webkit_soup_http_input_stream, G_TYPE_INPUT_STREAM, - G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE, - webkit_soup_http_input_stream_seekable_iface_init)) - -typedef void (*WebKitSoupHTTPInputStreamCallback)(GInputStream *); - -typedef struct { - SoupSession *session; - GMainContext *async_context; - SoupMessage *msg; - gboolean got_headers, finished; - goffset offset; - - GCancellable *cancellable; - GSource *cancel_watch; - WebKitSoupHTTPInputStreamCallback got_headers_cb; - WebKitSoupHTTPInputStreamCallback got_chunk_cb; - WebKitSoupHTTPInputStreamCallback finished_cb; - WebKitSoupHTTPInputStreamCallback cancelled_cb; - - guchar *leftover_buffer; - gsize leftover_bufsize, leftover_offset; - - guchar *caller_buffer; - gsize caller_bufsize, caller_nread; - GAsyncReadyCallback outstanding_callback; - GSimpleAsyncResult *result; -} WebKitSoupHTTPInputStreamPrivate; -#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamPrivate)) - - -static gssize webkit_soup_http_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error); -static gboolean webkit_soup_http_input_stream_close (GInputStream *stream, - GCancellable *cancellable, - GError **error); -static void webkit_soup_http_input_stream_read_async (GInputStream *stream, - void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gssize webkit_soup_http_input_stream_read_finish (GInputStream *stream, - GAsyncResult *result, - GError **error); -static void webkit_soup_http_input_stream_close_async (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gboolean webkit_soup_http_input_stream_close_finish (GInputStream *stream, - GAsyncResult *result, - GError **error); - -static goffset webkit_soup_http_input_stream_tell (GSeekable *seekable); - -static gboolean webkit_soup_http_input_stream_can_seek (GSeekable *seekable); -static gboolean webkit_soup_http_input_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error); - -static gboolean webkit_soup_http_input_stream_can_truncate (GSeekable *seekable); -static gboolean webkit_soup_http_input_stream_truncate (GSeekable *seekable, - goffset offset, - GCancellable *cancellable, - GError **error); - -static void webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream); -static void webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk, gpointer stream); -static void webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream); - -static void -webkit_soup_http_input_stream_finalize (GObject *object) -{ - WebKitSoupHTTPInputStream *stream = WEBKIT_SOUP_HTTP_INPUT_STREAM (object); - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - g_object_unref (priv->session); - - g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream); - g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream); - g_signal_handlers_disconnect_by_func (priv->msg, G_CALLBACK (webkit_soup_http_input_stream_finished), stream); - g_object_unref (priv->msg); - g_free (priv->leftover_buffer); - - if (G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize) - (*G_OBJECT_CLASS (webkit_soup_http_input_stream_parent_class)->finalize)(object); -} - -static void -webkit_soup_http_input_stream_class_init (WebKitSoupHTTPInputStreamClass *klass) -{ - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass); - - g_type_class_add_private (klass, sizeof (WebKitSoupHTTPInputStreamPrivate)); - - gobject_class->finalize = webkit_soup_http_input_stream_finalize; - - stream_class->read_fn = webkit_soup_http_input_stream_read; - stream_class->close_fn = webkit_soup_http_input_stream_close; - stream_class->read_async = webkit_soup_http_input_stream_read_async; - stream_class->read_finish = webkit_soup_http_input_stream_read_finish; - stream_class->close_async = webkit_soup_http_input_stream_close_async; - stream_class->close_finish = webkit_soup_http_input_stream_close_finish; -} - -static void -webkit_soup_http_input_stream_seekable_iface_init (GSeekableIface *seekable_iface) -{ - seekable_iface->tell = webkit_soup_http_input_stream_tell; - seekable_iface->can_seek = webkit_soup_http_input_stream_can_seek; - seekable_iface->seek = webkit_soup_http_input_stream_seek; - seekable_iface->can_truncate = webkit_soup_http_input_stream_can_truncate; - seekable_iface->truncate_fn = webkit_soup_http_input_stream_truncate; -} - -static void -webkit_soup_http_input_stream_init (WebKitSoupHTTPInputStream *stream) -{ - ; -} - -static void -webkit_soup_http_input_stream_queue_message (WebKitSoupHTTPInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->got_headers = priv->finished = FALSE; - - /* Add an extra ref since soup_session_queue_message steals one */ - g_object_ref (priv->msg); - soup_session_queue_message (priv->session, priv->msg, NULL, NULL); -} - -/** - * webkit_soup_http_input_stream_new: - * @session: the #SoupSession to use - * @msg: the #SoupMessage whose response will be streamed - * - * Prepares to send @msg over @session, and returns a #GInputStream - * that can be used to read the response. - * - * @msg may not be sent until the first read call; if you need to look - * at the status code or response headers before reading the body, you - * can use webkit_soup_http_input_stream_send() or webkit_soup_http_input_stream_send_async() - * to force the message to be sent and the response headers read. - * - * If @msg gets a non-2xx result, the first read (or send) will return - * an error with type %WEBKIT_SOUP_HTTP_INPUT_STREAM_HTTP_ERROR. - * - * Internally, #WebKitSoupHTTPInputStream is implemented using asynchronous I/O, - * so if you are using the synchronous API (eg, - * g_input_stream_read()), you should create a new #GMainContext and - * set it as the %SOUP_SESSION_ASYNC_CONTEXT property on @session. (If - * you don't, then synchronous #GInputStream calls will cause the main - * loop to be run recursively.) The async #GInputStream API works fine - * with %SOUP_SESSION_ASYNC_CONTEXT either set or unset. - * - * Returns: a new #GInputStream. - **/ -WebKitSoupHTTPInputStream * -webkit_soup_http_input_stream_new (SoupSession *session, SoupMessage *msg) -{ - WebKitSoupHTTPInputStream *stream; - WebKitSoupHTTPInputStreamPrivate *priv; - - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); - - stream = g_object_new (WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, NULL); - priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->session = g_object_ref (session); - priv->async_context = soup_session_get_async_context (session); - priv->msg = g_object_ref (msg); - - g_signal_connect (msg, "got_headers", - G_CALLBACK (webkit_soup_http_input_stream_got_headers), stream); - g_signal_connect (msg, "got_chunk", - G_CALLBACK (webkit_soup_http_input_stream_got_chunk), stream); - g_signal_connect (msg, "finished", - G_CALLBACK (webkit_soup_http_input_stream_finished), stream); - - webkit_soup_http_input_stream_queue_message (stream); - return stream; -} - -static void -webkit_soup_http_input_stream_got_headers (SoupMessage *msg, gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - /* If the status is unsuccessful, we just ignore the signal and let - * libsoup keep going (eventually either it will requeue the request - * (after handling authentication/redirection), or else the - * "finished" handler will run). - */ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) - return; - - priv->got_headers = TRUE; - if (!priv->caller_buffer) { - /* Not ready to read the body yet */ - soup_session_pause_message (priv->session, msg); - } - - if (priv->got_headers_cb) - priv->got_headers_cb (stream); -} - -static void -webkit_soup_http_input_stream_got_chunk (SoupMessage *msg, SoupBuffer *chunk_buffer, - gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - const gchar *chunk = chunk_buffer->data; - gsize chunk_size = chunk_buffer->length; - - /* We only pay attention to the chunk if it's part of a successful - * response. - */ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) - return; - - /* Sanity check */ - if (priv->caller_bufsize == 0 || priv->leftover_bufsize != 0) - g_warning ("webkit_soup_http_input_stream_got_chunk called again before previous chunk was processed"); - - /* Copy what we can into priv->caller_buffer */ - if (priv->caller_bufsize > priv->caller_nread) { - gsize nread = MIN (chunk_size, priv->caller_bufsize - priv->caller_nread); - - memcpy (priv->caller_buffer + priv->caller_nread, chunk, nread); - priv->caller_nread += nread; - priv->offset += nread; - chunk += nread; - chunk_size -= nread; - } - - if (chunk_size > 0) { - /* Copy the rest into priv->leftover_buffer. If - * there's already some data there, realloc and - * append. Otherwise just copy. - */ - if (priv->leftover_bufsize) { - priv->leftover_buffer = g_realloc (priv->leftover_buffer, - priv->leftover_bufsize + chunk_size); - memcpy (priv->leftover_buffer + priv->leftover_bufsize, - chunk, chunk_size); - priv->leftover_bufsize += chunk_size; - } else { - priv->leftover_bufsize = chunk_size; - priv->leftover_buffer = g_memdup (chunk, chunk_size); - priv->leftover_offset = 0; - } - } - - soup_session_pause_message (priv->session, msg); - if (priv->got_chunk_cb) - priv->got_chunk_cb (stream); -} - -static void -webkit_soup_http_input_stream_finished (SoupMessage *msg, gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->finished = TRUE; - - if (priv->finished_cb) - priv->finished_cb (stream); -} - -static gboolean -webkit_soup_http_input_stream_cancelled (GIOChannel *chan, GIOCondition condition, - gpointer stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - priv->cancel_watch = NULL; - - soup_session_pause_message (priv->session, priv->msg); - if (priv->cancelled_cb) - priv->cancelled_cb (stream); - - return FALSE; -} - -static void -webkit_soup_http_input_stream_prepare_for_io (GInputStream *stream, - GCancellable *cancellable, - guchar *buffer, - gsize count) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - int cancel_fd; - - priv->cancellable = cancellable; - cancel_fd = g_cancellable_get_fd (cancellable); - if (cancel_fd != -1) { - GIOChannel *chan = g_io_channel_unix_new (cancel_fd); - priv->cancel_watch = soup_add_io_watch (priv->async_context, chan, - G_IO_IN | G_IO_ERR | G_IO_HUP, - webkit_soup_http_input_stream_cancelled, - stream); - g_io_channel_unref (chan); - } - - priv->caller_buffer = buffer; - priv->caller_bufsize = count; - priv->caller_nread = 0; - - if (priv->got_headers) - soup_session_unpause_message (priv->session, priv->msg); -} - -static void -webkit_soup_http_input_stream_done_io (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - if (priv->cancel_watch) { - g_source_destroy (priv->cancel_watch); - priv->cancel_watch = NULL; - g_cancellable_release_fd (priv->cancellable); - } - priv->cancellable = NULL; - - priv->caller_buffer = NULL; - priv->caller_bufsize = 0; -} - -static gboolean -set_error_if_http_failed (SoupMessage *msg, GError **error) -{ - if (!SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)) { - g_set_error_literal (error, SOUP_HTTP_ERROR, - msg->status_code, msg->reason_phrase); - return TRUE; - } - return FALSE; -} - -static gsize -read_from_leftover (WebKitSoupHTTPInputStreamPrivate *priv, - gpointer buffer, gsize bufsize) -{ - gsize nread; - - if (priv->leftover_bufsize - priv->leftover_offset <= bufsize) { - nread = priv->leftover_bufsize - priv->leftover_offset; - memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread); - - g_free (priv->leftover_buffer); - priv->leftover_buffer = NULL; - priv->leftover_bufsize = priv->leftover_offset = 0; - } else { - nread = bufsize; - memcpy (buffer, priv->leftover_buffer + priv->leftover_offset, nread); - priv->leftover_offset += nread; - } - - priv->offset += nread; - return nread; -} - -/* This does the work of webkit_soup_http_input_stream_send(), assuming that the - * GInputStream pending flag has already been set. It is also used by - * webkit_soup_http_input_stream_send_async() in some circumstances. - */ -static gboolean -webkit_soup_http_input_stream_send_internal (GInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0); - while (!priv->finished && !priv->got_headers && - !g_cancellable_is_cancelled (cancellable)) - g_main_context_iteration (priv->async_context, TRUE); - webkit_soup_http_input_stream_done_io (stream); - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - else if (set_error_if_http_failed (priv->msg, error)) - return FALSE; - return TRUE; -} - -static void -send_sync_finished (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GError *error = NULL; - - if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error)) - set_error_if_http_failed (priv->msg, &error); - - priv->got_headers_cb = NULL; - priv->finished_cb = NULL; - - /* Wake up the main context iteration */ - g_source_attach (g_idle_source_new (), NULL); -} - -/** - * webkit_soup_http_input_stream_send: - * @httpstream: a #WebKitSoupHTTPInputStream - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @error: location to store the error occuring, or %NULL to ignore - * - * Synchronously sends the HTTP request associated with @stream, and - * reads the response headers. Call this after webkit_soup_http_input_stream_new() - * and before the first g_input_stream_read() if you want to check the - * HTTP status code before you start reading. - * - * Return value: %TRUE if msg has a successful (2xx) status, %FALSE if - * not. - **/ -gboolean -webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream); - GInputStream *istream = (GInputStream *)httpstream; - gboolean result; - - g_return_val_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream), FALSE); - - if (!g_input_stream_set_pending (istream, error)) - return FALSE; - - priv->got_headers_cb = send_sync_finished; - priv->finished_cb = send_sync_finished; - - result = webkit_soup_http_input_stream_send_internal (istream, cancellable, error); - g_input_stream_clear_pending (istream); - - return result; -} - -static gssize -webkit_soup_http_input_stream_read (GInputStream *stream, - void *buffer, - gsize count, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - if (priv->finished) - return 0; - - /* If there is data leftover from a previous read, return it. */ - if (priv->leftover_bufsize) - return read_from_leftover (priv, buffer, count); - - /* No leftover data, accept one chunk from the network */ - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count); - while (!priv->finished && priv->caller_nread == 0 && - !g_cancellable_is_cancelled (cancellable)) - g_main_context_iteration (priv->async_context, TRUE); - webkit_soup_http_input_stream_done_io (stream); - - if (priv->caller_nread > 0) - return priv->caller_nread; - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return -1; - else if (set_error_if_http_failed (priv->msg, error)) - return -1; - else - return 0; -} - -static gboolean -webkit_soup_http_input_stream_close (GInputStream *stream, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - if (!priv->finished) - soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED); - - return TRUE; -} - -static void -wrapper_callback (GObject *source_object, GAsyncResult *res, - gpointer user_data) -{ - GInputStream *stream = G_INPUT_STREAM (source_object); - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - g_input_stream_clear_pending (stream); - if (priv->outstanding_callback) - (*priv->outstanding_callback)(source_object, res, user_data); - priv->outstanding_callback = NULL; - g_object_unref (stream); -} - -static void -send_async_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - GError *error = NULL; - gboolean success; - - success = webkit_soup_http_input_stream_send_internal (G_INPUT_STREAM (object), - cancellable, &error); - g_simple_async_result_set_op_res_gboolean (res, success); - if (error) { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } -} - -static void -webkit_soup_http_input_stream_send_async_in_thread (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *res; - - res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, - webkit_soup_http_input_stream_send_async_in_thread); - g_simple_async_result_run_in_thread (res, send_async_thread, - io_priority, cancellable); - g_object_unref (res); -} - -static void -send_async_finished (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GSimpleAsyncResult *result; - GError *error = NULL; - - if (!g_cancellable_set_error_if_cancelled (priv->cancellable, &error)) - set_error_if_http_failed (priv->msg, &error); - - priv->got_headers_cb = NULL; - priv->finished_cb = NULL; - webkit_soup_http_input_stream_done_io (stream); - - result = priv->result; - priv->result = NULL; - - g_simple_async_result_set_op_res_gboolean (result, error == NULL); - if (error) { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } - g_simple_async_result_complete (result); - g_object_unref (result); -} - -static void -webkit_soup_http_input_stream_send_async_internal (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - - g_object_ref (stream); - priv->outstanding_callback = callback; - - /* If the session uses the default GMainContext, then we can do - * async I/O directly. But if it has its own main context, it's - * easier to just run it in another thread. - */ - if (soup_session_get_async_context (priv->session)) { - webkit_soup_http_input_stream_send_async_in_thread (stream, io_priority, cancellable, - wrapper_callback, user_data); - return; - } - - priv->got_headers_cb = send_async_finished; - priv->finished_cb = send_async_finished; - - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, NULL, 0); - priv->result = g_simple_async_result_new (G_OBJECT (stream), - wrapper_callback, user_data, - webkit_soup_http_input_stream_send_async); -} - -/** - * webkit_soup_http_input_stream_send_async: - * @httpstream: a #WebKitSoupHTTPInputStream - * @io_priority: the io priority of the request. - * @cancellable: optional #GCancellable object, %NULL to ignore. - * @callback: callback to call when the request is satisfied - * @user_data: the data to pass to callback function - * - * Asynchronously sends the HTTP request associated with @stream, and - * reads the response headers. Call this after webkit_soup_http_input_stream_new() - * and before the first g_input_stream_read_async() if you want to - * check the HTTP status code before you start reading. - **/ -void -webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GInputStream *istream = (GInputStream *)httpstream; - GError *error = NULL; - - g_return_if_fail (WEBKIT_IS_SOUP_HTTP_INPUT_STREAM (httpstream)); - - if (!g_input_stream_set_pending (istream, &error)) { - g_simple_async_report_gerror_in_idle (G_OBJECT (httpstream), - callback, - user_data, - error); - g_error_free (error); - return; - } - webkit_soup_http_input_stream_send_async_internal (istream, io_priority, cancellable, - callback, user_data); -} - -/** - * webkit_soup_http_input_stream_send_finish: - * @httpstream: a #WebKitSoupHTTPInputStream - * @result: a #GAsyncResult. - * @error: a #GError location to store the error occuring, or %NULL to - * ignore. - * - * Finishes a webkit_soup_http_input_stream_send_async() operation. - * - * Return value: %TRUE if the message was sent successfully and - * received a successful status code, %FALSE if not. - **/ -gboolean -webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), FALSE); - simple = G_SIMPLE_ASYNC_RESULT (result); - - g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_send_async, FALSE); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return g_simple_async_result_get_op_res_gboolean (simple); -} - -static void -read_async_done (GInputStream *stream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GSimpleAsyncResult *result; - GError *error = NULL; - - result = priv->result; - priv->result = NULL; - - if (g_cancellable_set_error_if_cancelled (priv->cancellable, &error) || - set_error_if_http_failed (priv->msg, &error)) { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } else - g_simple_async_result_set_op_res_gssize (result, priv->caller_nread); - - priv->got_chunk_cb = NULL; - priv->finished_cb = NULL; - priv->cancelled_cb = NULL; - webkit_soup_http_input_stream_done_io (stream); - - g_simple_async_result_complete (result); - g_object_unref (result); -} - -static void -webkit_soup_http_input_stream_read_async (GInputStream *stream, - void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (stream); - GSimpleAsyncResult *result; - - /* If the session uses the default GMainContext, then we can do - * async I/O directly. But if it has its own main context, we fall - * back to the async-via-sync-in-another-thread implementation. - */ - if (soup_session_get_async_context (priv->session)) { - G_INPUT_STREAM_CLASS (webkit_soup_http_input_stream_parent_class)-> - read_async (stream, buffer, count, io_priority, - cancellable, callback, user_data); - return; - } - - result = g_simple_async_result_new (G_OBJECT (stream), - callback, user_data, - webkit_soup_http_input_stream_read_async); - - if (priv->finished) { - g_simple_async_result_set_op_res_gssize (result, 0); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - return; - } - - if (priv->leftover_bufsize) { - gsize nread = read_from_leftover (priv, buffer, count); - g_simple_async_result_set_op_res_gssize (result, nread); - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); - return; - } - - priv->result = result; - - priv->got_chunk_cb = read_async_done; - priv->finished_cb = read_async_done; - priv->cancelled_cb = read_async_done; - webkit_soup_http_input_stream_prepare_for_io (stream, cancellable, buffer, count); -} - -static gssize -webkit_soup_http_input_stream_read_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (result), -1); - simple = G_SIMPLE_ASYNC_RESULT (result); - g_return_val_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_http_input_stream_read_async, -1); - - return g_simple_async_result_get_op_res_gssize (simple); -} - -static void -webkit_soup_http_input_stream_close_async (GInputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - gboolean success; - GError *error = NULL; - - result = g_simple_async_result_new (G_OBJECT (stream), - callback, user_data, - webkit_soup_http_input_stream_close_async); - success = webkit_soup_http_input_stream_close (stream, cancellable, &error); - g_simple_async_result_set_op_res_gboolean (result, success); - if (error) { - g_simple_async_result_set_from_error (result, error); - g_error_free (error); - } - - g_simple_async_result_complete_in_idle (result); - g_object_unref (result); -} - -static gboolean -webkit_soup_http_input_stream_close_finish (GInputStream *stream, - GAsyncResult *result, - GError **error) -{ - /* Failures handled in generic close_finish code */ - return TRUE; -} - -static goffset -webkit_soup_http_input_stream_tell (GSeekable *seekable) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable); - - return priv->offset; -} - -static gboolean -webkit_soup_http_input_stream_can_seek (GSeekable *seekable) -{ - return TRUE; -} - -extern void soup_message_io_cleanup (SoupMessage *msg); - -static gboolean -webkit_soup_http_input_stream_seek (GSeekable *seekable, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error) -{ - GInputStream *stream = G_INPUT_STREAM (seekable); - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (seekable); - char *range; - - if (type == G_SEEK_END) { - /* FIXME: we could send "bytes=-offset", but unless we - * know the Content-Length, we wouldn't be able to - * answer a tell() properly. We could find the - * Content-Length by doing a HEAD... - */ - - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "G_SEEK_END not currently supported"); - return FALSE; - } - - if (!g_input_stream_set_pending (stream, error)) - return FALSE; - - soup_session_cancel_message (priv->session, priv->msg, SOUP_STATUS_CANCELLED); - soup_message_io_cleanup (priv->msg); - - switch (type) { - case G_SEEK_CUR: - offset += priv->offset; - /* fall through */ - - case G_SEEK_SET: - range = g_strdup_printf ("bytes=%" G_GUINT64_FORMAT "-", (guint64)offset); - priv->offset = offset; - break; - - case G_SEEK_END: - range = NULL; /* keep compilers happy */ - g_return_val_if_reached (FALSE); - break; - - default: - g_return_val_if_reached (FALSE); - } - - soup_message_headers_remove (priv->msg->request_headers, "Range"); - soup_message_headers_append (priv->msg->request_headers, "Range", range); - g_free (range); - - webkit_soup_http_input_stream_queue_message (WEBKIT_SOUP_HTTP_INPUT_STREAM (stream)); - - g_input_stream_clear_pending (stream); - return TRUE; -} - -static gboolean -webkit_soup_http_input_stream_can_truncate (GSeekable *seekable) -{ - return FALSE; -} - -static gboolean -webkit_soup_http_input_stream_truncate (GSeekable *seekable, - goffset offset, - GCancellable *cancellable, - GError **error) -{ - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, - "Truncate not allowed on input stream"); - return FALSE; -} - -SoupMessage * -webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream) -{ - WebKitSoupHTTPInputStreamPrivate *priv = WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_PRIVATE (httpstream); - return priv->msg ? g_object_ref (priv->msg) : NULL; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h b/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h deleted file mode 100644 index 6b98559..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-http-input-stream.h +++ /dev/null @@ -1,77 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2006, 2007, 2009 Red Hat, Inc. - * Copyright (C) 2010 Igalia, S.L. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General - * Public License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place, Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ -#define __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ - -#include <gio/gio.h> -#include <libsoup/soup-types.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM (webkit_soup_http_input_stream_get_type ()) -#define WEBKIT_SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStream)) -#define WEBKIT_SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass)) -#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM)) -#define WEBKIT_IS_SOUP_HTTP_INPUT_STREAM_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM)) -#define WEBKIT_SOUP_HTTP_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), WEBKIT_TYPE_SOUP_HTTP_INPUT_STREAM, WebKitSoupHTTPInputStreamClass)) - -typedef struct WebKitSoupHTTPInputStream WebKitSoupHTTPInputStream; -typedef struct WebKitSoupHTTPInputStreamClass WebKitSoupHTTPInputStreamClass; - -struct WebKitSoupHTTPInputStream { - GInputStream parent; -}; - -struct WebKitSoupHTTPInputStreamClass { - GInputStreamClass parent_class; - - /* Padding for future expansion */ - void (*_g_reserved1)(void); - void (*_g_reserved2)(void); - void (*_g_reserved3)(void); - void (*_g_reserved4)(void); - void (*_g_reserved5)(void); -}; - -GType webkit_soup_http_input_stream_get_type (void) G_GNUC_CONST; - -WebKitSoupHTTPInputStream *webkit_soup_http_input_stream_new (SoupSession *session, - SoupMessage *msg); - -gboolean webkit_soup_http_input_stream_send (WebKitSoupHTTPInputStream *httpstream, - GCancellable *cancellable, - GError **error); - -void webkit_soup_http_input_stream_send_async (WebKitSoupHTTPInputStream *httpstream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean webkit_soup_http_input_stream_send_finish (WebKitSoupHTTPInputStream *httpstream, - GAsyncResult *result, - GError **error); - -SoupMessage *webkit_soup_http_input_stream_get_message (WebKitSoupHTTPInputStream *httpstream); - -G_END_DECLS - -#endif /* __WEBKIT_SOUP_HTTP_INPUT_STREAM_H__ */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-data.c b/Source/WebCore/platform/network/soup/cache/soup-request-data.c deleted file mode 100644 index ced5c4a..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-data.c +++ /dev/null @@ -1,170 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request-data.c: data: URI request object - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif -#include "soup-request-data.h" - -#include "soup-requester.h" -#include <libsoup/soup.h> -#include <glib/gi18n.h> - -G_DEFINE_TYPE (WebKitSoupRequestData, webkit_soup_request_data, WEBKIT_TYPE_SOUP_REQUEST) - -struct _WebKitSoupRequestDataPrivate { - gsize content_length; - char *content_type; -}; - -static void -webkit_soup_request_data_init (WebKitSoupRequestData *data) -{ - data->priv = G_TYPE_INSTANCE_GET_PRIVATE (data, WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataPrivate); -} - -static void -webkit_soup_request_data_finalize (GObject *object) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (object); - - g_free (data->priv->content_type); - - G_OBJECT_CLASS (webkit_soup_request_data_parent_class)->finalize (object); -} - -static gboolean -webkit_soup_request_data_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - return uri->host == NULL; -} - -static GInputStream * -webkit_soup_request_data_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request); - SoupURI *uri = webkit_soup_request_get_uri (request); - GInputStream *memstream; - const char *comma, *semi, *start, *end; - gboolean base64 = FALSE; - - gchar *uristr = soup_uri_to_string (uri, FALSE); - comma = strchr (uristr, ','); - if (comma && comma != uristr) { - /* Deal with MIME type / params */ - semi = memchr (uristr, ';', comma - uristr); - end = semi ? semi : comma; - - if (semi && !g_ascii_strncasecmp (semi, ";base64", MAX ((size_t) (comma - semi), strlen (";base64")))) - base64 = TRUE; - - if (end != uristr) - if (base64) - data->priv->content_type = g_strndup (uristr, end - uristr); - else - data->priv->content_type = - webkit_soup_request_uri_decoded_copy (uristr, end - uristr); - } - - memstream = g_memory_input_stream_new (); - - start = comma ? comma + 1 : uristr; - - if (*start) { - guchar *buf; - - if (base64) { - int inlen, state = 0; - guint save = 0; - - inlen = strlen (start); - buf = g_malloc0 (inlen * 3 / 4 + 3); - data->priv->content_length = - g_base64_decode_step (start, inlen, buf, - &state, &save); - if (state != 0) { - g_free (buf); - goto fail; - } - } else { - /* Cannot use g_uri_unescape_string nor - soup_uri_decode because we don't want to - fail for things like "%3E%%3C" -> ">%<" */ - buf = (guchar *)webkit_soup_request_uri_decoded_copy (start, strlen (start)); - if (!buf) - goto fail; - data->priv->content_length = strlen ((char *)buf); - } - - g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream), - buf, data->priv->content_length, - g_free); - } - g_free (uristr); - - return memstream; - - fail: - g_free (uristr); - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("Unable to decode URI: %s"), start); - g_object_unref (memstream); - return NULL; -} - -static goffset -webkit_soup_request_data_get_content_length (WebKitSoupRequest *request) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request); - - return data->priv->content_length; -} - -static const char * -webkit_soup_request_data_get_content_type (WebKitSoupRequest *request) -{ - WebKitSoupRequestData *data = WEBKIT_SOUP_REQUEST_DATA (request); - - return data->priv->content_type; -} - -static void -webkit_soup_request_data_class_init (WebKitSoupRequestDataClass *request_data_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_data_class); - WebKitSoupRequestClass *request_class = - WEBKIT_SOUP_REQUEST_CLASS (request_data_class); - - g_type_class_add_private (request_data_class, sizeof (WebKitSoupRequestDataPrivate)); - - object_class->finalize = webkit_soup_request_data_finalize; - - request_class->check_uri = webkit_soup_request_data_check_uri; - request_class->send = webkit_soup_request_data_send; - request_class->get_content_length = webkit_soup_request_data_get_content_length; - request_class->get_content_type = webkit_soup_request_data_get_content_type; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-data.h b/Source/WebCore/platform/network/soup/cache/soup-request-data.h deleted file mode 100644 index c9631a4..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-data.h +++ /dev/null @@ -1,52 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_DATA_H -#define WEBKIT_SOUP_REQUEST_DATA_H 1 - -#include "soup-request.h" - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST_DATA (webkit_soup_request_data_get_type ()) -#define WEBKIT_SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestData)) -#define WEBKIT_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass)) -#define WEBKIT_IS_SOUP_REQUEST_DATA(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_DATA)) -#define WEBKIT_IS_SOUP_REQUEST_DATA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_DATA)) -#define WEBKIT_SOUP_REQUEST_DATA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_DATA, WebKitSoupRequestDataClass)) - -typedef struct _WebKitSoupRequestDataPrivate WebKitSoupRequestDataPrivate; - -typedef struct { - WebKitSoupRequest parent; - - WebKitSoupRequestDataPrivate *priv; -} WebKitSoupRequestData; - -typedef struct { - WebKitSoupRequestClass parent; -} WebKitSoupRequestDataClass; - -GType webkit_soup_request_data_get_type (void); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_DATA_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-file.c b/Source/WebCore/platform/network/soup/cache/soup-request-file.c deleted file mode 100644 index 24ccb10..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-file.c +++ /dev/null @@ -1,331 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request-file.c: file: URI request object - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "soup-request-file.h" -#include "soup-directory-input-stream.h" -#include "soup-requester.h" -#include <glib/gi18n.h> - -G_DEFINE_TYPE (WebKitSoupRequestFile, webkit_soup_request_file, WEBKIT_TYPE_SOUP_REQUEST) - -struct _WebKitSoupRequestFilePrivate { - GFile *gfile; - - char *mime_type; - goffset size; -}; - -GFile * -webkit_soup_request_file_get_file (WebKitSoupRequestFile *file) -{ - return g_object_ref (file->priv->gfile); -} - -static void -webkit_soup_request_file_init (WebKitSoupRequestFile *file) -{ - file->priv = G_TYPE_INSTANCE_GET_PRIVATE (file, WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFilePrivate); - - file->priv->size = -1; -} - -static void -webkit_soup_request_file_finalize (GObject *object) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (object); - - if (file->priv->gfile) - g_object_unref (file->priv->gfile); - g_free (file->priv->mime_type); - - G_OBJECT_CLASS (webkit_soup_request_file_parent_class)->finalize (object); -} - -static gboolean -webkit_soup_request_file_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - /* "file:/foo" is not valid */ - if (!uri->host) - return FALSE; - - /* but it must be "file:///..." or "file://localhost/..." */ - if (uri->scheme == SOUP_URI_SCHEME_FILE && - *uri->host && - g_ascii_strcasecmp (uri->host, "localhost") != 0) - return FALSE; - - return TRUE; -} - -static void -webkit_soup_request_file_ftp_main_loop_quit (GObject *object, - GAsyncResult *result, - gpointer loop) -{ - g_main_loop_quit (loop); -} - -/* This is a somewhat hacky way to get FTP to almost work. The proper way to - * get FTP to _really_ work involves hacking GIO to have APIs to handle - * canoncial URLs. - */ -static GFile * -webkit_soup_request_file_ensure_file_ftp (SoupURI *uri, - GCancellable *cancellable, - GError **error) -{ - SoupURI *host; - char *s; - GFile *file, *result; - GMount *mount; - - host = soup_uri_copy_host (uri); - s = soup_uri_to_string (host, FALSE); - file = g_file_new_for_uri (s); - soup_uri_free (host); - g_free (s); - - mount = g_file_find_enclosing_mount (file, cancellable, error); - if (mount == NULL && g_file_supports_thread_contexts (file)) { - GMainContext *context = g_main_context_new (); - GMainLoop *loop = g_main_loop_new (context, FALSE); - - g_clear_error (error); - g_main_context_push_thread_default (context); - g_file_mount_enclosing_volume (file, - G_MOUNT_MOUNT_NONE, - NULL, /* FIXME! */ - cancellable, - webkit_soup_request_file_ftp_main_loop_quit, - loop); - g_main_loop_run (loop); - g_main_context_pop_thread_default (context); - g_main_loop_unref (loop); - g_main_context_unref (context); - mount = g_file_find_enclosing_mount (file, cancellable, error); - } - if (mount == NULL) - return NULL; - g_object_unref (file); - - file = g_mount_get_default_location (mount); - g_object_unref (mount); - - s = g_strdup (uri->path); - if (strchr (s, ';')) - *strchr (s, ';') = 0; - - result = g_file_resolve_relative_path (file, s); - g_free (s); - g_object_unref (file); - - return result; -} - -static gboolean -webkit_soup_request_file_ensure_file (WebKitSoupRequestFile *file, - GCancellable *cancellable, - GError **error) -{ - SoupURI *uri; - - if (file->priv->gfile) - return TRUE; - - uri = webkit_soup_request_get_uri (WEBKIT_SOUP_REQUEST (file)); - if (uri->scheme == SOUP_URI_SCHEME_FILE) { - /* We cannot use soup_uri_decode as it incorrectly - * returns NULL for incorrectly encoded URIs (that - * could be valid filenames). This will be hopefully - * shipped in libsoup 2.32.1 but we want to land this - * first. TODO: replace uri_decoded_copy by - * soup_uri_decode when the required libsoup version - * is bumped out to 2.32.1 - */ - gchar *decoded_uri = webkit_soup_request_uri_decoded_copy (uri->path, strlen (uri->path)); - - if (decoded_uri) { - /* Do not use new_for_uri() as the decoded URI - * could not be a valid URI - */ - file->priv->gfile = g_file_new_for_path (decoded_uri); - g_free (decoded_uri); - } - - return TRUE; - } else if (uri->scheme == SOUP_URI_SCHEME_FTP) { - file->priv->gfile = webkit_soup_request_file_ensure_file_ftp (uri, - cancellable, - error); - return file->priv->gfile != NULL; - } - - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME, - _ ("Unsupported URI scheme '%s'"), uri->scheme); - return FALSE; -} - -static GInputStream * -webkit_soup_request_file_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request); - GInputStream *stream; - GError *my_error = NULL; - - if (!webkit_soup_request_file_ensure_file (file, cancellable, error)) - return NULL; - - stream = G_INPUT_STREAM (g_file_read (file->priv->gfile, - cancellable, &my_error)); - if (stream == NULL) { - if (g_error_matches (my_error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY)) { - GFileEnumerator *enumerator; - g_clear_error (&my_error); - enumerator = g_file_enumerate_children (file->priv->gfile, - "*", - G_FILE_QUERY_INFO_NONE, - cancellable, - error); - if (enumerator) { - stream = webkit_soup_directory_input_stream_new (enumerator, - webkit_soup_request_get_uri (request)); - g_object_unref (enumerator); - file->priv->mime_type = g_strdup ("text/html"); - } - } else { - g_propagate_error (error, my_error); - } - } else { - GFileInfo *info = g_file_query_info (file->priv->gfile, - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," - G_FILE_ATTRIBUTE_STANDARD_SIZE, - 0, cancellable, NULL); - if (info) { - const char *content_type; - file->priv->size = g_file_info_get_size (info); - content_type = g_file_info_get_content_type (info); - - if (content_type) - file->priv->mime_type = g_content_type_get_mime_type (content_type); - g_object_unref (info); - } - } - - return stream; -} - -static void -webkit_soup_request_file_send_async_thread (GSimpleAsyncResult *res, - GObject *object, - GCancellable *cancellable) -{ - GInputStream *stream; - WebKitSoupRequest *request; - GError *error = NULL; - - request = WEBKIT_SOUP_REQUEST (object); - - stream = webkit_soup_request_file_send (request, cancellable, &error); - - if (stream == NULL) { - g_simple_async_result_set_from_error (res, error); - g_error_free (error); - } else { - g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref); - } -} - -static void -webkit_soup_request_file_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *res; - - res = g_simple_async_result_new (G_OBJECT (request), callback, user_data, webkit_soup_request_file_send_async); - - g_simple_async_result_run_in_thread (res, webkit_soup_request_file_send_async_thread, G_PRIORITY_DEFAULT, cancellable); - g_object_unref (res); -} - -static GInputStream * -webkit_soup_request_file_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); - - g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == webkit_soup_request_file_send_async); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); -} - -static goffset -webkit_soup_request_file_get_content_length (WebKitSoupRequest *request) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request); - - return file->priv->size; -} - -static const char * -webkit_soup_request_file_get_content_type (WebKitSoupRequest *request) -{ - WebKitSoupRequestFile *file = WEBKIT_SOUP_REQUEST_FILE (request); - - if (!file->priv->mime_type) - return "application/octet-stream"; - - return file->priv->mime_type; -} - -static void -webkit_soup_request_file_class_init (WebKitSoupRequestFileClass *request_file_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_file_class); - WebKitSoupRequestClass *request_class = - WEBKIT_SOUP_REQUEST_CLASS (request_file_class); - - g_type_class_add_private (request_file_class, sizeof (WebKitSoupRequestFilePrivate)); - - object_class->finalize = webkit_soup_request_file_finalize; - - request_class->check_uri = webkit_soup_request_file_check_uri; - request_class->send = webkit_soup_request_file_send; - request_class->send_async = webkit_soup_request_file_send_async; - request_class->send_finish = webkit_soup_request_file_send_finish; - request_class->get_content_length = webkit_soup_request_file_get_content_length; - request_class->get_content_type = webkit_soup_request_file_get_content_type; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-file.h b/Source/WebCore/platform/network/soup/cache/soup-request-file.h deleted file mode 100644 index 459e82a..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-file.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_FILE_H -#define WEBKIT_SOUP_REQUEST_FILE_H 1 - -#include "soup-request.h" - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST_FILE (webkit_soup_request_file_get_type ()) -#define WEBKIT_SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFile)) -#define WEBKIT_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass)) -#define WEBKIT_IS_SOUP_REQUEST_FILE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_FILE)) -#define WEBKIT_IS_SOUP_REQUEST_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_FILE)) -#define WEBKIT_SOUP_REQUEST_FILE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_FILE, WebKitSoupRequestFileClass)) - -typedef struct _WebKitSoupRequestFilePrivate WebKitSoupRequestFilePrivate; - -typedef struct { - WebKitSoupRequest parent; - - WebKitSoupRequestFilePrivate *priv; -} WebKitSoupRequestFile; - -typedef struct { - WebKitSoupRequestClass parent; -} WebKitSoupRequestFileClass; - -GType webkit_soup_request_file_get_type (void); - -GFile *webkit_soup_request_file_get_file (WebKitSoupRequestFile *file); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_FILE_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-http.c b/Source/WebCore/platform/network/soup/cache/soup-request-http.c deleted file mode 100644 index 777fd72..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-http.c +++ /dev/null @@ -1,356 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request-http.c: http: URI request object - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib/gi18n.h> - -#include "soup-cache.h" -#include "soup-cache-private.h" -#include "soup-http-input-stream.h" -#include "soup-request-http.h" - -G_DEFINE_TYPE (WebKitSoupRequestHTTP, webkit_soup_request_http, WEBKIT_TYPE_SOUP_REQUEST) - -struct _WebKitSoupRequestHTTPPrivate { - SoupMessage *msg; -}; - -/** - * webkit_soup_request_http_get_message: - * @http: a #WebKitSoupRequestHTTP object - * - * Gets a new reference to the #SoupMessage associated to this SoupRequest - * - * Returns: a new reference to the #SoupMessage - **/ -SoupMessage * -webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http) -{ - g_return_val_if_fail (WEBKIT_IS_SOUP_REQUEST_HTTP (http), NULL); - - return g_object_ref (http->priv->msg); -} - -static void -webkit_soup_request_http_init (WebKitSoupRequestHTTP *http) -{ - http->priv = G_TYPE_INSTANCE_GET_PRIVATE (http, WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPPrivate); -} - -static gboolean -webkit_soup_request_http_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - if (!SOUP_URI_VALID_FOR_HTTP (uri)) - return FALSE; - - http->priv->msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); - return TRUE; -} - -static void -webkit_soup_request_http_finalize (GObject *object) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (object); - - if (http->priv->msg) - g_object_unref (http->priv->msg); - - G_OBJECT_CLASS (webkit_soup_request_http_parent_class)->finalize (object); -} - -static GInputStream * -webkit_soup_request_http_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupHTTPInputStream *httpstream; - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), http->priv->msg); - if (!webkit_soup_http_input_stream_send (httpstream, cancellable, error)) { - g_object_unref (httpstream); - return NULL; - } - return (GInputStream *)httpstream; -} - - -static void -sent_async (GObject *source, GAsyncResult *result, gpointer user_data) -{ - WebKitSoupHTTPInputStream *httpstream = WEBKIT_SOUP_HTTP_INPUT_STREAM (source); - GSimpleAsyncResult *simple = user_data; - GError *error = NULL; - - if (webkit_soup_http_input_stream_send_finish (httpstream, result, &error)) { - g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref); - } else { - g_simple_async_result_set_from_error (simple, error); - g_error_free (error); - g_object_unref (httpstream); - } - g_simple_async_result_complete (simple); - g_object_unref (simple); -} - - -typedef struct { - WebKitSoupRequestHTTP *req; - SoupMessage *original; - GCancellable *cancellable; - GAsyncReadyCallback callback; - gpointer user_data; -} ConditionalHelper; - - -static void -conditional_get_ready_cb (SoupSession *session, SoupMessage *msg, gpointer user_data) -{ - ConditionalHelper *helper = (ConditionalHelper *)user_data; - GSimpleAsyncResult *simple; - WebKitSoupHTTPInputStream *httpstream; - - simple = g_simple_async_result_new (G_OBJECT (helper->req), - helper->callback, helper->user_data, - conditional_get_ready_cb); - - if (msg->status_code == SOUP_STATUS_NOT_MODIFIED) { - WebKitSoupCache *cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE); - - httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, msg); - if (httpstream) { - const gchar *content_type; - - g_simple_async_result_set_op_res_gpointer (simple, httpstream, g_object_unref); - - soup_message_got_headers (helper->original); - - /* FIXME: Uncomment this when this becomes part of libsoup - * if (!soup_message_disables_feature(helper->original, SOUP_TYPE_CONTENT_SNIFFER)) { - * const gchar *content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); - * soup_message_content_sniffed (helper->original, content_type, NULL); - * } - */ - content_type = soup_message_headers_get_content_type (msg->response_headers, NULL); - soup_message_content_sniffed (helper->original, content_type, NULL); - - g_simple_async_result_complete (simple); - - soup_message_finished (helper->original); - - g_object_unref (simple); - } else { - /* Ask again for the resource, somehow the cache cannot locate it */ - httpstream = webkit_soup_http_input_stream_new (session, helper->original); - webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT, - helper->cancellable, sent_async, simple); - } - } else { - /* It is in the cache but it was modified remotely */ - httpstream = webkit_soup_http_input_stream_new (session, helper->original); - webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT, - helper->cancellable, sent_async, simple); - } - - g_object_unref (helper->req); - g_object_unref (helper->original); - g_slice_free (ConditionalHelper, helper); -} - -typedef struct { - WebKitSoupRequestHTTP *http; - GAsyncReadyCallback callback; - gpointer user_data; - WebKitSoupHTTPInputStream *httpstream; -} SendAsyncHelper; - -static void webkit_soup_request_http_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -static gboolean -send_async_cb (gpointer data) -{ - GSimpleAsyncResult *simple; - SendAsyncHelper *helper = (SendAsyncHelper *)data; - const gchar *content_type; - - simple = g_simple_async_result_new (G_OBJECT (helper->http), - helper->callback, helper->user_data, - webkit_soup_request_http_send_async); - g_simple_async_result_set_op_res_gpointer (simple, helper->httpstream, g_object_unref); - - /* Update message status */ - soup_message_set_status (helper->http->priv->msg, SOUP_STATUS_OK); - - /* Issue signals */ - soup_message_got_headers (helper->http->priv->msg); - - /* FIXME: Uncomment this when this becomes part of libsoup - * if (!soup_message_disables_feature(helper->http->priv->msg, SOUP_TYPE_CONTENT_SNIFFER)) { - * const gchar *content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL); - * soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL); - * } - */ - content_type = soup_message_headers_get_content_type (helper->http->priv->msg->response_headers, NULL); - soup_message_content_sniffed (helper->http->priv->msg, content_type, NULL); - - g_simple_async_result_complete (simple); - - soup_message_finished (helper->http->priv->msg); - - g_object_unref (simple); - - g_object_unref (helper->http); - g_slice_free (SendAsyncHelper, helper); - - return FALSE; -} - -static void -webkit_soup_request_http_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - WebKitSoupHTTPInputStream *httpstream; - GSimpleAsyncResult *simple; - SoupSession *session; - WebKitSoupCache *cache; - - session = webkit_soup_request_get_session (request); - cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE); - - if (cache) { - WebKitSoupCacheResponse response; - - response = webkit_soup_cache_has_response (cache, http->priv->msg); - if (response == WEBKIT_SOUP_CACHE_RESPONSE_FRESH) { - WebKitSoupHTTPInputStream *httpstream; - - httpstream = (WebKitSoupHTTPInputStream *) - webkit_soup_cache_send_response (cache, SOUP_MESSAGE (http->priv->msg)); - - /* Cached resource file could have been deleted outside - */ - if (httpstream) { - /* Do return the stream asynchronously as in - * the other cases. It's not enough to use - * g_simple_async_result_complete_in_idle as - * the signals must be also emitted - * asynchronously - */ - SendAsyncHelper *helper = g_slice_new (SendAsyncHelper); - helper->http = g_object_ref (http); - helper->callback = callback; - helper->user_data = user_data; - helper->httpstream = httpstream; - g_timeout_add (0, send_async_cb, helper); - return; - } - } else if (response == WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION) { - SoupMessage *conditional_msg; - ConditionalHelper *helper; - - conditional_msg = webkit_soup_cache_generate_conditional_request (cache, http->priv->msg); - - helper = g_slice_new0 (ConditionalHelper); - helper->req = g_object_ref (http); - helper->original = g_object_ref (http->priv->msg); - helper->cancellable = cancellable; - helper->callback = callback; - helper->user_data = user_data; - soup_session_queue_message (session, conditional_msg, - conditional_get_ready_cb, - helper); - return; - } - } - - simple = g_simple_async_result_new (G_OBJECT (http), - callback, user_data, - webkit_soup_request_http_send_async); - httpstream = webkit_soup_http_input_stream_new (webkit_soup_request_get_session (request), - http->priv->msg); - webkit_soup_http_input_stream_send_async (httpstream, G_PRIORITY_DEFAULT, - cancellable, sent_async, simple); -} - -static GInputStream * -webkit_soup_request_http_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_http_send_async) || g_simple_async_result_is_valid (result, G_OBJECT (request), conditional_get_ready_cb), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple)); -} - -static goffset -webkit_soup_request_http_get_content_length (WebKitSoupRequest *request) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - return soup_message_headers_get_content_length (http->priv->msg->response_headers); -} - -static const char * -webkit_soup_request_http_get_content_type (WebKitSoupRequest *request) -{ - WebKitSoupRequestHTTP *http = WEBKIT_SOUP_REQUEST_HTTP (request); - - return soup_message_headers_get_content_type (http->priv->msg->response_headers, NULL); -} - -static void -webkit_soup_request_http_class_init (WebKitSoupRequestHTTPClass *request_http_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_http_class); - WebKitSoupRequestClass *request_class = - WEBKIT_SOUP_REQUEST_CLASS (request_http_class); - - g_type_class_add_private (request_http_class, sizeof (WebKitSoupRequestHTTPPrivate)); - - object_class->finalize = webkit_soup_request_http_finalize; - - request_class->check_uri = webkit_soup_request_http_check_uri; - request_class->send = webkit_soup_request_http_send; - request_class->send_async = webkit_soup_request_http_send_async; - request_class->send_finish = webkit_soup_request_http_send_finish; - request_class->get_content_length = webkit_soup_request_http_get_content_length; - request_class->get_content_type = webkit_soup_request_http_get_content_type; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request-http.h b/Source/WebCore/platform/network/soup/cache/soup-request-http.h deleted file mode 100644 index a06a821..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request-http.h +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_HTTP_H -#define WEBKIT_SOUP_REQUEST_HTTP_H 1 - -#include "soup-request.h" - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST_HTTP (webkit_soup_request_http_get_type ()) -#define WEBKIT_SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTP)) -#define WEBKIT_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass)) -#define WEBKIT_IS_SOUP_REQUEST_HTTP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), WEBKIT_TYPE_SOUP_REQUEST_HTTP)) -#define WEBKIT_IS_SOUP_REQUEST_HTTP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST_HTTP)) -#define WEBKIT_SOUP_REQUEST_HTTP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST_HTTP, WebKitSoupRequestHTTPClass)) - -typedef struct _WebKitSoupRequestHTTPPrivate WebKitSoupRequestHTTPPrivate; - -typedef struct { - WebKitSoupRequest parent; - - WebKitSoupRequestHTTPPrivate *priv; -} WebKitSoupRequestHTTP; - -typedef struct { - WebKitSoupRequestClass parent; -} WebKitSoupRequestHTTPClass; - -GType webkit_soup_request_http_get_type (void); - -SoupMessage *webkit_soup_request_http_get_message (WebKitSoupRequestHTTP *http); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_HTTP_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-request.c b/Source/WebCore/platform/network/soup/cache/soup-request.c deleted file mode 100644 index 46b9f5a..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request.c +++ /dev/null @@ -1,312 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-request.c: Protocol-independent streaming request interface - * - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010, 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <glib/gi18n.h> - -#include "soup-request.h" -#include "soup-requester.h" - -/** - * SECTION:soup-request - * @short_description: Protocol-independent streaming request interface - * - * FIXME - **/ - -/** - * WebKitSoupRequest: - * - * FIXME - * - * Since: 2.30 - **/ - -static void webkit_soup_request_initable_interface_init (GInitableIface *initable_interface); - -G_DEFINE_TYPE_WITH_CODE (WebKitSoupRequest, webkit_soup_request, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, - webkit_soup_request_initable_interface_init)) - -enum { - PROP_0, - PROP_URI, - PROP_SESSION -}; - -struct _WebKitSoupRequestPrivate { - SoupURI *uri; - SoupSession *session; -}; - -static void -webkit_soup_request_init (WebKitSoupRequest *request) -{ - request->priv = G_TYPE_INSTANCE_GET_PRIVATE (request, WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestPrivate); -} - -static void -webkit_soup_request_finalize (GObject *object) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object); - - if (request->priv->uri) - soup_uri_free (request->priv->uri); - if (request->priv->session) - g_object_unref (request->priv->session); - - G_OBJECT_CLASS (webkit_soup_request_parent_class)->finalize (object); -} - -static void -webkit_soup_request_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object); - - switch (prop_id) { - case PROP_URI: - if (request->priv->uri) - soup_uri_free (request->priv->uri); - request->priv->uri = g_value_dup_boxed (value); - break; - case PROP_SESSION: - if (request->priv->session) - g_object_unref (request->priv->session); - request->priv->session = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -webkit_soup_request_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (object); - - switch (prop_id) { - case PROP_URI: - g_value_set_boxed (value, request->priv->uri); - break; - case PROP_SESSION: - g_value_set_object (value, request->priv->session); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static gboolean -webkit_soup_request_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - WebKitSoupRequest *request = WEBKIT_SOUP_REQUEST (initable); - gboolean ok; - - if (!request->priv->uri) { - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("No URI provided")); - return FALSE; - } - - ok = WEBKIT_SOUP_REQUEST_GET_CLASS (initable)-> - check_uri (request, request->priv->uri, error); - - if (!ok && error) { - char *uri_string = soup_uri_to_string (request->priv->uri, FALSE); - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("Invalid '%s' URI: %s"), - request->priv->uri->scheme, - uri_string); - g_free (uri_string); - } - - return ok; -} - -static gboolean -webkit_soup_request_default_check_uri (WebKitSoupRequest *request, - SoupURI *uri, - GError **error) -{ - return TRUE; -} - -/* Default implementation: assume the sync implementation doesn't block */ -static void -webkit_soup_request_default_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - simple = g_simple_async_result_new (G_OBJECT (request), - callback, user_data, - webkit_soup_request_default_send_async); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); -} - -static GInputStream * -webkit_soup_request_default_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (request), webkit_soup_request_default_send_async), NULL); - - return webkit_soup_request_send (request, NULL, error); -} - -GInputStream * -webkit_soup_request_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)-> - send (request, cancellable, error); -} - -void -webkit_soup_request_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - WEBKIT_SOUP_REQUEST_GET_CLASS (request)-> - send_async (request, cancellable, callback, user_data); -} - -GInputStream * -webkit_soup_request_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)-> - send_finish (request, result, error); -} - -static void -webkit_soup_request_class_init (WebKitSoupRequestClass *request_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (request_class); - - g_type_class_add_private (request_class, sizeof (WebKitSoupRequestPrivate)); - - request_class->check_uri = webkit_soup_request_default_check_uri; - request_class->send_async = webkit_soup_request_default_send_async; - request_class->send_finish = webkit_soup_request_default_send_finish; - - object_class->finalize = webkit_soup_request_finalize; - object_class->set_property = webkit_soup_request_set_property; - object_class->get_property = webkit_soup_request_get_property; - - g_object_class_install_property ( - object_class, PROP_URI, - g_param_spec_boxed (WEBKIT_SOUP_REQUEST_URI, - "URI", - "The request URI", - SOUP_TYPE_URI, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - g_object_class_install_property ( - object_class, PROP_SESSION, - g_param_spec_object (WEBKIT_SOUP_REQUEST_SESSION, - "Session", - "The request's session", - SOUP_TYPE_SESSION, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); -} - -static void -webkit_soup_request_initable_interface_init (GInitableIface *initable_interface) -{ - initable_interface->init = webkit_soup_request_initable_init; -} - -SoupURI * -webkit_soup_request_get_uri (WebKitSoupRequest *request) -{ - return request->priv->uri; -} - -SoupSession * -webkit_soup_request_get_session (WebKitSoupRequest *request) -{ - return request->priv->session; -} - -goffset -webkit_soup_request_get_content_length (WebKitSoupRequest *request) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_length (request); -} - -const char * -webkit_soup_request_get_content_type (WebKitSoupRequest *request) -{ - return WEBKIT_SOUP_REQUEST_GET_CLASS (request)->get_content_type (request); -} - -#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10) -#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2])) - -/* Copy&pasted from libsoup's soup-uri.c after applying the patch in - * https://bugzilla.gnome.org/show_bug.cgi?id=630540. We need this - * instead of soup_uri_decode() as it incorrectly returns NULL for - * incorrectly encoded URLs. TODO: remove this when required libsoup - * version is bumped out to 2.32.1 - */ -gchar * -webkit_soup_request_uri_decoded_copy (const char *part, int length) -{ - unsigned char *s, *d; - char *decoded = g_strndup (part, length); - - s = d = (unsigned char *)decoded; - do { - if (*s == '%') { - if (!g_ascii_isxdigit (s[1]) || - !g_ascii_isxdigit (s[2])) { - *d++ = *s; - continue; - } - *d++ = HEXCHAR (s); - s += 2; - } else - *d++ = *s; - } while (*s++); - - return decoded; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-request.h b/Source/WebCore/platform/network/soup/cache/soup-request.h deleted file mode 100644 index 837d8f4..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-request.h +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2009 Red Hat, Inc. - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUEST_H -#define WEBKIT_SOUP_REQUEST_H 1 - -#include <libsoup/soup.h> -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUEST (webkit_soup_request_get_type ()) -#define WEBKIT_SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequest)) -#define WEBKIT_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass)) -#define WEBKIT_IS_SOUP_REQUEST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUEST)) -#define WEBKIT_IS_SOUP_REQUEST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), WEBKIT_TYPE_SOUP_REQUEST)) -#define WEBKIT_SOUP_REQUEST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUEST, WebKitSoupRequestClass)) - -typedef struct _WebKitSoupRequest WebKitSoupRequest; -typedef struct _WebKitSoupRequestPrivate WebKitSoupRequestPrivate; -typedef struct _WebKitSoupRequestClass WebKitSoupRequestClass; - -struct _WebKitSoupRequest { - GObject parent; - - WebKitSoupRequestPrivate *priv; -}; - -struct _WebKitSoupRequestClass { - GObjectClass parent; - - gboolean (*check_uri)(WebKitSoupRequest *req_base, - SoupURI *uri, - GError **error); - - GInputStream * (*send)(WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error); - void (*send_async)(WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - GInputStream * (*send_finish)(WebKitSoupRequest *request, - GAsyncResult *result, - GError **error); - - goffset (*get_content_length)(WebKitSoupRequest *request); - const char * (*get_content_type)(WebKitSoupRequest *request); -}; - -GType webkit_soup_request_get_type (void); - -#define WEBKIT_SOUP_REQUEST_URI "uri" -#define WEBKIT_SOUP_REQUEST_SESSION "session" - -GInputStream *webkit_soup_request_send (WebKitSoupRequest *request, - GCancellable *cancellable, - GError **error); -void webkit_soup_request_send_async (WebKitSoupRequest *request, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -GInputStream *webkit_soup_request_send_finish (WebKitSoupRequest *request, - GAsyncResult *result, - GError **error); - -SoupURI *webkit_soup_request_get_uri (WebKitSoupRequest *request); -SoupSession *webkit_soup_request_get_session (WebKitSoupRequest *request); - -goffset webkit_soup_request_get_content_length (WebKitSoupRequest *request); -const char *webkit_soup_request_get_content_type (WebKitSoupRequest *request); - -/* Used by WebKitSoupRequestFile and WebKitSoupRequestData. Ideally - * should be located in some util file but I'll place it here as it - * will be removed with libsoup 2.32.1 that will ship fixed versions - * of soup_uri_decode/normalize - */ -gchar *webkit_soup_request_uri_decoded_copy (const char *part, int length); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUEST_H */ diff --git a/Source/WebCore/platform/network/soup/cache/soup-requester.c b/Source/WebCore/platform/network/soup/cache/soup-requester.c deleted file mode 100644 index 4d8a860..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-requester.c +++ /dev/null @@ -1,188 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-requester.c: - * - * Copyright (C) 2010, 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "soup-requester.h" - -#include "soup-request-data.h" -#include "soup-request-file.h" -#include "soup-request-http.h" -#include <glib/gi18n.h> -#include <libsoup/soup.h> - -struct _WebKitSoupRequesterPrivate { - GHashTable *request_types; -}; - -#define WEBKIT_SOUP_REQUESTER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterPrivate)) - -G_DEFINE_TYPE (WebKitSoupRequester, webkit_soup_requester, G_TYPE_OBJECT) - -static void webkit_soup_requester_init (WebKitSoupRequester *requester) -{ - requester->priv = WEBKIT_SOUP_REQUESTER_GET_PRIVATE (requester); - - requester->priv->request_types = 0; -} - -static void finalize (GObject *object) -{ - WebKitSoupRequester *requester = WEBKIT_SOUP_REQUESTER (object); - - if (requester->priv->request_types) - g_hash_table_destroy (requester->priv->request_types); - - G_OBJECT_CLASS (webkit_soup_requester_parent_class)->finalize (object); -} - -static void webkit_soup_requester_class_init (WebKitSoupRequesterClass *requester_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (requester_class); - - g_type_class_add_private (requester_class, sizeof (WebKitSoupRequesterPrivate)); - - /* virtual method override */ - object_class->finalize = finalize; -} - -static void init_request_types (WebKitSoupRequesterPrivate *priv) -{ - if (priv->request_types) - return; - - priv->request_types = g_hash_table_new_full (soup_str_case_hash, - soup_str_case_equal, - g_free, 0); - g_hash_table_insert (priv->request_types, g_strdup ("file"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE)); - g_hash_table_insert (priv->request_types, g_strdup ("data"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_DATA)); - g_hash_table_insert (priv->request_types, g_strdup ("http"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP)); - g_hash_table_insert (priv->request_types, g_strdup ("https"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_HTTP)); - g_hash_table_insert (priv->request_types, g_strdup ("ftp"), - GSIZE_TO_POINTER (WEBKIT_TYPE_SOUP_REQUEST_FILE)); -} - -WebKitSoupRequester *webkit_soup_requester_new (void) -{ - return (WebKitSoupRequester *)g_object_new (WEBKIT_TYPE_SOUP_REQUESTER, NULL); -} - -WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester, const char *uriString, SoupSession *session, GError **error) -{ - SoupURI *uri = NULL; - WebKitSoupRequest *req; - - uri = soup_uri_new (uriString); - if (!uri) { - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_BAD_URI, - _ ("Could not parse URI '%s'"), uriString); - return 0; - } - - req = webkit_soup_requester_request_uri (requester, uri, session, error); - soup_uri_free (uri); - return req; -} - -WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester, SoupURI *uri, SoupSession *session, GError **error) -{ - GType requestType; - - g_return_val_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester), 0); - - init_request_types (requester->priv); - requestType = (GType)GPOINTER_TO_SIZE (g_hash_table_lookup (requester->priv->request_types, uri->scheme)); - if (!requestType) { - g_set_error (error, WEBKIT_SOUP_ERROR, WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME, - _ ("Unsupported URI scheme '%s'"), uri->scheme); - return 0; - } - - if (g_type_is_a (requestType, G_TYPE_INITABLE)) { - return (WebKitSoupRequest *)g_initable_new (requestType, 0, error, - "uri", uri, - "session", session, - NULL); - } else { - return (WebKitSoupRequest *)g_object_new (requestType, - "uri", uri, - "session", session, - NULL); - } -} - -/* RFC 2396, 3.1 */ -static gboolean -soup_scheme_is_valid (const char *scheme) -{ - if (scheme == NULL || - !g_ascii_isalpha (*scheme)) - return FALSE; - - scheme++; - while (*scheme) { - if (!g_ascii_isalpha (*scheme) && - !g_ascii_isdigit (*scheme) && - *scheme != '+' && - *scheme != '-' && - *scheme != '.') - return FALSE; - scheme++; - } - return TRUE; -} - -void -webkit_soup_requester_add_protocol (WebKitSoupRequester *requester, - const char *scheme, - GType request_type) -{ - g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester)); - g_return_if_fail (soup_scheme_is_valid (scheme)); - - init_request_types (requester->priv); - g_hash_table_insert (requester->priv->request_types, g_strdup (scheme), - GSIZE_TO_POINTER (request_type)); -} - -void -webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester, - const char *scheme) -{ - g_return_if_fail (WEBKIT_IS_SOUP_REQUESTER (requester)); - g_return_if_fail (soup_scheme_is_valid (scheme)); - - init_request_types (requester->priv); - g_hash_table_remove (requester->priv->request_types, scheme); -} - -GQuark -webkit_soup_error_quark (void) -{ - static GQuark error; - if (!error) - error = g_quark_from_static_string ("webkit_soup_error_quark"); - return error; -} diff --git a/Source/WebCore/platform/network/soup/cache/soup-requester.h b/Source/WebCore/platform/network/soup/cache/soup-requester.h deleted file mode 100644 index 71ff103..0000000 --- a/Source/WebCore/platform/network/soup/cache/soup-requester.h +++ /dev/null @@ -1,81 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_REQUESTER_H -#define WEBKIT_SOUP_REQUESTER_H 1 - -#include "soup-request.h" -#include <libsoup/soup.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_REQUESTER (webkit_soup_requester_get_type ()) -#define WEBKIT_SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequester)) -#define WEBKIT_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass)) -#define WEBKIT_IS_SOUP_REQUESTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER)) -#define WEBKIT_IS_SOUP_REQUESTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_REQUESTER)) -#define WEBKIT_SOUP_REQUESTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_REQUESTER, WebKitSoupRequesterClass)) - -#define WEBKIT_SOUP_ERROR webkit_soup_error_quark () - -typedef enum { - WEBKIT_SOUP_ERROR_BAD_URI, - WEBKIT_SOUP_ERROR_UNSUPPORTED_URI_SCHEME -} WebKitSoupError; - -typedef struct _WebKitSoupRequester WebKitSoupRequester; -typedef struct _WebKitSoupRequesterPrivate WebKitSoupRequesterPrivate; - -struct _WebKitSoupRequester { - GObject parent; - - WebKitSoupRequesterPrivate *priv; -}; - -typedef struct { - GObjectClass parent_class; -} WebKitSoupRequesterClass; - -GType webkit_soup_requester_get_type (void); - -WebKitSoupRequester *webkit_soup_requester_new (void); - -GQuark webkit_soup_error_quark (void); - -WebKitSoupRequest *webkit_soup_requester_request (WebKitSoupRequester *requester, - const char *uriString, - SoupSession *session, - GError **error); - -WebKitSoupRequest *webkit_soup_requester_request_uri (WebKitSoupRequester *requester, - SoupURI *uri, - SoupSession *session, - GError **error); - -void webkit_soup_requester_add_protocol (WebKitSoupRequester *requester, - const char *scheme, - GType request_type); - -void webkit_soup_requester_remove_protocol (WebKitSoupRequester *requester, - const char *scheme); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_REQUESTER_H */ diff --git a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h deleted file mode 100644 index 8af8de2..0000000 --- a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h +++ /dev/null @@ -1,42 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-cache-private.h: - * - * Copyright (C) 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_CACHE_PRIVATE_H -#define WEBKIT_SOUP_CACHE_PRIVATE_H 1 - -#include "soup-cache.h" -#include <libsoup/soup-message.h> - -G_BEGIN_DECLS - -WebKitSoupCacheResponse webkit_soup_cache_has_response (WebKitSoupCache *cache, - SoupMessage *msg); -GInputStream *webkit_soup_cache_send_response (WebKitSoupCache *cache, - SoupMessage *msg); -WebKitSoupCacheability webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, - SoupMessage *msg); -SoupMessage *webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, - SoupMessage *original); - -G_END_DECLS - -#endif /* WEBKIT_SOUP_CACHE_PRIVATE_H */ diff --git a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c deleted file mode 100644 index b96428d..0000000 --- a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.c +++ /dev/null @@ -1,1677 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-cache.c - * - * Copyright (C) 2009, 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -/* TODO: - * - Need to hook the feature in the sync SoupSession. - * - Need more tests. - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include "soup-cache.h" -#include "soup-cache-private.h" -#include <libsoup/soup.h> -#include <gio/gio.h> -#include <stdlib.h> - -static SoupSessionFeatureInterface *webkit_soup_cache_default_feature_interface; -static void webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data); - -#define DEFAULT_MAX_SIZE 50 * 1024 * 1024 -#define MAX_ENTRY_DATA_PERCENTAGE 10 /* Percentage of the total size - of the cache that can be - filled by a single entry */ - -typedef struct _WebKitSoupCacheEntry { - char *key; - char *filename; - guint freshness_lifetime; - gboolean must_revalidate; - GString *data; - gsize pos; - gsize length; - time_t corrected_initial_age; - time_t response_time; - gboolean writing; - gboolean dirty; - gboolean got_body; - gboolean being_validated; - SoupMessageHeaders *headers; - GOutputStream *stream; - GError *error; - guint hits; - GCancellable *cancellable; -} WebKitSoupCacheEntry; - -struct _WebKitSoupCachePrivate { - char *cache_dir; - GHashTable *cache; - guint n_pending; - SoupSession *session; - WebKitSoupCacheType cache_type; - guint size; - guint max_size; - guint max_entry_data_size; /* Computed value. Here for performance reasons */ - GList *lru_start; -}; - -typedef struct { - WebKitSoupCache *cache; - WebKitSoupCacheEntry *entry; - SoupMessage *msg; - gulong got_chunk_handler; - gulong got_body_handler; - gulong restarted_handler; -} WebKitSoupCacheWritingFixture; - -enum { - PROP_0, - PROP_CACHE_DIR, - PROP_CACHE_TYPE -}; - -#define WEBKIT_SOUP_CACHE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCachePrivate)) - -G_DEFINE_TYPE_WITH_CODE (WebKitSoupCache, webkit_soup_cache, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE, - webkit_soup_cache_session_feature_init)) - -static gboolean webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry); -static void make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add); -static gboolean cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add); - -static WebKitSoupCacheability -get_cacheability (WebKitSoupCache *cache, SoupMessage *msg) -{ - WebKitSoupCacheability cacheability; - const char *cache_control; - - /* 1. The request method must be cacheable */ - if (msg->method == SOUP_METHOD_GET) - cacheability = WEBKIT_SOUP_CACHE_CACHEABLE; - else if (msg->method == SOUP_METHOD_HEAD || - msg->method == SOUP_METHOD_TRACE || - msg->method == SOUP_METHOD_CONNECT) - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - else - return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES); - - cache_control = soup_message_headers_get (msg->response_headers, "Cache-Control"); - if (cache_control) { - GHashTable *hash; - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - - hash = soup_header_parse_param_list (cache_control); - - /* Shared caches MUST NOT store private resources */ - if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) { - if (g_hash_table_lookup_extended (hash, "private", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - } - } - - /* 2. The 'no-store' cache directive does not appear in the - * headers - */ - if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - } - - /* This does not appear in section 2.1, but I think it makes - * sense to check it too? - */ - if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - } - - soup_header_free_param_list (hash); - } - - switch (msg->status_code) { - case SOUP_STATUS_PARTIAL_CONTENT: - /* We don't cache partial responses, but they only - * invalidate cached full responses if the headers - * don't match. - */ - cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE; - break; - - case SOUP_STATUS_NOT_MODIFIED: - /* A 304 response validates an existing cache entry */ - cacheability = WEBKIT_SOUP_CACHE_VALIDATES; - break; - - case SOUP_STATUS_MULTIPLE_CHOICES: - case SOUP_STATUS_MOVED_PERMANENTLY: - case SOUP_STATUS_GONE: - /* FIXME: cacheable unless indicated otherwise */ - cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE; - break; - - case SOUP_STATUS_FOUND: - case SOUP_STATUS_TEMPORARY_REDIRECT: - /* FIXME: cacheable if explicitly indicated */ - cacheability = WEBKIT_SOUP_CACHE_UNCACHEABLE; - break; - - case SOUP_STATUS_SEE_OTHER: - case SOUP_STATUS_FORBIDDEN: - case SOUP_STATUS_NOT_FOUND: - case SOUP_STATUS_METHOD_NOT_ALLOWED: - return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES); - - default: - /* Any 5xx status or any 4xx status not handled above - * is uncacheable but doesn't break the cache. - */ - if ((msg->status_code >= SOUP_STATUS_BAD_REQUEST && - msg->status_code <= SOUP_STATUS_FAILED_DEPENDENCY) || - msg->status_code >= SOUP_STATUS_INTERNAL_SERVER_ERROR) - return WEBKIT_SOUP_CACHE_UNCACHEABLE; - - /* An unrecognized 2xx, 3xx, or 4xx response breaks - * the cache. - */ - if ((msg->status_code > SOUP_STATUS_PARTIAL_CONTENT && - msg->status_code < SOUP_STATUS_MULTIPLE_CHOICES) || - (msg->status_code > SOUP_STATUS_TEMPORARY_REDIRECT && - msg->status_code < SOUP_STATUS_INTERNAL_SERVER_ERROR)) - return (WEBKIT_SOUP_CACHE_UNCACHEABLE | WEBKIT_SOUP_CACHE_INVALIDATES); - break; - } - - return cacheability; -} - -static void -webkit_soup_cache_entry_free (WebKitSoupCacheEntry *entry, gboolean purge) -{ - if (purge) { - GFile *file = g_file_new_for_path (entry->filename); - g_file_delete (file, NULL, NULL); - g_object_unref (file); - } - - g_free (entry->filename); - entry->filename = NULL; - g_free (entry->key); - entry->key = NULL; - - if (entry->headers) { - soup_message_headers_free (entry->headers); - entry->headers = NULL; - } - - if (entry->data) { - g_string_free (entry->data, TRUE); - entry->data = NULL; - } - if (entry->error) { - g_error_free (entry->error); - entry->error = NULL; - } - if (entry->cancellable) { - g_object_unref (entry->cancellable); - entry->cancellable = NULL; - } - - g_slice_free (WebKitSoupCacheEntry, entry); -} - -static void -copy_headers (const char *name, const char *value, SoupMessageHeaders *headers) -{ - soup_message_headers_append (headers, name, value); -} - -static void -update_headers (const char *name, const char *value, SoupMessageHeaders *headers) -{ - if (soup_message_headers_get (headers, name)) - soup_message_headers_replace (headers, name, value); - else - soup_message_headers_append (headers, name, value); -} - -static guint -webkit_soup_cache_entry_get_current_age (WebKitSoupCacheEntry *entry) -{ - time_t now = time (NULL); - time_t resident_time; - - resident_time = now - entry->response_time; - return entry->corrected_initial_age + resident_time; -} - -static gboolean -webkit_soup_cache_entry_is_fresh_enough (WebKitSoupCacheEntry *entry, gint min_fresh) -{ - guint limit = (min_fresh == -1) ? webkit_soup_cache_entry_get_current_age (entry) : (guint) min_fresh; - return entry->freshness_lifetime > limit; -} - -static char * -soup_message_get_cache_key (SoupMessage *msg) -{ - SoupURI *uri = soup_message_get_uri (msg); - return soup_uri_to_string (uri, FALSE); -} - -static void -webkit_soup_cache_entry_set_freshness (WebKitSoupCacheEntry *entry, SoupMessage *msg, WebKitSoupCache *cache) -{ - const char *cache_control; - const char *expires, *date, *last_modified; - - cache_control = soup_message_headers_get (entry->headers, "Cache-Control"); - if (cache_control) { - const char *max_age, *s_maxage; - gint64 freshness_lifetime = 0; - GHashTable *hash; - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - - hash = soup_header_parse_param_list (cache_control); - - /* Should we re-validate the entry when it goes stale */ - entry->must_revalidate = g_hash_table_lookup_extended (hash, "must-revalidate", NULL, NULL); - - /* Section 2.3.1 */ - if (priv->cache_type == WEBKIT_SOUP_CACHE_SHARED) { - s_maxage = g_hash_table_lookup (hash, "s-maxage"); - if (s_maxage) { - freshness_lifetime = g_ascii_strtoll (s_maxage, NULL, 10); - if (freshness_lifetime) { - /* Implies proxy-revalidate. TODO: is it true? */ - entry->must_revalidate = TRUE; - soup_header_free_param_list (hash); - return; - } - } - } - - /* If 'max-age' cache directive is present, use that */ - max_age = g_hash_table_lookup (hash, "max-age"); - if (max_age) - freshness_lifetime = g_ascii_strtoll (max_age, NULL, 10); - - if (freshness_lifetime) { - entry->freshness_lifetime = (guint)MIN (freshness_lifetime, G_MAXUINT32); - soup_header_free_param_list (hash); - return; - } - - soup_header_free_param_list (hash); - } - - /* If the 'Expires' response header is present, use its value - * minus the value of the 'Date' response header - */ - expires = soup_message_headers_get (entry->headers, "Expires"); - date = soup_message_headers_get (entry->headers, "Date"); - if (expires && date) { - SoupDate *expires_d, *date_d; - time_t expires_t, date_t; - - expires_d = soup_date_new_from_string (expires); - if (expires_d) { - date_d = soup_date_new_from_string (date); - - expires_t = soup_date_to_time_t (expires_d); - date_t = soup_date_to_time_t (date_d); - - soup_date_free (expires_d); - soup_date_free (date_d); - - if (expires_t && date_t) { - entry->freshness_lifetime = (guint)MAX (expires_t - date_t, 0); - return; - } - } else { - /* If Expires is not a valid date we should - treat it as already expired, see section - 3.3 */ - entry->freshness_lifetime = 0; - return; - } - } - - /* Otherwise an heuristic may be used */ - - /* Heuristics MUST NOT be used with these status codes - (section 2.3.1.1) */ - if (msg->status_code != SOUP_STATUS_OK && - msg->status_code != SOUP_STATUS_NON_AUTHORITATIVE && - msg->status_code != SOUP_STATUS_PARTIAL_CONTENT && - msg->status_code != SOUP_STATUS_MULTIPLE_CHOICES && - msg->status_code != SOUP_STATUS_MOVED_PERMANENTLY && - msg->status_code != SOUP_STATUS_GONE) - goto expire; - - /* TODO: attach warning 113 if response's current_age is more - than 24h (section 2.3.1.1) when using heuristics */ - - /* Last-Modified based heuristic */ - last_modified = soup_message_headers_get (entry->headers, "Last-Modified"); - if (last_modified) { - SoupDate *soup_date; - time_t now, last_modified_t; - - soup_date = soup_date_new_from_string (last_modified); - last_modified_t = soup_date_to_time_t (soup_date); - now = time (NULL); - -#define HEURISTIC_FACTOR 0.1 /* From Section 2.3.1.1 */ - - entry->freshness_lifetime = MAX (0, (now - last_modified_t) * HEURISTIC_FACTOR); - soup_date_free (soup_date); - } - - return; - - expire: - /* If all else fails, make the entry expire immediately */ - entry->freshness_lifetime = 0; -} - -static WebKitSoupCacheEntry * -webkit_soup_cache_entry_new (WebKitSoupCache *cache, SoupMessage *msg, time_t request_time, time_t response_time) -{ - WebKitSoupCacheEntry *entry; - SoupMessageHeaders *headers; - const char *date; - char *md5; - - entry = g_slice_new0 (WebKitSoupCacheEntry); - entry->dirty = FALSE; - entry->writing = FALSE; - entry->got_body = FALSE; - entry->being_validated = FALSE; - entry->data = g_string_new (NULL); - entry->pos = 0; - entry->error = NULL; - - /* key & filename */ - entry->key = soup_message_get_cache_key (msg); - md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, entry->key, -1); - entry->filename = g_build_filename (cache->priv->cache_dir, md5, NULL); - g_free (md5); - - /* Headers */ - headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); - soup_message_headers_foreach (msg->response_headers, - (SoupMessageHeadersForeachFunc)copy_headers, - headers); - entry->headers = headers; - - /* LRU list */ - entry->hits = 0; - - /* Section 2.3.1, Freshness Lifetime */ - webkit_soup_cache_entry_set_freshness (entry, msg, cache); - - /* Section 2.3.2, Calculating Age */ - date = soup_message_headers_get (entry->headers, "Date"); - - if (date) { - SoupDate *soup_date; - const char *age; - time_t date_value, apparent_age, corrected_received_age, response_delay, age_value = 0; - - soup_date = soup_date_new_from_string (date); - date_value = soup_date_to_time_t (soup_date); - soup_date_free (soup_date); - - age = soup_message_headers_get (entry->headers, "Age"); - if (age) - age_value = g_ascii_strtoll (age, NULL, 10); - - entry->response_time = response_time; - apparent_age = MAX (0, entry->response_time - date_value); - corrected_received_age = MAX (apparent_age, age_value); - response_delay = entry->response_time - request_time; - entry->corrected_initial_age = corrected_received_age + response_delay; - } else { - /* Is this correct ? */ - entry->corrected_initial_age = time (NULL); - } - - return entry; -} - -static void -webkit_soup_cache_writing_fixture_free (WebKitSoupCacheWritingFixture *fixture) -{ - /* Free fixture. And disconnect signals, we don't want to - listen to more SoupMessage events as we're finished with - this resource */ - if (g_signal_handler_is_connected (fixture->msg, fixture->got_chunk_handler)) - g_signal_handler_disconnect (fixture->msg, fixture->got_chunk_handler); - if (g_signal_handler_is_connected (fixture->msg, fixture->got_body_handler)) - g_signal_handler_disconnect (fixture->msg, fixture->got_body_handler); - if (g_signal_handler_is_connected (fixture->msg, fixture->restarted_handler)) - g_signal_handler_disconnect (fixture->msg, fixture->restarted_handler); - g_object_unref (fixture->msg); - g_object_unref (fixture->cache); - g_slice_free (WebKitSoupCacheWritingFixture, fixture); -} - -static void -close_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture) -{ - WebKitSoupCacheEntry *entry = fixture->entry; - WebKitSoupCache *cache = fixture->cache; - GOutputStream *stream = G_OUTPUT_STREAM (source); - goffset content_length; - - g_warn_if_fail (entry->error == NULL); - - /* FIXME: what do we do on error ? */ - - if (stream) { - g_output_stream_close_finish (stream, result, NULL); - g_object_unref (stream); - } - entry->stream = NULL; - - content_length = soup_message_headers_get_content_length (entry->headers); - - /* If the process was cancelled, then delete the entry from - the cache. Do it also if the size of a chunked resource is - too much for the cache */ - if (g_cancellable_is_cancelled (entry->cancellable)) { - entry->dirty = FALSE; - webkit_soup_cache_entry_remove (cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - entry = NULL; - } else if ((soup_message_headers_get_encoding (entry->headers) == SOUP_ENCODING_CHUNKED) || - entry->length != (gsize) content_length) { - /** Two options here: - * - * 1. "chunked" data, entry was temporarily added to - * cache (as content-length is 0) and now that we have - * the actual size we have to evaluate if we want it - * in the cache or not - * - * 2. Content-Length has a different value than actual - * length, means that the content was encoded for - * transmission (typically compressed) and thus we - * have to substract the content-length value that was - * added to the cache and add the unencoded length - **/ - gint length_to_add = entry->length - content_length; - - /* Make room in cache if needed */ - if (cache_accepts_entries_of_size (cache, length_to_add)) { - make_room_for_new_entry (cache, length_to_add); - - cache->priv->size += length_to_add; - } else { - entry->dirty = FALSE; - webkit_soup_cache_entry_remove (cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - entry = NULL; - } - } - - if (entry) { - /* Get rid of the GString in memory for the resource now */ - if (entry->data) { - g_string_free (entry->data, TRUE); - entry->data = NULL; - } - - entry->dirty = FALSE; - entry->writing = FALSE; - entry->got_body = FALSE; - entry->pos = 0; - - g_object_unref (entry->cancellable); - entry->cancellable = NULL; - } - - cache->priv->n_pending--; - - /* Frees */ - webkit_soup_cache_writing_fixture_free (fixture); -} - -static void -write_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture) -{ - GOutputStream *stream = G_OUTPUT_STREAM (source); - GError *error = NULL; - gssize write_size; - WebKitSoupCacheEntry *entry = fixture->entry; - - if (g_cancellable_is_cancelled (entry->cancellable)) { - g_output_stream_close_async (stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); - return; - } - - write_size = g_output_stream_write_finish (stream, result, &error); - if (write_size <= 0 || error) { - if (error) - entry->error = error; - g_output_stream_close_async (stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); - /* FIXME: We should completely stop caching the - resource at this point */ - } else { - entry->pos += write_size; - - /* Are we still writing and is there new data to write - already ? */ - if (entry->data && entry->pos < entry->data->len) { - g_output_stream_write_async (entry->stream, - entry->data->str + entry->pos, - entry->data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } else { - entry->writing = FALSE; - - if (entry->got_body) { - /* If we already received 'got-body' - and we have written all the data, - we can close the stream */ - g_output_stream_close_async (entry->stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); - } - } - } -} - -static void -msg_got_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, WebKitSoupCacheWritingFixture *fixture) -{ - WebKitSoupCacheEntry *entry = fixture->entry; - - g_return_if_fail (chunk->data && chunk->length); - g_return_if_fail (entry); - - /* Ignore this if the writing or appending was cancelled */ - if (!g_cancellable_is_cancelled (entry->cancellable)) { - g_string_append_len (entry->data, chunk->data, chunk->length); - entry->length = entry->data->len; - - if (!cache_accepts_entries_of_size (fixture->cache, entry->length)) { - /* Quickly cancel the caching of the resource */ - g_cancellable_cancel (entry->cancellable); - } - } - - /* FIXME: remove the error check when we cancel the caching at - the first write error */ - /* Only write if the entry stream is ready */ - if (entry->writing == FALSE && entry->error == NULL && entry->stream) { - GString *data = entry->data; - entry->writing = TRUE; - g_output_stream_write_async (entry->stream, - data->str + entry->pos, - data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } -} - -static void -msg_got_body_cb (SoupMessage *msg, WebKitSoupCacheWritingFixture *fixture) -{ - WebKitSoupCacheEntry *entry = fixture->entry; - g_return_if_fail (entry); - - entry->got_body = TRUE; - - if (!entry->stream && entry->pos != entry->length) - /* The stream is not ready to be written but we still - have data to write, we'll write it when the stream - is opened for writing */ - return; - - - if (entry->pos != entry->length) { - /* If we still have data to write, write it, - write_ready_cb will close the stream */ - if (entry->writing == FALSE && entry->error == NULL && entry->stream) { - g_output_stream_write_async (entry->stream, - entry->data->str + entry->pos, - entry->data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } - return; - } - - if (entry->stream && !entry->writing) - g_output_stream_close_async (entry->stream, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)close_ready_cb, - fixture); -} - -static gboolean -webkit_soup_cache_entry_remove (WebKitSoupCache *cache, WebKitSoupCacheEntry *entry) -{ - GList *lru_item; - - /* if (entry->dirty && !g_cancellable_is_cancelled (entry->cancellable)) { */ - if (entry->dirty) { - g_cancellable_cancel (entry->cancellable); - return FALSE; - } - - g_assert (!entry->dirty); - g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache)); - - /* Remove from cache */ - if (!g_hash_table_remove (cache->priv->cache, entry->key)) - return FALSE; - - /* Remove from LRU */ - lru_item = g_list_find (cache->priv->lru_start, entry); - cache->priv->lru_start = g_list_delete_link (cache->priv->lru_start, lru_item); - - /* Adjust cache size */ - cache->priv->size -= entry->length; - - g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache)); - - return TRUE; -} - -static gint -lru_compare_func (gconstpointer a, gconstpointer b) -{ - WebKitSoupCacheEntry *entry_a = (WebKitSoupCacheEntry *)a; - WebKitSoupCacheEntry *entry_b = (WebKitSoupCacheEntry *)b; - - /** The rationale of this sorting func is - * - * 1. sort by hits -> LRU algorithm, then - * - * 2. sort by freshness lifetime, we better discard first - * entries that are close to expire - * - * 3. sort by size, replace first small size resources as they - * are cheaper to download - **/ - - /* Sort by hits */ - if (entry_a->hits != entry_b->hits) - return entry_a->hits - entry_b->hits; - - /* Sort by freshness_lifetime */ - if (entry_a->freshness_lifetime != entry_b->freshness_lifetime) - return entry_a->freshness_lifetime - entry_b->freshness_lifetime; - - /* Sort by size */ - return entry_a->length - entry_b->length; -} - -static gboolean -cache_accepts_entries_of_size (WebKitSoupCache *cache, guint length_to_add) -{ - /* We could add here some more heuristics. TODO: review how - this is done by other HTTP caches */ - - return length_to_add <= cache->priv->max_entry_data_size; -} - -static void -make_room_for_new_entry (WebKitSoupCache *cache, guint length_to_add) -{ - GList *lru_entry = cache->priv->lru_start; - - /* Check that there is enough room for the new entry. This is - an approximation as we're not working out the size of the - cache file or the size of the headers for performance - reasons. TODO: check if that would be really that expensive */ - - while (lru_entry && - (length_to_add + cache->priv->size > cache->priv->max_size)) { - WebKitSoupCacheEntry *old_entry = (WebKitSoupCacheEntry *)lru_entry->data; - - /* Discard entries. Once cancelled resources will be - * freed in close_ready_cb - */ - if (webkit_soup_cache_entry_remove (cache, old_entry)) { - webkit_soup_cache_entry_free (old_entry, TRUE); - lru_entry = cache->priv->lru_start; - } else - lru_entry = g_list_next (lru_entry); - } -} - -static gboolean -webkit_soup_cache_entry_insert_by_key (WebKitSoupCache *cache, - const char *key, - WebKitSoupCacheEntry *entry, - gboolean sort) -{ - guint length_to_add = 0; - - if (soup_message_headers_get_encoding (entry->headers) != SOUP_ENCODING_CHUNKED) - length_to_add = soup_message_headers_get_content_length (entry->headers); - - /* Check if we are going to store the resource depending on its size */ - if (length_to_add) { - if (!cache_accepts_entries_of_size (cache, length_to_add)) - return FALSE; - - /* Make room for new entry if needed */ - make_room_for_new_entry (cache, length_to_add); - } - - g_hash_table_insert (cache->priv->cache, g_strdup (key), entry); - - /* Compute new cache size */ - cache->priv->size += length_to_add; - - /* Update LRU */ - if (sort) - cache->priv->lru_start = g_list_insert_sorted (cache->priv->lru_start, entry, lru_compare_func); - else - cache->priv->lru_start = g_list_prepend (cache->priv->lru_start, entry); - - g_assert (g_list_length (cache->priv->lru_start) == g_hash_table_size (cache->priv->cache)); - - return TRUE; -} - -static void -msg_restarted_cb (SoupMessage *msg, WebKitSoupCacheEntry *entry) -{ - /* FIXME: What should we do here exactly? */ -} - -static void -append_to_ready_cb (GObject *source, GAsyncResult *result, WebKitSoupCacheWritingFixture *fixture) -{ - GFile *file = (GFile *)source; - GOutputStream *stream; - WebKitSoupCacheEntry *entry = fixture->entry; - - stream = (GOutputStream *)g_file_append_to_finish (file, result, &entry->error); - - if (g_cancellable_is_cancelled (entry->cancellable) || entry->error) { - fixture->cache->priv->n_pending--; - entry->dirty = FALSE; - webkit_soup_cache_entry_remove (fixture->cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - webkit_soup_cache_writing_fixture_free (fixture); - return; - } - - entry->stream = g_object_ref (stream); - g_object_unref (file); - - /* If we already got all the data we have to initiate the - writing here, since we won't get more 'got-chunk' - signals */ - if (entry->got_body) { - GString *data = entry->data; - - /* It could happen that reading the data from server - was completed before this happens. In that case - there is no data */ - if (data) { - entry->writing = TRUE; - g_output_stream_write_async (entry->stream, - data->str + entry->pos, - data->len - entry->pos, - G_PRIORITY_LOW, - entry->cancellable, - (GAsyncReadyCallback)write_ready_cb, - fixture); - } - } -} - -typedef struct { - time_t request_time; - SoupSessionFeature *feature; - gulong got_headers_handler; -} RequestHelper; - -static void -msg_got_headers_cb (SoupMessage *msg, gpointer user_data) -{ - WebKitSoupCache *cache; - WebKitSoupCacheability cacheable; - RequestHelper *helper; - time_t request_time, response_time; - - response_time = time (NULL); - - helper = (RequestHelper *)user_data; - cache = WEBKIT_SOUP_CACHE (helper->feature); - request_time = helper->request_time; - g_signal_handlers_disconnect_by_func (msg, msg_got_headers_cb, user_data); - g_slice_free (RequestHelper, helper); - - cacheable = webkit_soup_cache_get_cacheability (cache, msg); - - if (cacheable & WEBKIT_SOUP_CACHE_CACHEABLE) { - WebKitSoupCacheEntry *entry; - char *key; - GFile *file; - WebKitSoupCacheWritingFixture *fixture; - - /* Check if we are already caching this resource */ - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - if (entry && entry->dirty) - return; - - /* Create a new entry, deleting any old one if present */ - if (entry) { - webkit_soup_cache_entry_remove (cache, entry); - webkit_soup_cache_entry_free (entry, TRUE); - } - - entry = webkit_soup_cache_entry_new (cache, msg, request_time, response_time); - entry->hits = 1; - - /* Do not continue if it can not be stored */ - if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, TRUE)) { - webkit_soup_cache_entry_free (entry, TRUE); - return; - } - - fixture = g_slice_new0 (WebKitSoupCacheWritingFixture); - fixture->cache = g_object_ref (cache); - fixture->entry = entry; - fixture->msg = g_object_ref (msg); - - /* We connect now to these signals and buffer the data - if it comes before the file is ready for writing */ - fixture->got_chunk_handler = - g_signal_connect (msg, "got-chunk", G_CALLBACK (msg_got_chunk_cb), fixture); - fixture->got_body_handler = - g_signal_connect (msg, "got-body", G_CALLBACK (msg_got_body_cb), fixture); - fixture->restarted_handler = - g_signal_connect (msg, "restarted", G_CALLBACK (msg_restarted_cb), entry); - - /* Prepare entry */ - file = g_file_new_for_path (entry->filename); - cache->priv->n_pending++; - - entry->dirty = TRUE; - entry->cancellable = g_cancellable_new (); - g_file_append_to_async (file, 0, - G_PRIORITY_LOW, entry->cancellable, - (GAsyncReadyCallback)append_to_ready_cb, - fixture); - } else if (cacheable & WEBKIT_SOUP_CACHE_INVALIDATES) { - char *key; - WebKitSoupCacheEntry *entry; - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - if (entry) { - if (webkit_soup_cache_entry_remove (cache, entry)) - webkit_soup_cache_entry_free (entry, TRUE); - } - } else if (cacheable & WEBKIT_SOUP_CACHE_VALIDATES) { - char *key; - WebKitSoupCacheEntry *entry; - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - /* It's possible to get a CACHE_VALIDATES with no - * entry in the hash table. This could happen if for - * example the soup client is the one creating the - * conditional request. - */ - if (entry) { - entry->being_validated = FALSE; - - /* We update the headers of the existing cache item, - plus its age */ - soup_message_headers_foreach (msg->response_headers, - (SoupMessageHeadersForeachFunc)update_headers, - entry->headers); - webkit_soup_cache_entry_set_freshness (entry, msg, cache); - } - } -} - -GInputStream * -webkit_soup_cache_send_response (WebKitSoupCache *cache, SoupMessage *msg) -{ - char *key; - WebKitSoupCacheEntry *entry; - char *current_age; - GInputStream *stream = NULL; - GFile *file; - - g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL); - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL); - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - g_return_val_if_fail (entry, NULL); - - /* If we are told to send a response from cache any validation - in course is over by now */ - entry->being_validated = FALSE; - - /* Headers */ - soup_message_headers_foreach (entry->headers, - (SoupMessageHeadersForeachFunc)update_headers, - msg->response_headers); - - /* Add 'Age' header with the current age */ - current_age = g_strdup_printf ("%d", webkit_soup_cache_entry_get_current_age (entry)); - soup_message_headers_replace (msg->response_headers, - "Age", - current_age); - g_free (current_age); - - /* TODO: the original idea was to save reads, but current code - assumes that a stream is always returned. Need to reach - some agreement here. Also we have to handle the situation - were the file was no longer there (for example files - removed without notifying the cache */ - file = g_file_new_for_path (entry->filename); - stream = (GInputStream *)g_file_read (file, NULL, NULL); - - return stream; -} - -static void -request_started (SoupSessionFeature *feature, SoupSession *session, - SoupMessage *msg, SoupSocket *socket) -{ - RequestHelper *helper = g_slice_new0 (RequestHelper); - helper->request_time = time (NULL); - helper->feature = feature; - helper->got_headers_handler = g_signal_connect (msg, "got-headers", - G_CALLBACK (msg_got_headers_cb), - helper); -} - -static void -attach (SoupSessionFeature *feature, SoupSession *session) -{ - WebKitSoupCache *cache = WEBKIT_SOUP_CACHE (feature); - cache->priv->session = session; - - webkit_soup_cache_default_feature_interface->attach (feature, session); -} - -static void -webkit_soup_cache_session_feature_init (SoupSessionFeatureInterface *feature_interface, - gpointer interface_data) -{ - webkit_soup_cache_default_feature_interface = - g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE); - - feature_interface->attach = attach; - feature_interface->request_started = request_started; -} - -static void -webkit_soup_cache_init (WebKitSoupCache *cache) -{ - WebKitSoupCachePrivate *priv; - - priv = cache->priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - - priv->cache = g_hash_table_new_full (g_str_hash, - g_str_equal, - (GDestroyNotify)g_free, - NULL); - - /* LRU */ - priv->lru_start = NULL; - - /* */ - priv->n_pending = 0; - - /* Cache size */ - priv->max_size = DEFAULT_MAX_SIZE; - priv->max_entry_data_size = priv->max_size / MAX_ENTRY_DATA_PERCENTAGE; - priv->size = 0; -} - -static void -remove_cache_item (gpointer data, - gpointer user_data) -{ - WebKitSoupCache *cache = (WebKitSoupCache *) user_data; - WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; - - if (webkit_soup_cache_entry_remove (cache, entry)) - webkit_soup_cache_entry_free (entry, FALSE); -} - -static void -webkit_soup_cache_finalize (GObject *object) -{ - WebKitSoupCachePrivate *priv; - GList *entries; - - priv = WEBKIT_SOUP_CACHE (object)->priv; - - // Cannot use g_hash_table_foreach as callbacks must not modify the hash table - entries = g_hash_table_get_values (priv->cache); - g_list_foreach (entries, remove_cache_item, object); - g_list_free (entries); - - g_hash_table_destroy (priv->cache); - g_free (priv->cache_dir); - - g_list_free (priv->lru_start); - priv->lru_start = NULL; - - G_OBJECT_CLASS (webkit_soup_cache_parent_class)->finalize (object); -} - -static void -webkit_soup_cache_set_property (GObject *object, guint prop_id, - const GValue *value, GParamSpec *pspec) -{ - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv; - - switch (prop_id) { - case PROP_CACHE_DIR: - priv->cache_dir = g_value_dup_string (value); - /* Create directory if it does not exist (FIXME: should we?) */ - if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - g_mkdir_with_parents (priv->cache_dir, 0700); - break; - case PROP_CACHE_TYPE: - priv->cache_type = g_value_get_enum (value); - /* TODO: clear private entries and issue a warning if moving to shared? */ - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -webkit_soup_cache_get_property (GObject *object, guint prop_id, - GValue *value, GParamSpec *pspec) -{ - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE (object)->priv; - - switch (prop_id) { - case PROP_CACHE_DIR: - g_value_set_string (value, priv->cache_dir); - break; - case PROP_CACHE_TYPE: - g_value_set_enum (value, priv->cache_type); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - -static void -webkit_soup_cache_constructed (GObject *object) -{ - WebKitSoupCachePrivate *priv; - - priv = WEBKIT_SOUP_CACHE (object)->priv; - - if (!priv->cache_dir) { - /* Set a default cache dir, different for each user */ - priv->cache_dir = g_build_filename (g_get_user_cache_dir (), - "httpcache", - NULL); - if (!g_file_test (priv->cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) - g_mkdir_with_parents (priv->cache_dir, 0700); - } - - if (G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed) - G_OBJECT_CLASS (webkit_soup_cache_parent_class)->constructed (object); -} - -#define WEBKIT_SOUP_CACHE_TYPE_TYPE (webkit_soup_cache_type_get_type ()) -static GType -webkit_soup_cache_type_get_type (void) -{ - static GType cache_type = 0; - - static const GEnumValue cache_types[] = { - { WEBKIT_SOUP_CACHE_SINGLE_USER, "Single user cache", "user" }, - { WEBKIT_SOUP_CACHE_SHARED, "Shared cache", "shared" }, - { 0, NULL, NULL } - }; - - if (!cache_type) { - cache_type = g_enum_register_static ("WebKitSoupCacheTypeType", cache_types); - } - return cache_type; -} - -static void -webkit_soup_cache_class_init (WebKitSoupCacheClass *cache_class) -{ - GObjectClass *gobject_class = (GObjectClass *)cache_class; - - gobject_class->finalize = webkit_soup_cache_finalize; - gobject_class->constructed = webkit_soup_cache_constructed; - gobject_class->set_property = webkit_soup_cache_set_property; - gobject_class->get_property = webkit_soup_cache_get_property; - - cache_class->get_cacheability = get_cacheability; - - g_object_class_install_property (gobject_class, PROP_CACHE_DIR, - g_param_spec_string ("cache-dir", - "Cache directory", - "The directory to store the cache files", - NULL, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (gobject_class, PROP_CACHE_TYPE, - g_param_spec_enum ("cache-type", - "Cache type", - "Whether the cache is private or shared", - WEBKIT_SOUP_CACHE_TYPE_TYPE, - WEBKIT_SOUP_CACHE_SINGLE_USER, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); - - g_type_class_add_private (cache_class, sizeof (WebKitSoupCachePrivate)); -} - -/** - * webkit_soup_cache_new: - * @cache_dir: the directory to store the cached data, or %NULL to use the default one - * @cache_type: the #WebKitSoupCacheType of the cache - * - * Creates a new #WebKitSoupCache. - * - * Returns: a new #WebKitSoupCache - * - * Since: 2.28 - **/ -WebKitSoupCache * -webkit_soup_cache_new (const char *cache_dir, WebKitSoupCacheType cache_type) -{ - return g_object_new (WEBKIT_TYPE_SOUP_CACHE, - "cache-dir", cache_dir, - "cache-type", cache_type, - NULL); -} - -/** - * webkit_soup_cache_has_response: - * @cache: a #WebKitSoupCache - * @msg: a #SoupMessage - * - * This function calculates whether the @cache object has a proper - * response for the request @msg given the flags both in the request - * and the cached reply and the time ellapsed since it was cached. - * - * Returns: whether or not the @cache has a valid response for @msg - **/ -WebKitSoupCacheResponse -webkit_soup_cache_has_response (WebKitSoupCache *cache, SoupMessage *msg) -{ - char *key; - WebKitSoupCacheEntry *entry; - const char *cache_control; - gpointer value; - gboolean must_revalidate; - int max_age, max_stale, min_fresh; - GList *lru_item, *item; - - key = soup_message_get_cache_key (msg); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - /* 1. The presented Request-URI and that of stored response - * match - */ - if (!entry) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* Increase hit count. Take sorting into account */ - entry->hits++; - lru_item = g_list_find (cache->priv->lru_start, entry); - item = lru_item; - while (item->next && lru_compare_func (item->data, item->next->data) > 0) - item = g_list_next (item); - - if (item != lru_item) { - cache->priv->lru_start = g_list_remove_link (cache->priv->lru_start, lru_item); - item = g_list_insert_sorted (item, lru_item->data, lru_compare_func); - g_list_free (lru_item); - } - - if (entry->dirty || entry->being_validated) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* 2. The request method associated with the stored response - * allows it to be used for the presented request - */ - - /* In practice this means we only return our resource for GET, - * cacheability for other methods is a TODO in the RFC - * (TODO: although we could return the headers for HEAD - * probably). - */ - if (msg->method != SOUP_METHOD_GET) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* 3. Selecting request-headers nominated by the stored - * response (if any) match those presented. - */ - - /* TODO */ - - /* 4. The request is a conditional request issued by the client. - */ - if (soup_message_headers_get (msg->request_headers, "If-Modified-Since") || - soup_message_headers_get (msg->request_headers, "If-None-Match")) - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - - /* 5. The presented request and stored response are free from - * directives that would prevent its use. - */ - - must_revalidate = FALSE; - max_age = max_stale = min_fresh = -1; - - cache_control = soup_message_headers_get (msg->request_headers, "Cache-Control"); - if (cache_control) { - GHashTable *hash = soup_header_parse_param_list (cache_control); - - if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { - soup_header_free_param_list (hash); - return WEBKIT_SOUP_CACHE_RESPONSE_STALE; - } - - if (g_hash_table_lookup_extended (hash, "no-cache", NULL, NULL)) { - entry->must_revalidate = TRUE; - } - - if (g_hash_table_lookup_extended (hash, "max-age", NULL, &value)) { - max_age = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - } - - /* max-stale can have no value set, we need to use _extended */ - if (g_hash_table_lookup_extended (hash, "max-stale", NULL, &value)) { - if (value) - max_stale = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - else - max_stale = G_MAXINT32; - } - - value = g_hash_table_lookup (hash, "min-fresh"); - if (value) - min_fresh = (int)MIN (g_ascii_strtoll (value, NULL, 10), G_MAXINT32); - - soup_header_free_param_list (hash); - - if (max_age != -1) { - guint current_age = webkit_soup_cache_entry_get_current_age (entry); - - /* If we are over max-age and max-stale is not - set, do not use the value from the cache - without validation */ - if ((guint) max_age <= current_age && max_stale == -1) - return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION; - } - } - - /* 6. The stored response is either: fresh, allowed to be - * served stale or succesfully validated - */ - /* TODO consider also proxy-revalidate & s-maxage */ - if (entry->must_revalidate) - return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION; - - if (!webkit_soup_cache_entry_is_fresh_enough (entry, min_fresh)) { - /* Not fresh, can it be served stale? */ - if (max_stale != -1) { - /* G_MAXINT32 means we accept any staleness */ - if (max_stale == G_MAXINT32) - return WEBKIT_SOUP_CACHE_RESPONSE_FRESH; - - if ((webkit_soup_cache_entry_get_current_age (entry) - entry->freshness_lifetime) <= (guint) max_stale) - return WEBKIT_SOUP_CACHE_RESPONSE_FRESH; - } - - return WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION; - } - - return WEBKIT_SOUP_CACHE_RESPONSE_FRESH; -} - -/** - * webkit_soup_cache_get_cacheability: - * @cache: a #WebKitSoupCache - * @msg: a #SoupMessage - * - * Calculates whether the @msg can be cached or not. - * - * Returns: a #WebKitSoupCacheability value indicating whether the @msg can be cached or not. - **/ -WebKitSoupCacheability -webkit_soup_cache_get_cacheability (WebKitSoupCache *cache, SoupMessage *msg) -{ - g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), WEBKIT_SOUP_CACHE_UNCACHEABLE); - g_return_val_if_fail (SOUP_IS_MESSAGE (msg), WEBKIT_SOUP_CACHE_UNCACHEABLE); - - return WEBKIT_SOUP_CACHE_GET_CLASS (cache)->get_cacheability (cache, msg); -} - -static gboolean -force_flush_timeout (gpointer data) -{ - gboolean *forced = (gboolean *)data; - *forced = TRUE; - - return FALSE; -} - -/** - * webkit_soup_cache_flush: - * @cache: a #WebKitSoupCache - * @session: the #SoupSession associated with the @cache - * - * This function will force all pending writes in the @cache to be - * committed to disk. For doing so it will iterate the #GMainContext - * associated with the @session (which can be the default one) as long - * as needed. - **/ -void -webkit_soup_cache_flush (WebKitSoupCache *cache) -{ - GMainContext *async_context; - SoupSession *session; - guint timeout_id; - gboolean forced = FALSE; - - g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache)); - - session = cache->priv->session; - g_return_if_fail (SOUP_IS_SESSION (session)); - async_context = soup_session_get_async_context (session); - - /* We give cache 10 secs to finish */ - timeout_id = g_timeout_add (10000, force_flush_timeout, &forced); - - while (!forced && cache->priv->n_pending > 0) - g_main_context_iteration (async_context, FALSE); - - if (!forced) - g_source_remove (timeout_id); - else - g_warning ("Cache flush finished despite %d pending requests", cache->priv->n_pending); -} - -static void -clear_cache_item (gpointer data, - gpointer user_data) -{ - WebKitSoupCache *cache = (WebKitSoupCache *) user_data; - WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; - - if (webkit_soup_cache_entry_remove (cache, entry)) - webkit_soup_cache_entry_free (entry, TRUE); -} - -/** - * webkit_soup_cache_clear: - * @cache: a #WebKitSoupCache - * - * Will remove all entries in the @cache plus all the cache files - * associated with them. - **/ -void -webkit_soup_cache_clear (WebKitSoupCache *cache) -{ - GHashTable *hash; - GList *entries; - - g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache)); - - hash = cache->priv->cache; - g_return_if_fail (hash); - - // Cannot use g_hash_table_foreach as callbacks must not modify the hash table - entries = g_hash_table_get_values (hash); - g_list_foreach (entries, clear_cache_item, cache); - g_list_free (entries); -} - -SoupMessage * -webkit_soup_cache_generate_conditional_request (WebKitSoupCache *cache, SoupMessage *original) -{ - SoupMessage *msg; - SoupURI *uri; - WebKitSoupCacheEntry *entry; - char *key; - const char *value; - - g_return_val_if_fail (WEBKIT_IS_SOUP_CACHE (cache), NULL); - g_return_val_if_fail (SOUP_IS_MESSAGE (original), NULL); - - /* First copy the data we need from the original message */ - uri = soup_message_get_uri (original); - msg = soup_message_new_from_uri (original->method, uri); - - soup_message_headers_foreach (original->request_headers, - (SoupMessageHeadersForeachFunc)copy_headers, - msg->request_headers); - - /* Now add the validator entries in the header from the cached - data */ - key = soup_message_get_cache_key (original); - entry = g_hash_table_lookup (cache->priv->cache, key); - g_free (key); - - g_return_val_if_fail (entry, NULL); - - entry->being_validated = TRUE; - - value = soup_message_headers_get (entry->headers, "Last-Modified"); - if (value) - soup_message_headers_append (msg->request_headers, - "If-Modified-Since", - value); - value = soup_message_headers_get (entry->headers, "ETag"); - if (value) - soup_message_headers_append (msg->request_headers, - "If-None-Match", - value); - return msg; -} - -#define WEBKIT_SOUP_CACHE_FILE "soup.cache" - -#define WEBKIT_SOUP_CACHE_HEADERS_FORMAT "{ss}" -#define WEBKIT_SOUP_CACHE_PHEADERS_FORMAT "(ssbuuuuua" WEBKIT_SOUP_CACHE_HEADERS_FORMAT ")" -#define WEBKIT_SOUP_CACHE_ENTRIES_FORMAT "a" WEBKIT_SOUP_CACHE_PHEADERS_FORMAT - -/* Basically the same format than above except that some strings are - prepended with &. This way the GVariant returns a pointer to the - data instead of duplicating the string */ -#define WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT "{&s&s}" - -static void -pack_entry (gpointer data, - gpointer user_data) -{ - WebKitSoupCacheEntry *entry = (WebKitSoupCacheEntry *) data; - SoupMessageHeadersIter iter; - const gchar *header_key, *header_value; - GVariantBuilder *headers_builder; - GVariantBuilder *entries_builder = (GVariantBuilder *)user_data; - - /* Do not store non-consolidated entries */ - if (entry->dirty || entry->writing || !entry->key) - return; - - /* Pack headers */ - headers_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); - soup_message_headers_iter_init (&iter, entry->headers); - while (soup_message_headers_iter_next (&iter, &header_key, &header_value)) { - if (g_utf8_validate (header_value, -1, NULL)) - g_variant_builder_add (headers_builder, WEBKIT_SOUP_CACHE_HEADERS_FORMAT, - header_key, header_value); - } - - /* Entry data */ - g_variant_builder_add (entries_builder, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT, - entry->key, entry->filename, entry->must_revalidate, - entry->freshness_lifetime, entry->corrected_initial_age, - entry->response_time, entry->hits, entry->length, headers_builder); - - g_variant_builder_unref (headers_builder); -} - -void -webkit_soup_cache_dump (WebKitSoupCache *cache) -{ - WebKitSoupCachePrivate *priv = WEBKIT_SOUP_CACHE_GET_PRIVATE (cache); - gchar *filename; - GVariantBuilder *entries_builder; - GVariant *cache_variant; - - if (!g_list_length (cache->priv->lru_start)) - return; - - /* Create the builder and iterate over all entries */ - entries_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY); - g_list_foreach (cache->priv->lru_start, pack_entry, entries_builder); - - /* Serialize and dump */ - cache_variant = g_variant_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, entries_builder); - g_variant_builder_unref (entries_builder); - - filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL); - g_file_set_contents (filename, (const gchar *)g_variant_get_data (cache_variant), - g_variant_get_size (cache_variant), NULL); - g_free (filename); - g_variant_unref (cache_variant); -} - -void -webkit_soup_cache_load (WebKitSoupCache *cache) -{ - gchar *filename = NULL, *contents = NULL; - GVariant *cache_variant; - GVariantIter *entries_iter, *headers_iter; - GVariantType *variant_format; - gsize length; - WebKitSoupCacheEntry *entry; - WebKitSoupCachePrivate *priv = cache->priv; - - filename = g_build_filename (priv->cache_dir, WEBKIT_SOUP_CACHE_FILE, NULL); - if (!g_file_get_contents (filename, &contents, &length, NULL)) { - g_free (filename); - g_free (contents); - return; - } - g_free (filename); - - variant_format = g_variant_type_new (WEBKIT_SOUP_CACHE_ENTRIES_FORMAT); - cache_variant = g_variant_new_from_data (variant_format, (const gchar *)contents, length, FALSE, g_free, contents); - g_variant_type_free (variant_format); - - g_variant_get (cache_variant, WEBKIT_SOUP_CACHE_ENTRIES_FORMAT, &entries_iter); - entry = g_slice_new0 (WebKitSoupCacheEntry); - - while (g_variant_iter_loop (entries_iter, WEBKIT_SOUP_CACHE_PHEADERS_FORMAT, - &entry->key, &entry->filename, &entry->must_revalidate, - &entry->freshness_lifetime, &entry->corrected_initial_age, - &entry->response_time, &entry->hits, &entry->length, - &headers_iter)) { - const gchar *header_key, *header_value; - - /* SoupMessage Headers */ - entry->headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_RESPONSE); - while (g_variant_iter_loop (headers_iter, WEBKIT_SOUP_CACHE_DECODE_HEADERS_FORMAT, &header_key, &header_value)) - soup_message_headers_append (entry->headers, header_key, header_value); - - /* Insert in cache */ - if (!webkit_soup_cache_entry_insert_by_key (cache, (const gchar *)entry->key, entry, FALSE)) - webkit_soup_cache_entry_free (entry, TRUE); - - /* New entry for the next iteration. This creates an - extra object the last iteration but it's worth it - as we save several if's */ - entry = g_slice_new0 (WebKitSoupCacheEntry); - } - /* Remove last created entry */ - g_slice_free (WebKitSoupCacheEntry, entry); - - /* Sort LRU (shouldn't be needed). First reverse as elements - * are always prepended when inserting - */ - cache->priv->lru_start = g_list_reverse (cache->priv->lru_start); - cache->priv->lru_start = g_list_sort (cache->priv->lru_start, lru_compare_func); - - /* frees */ - g_variant_iter_free (entries_iter); - g_variant_unref (cache_variant); -} - -void -webkit_soup_cache_set_max_size (WebKitSoupCache *cache, - guint max_size) -{ - cache->priv->max_size = max_size; - cache->priv->max_entry_data_size = cache->priv->max_size / MAX_ENTRY_DATA_PERCENTAGE; -} - -guint -webkit_soup_cache_get_max_size (WebKitSoupCache *cache) -{ - return cache->priv->max_size; -} diff --git a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h b/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h deleted file mode 100644 index a926f98..0000000 --- a/Source/WebCore/platform/network/soup/cache/webkit/soup-cache.h +++ /dev/null @@ -1,106 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * soup-cache.h: - * - * Copyright (C) 2009, 2010 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 - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef WEBKIT_SOUP_CACHE_H -#define WEBKIT_SOUP_CACHE_H 1 - -#ifdef G_OS_WIN32 - #ifdef BUILDING_WEBKIT - #define WEBKIT_API __declspec(dllexport) - #else - #define WEBKIT_API __declspec(dllimport) - #endif - #define WEBKIT_OBSOLETE_API WEBKIT_API -#else - #define WEBKIT_API __attribute__((visibility("default"))) - #define WEBKIT_OBSOLETE_API WEBKIT_API __attribute__((deprecated)) -#endif - -#include <libsoup/soup-types.h> -#include <gio/gio.h> - -G_BEGIN_DECLS - -#define WEBKIT_TYPE_SOUP_CACHE (webkit_soup_cache_get_type ()) -#define WEBKIT_SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCache)) -#define WEBKIT_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass)) -#define WEBKIT_IS_SOUP_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE)) -#define WEBKIT_IS_SOUP_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), WEBKIT_TYPE_SOUP_CACHE)) -#define WEBKIT_SOUP_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), WEBKIT_TYPE_SOUP_CACHE, WebKitSoupCacheClass)) - -typedef struct _WebKitSoupCache WebKitSoupCache; -typedef struct _WebKitSoupCachePrivate WebKitSoupCachePrivate; - -typedef enum { - WEBKIT_SOUP_CACHE_CACHEABLE = (1 << 0), - WEBKIT_SOUP_CACHE_UNCACHEABLE = (1 << 1), - WEBKIT_SOUP_CACHE_INVALIDATES = (1 << 2), - WEBKIT_SOUP_CACHE_VALIDATES = (1 << 3) -} WebKitSoupCacheability; - -typedef enum { - WEBKIT_SOUP_CACHE_RESPONSE_FRESH, - WEBKIT_SOUP_CACHE_RESPONSE_NEEDS_VALIDATION, - WEBKIT_SOUP_CACHE_RESPONSE_STALE -} WebKitSoupCacheResponse; - -typedef enum { - WEBKIT_SOUP_CACHE_SINGLE_USER, - WEBKIT_SOUP_CACHE_SHARED -} WebKitSoupCacheType; - -struct _WebKitSoupCache { - GObject parent_instance; - - WebKitSoupCachePrivate *priv; -}; - -typedef struct { - GObjectClass parent_class; - - /* methods */ - WebKitSoupCacheability (*get_cacheability)(WebKitSoupCache *cache, SoupMessage *msg); - - /* Padding for future expansion */ - void (*_libsoup_reserved1)(void); - void (*_libsoup_reserved2)(void); - void (*_libsoup_reserved3)(void); -} WebKitSoupCacheClass; - -WEBKIT_API GType webkit_soup_cache_get_type (void); -WEBKIT_API WebKitSoupCache *webkit_soup_cache_new (const char *cache_dir, - WebKitSoupCacheType cache_type); -WEBKIT_API void webkit_soup_cache_flush (WebKitSoupCache *cache); -WEBKIT_API void webkit_soup_cache_clear (WebKitSoupCache *cache); - -WEBKIT_API void webkit_soup_cache_dump (WebKitSoupCache *cache); -WEBKIT_API void webkit_soup_cache_load (WebKitSoupCache *cache); - -WEBKIT_API void webkit_soup_cache_set_max_size (WebKitSoupCache *cache, - guint max_size); -WEBKIT_API guint webkit_soup_cache_get_max_size (WebKitSoupCache *cache); - -G_END_DECLS - - -#endif /* WEBKIT_SOUP_CACHE_H */ - diff --git a/Source/WebCore/platform/network/win/DownloadBundleWin.cpp b/Source/WebCore/platform/network/win/DownloadBundleWin.cpp new file mode 100644 index 0000000..f0f3027 --- /dev/null +++ b/Source/WebCore/platform/network/win/DownloadBundleWin.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2011 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 "DownloadBundle.h" + +#include <CoreFoundation/CoreFoundation.h> +#include <io.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +namespace DownloadBundle { + +static UInt32 magicNumber() +{ + return 0xDECAF4EA; +} + +const String& fileExtension() +{ + DEFINE_STATIC_LOCAL(const String, extension, (".download")); + return extension; +} + +bool appendResumeData(CFDataRef resumeData, const String& bundlePath) +{ + if (!resumeData) { + LOG_ERROR("Invalid resume data to write to bundle path"); + return false; + } + if (bundlePath.isEmpty()) { + LOG_ERROR("Cannot write resume data to empty download bundle path"); + return false; + } + + String nullifiedPath = bundlePath; + FILE* bundle = 0; + if (_wfopen_s(&bundle, nullifiedPath.charactersWithNullTermination(), TEXT("ab")) || !bundle) { + LOG_ERROR("Failed to open file %s to append resume data", bundlePath.ascii().data()); + return false; + } + + bool result = false; + + const UInt8* resumeBytes = CFDataGetBytePtr(resumeData); + ASSERT(resumeBytes); + if (!resumeBytes) + goto exit; + + CFIndex resumeLength = CFDataGetLength(resumeData); + ASSERT(resumeLength > 0); + if (resumeLength < 1) + goto exit; + + if (fwrite(resumeBytes, 1, resumeLength, bundle) != resumeLength) { + LOG_ERROR("Failed to write resume data to the bundle - errno(%i)", errno); + goto exit; + } + + if (fwrite(&resumeLength, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to write footer length to the bundle - errno(%i)", errno); + goto exit; + } + + const UInt32& magic = magicNumber(); + if (fwrite(&magic, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to write footer magic number to the bundle - errno(%i)", errno); + goto exit; + } + + result = true; +exit: + fclose(bundle); + return result; +} + +CFDataRef extractResumeData(const String& bundlePath) +{ + if (bundlePath.isEmpty()) { + LOG_ERROR("Cannot create resume data from empty download bundle path"); + return 0; + } + + // Open a handle to the bundle file + String nullifiedPath = bundlePath; + FILE* bundle = 0; + if (_wfopen_s(&bundle, nullifiedPath.charactersWithNullTermination(), TEXT("r+b")) || !bundle) { + LOG_ERROR("Failed to open file %s to get resume data", bundlePath.ascii().data()); + return 0; + } + + CFDataRef result = 0; + Vector<UInt8> footerBuffer; + + // Stat the file to get its size + struct _stat64 fileStat; + if (_fstat64(_fileno(bundle), &fileStat)) + goto exit; + + // Check for the bundle magic number at the end of the file + fpos_t footerMagicNumberPosition = fileStat.st_size - 4; + ASSERT(footerMagicNumberPosition >= 0); + if (footerMagicNumberPosition < 0) + goto exit; + if (fsetpos(bundle, &footerMagicNumberPosition)) + goto exit; + + UInt32 footerMagicNumber = 0; + if (fread(&footerMagicNumber, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to read footer magic number from the bundle - errno(%i)", errno); + goto exit; + } + + if (footerMagicNumber != magicNumber()) { + LOG_ERROR("Footer's magic number does not match 0x%X - errno(%i)", magicNumber(), errno); + goto exit; + } + + // Now we're *reasonably* sure this is a .download bundle we actually wrote. + // Get the length of the resume data + fpos_t footerLengthPosition = fileStat.st_size - 8; + ASSERT(footerLengthPosition >= 0); + if (footerLengthPosition < 0) + goto exit; + + if (fsetpos(bundle, &footerLengthPosition)) + goto exit; + + UInt32 footerLength = 0; + if (fread(&footerLength, 4, 1, bundle) != 1) { + LOG_ERROR("Failed to read ResumeData length from the bundle - errno(%i)", errno); + goto exit; + } + + // Make sure theres enough bytes to read in for the resume data, and perform the read + fpos_t footerStartPosition = fileStat.st_size - 8 - footerLength; + ASSERT(footerStartPosition >= 0); + if (footerStartPosition < 0) + goto exit; + if (fsetpos(bundle, &footerStartPosition)) + goto exit; + + footerBuffer.resize(footerLength); + if (fread(footerBuffer.data(), 1, footerLength, bundle) != footerLength) { + LOG_ERROR("Failed to read ResumeData from the bundle - errno(%i)", errno); + goto exit; + } + + // CFURLDownload will seek to the appropriate place in the file (before our footer) and start overwriting from there + // However, say we were within a few hundred bytes of the end of a download when it was paused - + // The additional footer extended the length of the file beyond its final length, and there will be junk data leftover + // at the end. Therefore, now that we've retrieved the footer data, we need to truncate it. + if (errno_t resizeError = _chsize_s(_fileno(bundle), footerStartPosition)) { + LOG_ERROR("Failed to truncate the resume footer off the end of the file - errno(%i)", resizeError); + goto exit; + } + + // Finally, make the resume data. Now, it is possible by some twist of fate the bundle magic number + // was naturally at the end of the file and its not actually a valid bundle. That, or someone engineered + // it that way to try to attack us. In that cause, this CFData will successfully create but when we + // actually try to start the CFURLDownload using this bogus data, it will fail and we will handle that gracefully + result = CFDataCreate(0, footerBuffer.data(), footerLength); +exit: + fclose(bundle); + return result; +} + +} // namespace DownloadBundle + +} // namespace WebCore diff --git a/Source/WebCore/platform/qt/ClipboardQt.h b/Source/WebCore/platform/qt/ClipboardQt.h index fb5abef..8d78634 100644 --- a/Source/WebCore/platform/qt/ClipboardQt.h +++ b/Source/WebCore/platform/qt/ClipboardQt.h @@ -26,8 +26,8 @@ #ifndef ClipboardQt_h #define ClipboardQt_h -#include "Clipboard.h" #include "CachedResourceClient.h" +#include "Clipboard.h" QT_BEGIN_NAMESPACE class QMimeData; @@ -35,56 +35,56 @@ QT_END_NAMESPACE namespace WebCore { - class CachedImage; +class CachedImage; - // State available during IE's events for drag and drop and copy/paste - class ClipboardQt : public Clipboard, public CachedResourceClient { - WTF_MAKE_FAST_ALLOCATED; - public: - static PassRefPtr<ClipboardQt> create(ClipboardAccessPolicy policy, const QMimeData* readableClipboard) - { - return adoptRef(new ClipboardQt(policy, readableClipboard)); - } - static PassRefPtr<ClipboardQt> create(ClipboardAccessPolicy policy, ClipboardType clipboardType = CopyAndPaste) - { - return adoptRef(new ClipboardQt(policy, clipboardType)); - } - virtual ~ClipboardQt(); +// State available during IE's events for drag and drop and copy/paste +class ClipboardQt : public Clipboard, public CachedResourceClient { + WTF_MAKE_FAST_ALLOCATED; +public: + static PassRefPtr<ClipboardQt> create(ClipboardAccessPolicy policy, const QMimeData* readableClipboard) + { + return adoptRef(new ClipboardQt(policy, readableClipboard)); + } + static PassRefPtr<ClipboardQt> create(ClipboardAccessPolicy policy, ClipboardType clipboardType = CopyAndPaste) + { + return adoptRef(new ClipboardQt(policy, clipboardType)); + } + virtual ~ClipboardQt(); - void clearData(const String& type); - void clearAllData(); - String getData(const String& type, bool& success) const; - bool setData(const String& type, const String& data); + void clearData(const String& type); + void clearAllData(); + String getData(const String& type, bool& success) const; + bool setData(const String& type, const String& data); - // extensions beyond IE's API - virtual HashSet<String> types() const; - virtual PassRefPtr<FileList> files() const; + // extensions beyond IE's API + virtual HashSet<String> types() const; + virtual PassRefPtr<FileList> files() const; - void setDragImage(CachedImage*, const IntPoint&); - void setDragImageElement(Node*, const IntPoint&); + void setDragImage(CachedImage*, const IntPoint&); + void setDragImageElement(Node*, const IntPoint&); - virtual DragImageRef createDragImage(IntPoint& dragLoc) const; - virtual void declareAndWriteDragImage(Element*, const KURL&, const String& title, Frame*); - virtual void writeURL(const KURL&, const String&, Frame*); - virtual void writeRange(Range*, Frame*); - virtual void writePlainText(const String&); + virtual DragImageRef createDragImage(IntPoint& dragLoc) const; + virtual void declareAndWriteDragImage(Element*, const KURL&, const String& title, Frame*); + virtual void writeURL(const KURL&, const String&, Frame*); + virtual void writeRange(Range*, Frame*); + virtual void writePlainText(const String&); - virtual bool hasData(); + virtual bool hasData(); - QMimeData* clipboardData() const { return m_writableData; } - void invalidateWritableData() { m_writableData = 0; } + QMimeData* clipboardData() const { return m_writableData; } + void invalidateWritableData() { m_writableData = 0; } - private: - ClipboardQt(ClipboardAccessPolicy, const QMimeData* readableClipboard); +private: + ClipboardQt(ClipboardAccessPolicy, const QMimeData* readableClipboard); - // Clipboard is writable so it will create its own QMimeData object - ClipboardQt(ClipboardAccessPolicy, ClipboardType); + // Clipboard is writable so it will create its own QMimeData object + ClipboardQt(ClipboardAccessPolicy, ClipboardType); - void setDragImage(CachedImage*, Node*, const IntPoint& loc); + void setDragImage(CachedImage*, Node*, const IntPoint& loc); - const QMimeData* m_readableData; - QMimeData* m_writableData; - }; + const QMimeData* m_readableData; + QMimeData* m_writableData; +}; } #endif // ClipboardQt_h diff --git a/Source/WebCore/platform/qt/CookieJarQt.cpp b/Source/WebCore/platform/qt/CookieJarQt.cpp index b7ff5d1..e5a2dcd 100644 --- a/Source/WebCore/platform/qt/CookieJarQt.cpp +++ b/Source/WebCore/platform/qt/CookieJarQt.cpp @@ -114,10 +114,12 @@ String cookieRequestHeaderFieldValue(const Document* document, const KURL &url) bool cookiesEnabled(const Document* document) { - if (QNetworkAccessManager* manager = networkAccessManager(document)) - return !!manager->cookieJar(); + QNetworkAccessManager* manager = networkAccessManager(document); + if (!manager) + return false; - return false; + QtNAMThreadSafeProxy managerProxy(manager); + return managerProxy.hasCookieJar(); } bool getRawCookies(const Document*, const KURL&, Vector<Cookie>& rawCookies) diff --git a/Source/WebCore/platform/qt/DragDataQt.cpp b/Source/WebCore/platform/qt/DragDataQt.cpp index f68ad1d..2554df4 100644 --- a/Source/WebCore/platform/qt/DragDataQt.cpp +++ b/Source/WebCore/platform/qt/DragDataQt.cpp @@ -125,7 +125,8 @@ String DragData::asURL(Frame*, FilenameConversionPolicy filenamePolicy, String*) if (urls.isEmpty()) return String(); - return encodeWithURLEscapeSequences(urls.first().toString()); + QByteArray encodedUrl = urls.first().toEncoded(); + return String(encodedUrl.constData(), encodedUrl.length()); } PassRefPtr<DocumentFragment> DragData::asFragment(Frame* frame, PassRefPtr<Range>, bool, bool&) const diff --git a/Source/WebCore/platform/qt/MIMETypeRegistryQt.cpp b/Source/WebCore/platform/qt/MIMETypeRegistryQt.cpp index 12db891..01cef12 100644 --- a/Source/WebCore/platform/qt/MIMETypeRegistryQt.cpp +++ b/Source/WebCore/platform/qt/MIMETypeRegistryQt.cpp @@ -29,6 +29,9 @@ #include "config.h" #include "MIMETypeRegistry.h" +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> + namespace WebCore { struct ExtensionMap { @@ -70,6 +73,8 @@ static const ExtensionMap extensionMap[] = { String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + String s = ext.lower(); const ExtensionMap *e = extensionMap; diff --git a/Source/WebCore/platform/qt/RenderThemeQt.cpp b/Source/WebCore/platform/qt/RenderThemeQt.cpp index 8d5cfcd..d4688cf 100644 --- a/Source/WebCore/platform/qt/RenderThemeQt.cpp +++ b/Source/WebCore/platform/qt/RenderThemeQt.cpp @@ -726,7 +726,7 @@ bool RenderThemeQt::paintMenuList(RenderObject* o, const PaintInfo& i, const Int initStyleOption(p.widget, opt); initializeCommonQStyleOptions(opt, o); - const QPoint topLeft = r.topLeft(); + const QPoint topLeft = r.location(); p.painter->translate(topLeft); opt.rect.moveTo(QPoint(0, 0)); opt.rect.setSize(r.size()); @@ -825,7 +825,7 @@ bool RenderThemeQt::paintProgressBar(RenderObject* o, const PaintInfo& pi, const option.minimum = 0; option.progress = (renderProgress->position() * std::numeric_limits<int>::max()); - const QPoint topLeft = r.topLeft(); + const QPoint topLeft = r.location(); p.painter->translate(topLeft); option.rect.moveTo(QPoint(0, 0)); option.rect.setSize(r.size()); @@ -887,7 +887,7 @@ bool RenderThemeQt::paintSliderTrack(RenderObject* o, const PaintInfo& pi, option.state |= QStyle::State_Sunken; } - const QPoint topLeft = r.topLeft(); + const QPoint topLeft = r.location(); p.painter->translate(topLeft); option.rect.moveTo(QPoint(0, 0)); option.rect.setSize(r.size()); @@ -1174,7 +1174,26 @@ QColor RenderThemeQt::getMediaControlForegroundColor(RenderObject* o) const bool RenderThemeQt::paintMediaFullscreenButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { - return RenderTheme::paintMediaFullscreenButton(o, paintInfo, r); + HTMLMediaElement* mediaElement = getMediaElementFromRenderObject(o); + if (!mediaElement) + return false; + + StylePainter p(this, paintInfo); + if (!p.isValid()) + return true; + + p.painter->setRenderHint(QPainter::Antialiasing, true); + + paintMediaBackground(p.painter, r); + + WorldMatrixTransformer transformer(p.painter, o, r); + const QPointF arrowPolygon[9] = { QPointF(20, 0), QPointF(100, 0), QPointF(100, 80), + QPointF(80, 80), QPointF(80, 30), QPointF(10, 100), QPointF(0, 90), QPointF(70, 20), QPointF(20, 20)}; + + p.painter->setBrush(getMediaControlForegroundColor(o)); + p.painter->drawPolygon(arrowPolygon, 9); + + return false; } bool RenderThemeQt::paintMediaMuteButton(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) diff --git a/Source/WebCore/platform/qt/ScrollbarQt.cpp b/Source/WebCore/platform/qt/ScrollbarQt.cpp index dda82e9..5afb1ae6 100644 --- a/Source/WebCore/platform/qt/ScrollbarQt.cpp +++ b/Source/WebCore/platform/qt/ScrollbarQt.cpp @@ -74,6 +74,11 @@ bool Scrollbar::contextMenu(const PlatformMouseEvent& event) QAction* actionSelected = menu.exec(globalPos); if (actionSelected == actScrollHere) { + // Set the pressed position to the middle of the thumb so that when we + // do move, the delta will be from the current pixel position of the + // thumb to the new position + int position = theme()->trackPosition(this) + theme()->thumbPosition(this) + theme()->thumbLength(this) / 2; + setPressedPos(position); const QPoint pos = convertFromContainingWindow(event.pos()); moveThumb(horizontal ? pos.x() : pos.y()); } else if (actionSelected == actScrollTop) diff --git a/Source/WebCore/platform/sql/SQLiteStatement.cpp b/Source/WebCore/platform/sql/SQLiteStatement.cpp index af9518a..d9186dc 100644 --- a/Source/WebCore/platform/sql/SQLiteStatement.cpp +++ b/Source/WebCore/platform/sql/SQLiteStatement.cpp @@ -176,6 +176,20 @@ int SQLiteStatement::bindBlob(int index, const void* blob, int size) return sqlite3_bind_blob(m_statement, index, blob, size, SQLITE_TRANSIENT); } +int SQLiteStatement::bindBlob(int index, const String& text) +{ + // String::characters() returns 0 for the empty string, which SQLite + // treats as a null, so we supply a non-null pointer for that case. + UChar anyCharacter = 0; + const UChar* characters; + if (text.isEmpty() && !text.isNull()) + characters = &anyCharacter; + else + characters = text.characters(); + + return bindBlob(index, characters, text.length() * sizeof(UChar)); +} + int SQLiteStatement::bindText(int index, const String& text) { ASSERT(m_isPrepared); @@ -355,7 +369,29 @@ int64_t SQLiteStatement::getColumnInt64(int col) return 0; return sqlite3_column_int64(m_statement, col); } - + +String SQLiteStatement::getColumnBlobAsString(int col) +{ + ASSERT(col >= 0); + + if (!m_statement && prepareAndStep() != SQLITE_ROW) + return String(); + + if (columnCount() <= col) + return String(); + + const void* blob = sqlite3_column_blob(m_statement, col); + if (!blob) + return String(); + + int size = sqlite3_column_bytes(m_statement, col); + if (size < 0) + return String(); + + ASSERT(!(size % sizeof(UChar))); + return String(static_cast<const UChar*>(blob), size / sizeof(UChar)); +} + void SQLiteStatement::getColumnBlobAsVector(int col, Vector<char>& result) { ASSERT(col >= 0); @@ -379,7 +415,7 @@ void SQLiteStatement::getColumnBlobAsVector(int col, Vector<char>& result) int size = sqlite3_column_bytes(m_statement, col); result.resize((size_t)size); for (int i = 0; i < size; ++i) - result[i] = ((const unsigned char*)blob)[i]; + result[i] = (static_cast<const unsigned char*>(blob))[i]; } const void* SQLiteStatement::getColumnBlob(int col, int& size) diff --git a/Source/WebCore/platform/sql/SQLiteStatement.h b/Source/WebCore/platform/sql/SQLiteStatement.h index fd1abfb..35dcecf 100644 --- a/Source/WebCore/platform/sql/SQLiteStatement.h +++ b/Source/WebCore/platform/sql/SQLiteStatement.h @@ -42,6 +42,7 @@ public: int prepare(); int bindBlob(int index, const void* blob, int size); + int bindBlob(int index, const String&); int bindText(int index, const String&); int bindInt(int index, int); int bindInt64(int index, int64_t); @@ -80,6 +81,7 @@ public: int getColumnInt(int col); int64_t getColumnInt64(int col); const void* getColumnBlob(int col, int& size); + String getColumnBlobAsString(int col); void getColumnBlobAsVector(int col, Vector<char>&); bool returnTextResults(int col, Vector<String>&); diff --git a/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp index 37f96be..6dbc384 100644 --- a/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp +++ b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp @@ -34,938 +34,23 @@ #include "PlatformBridge.h" #include <sqlite3.h> -#include <errno.h> #include <fcntl.h> #include <string.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <sys/types.h> #include <unistd.h> using namespace WebCore; -// Chromium's Posix implementation of SQLite VFS. -// This is heavily based on SQLite's os_unix.c, -// without parts we don't need. - -// Identifies a file by its device number and inode. -struct ChromiumFileId { - dev_t dev; // Device number. - ino_t ino; // Inode number. -}; - -// Information about file locks (one per open inode). Note that many open -// file descriptors may refer to the same inode. -struct ChromiumLockInfo { - ChromiumFileId lockKey; // File identifier. - int cnt; // Number of shared locks held. - int locktype; // Type of the lock. - int nRef; // Reference count. - - // Double-linked list pointers. - ChromiumLockInfo* pNext; - ChromiumLockInfo* pPrev; -}; - -// Information about a file descriptor that cannot be closed immediately. -struct ChromiumUnusedFd { - int fd; // File descriptor. - int flags; // Flags this file descriptor was opened with. - ChromiumUnusedFd* pNext; // Next unused file descriptor on the same file. -}; - -// Information about an open inode. When we want to close an inode -// that still has locks, we defer the close until all locks are cleared. -struct ChromiumOpenInfo { - ChromiumFileId fileId; // The lookup key. - int nRef; // Reference count. - int nLock; // Number of outstanding locks. - ChromiumUnusedFd* pUnused; // List of file descriptors to close. - - // Double-linked list pointers. - ChromiumOpenInfo* pNext; - ChromiumOpenInfo* pPrev; -}; - -// Keep track of locks and inodes in double-linked lists. -static struct ChromiumLockInfo* lockList = 0; -static struct ChromiumOpenInfo* openList = 0; - -// Extension of sqlite3_file specific to the chromium VFS. -struct ChromiumFile { - sqlite3_io_methods const* pMethod; // Implementation of sqlite3_file. - ChromiumOpenInfo* pOpen; // Information about all open file descriptors for this file. - ChromiumLockInfo* pLock; // Information about all locks for this file. - int h; // File descriptor. - int dirfd; // File descriptor for the file directory. - unsigned char locktype; // Type of the lock used for this file. - int lastErrno; // Value of errno for last operation on this file. - ChromiumUnusedFd* pUnused; // Information about unused file descriptors for this file. -}; - -// The following constants specify the range of bytes used for locking. -// SQLiteSharedSize is the number of bytes available in the pool from which -// a random byte is selected for a shared lock. The pool of bytes for -// shared locks begins at SQLiteSharedFirstByte. -// The values are the same as used by SQLite for compatibility. -static const off_t SQLitePendingByte = 0x40000000; -static const off_t SQLiteReservedByte = SQLitePendingByte + 1; -static const off_t SQLiteSharedFirstByte = SQLitePendingByte + 2; -static const off_t SQLiteSharedSize = 510; - -// Maps a POSIX error code to an SQLite error code. -static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) -{ - switch (posixError) { - case 0: - return SQLITE_OK; - case EAGAIN: - case ETIMEDOUT: - case EBUSY: - case EINTR: - case ENOLCK: - return SQLITE_BUSY; - case EACCES: - // EACCES is like EAGAIN during locking operations. - if ((sqliteIOErr == SQLITE_IOERR_LOCK) || - (sqliteIOErr == SQLITE_IOERR_UNLOCK) || - (sqliteIOErr == SQLITE_IOERR_RDLOCK) || - (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK)) - return SQLITE_BUSY; - return SQLITE_PERM; - case EPERM: - return SQLITE_PERM; - case EDEADLK: - return SQLITE_IOERR_BLOCKED; - default: - return sqliteIOErr; - } -} - -// Releases a ChromiumLockInfo structure previously allocated by findLockInfo(). -static void releaseLockInfo(ChromiumLockInfo* pLock) -{ - if (!pLock) - return; - - pLock->nRef--; - if (pLock->nRef > 0) - return; - - if (pLock->pPrev) { - ASSERT(pLock->pPrev->pNext == pLock); - pLock->pPrev->pNext = pLock->pNext; - } else { - ASSERT(lockList == pLock); - lockList = pLock->pNext; - } - if (pLock->pNext) { - ASSERT(pLock->pNext->pPrev == pLock); - pLock->pNext->pPrev = pLock->pPrev; - } - - sqlite3_free(pLock); -} - -// Releases a ChromiumOpenInfo structure previously allocated by findLockInfo(). -static void releaseOpenInfo(ChromiumOpenInfo* pOpen) -{ - if (!pOpen) - return; - - pOpen->nRef--; - if (pOpen->nRef > 0) - return; - - if (pOpen->pPrev) { - ASSERT(pOpen->pPrev->pNext == pOpen); - pOpen->pPrev->pNext = pOpen->pNext; - } else { - ASSERT(openList == pOpen); - openList = pOpen->pNext; - } - if (pOpen->pNext) { - ASSERT(pOpen->pNext->pPrev == pOpen); - pOpen->pNext->pPrev = pOpen->pPrev; - } - - ASSERT(!pOpen->pUnused); // Make sure we're not leaking memory and file descriptors. - - sqlite3_free(pOpen); -} - -// Locates ChromiumLockInfo and ChromiumOpenInfo for given file descriptor (creating new ones if needed). -// Returns a SQLite error code. -static int findLockInfo(ChromiumFile* pFile, ChromiumLockInfo** ppLock, ChromiumOpenInfo** ppOpen) -{ - int fd = pFile->h; - struct stat statbuf; - int rc = fstat(fd, &statbuf); - if (rc) { - pFile->lastErrno = errno; -#ifdef EOVERFLOW - if (pFile->lastErrno == EOVERFLOW) - return SQLITE_NOLFS; -#endif - return SQLITE_IOERR; - } - -#if OS(DARWIN) - // On OS X on an msdos/fat filesystems, the inode number is reported - // incorrectly for zero-size files. See http://www.sqlite.org/cvstrac/tktview?tn=3260. - // To work around this problem we always increase the file size to 1 by writing a single byte - // prior to accessing the inode number. The one byte written is an ASCII 'S' character which - // also happens to be the first byte in the header of every SQLite database. In this way, - // if there is a race condition such that another thread has already populated the first page - // of the database, no damage is done. - if (!statbuf.st_size) { - rc = write(fd, "S", 1); - if (rc != 1) - return SQLITE_IOERR; - rc = fstat(fd, &statbuf); - if (rc) { - pFile->lastErrno = errno; - return SQLITE_IOERR; - } - } -#endif - - ChromiumFileId fileId; - memset(&fileId, 0, sizeof(fileId)); - fileId.dev = statbuf.st_dev; - fileId.ino = statbuf.st_ino; - - ChromiumLockInfo* pLock = 0; - - if (ppLock) { - pLock = lockList; - while (pLock && memcmp(&fileId, &pLock->lockKey, sizeof(fileId))) - pLock = pLock->pNext; - if (pLock) - pLock->nRef++; - else { - pLock = static_cast<ChromiumLockInfo*>(sqlite3_malloc(sizeof(*pLock))); - if (!pLock) - return SQLITE_NOMEM; - pLock->lockKey = fileId; - pLock->nRef = 1; - pLock->cnt = 0; - pLock->locktype = 0; - pLock->pNext = lockList; - pLock->pPrev = 0; - if (lockList) - lockList->pPrev = pLock; - lockList = pLock; - } - *ppLock = pLock; - } - - if (ppOpen) { - ChromiumOpenInfo* pOpen = openList; - while (pOpen && memcmp(&fileId, &pOpen->fileId, sizeof(fileId))) - pOpen = pOpen->pNext; - if (pOpen) - pOpen->nRef++; - else { - pOpen = static_cast<ChromiumOpenInfo*>(sqlite3_malloc(sizeof(*pOpen))); - if (!pOpen) { - releaseLockInfo(pLock); - return SQLITE_NOMEM; - } - memset(pOpen, 0, sizeof(*pOpen)); - pOpen->fileId = fileId; - pOpen->nRef = 1; - pOpen->pNext = openList; - if (openList) - openList->pPrev = pOpen; - openList = pOpen; - } - *ppOpen = pOpen; - } - - return rc; -} - -// Checks if there is a RESERVED lock held on the specified file by this or any other process. -// If the lock is held, sets pResOut to a non-zero value. Returns a SQLite error code. -static int chromiumCheckReservedLock(sqlite3_file* id, int* pResOut) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - // Look for locks held by this process. - int reserved = 0; - if (pFile->pLock->locktype > SQLITE_LOCK_SHARED) - reserved = 1; - - // Look for locks held by other processes. - int rc = SQLITE_OK; - if (!reserved) { - struct flock lock; - lock.l_whence = SEEK_SET; - lock.l_start = SQLiteReservedByte; - lock.l_len = 1; - lock.l_type = F_WRLCK; - if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { - int tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); - pFile->lastErrno = tErrno; - } else if (lock.l_type != F_UNLCK) - reserved = 1; - } - - *pResOut = reserved; - return rc; -} - -// Performs a file locking operation on a range of bytes in a file. -// The |op| parameter should be one of F_RFLCK, F_WRLCK or F_UNLCK. -// Returns a Unix error code, and also writes it to pErrcode. -static int rangeLock(ChromiumFile* pFile, int op, int* pErrcode) -{ - struct flock lock; - lock.l_type = op; - lock.l_start = SQLiteSharedFirstByte; - lock.l_whence = SEEK_SET; - lock.l_len = SQLiteSharedSize; - int rc = fcntl(pFile->h, F_SETLK, &lock); - *pErrcode = errno; - return rc; -} - -// Locks the file with the lock specified by parameter locktype - one -// of the following: -// -// (1) SQLITE_LOCK_SHARED -// (2) SQLITE_LOCK_RESERVED -// (3) SQLITE_LOCK_PENDING -// (4) SQLITE_LOCK_EXCLUSIVE -// -// Sometimes when requesting one lock state, additional lock states -// are inserted in between. The locking might fail on one of the later -// transitions leaving the lock state different from what it started but -// still short of its goal. The following chart shows the allowed -// transitions and the inserted intermediate states: -// -// UNLOCKED -> SHARED -// SHARED -> RESERVED -// SHARED -> (PENDING) -> EXCLUSIVE -// RESERVED -> (PENDING) -> EXCLUSIVE -// PENDING -> EXCLUSIVE -static int chromiumLock(sqlite3_file* id, int locktype) -{ - // To obtain a SHARED lock, a read-lock is obtained on the 'pending - // byte'. If this is successful, a random byte from the 'shared byte - // range' is read-locked and the lock on the 'pending byte' released. - // - // A process may only obtain a RESERVED lock after it has a SHARED lock. - // A RESERVED lock is implemented by grabbing a write-lock on the - // 'reserved byte'. - // - // A process may only obtain a PENDING lock after it has obtained a - // SHARED lock. A PENDING lock is implemented by obtaining a write-lock - // on the 'pending byte'. This ensures that no new SHARED locks can be - // obtained, but existing SHARED locks are allowed to persist. A process - // does not have to obtain a RESERVED lock on the way to a PENDING lock. - // This property is used by the algorithm for rolling back a journal file - // after a crash. - // - // An EXCLUSIVE lock, obtained after a PENDING lock is held, is - // implemented by obtaining a write-lock on the entire 'shared byte - // range'. Since all other locks require a read-lock on one of the bytes - // within this range, this ensures that no other locks are held on the - // database. - - int rc = SQLITE_OK; - struct flock lock; - int s = 0; - int tErrno; - - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - ChromiumLockInfo* pLock = pFile->pLock; - - // If there is already a lock of this type or more restrictive, do nothing. - if (pFile->locktype >= locktype) - return SQLITE_OK; - - // Make sure we never move from unlocked to anything higher than shared lock. - ASSERT(pFile->locktype != SQLITE_LOCK_NONE || locktype == SQLITE_LOCK_SHARED); - - // Make sure we never request a pending lock. - ASSERT(locktype != SQLITE_LOCK_PENDING); - - // Make sure a shared lock is always held when a RESERVED lock is requested. - ASSERT(locktype != SQLITE_LOCK_RESERVED || pFile->locktype == SQLITE_LOCK_SHARED); - - // If some thread using this PID has a lock via a different ChromiumFile - // handle that precludes the requested lock, return BUSY. - if (pFile->locktype != pLock->locktype && - (pLock->locktype >= SQLITE_LOCK_PENDING || locktype > SQLITE_LOCK_SHARED)) - return SQLITE_BUSY; - - // If a SHARED lock is requested, and some thread using this PID already - // has a SHARED or RESERVED lock, then just increment reference counts. - if (locktype == SQLITE_LOCK_SHARED && - (pLock->locktype == SQLITE_LOCK_SHARED || pLock->locktype == SQLITE_LOCK_RESERVED)) { - ASSERT(!pFile->locktype); - ASSERT(pLock->cnt > 0); - pFile->locktype = SQLITE_LOCK_SHARED; - pLock->cnt++; - pFile->pOpen->nLock++; - return SQLITE_OK; - } - - // A PENDING lock is needed before acquiring a SHARED lock and before - // acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will - // be released. - lock.l_len = 1; - lock.l_whence = SEEK_SET; - if (locktype == SQLITE_LOCK_SHARED || - (locktype == SQLITE_LOCK_EXCLUSIVE && pFile->locktype < SQLITE_LOCK_PENDING)) { - lock.l_type = (locktype == SQLITE_LOCK_SHARED ? F_RDLCK : F_WRLCK); - lock.l_start = SQLitePendingByte; - s = fcntl(pFile->h, F_SETLK, &lock); - if (s == -1) { - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - return rc; - } - } - - if (locktype == SQLITE_LOCK_SHARED) { - ASSERT(!pLock->cnt); - ASSERT(!pLock->locktype); - - s = rangeLock(pFile, F_RDLCK, &tErrno); - - // Drop the temporary PENDING lock. - lock.l_start = SQLitePendingByte; - lock.l_len = 1; - lock.l_type = F_UNLCK; - if (fcntl(pFile->h, F_SETLK, &lock)) { - if (s != -1) { - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - return rc; - } - } - if (s == -1) { - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - } else { - pFile->locktype = SQLITE_LOCK_SHARED; - pFile->pOpen->nLock++; - pLock->cnt = 1; - } - } else if (locktype == SQLITE_LOCK_EXCLUSIVE && pLock->cnt > 1) { - // We are trying for an exclusive lock but another thread in the - // same process is still holding a shared lock. - rc = SQLITE_BUSY; - } else { - // The request was for a RESERVED or EXCLUSIVE lock. It is - // assumed that there is a SHARED or greater lock on the file - // already. - ASSERT(pFile->locktype); - lock.l_type = F_WRLCK; - switch (locktype) { - case SQLITE_LOCK_RESERVED: - lock.l_start = SQLiteReservedByte; - s = fcntl(pFile->h, F_SETLK, &lock); - tErrno = errno; - break; - case SQLITE_LOCK_EXCLUSIVE: - s = rangeLock(pFile, F_WRLCK, &tErrno); - break; - default: - ASSERT_NOT_REACHED(); - } - if (s == -1) { - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - } - } - - if (rc == SQLITE_OK) { - pFile->locktype = locktype; - pLock->locktype = locktype; - } else if (locktype == SQLITE_LOCK_EXCLUSIVE) { - pFile->locktype = SQLITE_LOCK_PENDING; - pLock->locktype = SQLITE_LOCK_PENDING; - } - - return rc; -} - -// Closes all file descriptors for given ChromiumFile for which the close has been deferred. -// Returns a SQLite error code. -static int closePendingFds(ChromiumFile* pFile) -{ - int rc = SQLITE_OK; - ChromiumOpenInfo* pOpen = pFile->pOpen; - ChromiumUnusedFd* pError = 0; - ChromiumUnusedFd* pNext; - for (ChromiumUnusedFd* p = pOpen->pUnused; p; p = pNext) { - pNext = p->pNext; - if (close(p->fd)) { - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - p->pNext = pError; - pError = p; - } else - sqlite3_free(p); - } - pOpen->pUnused = pError; - return rc; -} - -// Lowers the locking level on file descriptor. -// locktype must be either SQLITE_LOCK_NONE or SQLITE_LOCK_SHARED. -static int chromiumUnlock(sqlite3_file* id, int locktype) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - ASSERT(locktype <= SQLITE_LOCK_SHARED); - - if (pFile->locktype <= locktype) - return SQLITE_OK; - - ChromiumLockInfo* pLock = pFile->pLock; - ASSERT(pLock->cnt); - - struct flock lock; - int rc = SQLITE_OK; - int h = pFile->h; - int tErrno; - - if (pFile->locktype > SQLITE_LOCK_SHARED) { - ASSERT(pLock->locktype == pFile->locktype); - - if (locktype == SQLITE_LOCK_SHARED && rangeLock(pFile, F_RDLCK, &tErrno) == -1) { - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - if (rc == SQLITE_OK) - pFile->locktype = locktype; - return rc; - } - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SQLitePendingByte; - lock.l_len = 2; - if (fcntl(h, F_SETLK, &lock) != -1) - pLock->locktype = SQLITE_LOCK_SHARED; - else { - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - if (rc == SQLITE_OK) - pFile->locktype = locktype; - return rc; - } - } - if (locktype == SQLITE_LOCK_NONE) { - struct ChromiumOpenInfo *pOpen; - - pLock->cnt--; - - // Release the lock using an OS call only when all threads in this same process have released the lock. - if (!pLock->cnt) { - lock.l_type = F_UNLCK; - lock.l_whence = SEEK_SET; - lock.l_start = lock.l_len = 0L; - if (fcntl(h, F_SETLK, &lock) != -1) - pLock->locktype = SQLITE_LOCK_NONE; - else { - tErrno = errno; - rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); - if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) - pFile->lastErrno = tErrno; - pLock->locktype = SQLITE_LOCK_NONE; - pFile->locktype = SQLITE_LOCK_NONE; - } - } - - pOpen = pFile->pOpen; - pOpen->nLock--; - ASSERT(pOpen->nLock >= 0); - if (!pOpen->nLock) { - int rc2 = closePendingFds(pFile); - if (rc == SQLITE_OK) - rc = rc2; - } - } - - if (rc == SQLITE_OK) - pFile->locktype = locktype; - return rc; +// Defined in Chromium's codebase in third_party/sqlite/src/os_unix.c +extern "C" { +void chromium_sqlite3_initialize_unix_sqlite3_file(sqlite3_file* file); +int chromium_sqlite3_fill_in_unix_sqlite3_file(sqlite3_vfs* vfs, int fd, int dirfd, sqlite3_file* file, const char* fileName, int noLock); +int chromium_sqlite3_get_reusable_file_handle(sqlite3_file* file, const char* fileName, int flags, int* fd); +void chromium_sqlite3_update_reusable_file_handle(sqlite3_file* file, int fd, int flags); +void chromium_sqlite3_destroy_reusable_file_handle(sqlite3_file* file); } -// Closes all file handles for given ChromiumFile and sets all its fields to 0. -// Returns a SQLite error code. -static int chromiumCloseNoLock(sqlite3_file* id) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - if (!pFile) - return SQLITE_OK; - if (pFile->dirfd >= 0) { - if (close(pFile->dirfd)) { - pFile->lastErrno = errno; - return SQLITE_IOERR_DIR_CLOSE; - } - pFile->dirfd = -1; - } - if (pFile->h >= 0 && close(pFile->h)) { - pFile->lastErrno = errno; - return SQLITE_IOERR_CLOSE; - } - sqlite3_free(pFile->pUnused); - memset(pFile, 0, sizeof(ChromiumFile)); - return SQLITE_OK; -} - -// Closes a ChromiumFile, including locking operations. Returns a SQLite error code. -static int chromiumClose(sqlite3_file* id) -{ - if (!id) - return SQLITE_OK; - - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - chromiumUnlock(id, SQLITE_LOCK_NONE); - if (pFile->pOpen && pFile->pOpen->nLock) { - // If there are outstanding locks, do not actually close the file just - // yet because that would clear those locks. - ChromiumOpenInfo* pOpen = pFile->pOpen; - ChromiumUnusedFd* p = pFile->pUnused; - p->pNext = pOpen->pUnused; - pOpen->pUnused = p; - pFile->h = -1; - pFile->pUnused = 0; - } - releaseLockInfo(pFile->pLock); - releaseOpenInfo(pFile->pOpen); - return chromiumCloseNoLock(id); -} - -static int chromiumCheckReservedLockNoop(sqlite3_file*, int* pResOut) -{ - *pResOut = 0; - return SQLITE_OK; -} - -static int chromiumLockNoop(sqlite3_file*, int) -{ - return SQLITE_OK; -} - -static int chromiumUnlockNoop(sqlite3_file*, int) -{ - return SQLITE_OK; -} - -// Seeks to the requested offset and reads up to |cnt| bytes into |pBuf|. Returns number of bytes actually read. -static int seekAndRead(ChromiumFile* id, sqlite3_int64 offset, void* pBuf, int cnt) -{ - sqlite_int64 newOffset = lseek(id->h, offset, SEEK_SET); - if (newOffset != offset) { - id->lastErrno = (newOffset == -1) ? errno : 0; - return -1; - } - int got = read(id->h, pBuf, cnt); - if (got < 0) - id->lastErrno = errno; - return got; -} - -// Reads data from file into a buffer. Returns a SQLite error code. -static int chromiumRead(sqlite3_file* id, void* pBuf, int amt, sqlite3_int64 offset) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - // The bytes in the locking range should never be read. - ASSERT(!pFile->pUnused || offset >= SQLitePendingByte + 512 || offset + amt <= SQLitePendingByte); - - int got = seekAndRead(pFile, offset, pBuf, amt); - if (got == amt) - return SQLITE_OK; - - if (got < 0) - return SQLITE_IOERR_READ; - - // Unread parts of the buffer must be zero-filled. - memset(&(reinterpret_cast<char*>(pBuf))[got], 0, amt - got); - pFile->lastErrno = 0; - return SQLITE_IOERR_SHORT_READ; -} - -// Seeks to the requested offset and writes up to |cnt| bytes. Returns number of bytes actually written. -static int seekAndWrite(ChromiumFile* id, sqlite_int64 offset, const void* pBuf, int cnt) -{ - sqlite_int64 newOffset = lseek(id->h, offset, SEEK_SET); - if (newOffset != offset) { - id->lastErrno = (newOffset == -1) ? errno : 0; - return -1; - } - int got = write(id->h, pBuf, cnt); - if (got < 0) - id->lastErrno = errno; - return got; -} - -// Writes data from buffer into a file. Returns a SQLite error code. -static int chromiumWrite(sqlite3_file* id, const void* pBuf, int amt, sqlite3_int64 offset) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - ASSERT(amt > 0); - - // The bytes in the locking range should never be written. - ASSERT(!pFile->pUnused || offset >= SQLitePendingByte + 512 || offset + amt <= SQLitePendingByte); - - int wrote = 0; - while (amt > 0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt)) > 0) { - amt -= wrote; - offset += wrote; - pBuf = &(reinterpret_cast<const char*>(pBuf))[wrote]; - } - if (amt > 0) { - if (wrote < 0) - return SQLITE_IOERR_WRITE; - pFile->lastErrno = 0; - return SQLITE_FULL; - } - return SQLITE_OK; -} - -static bool syncWrapper(int fd, bool fullSync) -{ -#if OS(DARWIN) - bool success = false; - if (fullSync) - success = !fcntl(fd, F_FULLFSYNC, 0); - if (!success) - success = !fsync(fd); - return success; -#else - return !fdatasync(fd); -#endif -} - -// Makes sure all writes to a particular file are committed to disk. Returns a SQLite error code. -static int chromiumSync(sqlite3_file* id, int flags) -{ - ASSERT((flags & 0x0F) == SQLITE_SYNC_NORMAL || (flags & 0x0F) == SQLITE_SYNC_FULL); - - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - bool isFullSync = ((flags & 0x0F) == SQLITE_SYNC_FULL); - - if (!syncWrapper(pFile->h, isFullSync)) { - pFile->lastErrno = errno; - return SQLITE_IOERR_FSYNC; - } - - if (pFile->dirfd >= 0) { -#if !OS(DARWIN) - if (!isFullSync) { - // Ignore directory sync failures, see http://www.sqlite.org/cvstrac/tktview?tn=1657. - syncWrapper(pFile->dirfd, false); - } -#endif - if (!close(pFile->dirfd)) - pFile->dirfd = -1; - else { - pFile->lastErrno = errno; - return SQLITE_IOERR_DIR_CLOSE; - } - } - - return SQLITE_OK; -} - -// Truncates an open file to the specified size. Returns a SQLite error code. -static int chromiumTruncate(sqlite3_file* id, sqlite_int64 nByte) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - if (ftruncate(pFile->h, nByte)) { - pFile->lastErrno = errno; - return SQLITE_IOERR_TRUNCATE; - } - - return SQLITE_OK; -} - -// Determines the size of a file in bytes. Returns a SQLite error code. -static int chromiumFileSize(sqlite3_file* id, sqlite_int64* pSize) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - struct stat buf; - if (fstat(pFile->h, &buf)) { - pFile->lastErrno = errno; - return SQLITE_IOERR_FSTAT; - } - *pSize = buf.st_size; - - // When opening a zero-size database, findLockInfo writes a single byte into that file - // in order to work around a bug in the OS X msdos filesystem. In order to avoid problems - // with upper layers, we need to report this file size as zero even though it is really 1. - // See http://www.sqlite.org/cvstrac/tktview?tn=3260. - if (*pSize == 1) - *pSize = 0; - - return SQLITE_OK; -} - -static int chromiumFileControl(sqlite3_file* id, int op, void* pArg) -{ - ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); - ASSERT(pFile); - - switch (op) { - case SQLITE_FCNTL_LOCKSTATE: - *reinterpret_cast<int*>(pArg) = pFile->locktype; - return SQLITE_OK; - case SQLITE_LAST_ERRNO: - *reinterpret_cast<int*>(pArg) = pFile->lastErrno; - return SQLITE_OK; - } - return SQLITE_ERROR; -} - -// Same as SQLITE_DEFAULT_SECTOR_SIZE from sqlite's os.h. -static const int SQLiteDefaultSectorSize = 512; - -static int chromiumSectorSize(sqlite3_file*) -{ - return SQLiteDefaultSectorSize; -} - -static int chromiumDeviceCharacteristics(sqlite3_file*) -{ - return 0; -} - -static const sqlite3_io_methods posixIoMethods = { - 1, - chromiumClose, - chromiumRead, - chromiumWrite, - chromiumTruncate, - chromiumSync, - chromiumFileSize, - chromiumLock, - chromiumUnlock, - chromiumCheckReservedLock, - chromiumFileControl, - chromiumSectorSize, - chromiumDeviceCharacteristics -}; - -static const sqlite3_io_methods nolockIoMethods = { - 1, - chromiumCloseNoLock, - chromiumRead, - chromiumWrite, - chromiumTruncate, - chromiumSync, - chromiumFileSize, - chromiumLockNoop, - chromiumUnlockNoop, - chromiumCheckReservedLockNoop, - chromiumFileControl, - chromiumSectorSize, - chromiumDeviceCharacteristics -}; - -// Initializes a ChromiumFile. Returns a SQLite error code. -static int fillInChromiumFile(sqlite3_vfs* pVfs, int h, int dirfd, sqlite3_file* pId, const char* zFilename, int noLock) -{ - ChromiumFile* pNew = reinterpret_cast<ChromiumFile*>(pId); - - ASSERT(!pNew->pLock); - ASSERT(!pNew->pOpen); - - pNew->h = h; - pNew->dirfd = dirfd; - - int rc = SQLITE_OK; - const sqlite3_io_methods* pLockingStyle; - if (noLock) - pLockingStyle = &nolockIoMethods; - else { - pLockingStyle = &posixIoMethods; - rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); - if (rc != SQLITE_OK) { - // If an error occured in findLockInfo(), close the file descriptor - // immediately. This can happen in two scenarios: - // - // (a) A call to fstat() failed. - // (b) A malloc failed. - // - // Scenario (b) may only occur if the process is holding no other - // file descriptors open on the same file. If there were other file - // descriptors on this file, then no malloc would be required by - // findLockInfo(). If this is the case, it is quite safe to close - // handle h - as it is guaranteed that no posix locks will be released - // by doing so. - // - // If scenario (a) caused the error then things are not so safe. The - // implicit assumption here is that if fstat() fails, things are in - // such bad shape that dropping a lock or two doesn't matter much. - close(h); - h = -1; - } - } - - pNew->lastErrno = 0; - if (rc != SQLITE_OK) { - if (dirfd >= 0) - close(dirfd); - if (h >= 0) - close(h); - } else - pNew->pMethod = pLockingStyle; - return rc; -} - -// Searches for an unused file descriptor that was opened on the database -// file identified by zPath with matching flags. Returns 0 if not found. -static ChromiumUnusedFd* findReusableFd(const char* zPath, int flags) -{ - ChromiumUnusedFd* pUnused = 0; - - struct stat sStat; - if (!stat(zPath, &sStat)) { - ChromiumFileId id; - id.dev = sStat.st_dev; - id.ino = sStat.st_ino; - - ChromiumOpenInfo* pO = 0; - for (pO = openList; pO && memcmp(&id, &pO->fileId, sizeof(id)); pO = pO->pNext) { } - if (pO) { - ChromiumUnusedFd** pp; - for (pp = &pO->pUnused; *pp && (*pp)->flags != flags; pp = &((*pp)->pNext)) { } - pUnused = *pp; - if (pUnused) - *pp = pUnused->pNext; - } - } - return pUnused; -} +// Chromium's Posix implementation of SQLite VFS +namespace { // Opens a file. // @@ -974,26 +59,14 @@ static ChromiumUnusedFd* findReusableFd(const char* zPath, int flags) // id - the structure that will manipulate the newly opened file. // desiredFlags - the desired open mode flags. // usedFlags - the actual open mode flags that were used. -static int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, - sqlite3_file* id, int desiredFlags, int* usedFlags) +int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, + sqlite3_file* id, int desiredFlags, int* usedFlags) { - // The mask 0x00007F00 gives us the 7 bits that determine the type of the file SQLite is trying to open. - int fileType = desiredFlags & 0x00007F00; - - memset(id, 0, sizeof(ChromiumFile)); - ChromiumFile* chromiumFile = reinterpret_cast<ChromiumFile*>(id); + chromium_sqlite3_initialize_unix_sqlite3_file(id); int fd = -1; - if (fileType == SQLITE_OPEN_MAIN_DB) { - ChromiumUnusedFd* unusedFd = findReusableFd(fileName, desiredFlags); - if (unusedFd) - fd = unusedFd->fd; - else { - unusedFd = static_cast<ChromiumUnusedFd*>(sqlite3_malloc(sizeof(*unusedFd))); - if (!unusedFd) - return SQLITE_NOMEM; - } - chromiumFile->pUnused = unusedFd; - } + int result = chromium_sqlite3_get_reusable_file_handle(id, fileName, desiredFlags, &fd); + if (result != SQLITE_OK) + return result; if (fd < 0) { fd = PlatformBridge::databaseOpenFile(fileName, desiredFlags); @@ -1003,24 +76,23 @@ static int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, } } if (fd < 0) { - sqlite3_free(chromiumFile->pUnused); + chromium_sqlite3_destroy_reusable_file_handle(id); return SQLITE_CANTOPEN; } if (usedFlags) *usedFlags = desiredFlags; - if (chromiumFile->pUnused) { - chromiumFile->pUnused->fd = fd; - chromiumFile->pUnused->flags = desiredFlags; - } + chromium_sqlite3_update_reusable_file_handle(id, fd, desiredFlags); fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + // The mask 0x00007F00 gives us the 7 bits that determine the type of the file SQLite is trying to open. + int fileType = desiredFlags & 0x00007F00; int noLock = (fileType != SQLITE_OPEN_MAIN_DB); - int rc = fillInChromiumFile(vfs, fd, -1, id, fileName, noLock); - if (rc != SQLITE_OK) - sqlite3_free(chromiumFile->pUnused); - return rc; + result = chromium_sqlite3_fill_in_unix_sqlite3_file(vfs, fd, -1, id, fileName, noLock); + if (result != SQLITE_OK) + chromium_sqlite3_destroy_reusable_file_handle(id); + return result; } // Deletes the given file. @@ -1029,7 +101,7 @@ static int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, // fileName - the name of the file. // syncDir - determines if the directory to which this file belongs // should be synched after the file is deleted. -static int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) +int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) { return PlatformBridge::databaseDeleteFile(fileName, syncDir); } @@ -1040,7 +112,7 @@ static int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) // fileName - the name of the file. // flag - the type of test to make on this file. // res - the result. -static int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) +int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) { int attr = static_cast<int>(PlatformBridge::databaseGetFileAttributes(fileName)); if (attr < 0) { @@ -1050,7 +122,7 @@ static int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res switch (flag) { case SQLITE_ACCESS_EXISTS: - *res = 1; // if the file doesn't exist, attr < 0 + *res = 1; // if the file doesn't exist, attr < 0 break; case SQLITE_ACCESS_READWRITE: *res = (attr & W_OK) && (attr & R_OK); @@ -1071,8 +143,8 @@ static int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res // relativePath - the relative path. // bufSize - the size of the output buffer in bytes. // absolutePath - the output buffer where the absolute path will be stored. -static int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, - int, char* absolutePath) +int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, + int, char* absolutePath) { // The renderer process doesn't need to know the absolute path of the file sqlite3_snprintf(vfs->mxPathname, absolutePath, "%s", relativePath); @@ -1080,107 +152,44 @@ static int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, } #ifndef SQLITE_OMIT_LOAD_EXTENSION -// We disallow loading DSOs inside the renderer process, so the following procedures are no-op. -static void* chromiumDlOpen(sqlite3_vfs*, const char*) -{ - return 0; -} - -static void chromiumDlError(sqlite3_vfs*, int, char*) -{ -} - -static void (*chromiumDlSym(sqlite3_vfs*, void*, const char*))() +// Returns NULL, thus disallowing loading libraries in the renderer process. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the shared library file. +void* chromiumDlOpen(sqlite3_vfs*, const char*) { return 0; } - -static void chromiumDlClose(sqlite3_vfs*, void*) -{ -} #else #define chromiumDlOpen 0 -#define chromiumDlError 0 -#define chromiumDlSym 0 -#define chromiumDlClose 0 #endif // SQLITE_OMIT_LOAD_EXTENSION -// Generates a seed for SQLite's PRNG. -static int chromiumRandomness(sqlite3_vfs*, int nBuf, char *zBuf) -{ - ASSERT(static_cast<size_t>(nBuf) >= (sizeof(time_t) + sizeof(int))); - - memset(zBuf, 0, nBuf); - int fd = open("/dev/urandom", O_RDONLY); - if (fd < 0) { - time_t t; - time(&t); - memcpy(zBuf, &t, sizeof(t)); - int pid = getpid(); - memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); - return sizeof(t) + sizeof(pid); - } - nBuf = read(fd, zBuf, nBuf); - close(fd); - return nBuf; -} - -// Sleeps for at least |microseconds|, and returns the actual -// amount of time spent sleeping (in microseconds). -static int chromiumSleep(sqlite3_vfs*, int microseconds) -{ -#if OS(DARWIN) - usleep(microseconds); - return microseconds; -#else - // Round to the nearest second. - int seconds = (microseconds + 999999) / 1000000; - sleep(seconds); - return seconds * 1000000; -#endif -} - -// Retrieves the current system time (UTC). -static int chromiumCurrentTime(sqlite3_vfs*, double* now) -{ - struct timeval timeval; - gettimeofday(&timeval, 0); - *now = 2440587.5 + timeval.tv_sec / 86400.0 + timeval.tv_usec / 86400000000.0; - return 0; -} - -// This is not yet implemented in SQLite core. -static int chromiumGetLastError(sqlite3_vfs*, int, char*) -{ - return 0; -} - -// Same as MAX_PATHNAME from sqlite's os_unix.c. -static const int chromiumMaxPathname = 512; +} // namespace namespace WebCore { void SQLiteFileSystem::registerSQLiteVFS() { + sqlite3_vfs* unix_vfs = sqlite3_vfs_find("unix"); static sqlite3_vfs chromium_vfs = { 1, - sizeof(ChromiumFile), - chromiumMaxPathname, + unix_vfs->szOsFile, + unix_vfs->mxPathname, 0, "chromium_vfs", - 0, + unix_vfs->pAppData, chromiumOpen, chromiumDelete, chromiumAccess, chromiumFullPathname, chromiumDlOpen, - chromiumDlError, - chromiumDlSym, - chromiumDlClose, - chromiumRandomness, - chromiumSleep, - chromiumCurrentTime, - chromiumGetLastError + unix_vfs->xDlError, + unix_vfs->xDlSym, + unix_vfs->xDlClose, + unix_vfs->xRandomness, + unix_vfs->xSleep, + unix_vfs->xCurrentTime, + unix_vfs->xGetLastError }; sqlite3_vfs_register(&chromium_vfs, 0); } diff --git a/Source/WebCore/platform/text/Base64.cpp b/Source/WebCore/platform/text/Base64.cpp index 98b537a..bf706f6 100644 --- a/Source/WebCore/platform/text/Base64.cpp +++ b/Source/WebCore/platform/text/Base64.cpp @@ -60,9 +60,11 @@ static const char base64DecMap[128] = { 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00 }; -void base64Encode(const Vector<char>& in, Vector<char>& out, bool insertLFs) +String base64Encode(const char* data, unsigned length, bool insertLFs) { - base64Encode(in.data(), in.size(), out, insertLFs); + Vector<char> result; + base64Encode(data, length, result, insertLFs); + return String(result.data(), result.size()); } void base64Encode(const char* data, unsigned len, Vector<char>& out, bool insertLFs) diff --git a/Source/WebCore/platform/text/Base64.h b/Source/WebCore/platform/text/Base64.h index 211bd3c..70855de 100644 --- a/Source/WebCore/platform/text/Base64.h +++ b/Source/WebCore/platform/text/Base64.h @@ -27,20 +27,45 @@ #ifndef Base64_h #define Base64_h -#include <wtf/Forward.h> #include <wtf/Vector.h> +#include <wtf/text/CString.h> +#include <wtf/text/WTFString.h> namespace WebCore { enum Base64DecodePolicy { FailOnInvalidCharacter, IgnoreWhitespace, IgnoreInvalidCharacters }; -void base64Encode(const Vector<char>&, Vector<char>&, bool insertLFs = false); void base64Encode(const char*, unsigned, Vector<char>&, bool insertLFs = false); +void base64Encode(const Vector<char>&, Vector<char>&, bool insertLFs = false); +void base64Encode(const CString&, Vector<char>&, bool insertLFs = false); +String base64Encode(const char*, unsigned, bool insertLFs = false); +String base64Encode(const Vector<char>&, bool insertLFs = false); +String base64Encode(const CString&, bool insertLFs = false); bool base64Decode(const String&, Vector<char>&, Base64DecodePolicy = FailOnInvalidCharacter); bool base64Decode(const Vector<char>&, Vector<char>&, Base64DecodePolicy = FailOnInvalidCharacter); bool base64Decode(const char*, unsigned, Vector<char>&, Base64DecodePolicy = FailOnInvalidCharacter); +inline void base64Encode(const Vector<char>& in, Vector<char>& out, bool insertLFs) +{ + base64Encode(in.data(), in.size(), out, insertLFs); } +inline void base64Encode(const CString& in, Vector<char>& out, bool insertLFs) +{ + base64Encode(in.data(), in.length(), out, insertLFs); +} + +inline String base64Encode(const Vector<char>& in, bool insertLFs) +{ + return base64Encode(in.data(), in.size(), insertLFs); +} + +inline String base64Encode(const CString& in, bool insertLFs) +{ + return base64Encode(in.data(), in.length(), insertLFs); +} + +} // namespace WebCore + #endif // Base64_h diff --git a/Source/WebCore/platform/text/BidiResolver.h b/Source/WebCore/platform/text/BidiResolver.h index 8abd698..72d163c 100644 --- a/Source/WebCore/platform/text/BidiResolver.h +++ b/Source/WebCore/platform/text/BidiResolver.h @@ -161,7 +161,7 @@ public : MidpointState<Iterator>& midpointState() { return m_midpointState; } void embed(WTF::Unicode::Direction); - void commitExplicitEmbedding(); + bool commitExplicitEmbedding(); void createBidiRunsForLine(const Iterator& end, bool visualOrder = false, bool hardLineBreak = false); @@ -400,7 +400,7 @@ void BidiResolver<Iterator, Run>::raiseExplicitEmbeddingLevel(WTF::Unicode::Dire } template <class Iterator, class Run> -void BidiResolver<Iterator, Run>::commitExplicitEmbedding() +bool BidiResolver<Iterator, Run>::commitExplicitEmbedding() { using namespace WTF::Unicode; @@ -440,6 +440,8 @@ void BidiResolver<Iterator, Run>::commitExplicitEmbedding() setContext(toContext); m_currentExplicitEmbeddingSequence.clear(); + + return fromLevel != toLevel; } template <class Iterator, class Run> @@ -881,8 +883,8 @@ void BidiResolver<Iterator, Run>::createBidiRunsForLine(const Iterator& end, boo increment(); if (!m_currentExplicitEmbeddingSequence.isEmpty()) { - commitExplicitEmbedding(); - if (pastEnd) { + bool committed = commitExplicitEmbedding(); + if (committed && pastEnd) { current = end; m_status = stateAtEnd.m_status; sor = stateAtEnd.sor; diff --git a/Source/WebCore/platform/text/CharacterNames.h b/Source/WebCore/platform/text/CharacterNames.h deleted file mode 100644 index c4b496e..0000000 --- a/Source/WebCore/platform/text/CharacterNames.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (C) 2007, 2009, 2010 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * 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 CharacterNames_h -#define CharacterNames_h - -#include <wtf/unicode/Unicode.h> - -namespace WebCore { - -// Names here are taken from the Unicode standard. - -// Most of these are UChar constants, not UChar32, which makes them -// more convenient for WebCore code that mostly uses UTF-16. - -const UChar32 aegeanWordSeparatorLine = 0x10100; -const UChar32 aegeanWordSeparatorDot = 0x10101; -const UChar blackCircle = 0x25CF; -const UChar blackSquare = 0x25A0; -const UChar blackUpPointingTriangle = 0x25B2; -const UChar bullet = 0x2022; -const UChar bullseye = 0x25CE; -const UChar carriageReturn = 0x000D; -const UChar ethiopicPrefaceColon = 0x1366; -const UChar ethiopicWordspace = 0x1361; -const UChar fisheye = 0x25C9; -const UChar hebrewPunctuationGeresh = 0x05F3; -const UChar hebrewPunctuationGershayim = 0x05F4; -const UChar horizontalEllipsis = 0x2026; -const UChar hyphen = 0x2010; -const UChar hyphenMinus = 0x002D; -const UChar ideographicComma = 0x3001; -const UChar ideographicFullStop = 0x3002; -const UChar ideographicSpace = 0x3000; -const UChar leftDoubleQuotationMark = 0x201C; -const UChar leftSingleQuotationMark = 0x2018; -const UChar leftToRightEmbed = 0x202A; -const UChar leftToRightMark = 0x200E; -const UChar leftToRightOverride = 0x202D; -const UChar minusSign = 0x2212; -const UChar newlineCharacter = 0x000A; -const UChar noBreakSpace = 0x00A0; -const UChar objectReplacementCharacter = 0xFFFC; -const UChar popDirectionalFormatting = 0x202C; -const UChar replacementCharacter = 0xFFFD; -const UChar rightDoubleQuotationMark = 0x201D; -const UChar rightSingleQuotationMark = 0x2019; -const UChar rightToLeftEmbed = 0x202B; -const UChar rightToLeftMark = 0x200F; -const UChar rightToLeftOverride = 0x202E; -const UChar sesameDot = 0xFE45; -const UChar softHyphen = 0x00AD; -const UChar space = 0x0020; -const UChar tibetanMarkIntersyllabicTsheg = 0x0F0B; -const UChar tibetanMarkDelimiterTshegBstar = 0x0F0C; -const UChar32 ugariticWordDivider = 0x1039F; -const UChar whiteBullet = 0x25E6; -const UChar whiteCircle = 0x25CB; -const UChar whiteSesameDot = 0xFE46; -const UChar whiteUpPointingTriangle = 0x25B3; -const UChar yenSign = 0x00A5; -const UChar zeroWidthJoiner = 0x200D; -const UChar zeroWidthNonJoiner = 0x200C; -const UChar zeroWidthSpace = 0x200B; - -} - -#endif // CharacterNames_h diff --git a/Source/WebCore/platform/graphics/chromium/IconChromiumMac.cpp b/Source/WebCore/platform/text/LocalizedNumber.h index a24afb2..45873b8 100644 --- a/Source/WebCore/platform/graphics/chromium/IconChromiumMac.cpp +++ b/Source/WebCore/platform/text/LocalizedNumber.h @@ -1,10 +1,10 @@ /* - * Copyright (c) 2008, Google Inc. All rights reserved. - * + * Copyright (C) 2011 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 @@ -28,23 +28,32 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "config.h" -#include "Icon.h" +#ifndef LocalizedNumber_h +#define LocalizedNumber_h -#include "PassRefPtr.h" +#include <wtf/text/WTFString.h> -// FIXME: These are temporary stubs, we need real implementations which -// may come in the form of IconChromium.cpp. The Windows Chromium -// implementation is currently in IconWin.cpp. - namespace WebCore { -Icon::~Icon() -{ -} +// Parses a string representation of a floating point number localized +// for the browser's current locale. If the input string is not valid +// or an implementation doesn't support localized numbers, this +// function returns NaN. This function doesn't need to support +// scientific notation, NaN, +Infinity and -Infinity, and doesn't need +// to support the standard representations of ECMAScript and HTML5. +double parseLocalizedNumber(const String&); -void Icon::paint(GraphicsContext*, const IntRect&) -{ -} +// Serializes the specified floating point number for the browser's +// current locale. If an implementation doesn't support localized +// numbers or the input value is NaN or Infinitiy, the function should +// return an empty string. +String formatLocalizedNumber(double); -} +// Returns true if the input character can be used to represent a +// number in the browser locale. For example, this should return true for 0-9 . +// , + - for en-US locale. +bool isLocalizedNumberCharacter(UChar32); + +} // namespace WebCore + +#endif // LocalizedNumber_h diff --git a/Source/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp b/Source/WebCore/platform/text/LocalizedNumberNone.cpp index 16f55e2..6f017e9 100644 --- a/Source/WebCore/platform/graphics/chromium/IconChromiumLinux.cpp +++ b/Source/WebCore/platform/text/LocalizedNumberNone.cpp @@ -1,10 +1,10 @@ /* - * Copyright (c) 2008, 2009 Google Inc. All rights reserved. - * + * Copyright (C) 2011 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 @@ -29,26 +29,27 @@ */ #include "config.h" -#include "Icon.h" +#include "LocalizedNumber.h" -#include "GraphicsContext.h" -#include "NotImplemented.h" -#include "PlatformString.h" +#include <limits> + +using namespace std; namespace WebCore { -Icon::Icon(const PlatformIcon& icon) - : m_icon(icon) +double parseLocalizedNumber(const String&) { + return numeric_limits<double>::quiet_NaN(); } -Icon::~Icon() +String formatLocalizedNumber(double) { + return String(); } -void Icon::paint(GraphicsContext*, const IntRect&) +bool isLocalizedNumberCharacter(UChar32) { - notImplemented(); + return false; } } // namespace WebCore diff --git a/Source/WebCore/platform/text/RegularExpression.cpp b/Source/WebCore/platform/text/RegularExpression.cpp index 9b063c9..e020b91 100644 --- a/Source/WebCore/platform/text/RegularExpression.cpp +++ b/Source/WebCore/platform/text/RegularExpression.cpp @@ -1,6 +1,7 @@ /* * Copyright (C) 2004, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2008 Collabora Ltd. + * Copyright (C) 2011 Peter Varga (pvarga@webkit.org), University of Szeged * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -27,52 +28,48 @@ #include "config.h" #include "RegularExpression.h" +#include <wtf/BumpPointerAllocator.h> +#include <yarr/Yarr.h> #include "Logging.h" -#include <pcre/pcre.h> namespace WebCore { class RegularExpression::Private : public RefCounted<RegularExpression::Private> { public: - static PassRefPtr<Private> create(const String& pattern, TextCaseSensitivity); - ~Private(); + static PassRefPtr<Private> create(const String& pattern, TextCaseSensitivity caseSensitivity) + { + return adoptRef(new Private(pattern, caseSensitivity)); + } - JSRegExp* regexp() const { return m_regexp; } - int lastMatchLength; + int lastMatchLength; -private: - Private(const String& pattern, TextCaseSensitivity); - static JSRegExp* compile(const String& pattern, TextCaseSensitivity); + unsigned m_numSubpatterns; + OwnPtr<JSC::Yarr::BytecodePattern> m_regExpByteCode; - JSRegExp* m_regexp; -}; +private: + Private(const String& pattern, TextCaseSensitivity caseSensitivity) + : lastMatchLength(-1) + , m_regExpByteCode(compile(pattern, caseSensitivity)) + , m_constructionError(0) + { + } -inline JSRegExp* RegularExpression::Private::compile(const String& pattern, TextCaseSensitivity caseSensitivity) -{ - const char* errorMessage; - JSRegExp* regexp = jsRegExpCompile(pattern.characters(), pattern.length(), - caseSensitivity == TextCaseSensitive ? JSRegExpDoNotIgnoreCase : JSRegExpIgnoreCase, JSRegExpSingleLine, - 0, &errorMessage); - if (!regexp) - LOG_ERROR("RegularExpression: pcre_compile failed with '%s'", errorMessage); - return regexp; -} + PassOwnPtr<JSC::Yarr::BytecodePattern> compile(const String& patternString, TextCaseSensitivity caseSensitivity) + { + JSC::Yarr::YarrPattern pattern(JSC::UString(patternString.impl()), (caseSensitivity == TextCaseInsensitive), false, &m_constructionError); + if (m_constructionError) { + LOG_ERROR("RegularExpression: YARR compile failed with '%s'", m_constructionError); + return PassOwnPtr<JSC::Yarr::BytecodePattern>(); + } -inline RegularExpression::Private::Private(const String& pattern, TextCaseSensitivity caseSensitivity) - : lastMatchLength(-1) - , m_regexp(compile(pattern, caseSensitivity)) -{ -} + m_numSubpatterns = pattern.m_numSubpatterns; -inline PassRefPtr<RegularExpression::Private> RegularExpression::Private::create(const String& pattern, TextCaseSensitivity caseSensitivity) -{ - return adoptRef(new Private(pattern, caseSensitivity)); -} + return JSC::Yarr::byteCompile(pattern, &m_regexAllocator); + } -RegularExpression::Private::~Private() -{ - jsRegExpFree(m_regexp); -} + BumpPointerAllocator m_regexAllocator; + const char* m_constructionError; +}; RegularExpression::RegularExpression(const String& pattern, TextCaseSensitivity caseSensitivity) : d(Private::create(pattern, caseSensitivity)) @@ -96,28 +93,36 @@ RegularExpression& RegularExpression::operator=(const RegularExpression& re) int RegularExpression::match(const String& str, int startFrom, int* matchLength) const { - if (!d->regexp()) + if (!d->m_regExpByteCode) return -1; if (str.isNull()) return -1; - // First 2 offsets are start and end offsets; 3rd entry is used internally by pcre - static const size_t maxOffsets = 3; - int offsets[maxOffsets]; - int result = jsRegExpExecute(d->regexp(), str.characters(), str.length(), startFrom, offsets, maxOffsets); + int offsetVectorSize = (d->m_numSubpatterns + 1) * 2; + int* offsetVector; + Vector<int, 32> nonReturnedOvector; + + nonReturnedOvector.resize(offsetVectorSize); + offsetVector = nonReturnedOvector.data(); + + ASSERT(offsetVector); + for (unsigned j = 0, i = 0; i < d->m_numSubpatterns + 1; j += 2, i++) + offsetVector[j] = -1; + + int result = JSC::Yarr::interpret(d->m_regExpByteCode.get(), str.characters(), startFrom, str.length(), offsetVector); + ASSERT(result >= -1); + if (result < 0) { - if (result != JSRegExpErrorNoMatch) - LOG_ERROR("RegularExpression: pcre_exec() failed with result %d", result); d->lastMatchLength = -1; return -1; } - // 1 means 1 match; 0 means more than one match. First match is recorded in offsets. - d->lastMatchLength = offsets[1] - offsets[0]; + // 1 means 1 match; 0 means more than one match. First match is recorded in offsetVector. + d->lastMatchLength = offsetVector[1] - offsetVector[0]; if (matchLength) *matchLength = d->lastMatchLength; - return offsets[0]; + return offsetVector[0]; } int RegularExpression::searchRev(const String& str) const diff --git a/Source/WebCore/platform/text/SegmentedString.cpp b/Source/WebCore/platform/text/SegmentedString.cpp index 5e9755b..7c859dc 100644 --- a/Source/WebCore/platform/text/SegmentedString.cpp +++ b/Source/WebCore/platform/text/SegmentedString.cpp @@ -186,17 +186,6 @@ void SegmentedString::advanceSubstring() } } -int SegmentedString::numberOfCharactersConsumedSlow() const -{ - int result = m_numberOfCharactersConsumedPriorToCurrentString + m_currentString.numberOfCharactersConsumed(); - if (m_pushedChar1) { - --result; - if (m_pushedChar2) - --result; - } - return result; -} - String SegmentedString::toString() const { String result; @@ -262,14 +251,14 @@ WTF::ZeroBasedNumber SegmentedString::currentLine() const WTF::ZeroBasedNumber SegmentedString::currentColumn() const { - int zeroBasedColumn = numberOfCharactersConsumedSlow() - m_numberOfCharactersConsumedPriorToCurrentLine; + int zeroBasedColumn = numberOfCharactersConsumed() - m_numberOfCharactersConsumedPriorToCurrentLine; return WTF::ZeroBasedNumber::fromZeroBasedInt(zeroBasedColumn); } void SegmentedString::setCurrentPosition(WTF::ZeroBasedNumber line, WTF::ZeroBasedNumber columnAftreProlog, int prologLength) { m_currentLine = line.zeroBasedInt(); - m_numberOfCharactersConsumedPriorToCurrentLine = numberOfCharactersConsumedSlow() + prologLength - columnAftreProlog.zeroBasedInt(); + m_numberOfCharactersConsumedPriorToCurrentLine = numberOfCharactersConsumed() + prologLength - columnAftreProlog.zeroBasedInt(); } } diff --git a/Source/WebCore/platform/text/SegmentedString.h b/Source/WebCore/platform/text/SegmentedString.h index 30c899d..3784b50 100644 --- a/Source/WebCore/platform/text/SegmentedString.h +++ b/Source/WebCore/platform/text/SegmentedString.h @@ -206,13 +206,15 @@ public: int numberOfCharactersConsumed() const { - // We don't currently handle the case when there are pushed character. - ASSERT(!m_pushedChar1); - return m_numberOfCharactersConsumedPriorToCurrentString + m_currentString.numberOfCharactersConsumed(); + int numberOfPushedCharacters = 0; + if (m_pushedChar1) { + ++numberOfPushedCharacters; + if (m_pushedChar2) + ++numberOfPushedCharacters; + } + return m_numberOfCharactersConsumedPriorToCurrentString + m_currentString.numberOfCharactersConsumed() - numberOfPushedCharacters; } - int numberOfCharactersConsumedSlow() const; - String toString() const; const UChar& operator*() const { return *current(); } diff --git a/Source/WebCore/platform/text/TextCodecICU.cpp b/Source/WebCore/platform/text/TextCodecICU.cpp index 6a579f9..92a158a 100644 --- a/Source/WebCore/platform/text/TextCodecICU.cpp +++ b/Source/WebCore/platform/text/TextCodecICU.cpp @@ -27,7 +27,6 @@ #include "config.h" #include "TextCodecICU.h" -#include "CharacterNames.h" #include "PlatformString.h" #include "ThreadGlobalData.h" #include <unicode/ucnv.h> @@ -37,6 +36,7 @@ #include <wtf/PassOwnPtr.h> #include <wtf/StringExtras.h> #include <wtf/Threading.h> +#include <wtf/unicode/CharacterNames.h> using std::min; diff --git a/Source/WebCore/platform/text/TextCodecUTF16.cpp b/Source/WebCore/platform/text/TextCodecUTF16.cpp index e88e83b..4ceed23 100644 --- a/Source/WebCore/platform/text/TextCodecUTF16.cpp +++ b/Source/WebCore/platform/text/TextCodecUTF16.cpp @@ -71,6 +71,8 @@ String TextCodecUTF16::decode(const char* bytes, size_t length, bool, bool, bool if (!length) return String(); + // FIXME: This should generate an error if there is an unpaired surrogate. + const unsigned char* p = reinterpret_cast<const unsigned char*>(bytes); size_t numBytes = length + m_haveBufferedByte; size_t numChars = numBytes / 2; diff --git a/Source/WebCore/platform/text/TextCodecUTF8.cpp b/Source/WebCore/platform/text/TextCodecUTF8.cpp new file mode 100644 index 0000000..8944d68 --- /dev/null +++ b/Source/WebCore/platform/text/TextCodecUTF8.cpp @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2004, 2006, 2008, 2011 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 "TextCodecUTF8.h" + +#include <wtf/text/CString.h> +#include <wtf/text/StringBuffer.h> +#include <wtf/unicode/UTF8.h> + +using namespace WTF::Unicode; +using namespace std; + +namespace WebCore { + +// Assuming that a pointer is the size of a "machine word", then +// uintptr_t is an integer type that is also a machine word. +typedef uintptr_t MachineWord; + +// This constant has type uintptr_t since we will use it to align +// pointers. Not because MachineWord is uintptr_t. +const uintptr_t machineWordAlignmentMask = sizeof(MachineWord) - 1; + +template<size_t size> struct NonASCIIMask; +template<> struct NonASCIIMask<4> { + static unsigned value() { return 0x80808080U; } +}; +template<> struct NonASCIIMask<8> { + static unsigned long long value() { return 0x8080808080808080ULL; } +}; + +template<size_t size> struct UCharByteFiller; +template<> struct UCharByteFiller<4> { + static void copy(UChar* destination, const uint8_t* source) + { + destination[0] = source[0]; + destination[1] = source[1]; + destination[2] = source[2]; + destination[3] = source[3]; + } +}; +template<> struct UCharByteFiller<8> { + static void copy(UChar* destination, const uint8_t* source) + { + destination[0] = source[0]; + destination[1] = source[1]; + destination[2] = source[2]; + destination[3] = source[3]; + destination[4] = source[4]; + destination[5] = source[5]; + destination[6] = source[6]; + destination[7] = source[7]; + } +}; + +static inline bool isAlignedToMachineWord(const void* pointer) +{ + return !(reinterpret_cast<uintptr_t>(pointer) & machineWordAlignmentMask); +} + +template<typename T> static inline T* alignToMachineWord(T* pointer) +{ + return reinterpret_cast<T*>(reinterpret_cast<uintptr_t>(pointer) & ~machineWordAlignmentMask); +} + +PassOwnPtr<TextCodec> TextCodecUTF8::create(const TextEncoding&, const void*) +{ + return adoptPtr(new TextCodecUTF8); +} + +void TextCodecUTF8::registerEncodingNames(EncodingNameRegistrar registrar) +{ + registrar("UTF-8", "UTF-8"); +} + +void TextCodecUTF8::registerCodecs(TextCodecRegistrar registrar) +{ + registrar("UTF-8", create, 0); +} + +static inline int nonASCIISequenceLength(unsigned char firstByte) +{ + ASSERT(!isASCII(firstByte)); + switch (firstByte >> 4) { + case 0xF: + return 4; + case 0xE: + return 3; + } + return 2; +} + +static inline int decodeNonASCIISequence(const unsigned char* sequence, unsigned length) +{ + ASSERT(!isASCII(sequence[0])); + if (length == 2) { + ASSERT(sequence[0] <= 0xDF); + if (sequence[0] < 0xC2) + return -1; + if (sequence[1] < 0x80 || sequence[1] > 0xBF) + return -1; + return ((sequence[0] << 6) + sequence[1]) - 0x00003080; + } + if (length == 3) { + ASSERT(sequence[0] >= 0xE0 && sequence[0] <= 0xEF); + switch (sequence[0]) { + case 0xE0: + if (sequence[1] < 0xA0 || sequence[1] > 0xBF) + return -1; + break; + case 0xED: + if (sequence[1] < 0x80 || sequence[1] > 0x9F) + return -1; + break; + default: + if (sequence[1] < 0x80 || sequence[1] > 0xBF) + return -1; + } + if (sequence[2] < 0x80 || sequence[2] > 0xBF) + return -1; + return ((sequence[0] << 12) + (sequence[1] << 6) + sequence[2]) - 0x000E2080; + } + ASSERT(length == 4); + ASSERT(sequence[0] >= 0xF0 && sequence[0] <= 0xF4); + switch (sequence[0]) { + case 0xF0: + if (sequence[1] < 0x90 || sequence[1] > 0xBF) + return -1; + break; + case 0xF4: + if (sequence[1] < 0x80 || sequence[1] > 0x8F) + return -1; + break; + default: + if (sequence[1] < 0x80 || sequence[1] > 0xBF) + return -1; + } + if (sequence[2] < 0x80 || sequence[2] > 0xBF) + return -1; + if (sequence[3] < 0x80 || sequence[3] > 0xBF) + return -1; + return ((sequence[0] << 18) + (sequence[1] << 12) + (sequence[2] << 6) + sequence[3]) - 0x03C82080; +} + +String TextCodecUTF8::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError) +{ + StringBuffer buffer(length); + + const uint8_t* source = reinterpret_cast<const uint8_t*>(bytes); + const uint8_t* end = source + length; + const uint8_t* alignedEnd = alignToMachineWord(end); + UChar* destination = buffer.characters(); + + int count; + int character; + + if (m_partialSequenceSize) { + count = nonASCIISequenceLength(m_partialSequence[0]); + ASSERT(count > m_partialSequenceSize); + if (count - m_partialSequenceSize > end - source) { + memcpy(m_partialSequence + m_partialSequenceSize, source, end - source); + m_partialSequenceSize += end - source; + source = end; + } else { + uint8_t completeSequence[U8_MAX_LENGTH]; + memcpy(completeSequence, m_partialSequence, m_partialSequenceSize); + memcpy(completeSequence + m_partialSequenceSize, source, count - m_partialSequenceSize); + source += count - m_partialSequenceSize; + m_partialSequenceSize = 0; + character = decodeNonASCIISequence(completeSequence, count); + goto decodedNonASCII; + } + } + + while (source < end) { + if (isASCII(*source)) { + // Fast path for ASCII. Most UTF-8 text will be ASCII. + if (isAlignedToMachineWord(source)) { + while (source < alignedEnd) { + MachineWord chunk = *reinterpret_cast_ptr<const MachineWord*>(source); + if (chunk & NonASCIIMask<sizeof(MachineWord)>::value()) { + if (isASCII(*source)) + break; + goto nonASCII; + } + UCharByteFiller<sizeof(MachineWord)>::copy(destination, source); + source += sizeof(MachineWord); + destination += sizeof(MachineWord); + } + if (source == end) + break; + } + *destination++ = *source++; + } else { +nonASCII: + count = nonASCIISequenceLength(*source); + ASSERT(count >= 2); + ASSERT(count <= 4); + if (count > end - source) { + ASSERT(end - source <= static_cast<ptrdiff_t>(sizeof(m_partialSequence))); + ASSERT(!m_partialSequenceSize); + m_partialSequenceSize = end - source; + memcpy(m_partialSequence, source, m_partialSequenceSize); + break; + } + character = decodeNonASCIISequence(source, count); + source += count; +decodedNonASCII: + if (character < 0) { + if (stopOnError) { + sawError = true; + break; + } + } else { + ASSERT(!U_IS_SURROGATE(character)); + if (U_IS_BMP(character)) + *destination++ = character; + else { + *destination++ = U16_LEAD(character); + *destination++ = U16_TRAIL(character); + } + } + } + } + + buffer.shrink(destination - buffer.characters()); + + if (flush && m_partialSequenceSize) + sawError = true; + + return String::adopt(buffer); +} + +CString TextCodecUTF8::encode(const UChar* characters, size_t length, UnencodableHandling) +{ + // The maximum number of UTF-8 bytes needed per UTF-16 code unit is 3. + // BMP characters take only one UTF-16 code unit and can take up to 3 bytes (3x). + // Non-BMP characters take two UTF-16 code units and can take up to 4 bytes (2x). + if (length > numeric_limits<size_t>::max() / 3) + CRASH(); + Vector<uint8_t> bytes(length * 3); + + size_t i = 0; + size_t bytesWritten = 0; + while (i < length) { + UChar32 character; + U16_NEXT(characters, i, length, character); + U8_APPEND_UNSAFE(bytes.data(), bytesWritten, character); + } + + return CString(reinterpret_cast<char*>(bytes.data()), bytesWritten); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/text/TextCodecUTF8.h b/Source/WebCore/platform/text/TextCodecUTF8.h new file mode 100644 index 0000000..f3b6b7a --- /dev/null +++ b/Source/WebCore/platform/text/TextCodecUTF8.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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 TextCodecUTF8_h +#define TextCodecUTF8_h + +#include "TextCodec.h" + +namespace WebCore { + +class TextCodecUTF8 : public TextCodec { +public: + static void registerEncodingNames(EncodingNameRegistrar); + static void registerCodecs(TextCodecRegistrar); + + virtual String decode(const char*, size_t length, bool flush, bool stopOnError, bool& sawError); + virtual CString encode(const UChar*, size_t length, UnencodableHandling); + +private: + static PassOwnPtr<TextCodec> create(const TextEncoding&, const void*); + TextCodecUTF8() : m_partialSequenceSize(0) { } + + int m_partialSequenceSize; + char m_partialSequence[U8_MAX_LENGTH - 1]; + +}; + +} // namespace WebCore + +#endif // TextCodecUTF8_h diff --git a/Source/WebCore/platform/text/TextEncodingRegistry.cpp b/Source/WebCore/platform/text/TextEncodingRegistry.cpp index c0c0255..1dc09ee 100644 --- a/Source/WebCore/platform/text/TextEncodingRegistry.cpp +++ b/Source/WebCore/platform/text/TextEncodingRegistry.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006, 2007, 2011 Apple Inc. All rights reserved. * Copyright (C) 2007-2009 Torch Mobile, Inc. * * Redistribution and use in source and binary forms, with or without @@ -27,14 +27,12 @@ #include "config.h" #include "TextEncodingRegistry.h" -#include "PlatformString.h" #include "TextCodecLatin1.h" #include "TextCodecUserDefined.h" #include "TextCodecUTF16.h" +#include "TextCodecUTF8.h" #include "TextEncoding.h" #include <wtf/ASCIICType.h> -#include <wtf/Assertions.h> -#include <wtf/HashFunctions.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/StdLibExtras.h> @@ -68,7 +66,6 @@ const size_t maxEncodingNameLength = 63; // Hash for all-ASCII strings that does case folding. struct TextEncodingNameHash { - static bool equal(const char* s1, const char* s2) { char c1; @@ -129,9 +126,7 @@ static bool didExtendTextCodecMaps; static HashSet<const char*>* japaneseEncodings; static HashSet<const char*>* nonBackslashEncodings; -static const char* const textEncodingNameBlacklist[] = { - "UTF-7" -}; +static const char* const textEncodingNameBlacklist[] = { "UTF-7" }; #if ERROR_DISABLED @@ -268,7 +263,7 @@ static void buildQuirksSets() ASSERT(!japaneseEncodings); ASSERT(!nonBackslashEncodings); - japaneseEncodings = new HashSet<const char*>(); + japaneseEncodings = new HashSet<const char*>; addEncodingName(japaneseEncodings, "EUC-JP"); addEncodingName(japaneseEncodings, "ISO-2022-JP"); addEncodingName(japaneseEncodings, "ISO-2022-JP-1"); @@ -284,7 +279,7 @@ static void buildQuirksSets() addEncodingName(japaneseEncodings, "cp932"); addEncodingName(japaneseEncodings, "x-mac-japanese"); - nonBackslashEncodings = new HashSet<const char*>(); + nonBackslashEncodings = new HashSet<const char*>; // The text encodings below treat backslash as a currency symbol for IE compatibility. // See http://blogs.msdn.com/michkap/archive/2005/09/17/469941.aspx for more information. addEncodingName(nonBackslashEncodings, "x-mac-japanese"); diff --git a/Source/WebCore/platform/text/mac/TextCodecMac.cpp b/Source/WebCore/platform/text/mac/TextCodecMac.cpp index b743f3d..64d0485 100644 --- a/Source/WebCore/platform/text/mac/TextCodecMac.cpp +++ b/Source/WebCore/platform/text/mac/TextCodecMac.cpp @@ -27,15 +27,15 @@ #include "config.h" #include "TextCodecMac.h" -#include "CharacterNames.h" #include "CharsetData.h" #include "PlatformString.h" #include "ThreadGlobalData.h" #include <wtf/Assertions.h> -#include <wtf/text/CString.h> #include <wtf/PassOwnPtr.h> #include <wtf/RetainPtr.h> #include <wtf/Threading.h> +#include <wtf/text/CString.h> +#include <wtf/unicode/CharacterNames.h> using namespace std; diff --git a/Source/WebCore/platform/text/transcoder/FontTranscoder.cpp b/Source/WebCore/platform/text/transcoder/FontTranscoder.cpp index 68601f9..4e07f50 100644 --- a/Source/WebCore/platform/text/transcoder/FontTranscoder.cpp +++ b/Source/WebCore/platform/text/transcoder/FontTranscoder.cpp @@ -31,9 +31,9 @@ #include "config.h" #include "FontTranscoder.h" -#include "CharacterNames.h" #include "FontDescription.h" #include "TextEncoding.h" +#include <wtf/unicode/CharacterNames.h> namespace WebCore { diff --git a/Source/WebCore/platform/win/BString.cpp b/Source/WebCore/platform/win/BString.cpp index 4d6d11e..6622f96 100644 --- a/Source/WebCore/platform/win/BString.cpp +++ b/Source/WebCore/platform/win/BString.cpp @@ -28,9 +28,8 @@ #include "KURL.h" #include "PlatformString.h" -#include <wtf/text/AtomicString.h> -#include <tchar.h> #include <windows.h> +#include <wtf/text/AtomicString.h> #if PLATFORM(CF) #include <CoreFoundation/CoreFoundation.h> @@ -102,7 +101,7 @@ BString::BString(CFStringRef cfstr) const UniChar* uniChars = CFStringGetCharactersPtr(cfstr); if (uniChars) { - m_bstr = SysAllocStringLen((LPCTSTR)uniChars, CFStringGetLength(cfstr)); + m_bstr = SysAllocStringLen((LPCWSTR)uniChars, CFStringGetLength(cfstr)); return; } @@ -158,7 +157,7 @@ bool operator ==(const BString& a, const BString& b) return true; if (!(BSTR)a || !(BSTR)b) return false; - return !_tcscmp((BSTR)a, (BSTR)b); + return !wcscmp((BSTR)a, (BSTR)b); } bool operator !=(const BString& a, const BString& b) @@ -174,7 +173,7 @@ bool operator ==(const BString& a, BSTR b) return true; if (!(BSTR)a || !b) return false; - return !_tcscmp((BSTR)a, b); + return !wcscmp((BSTR)a, b); } bool operator !=(const BString& a, BSTR b) @@ -190,7 +189,7 @@ bool operator ==(BSTR a, const BString& b) return true; if (!a || !(BSTR)b) return false; - return !_tcscmp(a, (BSTR)b); + return !wcscmp(a, (BSTR)b); } bool operator !=(BSTR a, const BString& b) diff --git a/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp b/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp index eb1e659..77b95ef 100644 --- a/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp +++ b/Source/WebCore/platform/win/ClipboardUtilitiesWin.cpp @@ -31,6 +31,7 @@ #include "PlatformString.h" #include "TextEncoding.h" #include "markup.h" +#include <shlobj.h> #include <shlwapi.h> #include <wininet.h> // for INTERNET_MAX_URL_LENGTH #include <wtf/StringExtras.h> @@ -70,6 +71,15 @@ static bool urlFromPath(CFStringRef path, String& url) } #endif +static bool getDataMapItem(const DragDataMap* dataObject, FORMATETC* format, String& item) +{ + DragDataMap::const_iterator found = dataObject->find(format->cfFormat); + if (found == dataObject->end()) + return false; + item = found->second[0]; + return true; +} + static bool getWebLocData(IDataObject* dataObject, String& url, String* title) { bool succeeded = false; @@ -111,6 +121,34 @@ exit: return succeeded; } +static bool getWebLocData(const DragDataMap* dataObject, String& url, String* title) +{ +#if PLATFORM(CF) + WCHAR filename[MAX_PATH]; + WCHAR urlBuffer[INTERNET_MAX_URL_LENGTH]; + + if (!dataObject->contains(cfHDropFormat()->cfFormat)) + return false; + + wcscpy(filename, dataObject->get(cfHDropFormat()->cfFormat)[0].characters()); + if (_wcsicmp(PathFindExtensionW(filename), L".url")) + return false; + + if (!GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, urlBuffer, WTF_ARRAY_LENGTH(urlBuffer), filename)) + return false; + + if (title) { + PathRemoveExtension(filename); + *title = filename; + } + + url = urlBuffer; + return true; +#else + return false; +#endif +} + static String extractURL(const String &inURL, String* title) { String url = inURL; @@ -386,6 +424,33 @@ String getURL(IDataObject* dataObject, DragData::FilenameConversionPolicy filena return url; } +String getURL(const DragDataMap* data, DragData::FilenameConversionPolicy filenamePolicy, String* title) +{ + String url; + + if (getWebLocData(data, url, title)) + return url; + if (getDataMapItem(data, urlWFormat(), url)) + return extractURL(url, title); + if (getDataMapItem(data, urlFormat(), url)) + return extractURL(url, title); +#if PLATFORM(CF) + if (filenamePolicy != DragData::ConvertFilenames) + return url; + + String stringData; + if (!getDataMapItem(data, filenameWFormat(), stringData)) + getDataMapItem(data, filenameFormat(), stringData); + + if (stringData.isEmpty() || (!PathFileExists(stringData.charactersWithNullTermination()) && !PathIsUNC(stringData.charactersWithNullTermination()))) + return url; + RetainPtr<CFStringRef> pathAsCFString(AdoptCF, CFStringCreateWithCharacters(kCFAllocatorDefault, (const UniChar *)stringData.charactersWithNullTermination(), stringData.length())); + if (urlFromPath(pathAsCFString.get(), url) && title) + *title = url; +#endif + return url; +} + String getPlainText(IDataObject* dataObject, bool& success) { STGMEDIUM store; @@ -415,6 +480,17 @@ String getPlainText(IDataObject* dataObject, bool& success) return text; } +String getPlainText(const DragDataMap* data) +{ + String text; + + if (getDataMapItem(data, plainTextWFormat(), text)) + return text; + if (getDataMapItem(data, plainTextFormat(), text)) + return text; + return getURL(data, DragData::DoNotConvertFilenames); +} + String getTextHTML(IDataObject* data, bool& success) { STGMEDIUM store; @@ -430,6 +506,13 @@ String getTextHTML(IDataObject* data, bool& success) return html; } +String getTextHTML(const DragDataMap* data) +{ + String text; + getDataMapItem(data, texthtmlFormat(), text); + return text; +} + String getCFHTML(IDataObject* data, bool& success) { String cfhtml = getFullCFHTML(data, success); @@ -438,18 +521,37 @@ String getCFHTML(IDataObject* data, bool& success) return String(); } +String getCFHTML(const DragDataMap* dataMap) +{ + String cfhtml; + getDataMapItem(dataMap, htmlFormat(), cfhtml); + return extractMarkupFromCFHTML(cfhtml); +} + PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*) { // FIXME: We should be able to create fragments from files return 0; } +PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*) +{ + // FIXME: We should be able to create fragments from files + return 0; +} + bool containsFilenames(const IDataObject*) { // FIXME: We'll want to update this once we can produce fragments from files return false; } +bool containsFilenames(const DragDataMap*) +{ + // FIXME: We'll want to update this once we can produce fragments from files + return false; +} + // Convert a String containing CF_HTML formatted text to a DocumentFragment PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document* doc, const String& cfhtml) { @@ -477,8 +579,8 @@ PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data) bool success = false; String cfhtml = getFullCFHTML(data, success); if (success) { - if (PassRefPtr<DocumentFragment> fragment = fragmentFromCFHTML(doc, cfhtml)) - return fragment; + if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(doc, cfhtml)) + return fragment.release(); } String html = getTextHTML(data, success); @@ -489,9 +591,180 @@ PassRefPtr<DocumentFragment> fragmentFromHTML(Document* doc, IDataObject* data) return 0; } +PassRefPtr<DocumentFragment> fragmentFromHTML(Document* document, const DragDataMap* data) +{ + if (!document || !data || data->isEmpty()) + return 0; + + String stringData; + if (getDataMapItem(data, htmlFormat(), stringData)) { + if (RefPtr<DocumentFragment> fragment = fragmentFromCFHTML(document, stringData)) + return fragment.release(); + } + + String srcURL; + if (getDataMapItem(data, texthtmlFormat(), stringData)) + return createFragmentFromMarkup(document, stringData, srcURL, FragmentScriptingNotAllowed); + + return 0; +} + bool containsHTML(IDataObject* data) { return SUCCEEDED(data->QueryGetData(texthtmlFormat())) || SUCCEEDED(data->QueryGetData(htmlFormat())); } +bool containsHTML(const DragDataMap* data) +{ + return data->contains(texthtmlFormat()->cfFormat) || data->contains(htmlFormat()->cfFormat); +} + +typedef void (*GetStringFunction)(IDataObject*, FORMATETC*, Vector<String>&); +typedef void (*SetStringFunction)(IDataObject*, FORMATETC*, const Vector<String>&); + +struct ClipboardDataItem { + GetStringFunction getString; + SetStringFunction setString; + FORMATETC* format; + + ClipboardDataItem(FORMATETC* format, GetStringFunction getString, SetStringFunction setString): format(format), getString(getString), setString(setString) { } +}; + +typedef HashMap<UINT, ClipboardDataItem*> ClipboardFormatMap; + +// Getter functions. + +template<typename T> void getStringData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings) +{ + STGMEDIUM store; + if (FAILED(data->GetData(format, &store))) + return; + dataStrings.append(String(static_cast<T*>(GlobalLock(store.hGlobal)), ::GlobalSize(store.hGlobal) / sizeof(T))); + GlobalUnlock(store.hGlobal); + ReleaseStgMedium(&store); +} + +void getUtf8Data(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings) +{ + STGMEDIUM store; + if (FAILED(data->GetData(format, &store))) + return; + dataStrings.append(String(UTF8Encoding().decode(static_cast<char*>(GlobalLock(store.hGlobal)), GlobalSize(store.hGlobal)))); + GlobalUnlock(store.hGlobal); + ReleaseStgMedium(&store); +} + +#if PLATFORM(CF) +void getCFData(IDataObject* data, FORMATETC* format, Vector<String>& dataStrings) +{ + STGMEDIUM store; + if (FAILED(data->GetData(format, &store))) + return; + + HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(store.hGlobal)); + if (!hdrop) + return; + + WCHAR filename[MAX_PATH]; + UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); + for (UINT i = 0; i < fileCount; i++) { + if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename))) + continue; + dataStrings.append(static_cast<UChar*>(filename)); + } + + GlobalUnlock(store.hGlobal); + ReleaseStgMedium(&store); +} +#endif + +// Setter functions. + +void setUCharData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings) +{ + STGMEDIUM medium = {0}; + medium.tymed = TYMED_HGLOBAL; + + medium.hGlobal = createGlobalData(dataStrings.first()); + if (!medium.hGlobal) + return; + data->SetData(format, &medium, FALSE); + ::GlobalFree(medium.hGlobal); +} + +void setUtf8Data(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings) +{ + STGMEDIUM medium = {0}; + medium.tymed = TYMED_HGLOBAL; + + CString charString = dataStrings.first().utf8(); + size_t stringLength = charString.length(); + medium.hGlobal = ::GlobalAlloc(GPTR, stringLength + 1); + if (!medium.hGlobal) + return; + char* buffer = static_cast<char*>(GlobalLock(medium.hGlobal)); + memcpy(buffer, charString.data(), stringLength); + buffer[stringLength] = 0; + GlobalUnlock(medium.hGlobal); + data->SetData(format, &medium, FALSE); + ::GlobalFree(medium.hGlobal); +} + +#if PLATFORM(CF) +void setCFData(IDataObject* data, FORMATETC* format, const Vector<String>& dataStrings) +{ + STGMEDIUM medium = {0}; + SIZE_T dropFilesSize = sizeof(DROPFILES) + (sizeof(WCHAR) * (dataStrings.first().length() + 2)); + medium.hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, dropFilesSize); + if (!medium.hGlobal) + return; + + DROPFILES* dropFiles = reinterpret_cast<DROPFILES *>(GlobalLock(medium.hGlobal)); + dropFiles->pFiles = sizeof(DROPFILES); + dropFiles->fWide = TRUE; + String filename = dataStrings.first(); + wcscpy(reinterpret_cast<LPWSTR>(dropFiles + 1), filename.charactersWithNullTermination()); + GlobalUnlock(medium.hGlobal); + data->SetData(format, &medium, FALSE); + ::GlobalFree(medium.hGlobal); +} +#endif + +static const ClipboardFormatMap& getClipboardMap() +{ + static ClipboardFormatMap formatMap; + if (formatMap.isEmpty()) { + formatMap.add(htmlFormat()->cfFormat, new ClipboardDataItem(htmlFormat(), getUtf8Data, setUtf8Data)); + formatMap.add(texthtmlFormat()->cfFormat, new ClipboardDataItem(texthtmlFormat(), getStringData<UChar>, setUCharData)); + formatMap.add(plainTextFormat()->cfFormat, new ClipboardDataItem(plainTextFormat(), getStringData<char>, setUtf8Data)); + formatMap.add(plainTextWFormat()->cfFormat, new ClipboardDataItem(plainTextWFormat(), getStringData<UChar>, setUCharData)); +#if PLATFORM(CF) + formatMap.add(cfHDropFormat()->cfFormat, new ClipboardDataItem(cfHDropFormat(), getCFData, setCFData)); +#endif + formatMap.add(filenameFormat()->cfFormat, new ClipboardDataItem(filenameFormat(), getStringData<char>, setUtf8Data)); + formatMap.add(filenameWFormat()->cfFormat, new ClipboardDataItem(filenameWFormat(), getStringData<UChar>, setUCharData)); + formatMap.add(urlFormat()->cfFormat, new ClipboardDataItem(urlFormat(), getStringData<char>, setUtf8Data)); + formatMap.add(urlWFormat()->cfFormat, new ClipboardDataItem(urlWFormat(), getStringData<UChar>, setUCharData)); + } + return formatMap; +} + +void getClipboardData(IDataObject* dataObject, FORMATETC* format, Vector<String>& dataStrings) +{ + const ClipboardFormatMap& formatMap = getClipboardMap(); + ClipboardFormatMap::const_iterator found = formatMap.find(format->cfFormat); + if (found == formatMap.end()) + return; + found->second->getString(dataObject, found->second->format, dataStrings); +} + +void setClipboardData(IDataObject* dataObject, UINT format, const Vector<String>& dataStrings) +{ + const ClipboardFormatMap& formatMap = getClipboardMap(); + ClipboardFormatMap::const_iterator found = formatMap.find(format); + if (found == formatMap.end()) + return; + found->second->setString(dataObject, found->second->format, dataStrings); +} + } // namespace WebCore diff --git a/Source/WebCore/platform/win/ClipboardUtilitiesWin.h b/Source/WebCore/platform/win/ClipboardUtilitiesWin.h index 1a29e7e..36508d4 100644 --- a/Source/WebCore/platform/win/ClipboardUtilitiesWin.h +++ b/Source/WebCore/platform/win/ClipboardUtilitiesWin.h @@ -55,16 +55,27 @@ void replaceNewlinesWithWindowsStyleNewlines(String&); void replaceNBSPWithSpace(String&); bool containsFilenames(const IDataObject*); +bool containsFilenames(const DragDataMap*); bool containsHTML(IDataObject*); +bool containsHTML(const DragDataMap*); PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const IDataObject*); +PassRefPtr<DocumentFragment> fragmentFromFilenames(Document*, const DragDataMap*); PassRefPtr<DocumentFragment> fragmentFromHTML(Document*, IDataObject*); +PassRefPtr<DocumentFragment> fragmentFromHTML(Document*, const DragDataMap*); PassRefPtr<DocumentFragment> fragmentFromCFHTML(Document*, const String& cfhtml); String getURL(IDataObject*, DragData::FilenameConversionPolicy, bool& success, String* title = 0); +String getURL(const DragDataMap*, DragData::FilenameConversionPolicy, String* title = 0); String getPlainText(IDataObject*, bool& success); +String getPlainText(const DragDataMap*); String getTextHTML(IDataObject*, bool& success); +String getTextHTML(const DragDataMap*); String getCFHTML(IDataObject*, bool& success); +String getCFHTML(const DragDataMap*); + +void getClipboardData(IDataObject*, FORMATETC* fetc, Vector<String>& dataStrings); +void setClipboardData(IDataObject*, UINT format, const Vector<String>& dataStrings); } // namespace WebCore diff --git a/Source/WebCore/platform/win/ClipboardWin.cpp b/Source/WebCore/platform/win/ClipboardWin.cpp index 58cfe44..c0af712 100644 --- a/Source/WebCore/platform/win/ClipboardWin.cpp +++ b/Source/WebCore/platform/win/ClipboardWin.cpp @@ -115,7 +115,7 @@ static inline void pathRemoveBadFSCharacters(PWSTR psz, size_t length) } #endif -static String filesystemPathFromUrlOrTitle(const String& url, const String& title, TCHAR* extension, bool isLink) +static String filesystemPathFromUrlOrTitle(const String& url, const String& title, const UChar* extension, bool isLink) { #if OS(WINCE) notImplemented(); @@ -163,7 +163,7 @@ static String filesystemPathFromUrlOrTitle(const String& url, const String& titl } String result(static_cast<UChar*>(fsPathBuffer)); - result += String(static_cast<UChar*>(extension)); + result += String(extension); return result; #endif } @@ -195,7 +195,7 @@ static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, Share // windows does not enjoy a leading slash on paths if (localPath[0] == '/') localPath = localPath.substring(1); - LPCTSTR localPathStr = localPath.charactersWithNullTermination(); + LPCWSTR localPathStr = localPath.charactersWithNullTermination(); if (wcslen(localPathStr) + 1 < MAX_PATH) wcscpy_s(filePath, MAX_PATH, localPathStr); else @@ -277,7 +277,7 @@ static HGLOBAL createGlobalImageFileDescriptor(const String& url, const String& return 0; } extension.insert(".", 0); - fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, (TCHAR*)extension.charactersWithNullTermination(), false); + fsPath = filesystemPathFromUrlOrTitle(url, preferredTitle, extension.charactersWithNullTermination(), false); if (fsPath.length() <= 0) { GlobalUnlock(memObj); @@ -340,7 +340,9 @@ exit: PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame) { - return ClipboardWin::create(DragAndDrop, dragData->platformData(), policy, frame); + if (dragData->platformData()) + return ClipboardWin::create(DragAndDrop, dragData->platformData(), policy, frame); + return ClipboardWin::create(DragAndDrop, dragData->dragDataMap(), policy, frame); } ClipboardWin::ClipboardWin(ClipboardType clipboardType, IDataObject* dataObject, ClipboardAccessPolicy policy, Frame* frame) @@ -359,6 +361,15 @@ ClipboardWin::ClipboardWin(ClipboardType clipboardType, WCDataObject* dataObject { } +ClipboardWin::ClipboardWin(ClipboardType clipboardType, const DragDataMap& dataMap, ClipboardAccessPolicy policy, Frame* frame) + : Clipboard(policy, clipboardType) + , m_dataObject(0) + , m_writableDataObject(0) + , m_frame(frame) + , m_dragDataMap(dataMap) +{ +} + ClipboardWin::~ClipboardWin() { } @@ -442,19 +453,19 @@ void ClipboardWin::clearAllData() String ClipboardWin::getData(const String& type, bool& success) const { success = false; - if (policy() != ClipboardReadable || !m_dataObject) + if (policy() != ClipboardReadable || (!m_dataObject && m_dragDataMap.isEmpty())) return ""; ClipboardDataType dataType = clipboardTypeFromMIMEType(type); if (dataType == ClipboardDataTypeText) - return getPlainText(m_dataObject.get(), success); + return m_dataObject ? getPlainText(m_dataObject.get(), success) : getPlainText(&m_dragDataMap); if (dataType == ClipboardDataTypeURL) - return getURL(m_dataObject.get(), DragData::DoNotConvertFilenames, success); + return m_dataObject ? getURL(m_dataObject.get(), DragData::DoNotConvertFilenames, success) : getURL(&m_dragDataMap, DragData::DoNotConvertFilenames); else if (dataType == ClipboardDataTypeTextHTML) { - String data = getTextHTML(m_dataObject.get(), success); + String data = m_dataObject ? getTextHTML(m_dataObject.get(), success) : getTextHTML(&m_dragDataMap); if (success) return data; - return getCFHTML(m_dataObject.get(), success); + return m_dataObject ? getCFHTML(m_dataObject.get(), success) : getCFHTML(&m_dragDataMap); } return ""; @@ -510,22 +521,30 @@ HashSet<String> ClipboardWin::types() const if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) return results; - if (!m_dataObject) + if (!m_dataObject && m_dragDataMap.isEmpty()) return results; - COMPtr<IEnumFORMATETC> itr; + if (m_dataObject) { + COMPtr<IEnumFORMATETC> itr; - if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) - return results; + if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) + return results; - if (!itr) - return results; + if (!itr) + return results; - FORMATETC data; + FORMATETC data; - // IEnumFORMATETC::Next returns S_FALSE if there are no more items. - while (itr->Next(1, &data, 0) == S_OK) - addMimeTypesForFormat(results, data); + // IEnumFORMATETC::Next returns S_FALSE if there are no more items. + while (itr->Next(1, &data, 0) == S_OK) + addMimeTypesForFormat(results, data); + } else { + for (DragDataMap::const_iterator it = m_dragDataMap.begin(); it != m_dragDataMap.end(); ++it) { + FORMATETC data; + data.cfFormat = (*it).first; + addMimeTypesForFormat(results, data); + } + } return results; } @@ -540,27 +559,35 @@ PassRefPtr<FileList> ClipboardWin::files() const if (policy() != ClipboardReadable && policy() != ClipboardTypesReadable) return files.release(); - if (!m_dataObject) + if (!m_dataObject && m_dragDataMap.isEmpty()) return files.release(); - STGMEDIUM medium; - if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium))) - return files.release(); + if (m_dataObject) { + STGMEDIUM medium; + if (FAILED(m_dataObject->GetData(cfHDropFormat(), &medium))) + return files.release(); + + HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal)); + if (!hdrop) + return files.release(); + + WCHAR filename[MAX_PATH]; + UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); + for (UINT i = 0; i < fileCount; i++) { + if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename))) + continue; + files->append(File::create(reinterpret_cast<UChar*>(filename))); + } - HDROP hdrop = reinterpret_cast<HDROP>(GlobalLock(medium.hGlobal)); - if (!hdrop) + GlobalUnlock(medium.hGlobal); + ReleaseStgMedium(&medium); return files.release(); - - WCHAR filename[MAX_PATH]; - UINT fileCount = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); - for (UINT i = 0; i < fileCount; i++) { - if (!DragQueryFileW(hdrop, i, filename, WTF_ARRAY_LENGTH(filename))) - continue; - files->append(File::create(reinterpret_cast<UChar*>(filename))); } - - GlobalUnlock(medium.hGlobal); - ReleaseStgMedium(&medium); + if (!m_dragDataMap.contains(cfHDropFormat()->cfFormat)) + return files.release(); + Vector<String> filesVector = m_dragDataMap.get(cfHDropFormat()->cfFormat); + for (Vector<String>::iterator it = filesVector.begin(); it != filesVector.end(); ++it) + files->append(File::create((*it).characters())); return files.release(); #endif } @@ -778,25 +805,28 @@ void ClipboardWin::writePlainText(const String& text) bool ClipboardWin::hasData() { - if (!m_dataObject) + if (!m_dataObject && m_dragDataMap.isEmpty()) return false; - COMPtr<IEnumFORMATETC> itr; - if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) - return false; + if (m_dataObject) { + COMPtr<IEnumFORMATETC> itr; + if (FAILED(m_dataObject->EnumFormatEtc(DATADIR_GET, &itr))) + return false; - if (!itr) - return false; + if (!itr) + return false; - FORMATETC data; + FORMATETC data; - // IEnumFORMATETC::Next returns S_FALSE if there are no more items. - if (itr->Next(1, &data, 0) == S_OK) { - // There is at least one item in the IDataObject - return true; - } + // IEnumFORMATETC::Next returns S_FALSE if there are no more items. + if (itr->Next(1, &data, 0) == S_OK) { + // There is at least one item in the IDataObject + return true; + } - return false; + return false; + } + return !m_dragDataMap.isEmpty(); } void ClipboardWin::setExternalDataObject(IDataObject *dataObject) diff --git a/Source/WebCore/platform/win/ClipboardWin.h b/Source/WebCore/platform/win/ClipboardWin.h index 779da26..7530eeb 100644 --- a/Source/WebCore/platform/win/ClipboardWin.h +++ b/Source/WebCore/platform/win/ClipboardWin.h @@ -29,6 +29,7 @@ #include "COMPtr.h" #include "CachedResourceClient.h" #include "Clipboard.h" +#include "DragData.h" struct IDataObject; @@ -51,6 +52,10 @@ public: { return adoptRef(new ClipboardWin(clipboardType, dataObject, policy, frame)); } + static PassRefPtr<ClipboardWin> create(ClipboardType clipboardType, const DragDataMap& dataMap, ClipboardAccessPolicy policy, Frame* frame) + { + return adoptRef(new ClipboardWin(clipboardType, dataMap, policy, frame)); + } ~ClipboardWin(); void clearData(const String& type); @@ -80,12 +85,14 @@ public: private: ClipboardWin(ClipboardType, IDataObject*, ClipboardAccessPolicy, Frame*); ClipboardWin(ClipboardType, WCDataObject*, ClipboardAccessPolicy, Frame*); + ClipboardWin(ClipboardType, const DragDataMap&, ClipboardAccessPolicy, Frame*); void resetFromClipboard(); void setDragImage(CachedImage*, Node*, const IntPoint&); COMPtr<IDataObject> m_dataObject; COMPtr<WCDataObject> m_writableDataObject; + DragDataMap m_dragDataMap; Frame* m_frame; }; diff --git a/Source/WebCore/platform/win/ContextMenuWin.cpp b/Source/WebCore/platform/win/ContextMenuWin.cpp index ed1b895..dad0f2e 100644 --- a/Source/WebCore/platform/win/ContextMenuWin.cpp +++ b/Source/WebCore/platform/win/ContextMenuWin.cpp @@ -31,7 +31,6 @@ #include "FrameView.h" #include "Node.h" #include "NotImplemented.h" -#include <tchar.h> #include <windows.h> #include <wtf/Vector.h> #include <wtf/text/CString.h> @@ -73,7 +72,7 @@ void ContextMenu::getContextMenuItems(HMENU menu, Vector<ContextMenuItem>& items } int menuStringLength = info.cch + 1; - OwnArrayPtr<WCHAR> menuString(new WCHAR[menuStringLength]); + OwnArrayPtr<WCHAR> menuString = adoptArrayPtr(new WCHAR[menuStringLength]); info.dwTypeData = menuString.get(); info.cch = menuStringLength; diff --git a/Source/WebCore/platform/win/CursorWin.cpp b/Source/WebCore/platform/win/CursorWin.cpp index 2dd1452..0036388 100644 --- a/Source/WebCore/platform/win/CursorWin.cpp +++ b/Source/WebCore/platform/win/CursorWin.cpp @@ -123,9 +123,9 @@ static PassRefPtr<SharedCursor> createSharedCursor(Image* img, const IntPoint& h return impl.release(); } -static PassRefPtr<SharedCursor> loadSharedCursor(HINSTANCE hInstance, LPCTSTR lpCursorName) +static PassRefPtr<SharedCursor> loadSharedCursor(HINSTANCE hInstance, LPCWSTR lpCursorName) { - return SharedCursor::create(::LoadCursor(hInstance, lpCursorName)); + return SharedCursor::create(::LoadCursorW(hInstance, lpCursorName)); } static PassRefPtr<SharedCursor> loadCursorByName(char* name, int x, int y) diff --git a/Source/WebCore/platform/win/DragDataWin.cpp b/Source/WebCore/platform/win/DragDataWin.cpp index 906119d..c5b99ea 100644 --- a/Source/WebCore/platform/win/DragDataWin.cpp +++ b/Source/WebCore/platform/win/DragDataWin.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "DragData.h" +#include "COMPtr.h" #include "ClipboardUtilitiesWin.h" #include "Frame.h" #include "DocumentFragment.h" @@ -35,65 +36,109 @@ #include <objidl.h> #include <shlwapi.h> #include <wininet.h> +#include <wtf/Forward.h> +#include <wtf/Hashmap.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> namespace WebCore { +DragData::DragData(const DragDataMap& data, const IntPoint& clientPosition, const IntPoint& globalPosition, + DragOperation sourceOperationMask, DragApplicationFlags flags) + : m_clientPosition(clientPosition) + , m_globalPosition(globalPosition) + , m_platformDragData(0) + , m_draggingSourceOperationMask(sourceOperationMask) + , m_applicationFlags(flags) + , m_dragDataMap(data) +{ +} + bool DragData::containsURL(Frame*, FilenameConversionPolicy filenamePolicy) const { - return SUCCEEDED(m_platformDragData->QueryGetData(urlWFormat())) - || SUCCEEDED(m_platformDragData->QueryGetData(urlFormat())) - || (filenamePolicy == ConvertFilenames - && (SUCCEEDED(m_platformDragData->QueryGetData(filenameWFormat())) - || SUCCEEDED(m_platformDragData->QueryGetData(filenameFormat())))); + if (m_platformDragData) + return SUCCEEDED(m_platformDragData->QueryGetData(urlWFormat())) + || SUCCEEDED(m_platformDragData->QueryGetData(urlFormat())) + || (filenamePolicy == ConvertFilenames + && (SUCCEEDED(m_platformDragData->QueryGetData(filenameWFormat())) + || SUCCEEDED(m_platformDragData->QueryGetData(filenameFormat())))); + return m_dragDataMap.contains(urlWFormat()->cfFormat) || m_dragDataMap.contains(urlFormat()->cfFormat) + || (filenamePolicy == ConvertFilenames && (m_dragDataMap.contains(filenameWFormat()->cfFormat) || m_dragDataMap.contains(filenameFormat()->cfFormat))); +} + +const DragDataMap& DragData::dragDataMap() +{ + if (!m_dragDataMap.isEmpty() || !m_platformDragData) + return m_dragDataMap; + // Enumerate clipboard content and load it in the map. + COMPtr<IEnumFORMATETC> itr; + + if (FAILED(m_platformDragData->EnumFormatEtc(DATADIR_GET, &itr)) || !itr) + return m_dragDataMap; + + FORMATETC dataFormat; + while (itr->Next(1, &dataFormat, 0) == S_OK) { + Vector<String> dataStrings; + getClipboardData(m_platformDragData, &dataFormat, dataStrings); + if (!dataStrings.isEmpty()) + m_dragDataMap.set(dataFormat.cfFormat, dataStrings); + } + return m_dragDataMap; } String DragData::asURL(Frame*, FilenameConversionPolicy filenamePolicy, String* title) const { bool success; - return getURL(m_platformDragData, filenamePolicy, success, title); + return (m_platformDragData) ? getURL(m_platformDragData, filenamePolicy, success, title) : getURL(&m_dragDataMap, filenamePolicy, title); } bool DragData::containsFiles() const { - return SUCCEEDED(m_platformDragData->QueryGetData(cfHDropFormat())); + return (m_platformDragData) ? SUCCEEDED(m_platformDragData->QueryGetData(cfHDropFormat())) : m_dragDataMap.contains(cfHDropFormat()->cfFormat); } void DragData::asFilenames(Vector<String>& result) const { - WCHAR filename[MAX_PATH]; - - STGMEDIUM medium; - if (FAILED(m_platformDragData->GetData(cfHDropFormat(), &medium))) - return; - - HDROP hdrop = (HDROP)GlobalLock(medium.hGlobal); - - if (!hdrop) - return; + if (m_platformDragData) { + WCHAR filename[MAX_PATH]; + + STGMEDIUM medium; + if (FAILED(m_platformDragData->GetData(cfHDropFormat(), &medium))) + return; + + HDROP hdrop = (HDROP)GlobalLock(medium.hGlobal); + + if (!hdrop) + return; - const unsigned numFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); - for (unsigned i = 0; i < numFiles; i++) { - if (!DragQueryFileW(hdrop, 0, filename, WTF_ARRAY_LENGTH(filename))) - continue; - result.append((UChar*)filename); - } + const unsigned numFiles = DragQueryFileW(hdrop, 0xFFFFFFFF, 0, 0); + for (unsigned i = 0; i < numFiles; i++) { + if (!DragQueryFileW(hdrop, 0, filename, WTF_ARRAY_LENGTH(filename))) + continue; + result.append((UChar*)filename); + } - // Free up memory from drag - DragFinish(hdrop); + // Free up memory from drag + DragFinish(hdrop); - GlobalUnlock(medium.hGlobal); + GlobalUnlock(medium.hGlobal); + return; + } + result = m_dragDataMap.get(cfHDropFormat()->cfFormat); } bool DragData::containsPlainText() const { - return SUCCEEDED(m_platformDragData->QueryGetData(plainTextWFormat())) - || SUCCEEDED(m_platformDragData->QueryGetData(plainTextFormat())); + if (m_platformDragData) + return SUCCEEDED(m_platformDragData->QueryGetData(plainTextWFormat())) + || SUCCEEDED(m_platformDragData->QueryGetData(plainTextFormat())); + return m_dragDataMap.contains(plainTextWFormat()->cfFormat) || m_dragDataMap.contains(plainTextFormat()->cfFormat); } String DragData::asPlainText(Frame*) const { bool success; - return getPlainText(m_platformDragData, success); + return (m_platformDragData) ? getPlainText(m_platformDragData, success) : getPlainText(&m_dragDataMap); } bool DragData::containsColor() const @@ -103,14 +148,16 @@ bool DragData::containsColor() const bool DragData::canSmartReplace() const { - return SUCCEEDED(m_platformDragData->QueryGetData(smartPasteFormat())); + if (m_platformDragData) + return SUCCEEDED(m_platformDragData->QueryGetData(smartPasteFormat())); + return m_dragDataMap.contains(smartPasteFormat()->cfFormat); } bool DragData::containsCompatibleContent() const { return containsPlainText() || containsURL(0) - || containsHTML(m_platformDragData) - || containsFilenames(m_platformDragData) + || ((m_platformDragData) ? (containsHTML(m_platformDragData) || containsFilenames(m_platformDragData)) + : (containsHTML(&m_dragDataMap) || containsFilenames(&m_dragDataMap))) || containsColor(); } @@ -125,16 +172,29 @@ PassRefPtr<DocumentFragment> DragData::asFragment(Frame* frame, PassRefPtr<Range * * TIFF * * PICT */ - - if (containsFilenames(m_platformDragData)) - if (PassRefPtr<DocumentFragment> fragment = fragmentFromFilenames(frame->document(), m_platformDragData)) - return fragment; - - if (containsHTML(m_platformDragData)) - if (PassRefPtr<DocumentFragment> fragment = fragmentFromHTML(frame->document(), m_platformDragData)) - return fragment; - - return 0; + + if (m_platformDragData) { + if (containsFilenames(m_platformDragData)) { + if (PassRefPtr<DocumentFragment> fragment = fragmentFromFilenames(frame->document(), m_platformDragData)) + return fragment; + } + + if (containsHTML(m_platformDragData)) { + if (PassRefPtr<DocumentFragment> fragment = fragmentFromHTML(frame->document(), m_platformDragData)) + return fragment; + } + } else { + if (containsFilenames(&m_dragDataMap)) { + if (PassRefPtr<DocumentFragment> fragment = fragmentFromFilenames(frame->document(), &m_dragDataMap)) + return fragment; + } + + if (containsHTML(&m_dragDataMap)) { + if (PassRefPtr<DocumentFragment> fragment = fragmentFromHTML(frame->document(), &m_dragDataMap)) + return fragment; + } + } + return 0; } Color DragData::asColor() const @@ -143,4 +203,3 @@ Color DragData::asColor() const } } - diff --git a/Source/WebCore/platform/win/DragImageWin.cpp b/Source/WebCore/platform/win/DragImageWin.cpp index 135e9d0..4e5d168 100644 --- a/Source/WebCore/platform/win/DragImageWin.cpp +++ b/Source/WebCore/platform/win/DragImageWin.cpp @@ -27,14 +27,25 @@ #include "DragImage.h" #include "CachedImage.h" +#include "Font.h" +#include "FontDescription.h" +#include "FontSelector.h" +#include "Frame.h" #include "GraphicsContext.h" #include "Image.h" #include "RetainPtr.h" +#include "Settings.h" +#include "StringTruncator.h" +#include "TextRun.h" +#include "WebCoreTextRenderer.h" #include <windows.h> namespace WebCore { +HBITMAP allocImage(HDC, IntSize, PlatformGraphicsContext** targetRef); +void deallocContext(PlatformGraphicsContext* target); + IntSize dragImageSize(DragImageRef image) { if (!image) @@ -79,5 +90,141 @@ DragImageRef createDragImageIconForCachedImage(CachedImage* image) return iconInfo.hbmColor; } + +const float DragLabelBorderX = 4; +// Keep border_y in synch with DragController::LinkDragBorderInset. +const float DragLabelBorderY = 2; +const float DragLabelRadius = 5; +const float LabelBorderYOffset = 2; + +const float MinDragLabelWidthBeforeClip = 120; +const float MaxDragLabelWidth = 200; +const float MaxDragLabelStringWidth = (MaxDragLabelWidth - 2 * DragLabelBorderX); + +const float DragLinkLabelFontsize = 11; +const float DragLinkUrlFontSize = 10; + +static Font dragLabelFont(int size, bool bold, FontRenderingMode renderingMode) +{ + NONCLIENTMETRICS metrics; + metrics.cbSize = sizeof(metrics); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, metrics.cbSize, &metrics, 0); + + FontDescription description; + description.setWeight(bold ? FontWeightBold : FontWeightNormal); + + FontFamily family; + family.setFamily(metrics.lfSmCaptionFont.lfFaceName); + description.setFamily(family); + description.setSpecifiedSize((float)size); + description.setComputedSize((float)size); + description.setRenderingMode(renderingMode); + Font result = Font(description, 0, 0); + result.update(0); + return result; +} + +DragImageRef createDragImageForLink(KURL& url, const String& inLabel, Frame* frame) +{ + // This is more or less an exact match for the Mac OS X code. + + const Font* labelFont; + const Font* urlFont; + + if (frame->settings() && frame->settings()->fontRenderingMode() == AlternateRenderingMode) { + static const Font alternateRenderingModeLabelFont = dragLabelFont(DragLinkLabelFontsize, true, AlternateRenderingMode); + static const Font alternateRenderingModeURLFont = dragLabelFont(DragLinkUrlFontSize, false, AlternateRenderingMode); + labelFont = &alternateRenderingModeLabelFont; + urlFont = &alternateRenderingModeURLFont; + } else { + static const Font normalRenderingModeLabelFont = dragLabelFont(DragLinkLabelFontsize, true, NormalRenderingMode); + static const Font normalRenderingModeURLFont = dragLabelFont(DragLinkUrlFontSize, false, NormalRenderingMode); + labelFont = &normalRenderingModeLabelFont; + urlFont = &normalRenderingModeURLFont; + } + + bool drawURLString = true; + bool clipURLString = false; + bool clipLabelString = false; + + String urlString = url.string(); + String label = inLabel; + if (label.isEmpty()) { + drawURLString = false; + label = urlString; + } + + // First step in drawing the link drag image width. + TextRun labelRun(label.impl()); + TextRun urlRun(urlString.impl()); + IntSize labelSize(labelFont->width(labelRun), labelFont->fontMetrics().ascent() + labelFont->fontMetrics().descent()); + + if (labelSize.width() > MaxDragLabelStringWidth) { + labelSize.setWidth(MaxDragLabelStringWidth); + clipLabelString = true; + } + + IntSize urlStringSize; + IntSize imageSize(labelSize.width() + DragLabelBorderX * 2, labelSize.height() + DragLabelBorderY * 2); + + if (drawURLString) { + urlStringSize.setWidth(urlFont->width(urlRun)); + urlStringSize.setHeight(urlFont->fontMetrics().ascent() + urlFont->fontMetrics().descent()); + imageSize.setHeight(imageSize.height() + urlStringSize.height()); + if (urlStringSize.width() > MaxDragLabelStringWidth) { + imageSize.setWidth(MaxDragLabelWidth); + clipURLString = true; + } else + imageSize.setWidth(std::max(labelSize.width(), urlStringSize.width()) + DragLabelBorderX * 2); + } + + // We now know how big the image needs to be, so we create and + // fill the background + HBITMAP image = 0; + HDC dc = GetDC(0); + HDC workingDC = CreateCompatibleDC(dc); + if (!workingDC) { + ReleaseDC(0, dc); + return 0; + } + + PlatformGraphicsContext* contextRef; + image = allocImage(workingDC, imageSize, &contextRef); + if (!image) { + DeleteDC(workingDC); + ReleaseDC(0, dc); + return 0; + } + + SelectObject(workingDC, image); + GraphicsContext context(contextRef); + // On Mac alpha is {0.7, 0.7, 0.7, 0.8}, however we can't control alpha + // for drag images on win, so we use 1 + static const Color backgroundColor(140, 140, 140); + static const IntSize radii(DragLabelRadius, DragLabelRadius); + IntRect rect(0, 0, imageSize.width(), imageSize.height()); + context.fillRoundedRect(rect, radii, radii, radii, radii, backgroundColor, ColorSpaceDeviceRGB); + + // Draw the text + static const Color topColor(0, 0, 0, 255); // original alpha = 0.75 + static const Color bottomColor(255, 255, 255, 127); // original alpha = 0.5 + if (drawURLString) { + if (clipURLString) + urlString = StringTruncator::rightTruncate(urlString, imageSize.width() - (DragLabelBorderX * 2.0f), *urlFont, false); + IntPoint textPos(DragLabelBorderX, imageSize.height() - (LabelBorderYOffset + urlFont->fontMetrics().descent())); + WebCoreDrawDoubledTextAtPoint(context, urlString, textPos, *urlFont, topColor, bottomColor); + } + if (clipLabelString) + label = StringTruncator::rightTruncate(label, imageSize.width() - (DragLabelBorderX * 2.0f), *labelFont, false); + + IntPoint textPos(DragLabelBorderX, DragLabelBorderY + labelFont->pixelSize()); + WebCoreDrawDoubledTextAtPoint(context, label, textPos, *labelFont, topColor, bottomColor); + + deallocContext(contextRef); + DeleteDC(workingDC); + ReleaseDC(0, dc); + return image; +} + } diff --git a/Source/WebCore/platform/win/FileChooserWin.cpp b/Source/WebCore/platform/win/FileChooserWin.cpp index 7d07b5d..195b8eb 100644 --- a/Source/WebCore/platform/win/FileChooserWin.cpp +++ b/Source/WebCore/platform/win/FileChooserWin.cpp @@ -29,7 +29,6 @@ #include "LocalizedStrings.h" #include "StringTruncator.h" #include <shlwapi.h> -#include <tchar.h> #include <windows.h> namespace WebCore { @@ -44,7 +43,7 @@ String FileChooser::basenameForWidth(const Font& font, int width) const string = fileButtonNoFileSelectedLabel(); else if (m_filenames.size() == 1) { String tmpFilename = m_filenames[0]; - LPTSTR basename = PathFindFileName(tmpFilename.charactersWithNullTermination()); + LPWSTR basename = PathFindFileNameW(tmpFilename.charactersWithNullTermination()); string = String(basename); } else return StringTruncator::rightTruncate(String::number(m_filenames.size()) + " files", width, font, false); diff --git a/Source/WebCore/platform/win/LoggingWin.cpp b/Source/WebCore/platform/win/LoggingWin.cpp index fe237e5..1d051ae 100644 --- a/Source/WebCore/platform/win/LoggingWin.cpp +++ b/Source/WebCore/platform/win/LoggingWin.cpp @@ -37,7 +37,7 @@ static inline void initializeWithUserDefault(WTFLogChannel& channel) if (!length) return; - OwnArrayPtr<char> buffer(new char[length]); + OwnArrayPtr<char> buffer = adoptArrayPtr(new char[length]); if (!GetEnvironmentVariableA(channel.defaultName, buffer.get(), length)) return; diff --git a/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp b/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp index 980742a..56ddab1 100644 --- a/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp +++ b/Source/WebCore/platform/win/MIMETypeRegistryWin.cpp @@ -27,7 +27,9 @@ #include "MIMETypeRegistry.h" #include <shlwapi.h> +#include <wtf/Assertions.h> #include <wtf/HashMap.h> +#include <wtf/MainThread.h> namespace WebCore { @@ -64,6 +66,8 @@ String MIMETypeRegistry::getPreferredExtensionForMIMEType(const String& type) String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + if (ext.isEmpty()) return String(); diff --git a/Source/WebCore/platform/win/PopupMenuWin.cpp b/Source/WebCore/platform/win/PopupMenuWin.cpp index 15871e6..3f3afa2 100644 --- a/Source/WebCore/platform/win/PopupMenuWin.cpp +++ b/Source/WebCore/platform/win/PopupMenuWin.cpp @@ -42,7 +42,6 @@ #include "SimpleFontData.h" #include "TextRun.h" #include "WebCoreInstanceHandle.h" -#include <tchar.h> #include <windows.h> #include <windowsx.h> #if OS(WINCE) @@ -64,7 +63,7 @@ static const int maxPopupHeight = 320; const int optionSpacingMiddle = 1; const int popupWindowBorderWidth = 1; -static LPCTSTR kPopupWindowClassName = _T("PopupWindowClass"); +static LPCWSTR kPopupWindowClassName = L"PopupWindowClass"; // This is used from within our custom message pump when we want to send a // message to the web view and not have our message stolen and sent to @@ -121,7 +120,7 @@ void PopupMenuWin::disconnectClient() m_popupClient = 0; } -LPCTSTR PopupMenuWin::popupClassName() +LPCWSTR PopupMenuWin::popupClassName() { return kPopupWindowClassName; } @@ -145,7 +144,7 @@ void PopupMenuWin::show(const IntRect& r, FrameView* view, int index) DWORD exStyle = WS_EX_LTRREADING; - m_popup = ::CreateWindowEx(exStyle, kPopupWindowClassName, _T("PopupMenu"), + m_popup = ::CreateWindowExW(exStyle, kPopupWindowClassName, L"PopupMenu", WS_POPUP | WS_BORDER, m_windowRect.x(), m_windowRect.y(), m_windowRect.width(), m_windowRect.height(), hostWindow, 0, WebCore::instanceHandle(), this); @@ -306,7 +305,7 @@ void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v) // First, determine the popup's height int itemCount = client()->listSize(); - m_itemHeight = client()->menuStyle().font().height() + optionSpacingMiddle; + m_itemHeight = client()->menuStyle().font().fontMetrics().height() + optionSpacingMiddle; int naturalHeight = m_itemHeight * itemCount; int popupHeight = min(maxPopupHeight, naturalHeight); // The popup should show an integral number of items (i.e. no partial items should be visible) @@ -347,13 +346,13 @@ void PopupMenuWin::calculatePositionAndSize(const IntRect& r, FrameView* v) // Always left-align items in the popup. This matches popup menus on the mac. int popupX = rScreenCoords.x() + client()->clientInsetLeft(); - IntRect popupRect(popupX, rScreenCoords.bottom(), popupWidth, popupHeight); + IntRect popupRect(popupX, rScreenCoords.maxY(), popupWidth, popupHeight); // The popup needs to stay within the bounds of the screen and not overlap any toolbars FloatRect screen = screenAvailableRect(v); // Check that we don't go off the screen vertically - if (popupRect.bottom() > screen.height()) { + if (popupRect.maxY() > screen.height()) { // The popup will go off the screen, so try placing it above the client if (rScreenCoords.y() - popupRect.height() < 0) { // The popup won't fit above, either, so place it whereever's bigger and resize it to fit @@ -602,7 +601,7 @@ void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) IntRect listRect = damageRect; listRect.move(IntSize(0, m_scrollOffset * m_itemHeight)); - for (int y = listRect.y(); y < listRect.bottom(); y += m_itemHeight) { + for (int y = listRect.y(); y < listRect.maxY(); y += m_itemHeight) { int index = y / m_itemHeight; Color optionBackgroundColor, optionTextColor; @@ -632,7 +631,7 @@ void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) unsigned length = itemText.length(); const UChar* string = itemText.characters(); - TextRun textRun(string, length, false, 0, 0, itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft); + TextRun textRun(string, length, false, 0, 0, TextRun::AllowTrailingExpansion, itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft); context.setFillColor(optionTextColor, ColorSpaceDeviceRGB); @@ -649,7 +648,7 @@ void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) int textX = max(0, client()->clientPaddingLeft() - client()->clientInsetLeft()); if (RenderTheme::defaultTheme()->popupOptionSupportsTextIndent() && itemStyle.textDirection() == LTR) textX += itemStyle.textIndent().calcMinValue(itemRect.width()); - int textY = itemRect.y() + itemFont.ascent() + (itemRect.height() - itemFont.height()) / 2; + int textY = itemRect.y() + itemFont.fontMetrics().ascent() + (itemRect.height() - itemFont.fontMetrics().height()) / 2; context.drawBidiText(itemFont, textRun, IntPoint(textX, textY)); } } diff --git a/Source/WebCore/platform/win/PopupMenuWin.h b/Source/WebCore/platform/win/PopupMenuWin.h index 0d7630c..05edb07 100644 --- a/Source/WebCore/platform/win/PopupMenuWin.h +++ b/Source/WebCore/platform/win/PopupMenuWin.h @@ -49,7 +49,7 @@ public: virtual void updateFromElement(); virtual void disconnectClient(); - static LPCTSTR popupClassName(); + static LPCWSTR popupClassName(); private: PopupMenuClient* client() const { return m_popupClient; } diff --git a/Source/WebCore/platform/win/SystemTimeWin.cpp b/Source/WebCore/platform/win/SystemTimeWin.cpp index 451262d..547decc 100644 --- a/Source/WebCore/platform/win/SystemTimeWin.cpp +++ b/Source/WebCore/platform/win/SystemTimeWin.cpp @@ -26,13 +26,9 @@ #include "config.h" #include "SystemTime.h" +#include <limits> #include <windows.h> -#if COMPILER(MINGW) || (PLATFORM(QT) && COMPILER(MSVC)) -#include <float.h> -#define FLOAT_MAX FLT_MAX -#endif - namespace WebCore { float userIdleTime() @@ -43,7 +39,8 @@ float userIdleTime() if (::GetLastInputInfo(&lastInputInfo)) return (GetTickCount() - lastInputInfo.dwTime) * 0.001; // ::GetTickCount returns ms of uptime valid for up to 49.7 days. #endif - return FLT_MAX; // return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed. + // Return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed. + return std::numeric_limits<float>::max(); } -} +} // namespace WebCore diff --git a/Source/WebCore/platform/win/WCDataObject.cpp b/Source/WebCore/platform/win/WCDataObject.cpp index 6b4c859..0c03ce0 100644 --- a/Source/WebCore/platform/win/WCDataObject.cpp +++ b/Source/WebCore/platform/win/WCDataObject.cpp @@ -26,6 +26,8 @@ #include "config.h" #include "WCDataObject.h" +#include "ClipboardUtilitiesWin.h" +#include "DragData.h" #include "PlatformString.h" namespace WebCore { @@ -160,6 +162,17 @@ HRESULT WCDataObject::createInstance(WCDataObject** result) return S_OK; } +HRESULT WCDataObject::createInstance(WCDataObject** result, const DragDataMap& dataMap) +{ + if (!result) + return E_POINTER; + *result = new WCDataObject; + + for (DragDataMap::const_iterator it = dataMap.begin(); it != dataMap.end(); ++it) + setClipboardData(*result, it->first, it->second); + return S_OK; +} + WCDataObject::WCDataObject() : m_ref(1) { @@ -380,5 +393,4 @@ void WCDataObject::clearData(CLIPFORMAT format) } } - } diff --git a/Source/WebCore/platform/win/WCDataObject.h b/Source/WebCore/platform/win/WCDataObject.h index 133115d..e5fa298 100644 --- a/Source/WebCore/platform/win/WCDataObject.h +++ b/Source/WebCore/platform/win/WCDataObject.h @@ -26,10 +26,11 @@ #ifndef WCDataObject_h #define WCDataObject_h -#include <wtf/Forward.h> -#include <wtf/Vector.h> +#include "DragData.h" #include <ShlObj.h> #include <objidl.h> +#include <wtf/Forward.h> +#include <wtf/Vector.h> namespace WebCore { @@ -56,6 +57,7 @@ public: void clearData(CLIPFORMAT); static HRESULT createInstance(WCDataObject**); + static HRESULT createInstance(WCDataObject**, const DragDataMap&); private: WCDataObject(); virtual ~WCDataObject(); diff --git a/Source/WebCore/platform/wince/DragDataWinCE.cpp b/Source/WebCore/platform/wince/DragDataWinCE.cpp index 8f531c7..679229d 100644 --- a/Source/WebCore/platform/wince/DragDataWinCE.cpp +++ b/Source/WebCore/platform/wince/DragDataWinCE.cpp @@ -32,6 +32,11 @@ bool DragData::containsURL(Frame*, FilenameConversionPolicy filenamePolicy) cons return false; } +const DragDataMap& DragData::dragDataMap() +{ + return m_dragDataMap; +} + String DragData::asURL(Frame*, FilenameConversionPolicy filenamePolicy, String* title) const { return String(); diff --git a/Source/WebCore/platform/wince/KeygenWinCE.cpp b/Source/WebCore/platform/wince/KeygenWinCE.cpp index 0c1b3c6..8537f44 100644 --- a/Source/WebCore/platform/wince/KeygenWinCE.cpp +++ b/Source/WebCore/platform/wince/KeygenWinCE.cpp @@ -80,10 +80,7 @@ String WebCore::signedPublicKeyAndChallengeString(unsigned index, const String& if (!CryptSignAndEncodeCertificate(hContext, AT_KEYEXCHANGE, X509_ASN_ENCODING, X509_KEYGEN_REQUEST_TO_BE_SIGNED, &requestInfo, &signAlgo, 0, reinterpret_cast<LPBYTE>(binary.data()), &dwEncodedLength)) break; - Vector<char> base64; - base64Encode(binary, base64); - keyString = String(base64.data(), base64.size()); - + keyString = base64Encode(binary); } while(0); if (pPubInfo) diff --git a/Source/WebCore/platform/wince/MIMETypeRegistryWinCE.cpp b/Source/WebCore/platform/wince/MIMETypeRegistryWinCE.cpp index 7534b91..8a7ac8f 100644 --- a/Source/WebCore/platform/wince/MIMETypeRegistryWinCE.cpp +++ b/Source/WebCore/platform/wince/MIMETypeRegistryWinCE.cpp @@ -27,7 +27,9 @@ #include "config.h" #include "MIMETypeRegistry.h" +#include <wtf/Assertions.h> #include <wtf/HashMap.h> +#include <wtf/MainThread.h> #include <windows.h> #include <winreg.h> @@ -119,6 +121,8 @@ String MIMETypeRegistry::getPreferredExtensionForMIMEType(const String& type) String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + if (ext.isEmpty()) return String(); diff --git a/Source/WebCore/platform/wx/MimeTypeRegistryWx.cpp b/Source/WebCore/platform/wx/MimeTypeRegistryWx.cpp index e7cc0e2..94a815e 100644 --- a/Source/WebCore/platform/wx/MimeTypeRegistryWx.cpp +++ b/Source/WebCore/platform/wx/MimeTypeRegistryWx.cpp @@ -28,6 +28,9 @@ #include "config.h" #include "MIMETypeRegistry.h" +#include <wtf/Assertions.h> +#include <wtf/MainThread.h> + namespace WebCore { struct ExtensionMap { @@ -59,6 +62,8 @@ static const ExtensionMap extensionMap [] = { String MIMETypeRegistry::getMIMETypeForExtension(const String &ext) { + ASSERT(isMainThread()); + String s = ext.lower(); const ExtensionMap *e = extensionMap; while (e->extension) { diff --git a/Source/WebCore/platform/wx/RenderThemeWx.cpp b/Source/WebCore/platform/wx/RenderThemeWx.cpp index c68bde9..a34e2d4 100644 --- a/Source/WebCore/platform/wx/RenderThemeWx.cpp +++ b/Source/WebCore/platform/wx/RenderThemeWx.cpp @@ -31,6 +31,7 @@ #include "GraphicsContext.h" #include "HostWindow.h" #include "NotImplemented.h" +#include "PaintInfo.h" #include "RenderView.h" #include <wx/defs.h> diff --git a/Source/WebCore/platform/wx/SystemTimeWx.cpp b/Source/WebCore/platform/wx/SystemTimeWx.cpp index f607cba..668ec00 100644 --- a/Source/WebCore/platform/wx/SystemTimeWx.cpp +++ b/Source/WebCore/platform/wx/SystemTimeWx.cpp @@ -26,18 +26,16 @@ #include "config.h" #include "SystemTime.h" -#include <float.h> - #include "NotImplemented.h" +#include <limits> namespace WebCore { float userIdleTime() { notImplemented(); - // return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed - return FLT_MAX; -} - + // Return an arbitrarily high userIdleTime so that releasing pages from the page cache isn't postponed. + return std::numeric_limits<float>::max(); } +} // namespace WebCore |