diff options
Diffstat (limited to 'WebCore/platform')
362 files changed, 16586 insertions, 6464 deletions
diff --git a/WebCore/platform/AsyncFileSystem.h b/WebCore/platform/AsyncFileSystem.h index 1bf7580..3104ebc 100644 --- a/WebCore/platform/AsyncFileSystem.h +++ b/WebCore/platform/AsyncFileSystem.h @@ -59,6 +59,10 @@ public: static bool isAvailable(); + // Subclass must implement this if it supports synchronous operations. + // This should return false if there are no pending operations. + 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); @@ -76,10 +80,16 @@ public: virtual void copy(const String& srcPath, const String& destPath, PassOwnPtr<AsyncFileSystemCallbacks>) = 0; // Deletes a file or directory at a given path. + // It is an error to try to remove a directory that is not empty. // AsyncFileSystemCallbacks::didSucceed() is called when the operation is completed successfully. // AsyncFileSystemCallbacks::didFail() is called otherwise. virtual void remove(const String& path, PassOwnPtr<AsyncFileSystemCallbacks>) = 0; + // Recursively deletes a directory at a given path. + // AsyncFileSystemCallbacks::didSucceed() is called when the operation is completed successfully. + // AsyncFileSystemCallbacks::didFail() is called otherwise. + virtual void removeRecursively(const String& path, PassOwnPtr<AsyncFileSystemCallbacks>) = 0; + // Retrieves the metadata information of the file or directory at a given path. // AsyncFileSystemCallbacks::didReadMetadata() is called when the operation is completed successfully. // AsyncFileSystemCallbacks::didFail() is called otherwise. diff --git a/WebCore/platform/ColorData.gperf b/WebCore/platform/ColorData.gperf index dd726cc..f83e793 100644 --- a/WebCore/platform/ColorData.gperf +++ b/WebCore/platform/ColorData.gperf @@ -10,152 +10,153 @@ struct NamedColor; %define hash-function-name colordata_hash_function %enum %% -aliceblue, 0xf0f8ff -antiquewhite, 0xfaebd7 -aqua, 0x00ffff -aquamarine, 0x7fffd4 -azure, 0xf0ffff -beige, 0xf5f5dc -bisque, 0xffe4c4 -black, 0x000000 -blanchedalmond, 0xffebcd -blue, 0x0000ff -blueviolet, 0x8a2be2 -brown, 0xa52a2a -burlywood, 0xdeb887 -cadetblue, 0x5f9ea0 -chartreuse, 0x7fff00 -chocolate, 0xd2691e -coral, 0xff7f50 -cornflowerblue, 0x6495ed -cornsilk, 0xfff8dc -crimson, 0xdc143c -cyan, 0x00ffff -darkblue, 0x00008b -darkcyan, 0x008b8b -darkgoldenrod, 0xb8860b -darkgray, 0xa9a9a9 -darkgrey, 0xa9a9a9 -darkgreen, 0x006400 -darkkhaki, 0xbdb76b -darkmagenta, 0x8b008b -darkolivegreen, 0x556b2f -darkorange, 0xff8c00 -darkorchid, 0x9932cc -darkred, 0x8b0000 -darksalmon, 0xe9967a -darkseagreen, 0x8fbc8f -darkslateblue, 0x483d8b -darkslategray, 0x2f4f4f -darkslategrey, 0x2f4f4f -darkturquoise, 0x00ced1 -darkviolet, 0x9400d3 -deeppink, 0xff1493 -deepskyblue, 0x00bfff -dimgray, 0x696969 -dimgrey, 0x696969 -dodgerblue, 0x1e90ff -firebrick, 0xb22222 -floralwhite, 0xfffaf0 -forestgreen, 0x228b22 -fuchsia, 0xff00ff -gainsboro, 0xdcdcdc -ghostwhite, 0xf8f8ff -gold, 0xffd700 -goldenrod, 0xdaa520 -gray, 0x808080 -grey, 0x808080 -green, 0x008000 -greenyellow, 0xadff2f -honeydew, 0xf0fff0 -hotpink, 0xff69b4 -indianred, 0xcd5c5c -indigo, 0x4b0082 -ivory, 0xfffff0 -khaki, 0xf0e68c -lavender, 0xe6e6fa -lavenderblush, 0xfff0f5 -lawngreen, 0x7cfc00 -lemonchiffon, 0xfffacd -lightblue, 0xadd8e6 -lightcoral, 0xf08080 -lightcyan, 0xe0ffff -lightgoldenrodyellow, 0xfafad2 -lightgray, 0xd3d3d3 -lightgrey, 0xd3d3d3 -lightgreen, 0x90ee90 -lightpink, 0xffb6c1 -lightsalmon, 0xffa07a -lightseagreen, 0x20b2aa -lightskyblue, 0x87cefa -lightslateblue, 0x8470ff -lightslategray, 0x778899 -lightslategrey, 0x778899 -lightsteelblue, 0xb0c4de -lightyellow, 0xffffe0 -lime, 0x00ff00 -limegreen, 0x32cd32 -linen, 0xfaf0e6 -magenta, 0xff00ff -maroon, 0x800000 -mediumaquamarine, 0x66cdaa -mediumblue, 0x0000cd -mediumorchid, 0xba55d3 -mediumpurple, 0x9370d8 -mediumseagreen, 0x3cb371 -mediumslateblue, 0x7b68ee -mediumspringgreen, 0x00fa9a -mediumturquoise, 0x48d1cc -mediumvioletred, 0xc71585 -midnightblue, 0x191970 -mintcream, 0xf5fffa -mistyrose, 0xffe4e1 -moccasin, 0xffe4b5 -navajowhite, 0xffdead -navy, 0x000080 -oldlace, 0xfdf5e6 -olive, 0x808000 -olivedrab, 0x6b8e23 -orange, 0xffa500 -orangered, 0xff4500 -orchid, 0xda70d6 -palegoldenrod, 0xeee8aa -palegreen, 0x98fb98 -paleturquoise, 0xafeeee -palevioletred, 0xd87093 -papayawhip, 0xffefd5 -peachpuff, 0xffdab9 -peru, 0xcd853f -pink, 0xffc0cb -plum, 0xdda0dd -powderblue, 0xb0e0e6 -purple, 0x800080 -red, 0xff0000 -rosybrown, 0xbc8f8f -royalblue, 0x4169e1 -saddlebrown, 0x8b4513 -salmon, 0xfa8072 -sandybrown, 0xf4a460 -seagreen, 0x2e8b57 -seashell, 0xfff5ee -sienna, 0xa0522d -silver, 0xc0c0c0 -skyblue, 0x87ceeb -slateblue, 0x6a5acd -slategray, 0x708090 -slategrey, 0x708090 -snow, 0xfffafa -springgreen, 0x00ff7f -steelblue, 0x4682b4 -tan, 0xd2b48c -teal, 0x008080 -thistle, 0xd8bfd8 -tomato, 0xff6347 -turquoise, 0x40e0d0 -violet, 0xee82ee -violetred, 0xd02090 -wheat, 0xf5deb3 -white, 0xffffff -whitesmoke, 0xf5f5f5 -yellow, 0xffff00 -yellowgreen, 0x9acd32 +aliceblue, 0xfff0f8ff +antiquewhite, 0xfffaebd7 +aqua, 0xff00ffff +aquamarine, 0xff7fffd4 +azure, 0xfff0ffff +beige, 0xfff5f5dc +bisque, 0xffffe4c4 +black, 0xff000000 +blanchedalmond, 0xffffebcd +blue, 0xff0000ff +blueviolet, 0xff8a2be2 +brown, 0xffa52a2a +burlywood, 0xffdeb887 +cadetblue, 0xff5f9ea0 +chartreuse, 0xff7fff00 +chocolate, 0xffd2691e +coral, 0xffff7f50 +cornflowerblue, 0xff6495ed +cornsilk, 0xfffff8dc +crimson, 0xffdc143c +cyan, 0xff00ffff +darkblue, 0xff00008b +darkcyan, 0xff008b8b +darkgoldenrod, 0xffb8860b +darkgray, 0xffa9a9a9 +darkgrey, 0xffa9a9a9 +darkgreen, 0xff006400 +darkkhaki, 0xffbdb76b +darkmagenta, 0xff8b008b +darkolivegreen, 0xff556b2f +darkorange, 0xffff8c00 +darkorchid, 0xff9932cc +darkred, 0xff8b0000 +darksalmon, 0xffe9967a +darkseagreen, 0xff8fbc8f +darkslateblue, 0xff483d8b +darkslategray, 0xff2f4f4f +darkslategrey, 0xff2f4f4f +darkturquoise, 0xff00ced1 +darkviolet, 0xff9400d3 +deeppink, 0xffff1493 +deepskyblue, 0xff00bfff +dimgray, 0xff696969 +dimgrey, 0xff696969 +dodgerblue, 0xff1e90ff +firebrick, 0xffb22222 +floralwhite, 0xfffffaf0 +forestgreen, 0xff228b22 +fuchsia, 0xffff00ff +gainsboro, 0xffdcdcdc +ghostwhite, 0xfff8f8ff +gold, 0xffffd700 +goldenrod, 0xffdaa520 +gray, 0xff808080 +grey, 0xff808080 +green, 0xff008000 +greenyellow, 0xffadff2f +honeydew, 0xfff0fff0 +hotpink, 0xffff69b4 +indianred, 0xffcd5c5c +indigo, 0xff4b0082 +ivory, 0xfffffff0 +khaki, 0xfff0e68c +lavender, 0xffe6e6fa +lavenderblush, 0xfffff0f5 +lawngreen, 0xff7cfc00 +lemonchiffon, 0xfffffacd +lightblue, 0xffadd8e6 +lightcoral, 0xfff08080 +lightcyan, 0xffe0ffff +lightgoldenrodyellow, 0xfffafad2 +lightgray, 0xffd3d3d3 +lightgrey, 0xffd3d3d3 +lightgreen, 0xff90ee90 +lightpink, 0xffffb6c1 +lightsalmon, 0xffffa07a +lightseagreen, 0xff20b2aa +lightskyblue, 0xff87cefa +lightslateblue, 0xff8470ff +lightslategray, 0xff778899 +lightslategrey, 0xff778899 +lightsteelblue, 0xffb0c4de +lightyellow, 0xffffffe0 +lime, 0xff00ff00 +limegreen, 0xff32cd32 +linen, 0xfffaf0e6 +magenta, 0xffff00ff +maroon, 0xff800000 +mediumaquamarine, 0xff66cdaa +mediumblue, 0xff0000cd +mediumorchid, 0xffba55d3 +mediumpurple, 0xff9370d8 +mediumseagreen, 0xff3cb371 +mediumslateblue, 0xff7b68ee +mediumspringgreen, 0xff00fa9a +mediumturquoise, 0xff48d1cc +mediumvioletred, 0xffc71585 +midnightblue, 0xff191970 +mintcream, 0xfff5fffa +mistyrose, 0xffffe4e1 +moccasin, 0xffffe4b5 +navajowhite, 0xffffdead +navy, 0xff000080 +oldlace, 0xfffdf5e6 +olive, 0xff808000 +olivedrab, 0xff6b8e23 +orange, 0xffffa500 +orangered, 0xffff4500 +orchid, 0xffda70d6 +palegoldenrod, 0xffeee8aa +palegreen, 0xff98fb98 +paleturquoise, 0xffafeeee +palevioletred, 0xffd87093 +papayawhip, 0xffffefd5 +peachpuff, 0xffffdab9 +peru, 0xffcd853f +pink, 0xffffc0cb +plum, 0xffdda0dd +powderblue, 0xffb0e0e6 +purple, 0xff800080 +red, 0xffff0000 +rosybrown, 0xffbc8f8f +royalblue, 0xff4169e1 +saddlebrown, 0xff8b4513 +salmon, 0xfffa8072 +sandybrown, 0xfff4a460 +seagreen, 0xff2e8b57 +seashell, 0xfffff5ee +sienna, 0xffa0522d +silver, 0xffc0c0c0 +skyblue, 0xff87ceeb +slateblue, 0xff6a5acd +slategray, 0xff708090 +slategrey, 0xff708090 +snow, 0xfffffafa +springgreen, 0xff00ff7f +steelblue, 0xff4682b4 +tan, 0xffd2b48c +teal, 0xff008080 +thistle, 0xffd8bfd8 +tomato, 0xffff6347 +transparent, 0x00000000 +turquoise, 0xff40e0d0 +violet, 0xffee82ee +violetred, 0xffd02090 +wheat, 0xfff5deb3 +white, 0xffffffff +whitesmoke, 0xfff5f5f5 +yellow, 0xffffff00 +yellowgreen, 0xff9acd32 diff --git a/WebCore/platform/CrossThreadCopier.cpp b/WebCore/platform/CrossThreadCopier.cpp index 683ba54..4280486 100644 --- a/WebCore/platform/CrossThreadCopier.cpp +++ b/WebCore/platform/CrossThreadCopier.cpp @@ -37,6 +37,7 @@ #include "ResourceError.h" #include "ResourceRequest.h" #include "ResourceResponse.h" +#include "SerializedScriptValue.h" namespace WebCore { diff --git a/WebCore/platform/KURL.cpp b/WebCore/platform/KURL.cpp index 16b84b5..3ac5d86 100644 --- a/WebCore/platform/KURL.cpp +++ b/WebCore/platform/KURL.cpp @@ -616,6 +616,33 @@ bool KURL::hasFragmentIdentifier() const return m_fragmentEnd != m_queryEnd; } +void KURL::copyParsedQueryTo(ParsedURLParameters& parameters) const +{ + const UChar* pos = m_string.characters() + m_pathEnd + 1; + const UChar* end = m_string.characters() + m_queryEnd; + while (pos < end) { + const UChar* parameterStart = pos; + while (pos < end && *pos != '&') + ++pos; + const UChar* parameterEnd = pos; + if (pos < end) { + ASSERT(*pos == '&'); + ++pos; + } + if (parameterStart == parameterEnd) + continue; + const UChar* nameStart = parameterStart; + const UChar* equalSign = parameterStart; + while (equalSign < parameterEnd && *equalSign != '=') + ++equalSign; + if (equalSign == nameStart) + continue; + String name(nameStart, equalSign - nameStart); + String value = equalSign == parameterEnd ? String() : String(equalSign + 1, parameterEnd - equalSign - 1); + parameters.set(name, value); + } +} + String KURL::baseAsString() const { return m_string.left(m_pathAfterLastSlash); diff --git a/WebCore/platform/KURL.h b/WebCore/platform/KURL.h index e5e8ec4..cbf0d8b 100644 --- a/WebCore/platform/KURL.h +++ b/WebCore/platform/KURL.h @@ -27,6 +27,7 @@ #define KURL_h #include "PlatformString.h" +#include <wtf/HashMap.h> #if PLATFORM(CF) typedef const struct __CFURL* CFURLRef; @@ -59,6 +60,8 @@ namespace WebCore { class TextEncoding; struct KURLHash; +typedef HashMap<String, String> ParsedURLParameters; + enum ParsedURLStringTag { ParsedURLString }; class KURL { @@ -133,6 +136,8 @@ public: String fragmentIdentifier() const; bool hasFragmentIdentifier() const; + void copyParsedQueryTo(ParsedURLParameters&) const; + String baseAsString() const; String prettyURL() const; @@ -141,6 +146,7 @@ public: // Returns true if the current URL's protocol is the same as the null- // terminated ASCII argument. The argument must be lower-case. bool protocolIs(const char*) const; + bool protocolIsData() const { return protocolIs("data"); } bool protocolInHTTPFamily() const; bool isLocalFile() const; diff --git a/WebCore/platform/KURLGoogle.cpp b/WebCore/platform/KURLGoogle.cpp index 3d23fcf..726bc57 100644 --- a/WebCore/platform/KURLGoogle.cpp +++ b/WebCore/platform/KURLGoogle.cpp @@ -549,6 +549,34 @@ bool KURL::hasFragmentIdentifier() const return m_url.m_parsed.ref.len >= 0; } +void KURL::copyParsedQueryTo(ParsedURLParameters& parameters) const +{ + String query = m_url.componentString(m_url.m_parsed.query); + const UChar* pos = query.characters(); + const UChar* end = query.characters() + query.length(); + while (pos < end) { + const UChar* parameterStart = pos; + while (pos < end && *pos != '&') + ++pos; + const UChar* parameterEnd = pos; + if (pos < end) { + ASSERT(*pos == '&'); + ++pos; + } + if (parameterStart == parameterEnd) + continue; + const UChar* nameStart = parameterStart; + const UChar* equalSign = parameterStart; + while (equalSign < parameterEnd && *equalSign != '=') + ++equalSign; + if (equalSign == nameStart) + continue; + String name(nameStart, equalSign - nameStart); + String value = equalSign == parameterEnd ? String() : String(equalSign + 1, parameterEnd - equalSign - 1); + parameters.set(name, value); + } +} + String KURL::baseAsString() const { // FIXME: There is probably a more efficient way to do this? diff --git a/WebCore/platform/Language.cpp b/WebCore/platform/Language.cpp new file mode 100644 index 0000000..6cf2597 --- /dev/null +++ b/WebCore/platform/Language.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 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 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. + */ + +#include "config.h" +#include "Language.h" + +#include "PlatformString.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +typedef HashMap<void*, LanguageChangeObserverFunction> ObserverMap; +static ObserverMap& observerMap() +{ + DEFINE_STATIC_LOCAL(ObserverMap, map, ()); + return map; +} + +void addLanguageChangeObserver(void* context, LanguageChangeObserverFunction customObserver) +{ + observerMap().set(context, customObserver); +} + +void removeLanguageChangeObserver(void* context) +{ + ASSERT(observerMap().contains(context)); + observerMap().remove(context); +} + +void languageDidChange() +{ + ObserverMap::iterator end = observerMap().end(); + for (ObserverMap::iterator iter = observerMap().begin(); iter != end; ++iter) + iter->second(iter->first); +} + +static String& languageOverride() +{ + DEFINE_STATIC_LOCAL(String, override, ()); + return override; +} + +String defaultLanguage() +{ + const String& override = languageOverride(); + if (!override.isNull()) + return override; + + return platformDefaultLanguage(); +} + +void overrideDefaultLanguage(const String& override) +{ + languageOverride() = override; +} + +} diff --git a/WebCore/platform/Language.h b/WebCore/platform/Language.h index 4c92755..656caee 100644 --- a/WebCore/platform/Language.h +++ b/WebCore/platform/Language.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2006, 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 @@ -10,17 +10,17 @@ * 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. + * 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 Language_h @@ -30,8 +30,16 @@ namespace WebCore { - String defaultLanguage(); +String defaultLanguage(); +void overrideDefaultLanguage(const String&); +// The observer function will be called when system language changes (unless it's overridden by overrideDefaultLanguage()). +typedef void (*LanguageChangeObserverFunction)(void* context); +void addLanguageChangeObserver(void* context, LanguageChangeObserverFunction); +void removeLanguageChangeObserver(void* context); + +String platformDefaultLanguage(); +void languageDidChange(); } #endif diff --git a/WebCore/platform/Logging.cpp b/WebCore/platform/Logging.cpp index 7fb15b1..8f64282 100644 --- a/WebCore/platform/Logging.cpp +++ b/WebCore/platform/Logging.cpp @@ -33,13 +33,11 @@ WTFLogChannel LogNotYetImplemented = { 0x00000001, "WebCoreLogLevel", WTFLogChan WTFLogChannel LogFrames = { 0x00000010, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogLoading = { 0x00000020, "WebCoreLogLevel", WTFLogChannelOff }; - WTFLogChannel LogPopupBlocking = { 0x00000040, "WebCoreLogLevel", WTFLogChannelOff }; - WTFLogChannel LogEvents = { 0x00000080, "WebCoreLogLevel", WTFLogChannelOff }; + WTFLogChannel LogEditing = { 0x00000100, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogLiveConnect = { 0x00000200, "WebCoreLogLevel", WTFLogChannelOff }; - WTFLogChannel LogIconDatabase = { 0x00000400, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogSQLDatabase = { 0x00000800, "WebCoreLogLevel", WTFLogChannelOff }; @@ -49,6 +47,7 @@ WTFLogChannel LogHistory = { 0x00004000, "WebCoreLogLevel", WTFLogChan WTFLogChannel LogPageCache = { 0x00008000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogPlatformLeaks = { 0x00010000, "WebCoreLogLevel", WTFLogChannelOff }; +WTFLogChannel LogResourceLoading = { 0x00020000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogNetwork = { 0x00100000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogFTP = { 0x00200000, "WebCoreLogLevel", WTFLogChannelOff }; @@ -56,10 +55,10 @@ WTFLogChannel LogThreading = { 0x00400000, "WebCoreLogLevel", WTFLogChan WTFLogChannel LogStorageAPI = { 0x00800000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogMedia = { 0x01000000, "WebCoreLogLevel", WTFLogChannelOff }; - WTFLogChannel LogPlugins = { 0x02000000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogArchives = { 0x04000000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel LogProgress = { 0x08000000, "WebCoreLogLevel", WTFLogChannelOff }; + WTFLogChannel LogFileAPI = { 0x10000000, "WebCoreLogLevel", WTFLogChannelOff }; WTFLogChannel* getChannelFromName(const String& channelName) @@ -106,6 +105,9 @@ WTFLogChannel* getChannelFromName(const String& channelName) if (equalIgnoringCase(channelName, String("PlatformLeaks"))) return &LogPlatformLeaks; + if (equalIgnoringCase(channelName, String("ResourceLoading"))) + return &LogResourceLoading; + if (equalIgnoringCase(channelName, String("Plugins"))) return &LogPlugins; diff --git a/WebCore/platform/Logging.h b/WebCore/platform/Logging.h index df3c6fd..11ad1db 100644 --- a/WebCore/platform/Logging.h +++ b/WebCore/platform/Logging.h @@ -49,6 +49,7 @@ namespace WebCore { extern WTFLogChannel LogHistory; extern WTFLogChannel LogPageCache; extern WTFLogChannel LogPlatformLeaks; + extern WTFLogChannel LogResourceLoading; extern WTFLogChannel LogNetwork; extern WTFLogChannel LogFTP; extern WTFLogChannel LogThreading; diff --git a/WebCore/platform/PlatformKeyboardEvent.h b/WebCore/platform/PlatformKeyboardEvent.h index 6ec4879..7ce7017 100644 --- a/WebCore/platform/PlatformKeyboardEvent.h +++ b/WebCore/platform/PlatformKeyboardEvent.h @@ -186,6 +186,8 @@ namespace WebCore { #if PLATFORM(QT) PlatformKeyboardEvent(QKeyEvent*); QKeyEvent* qtEvent() const { return m_qtEvent; } + uint32_t nativeModifiers() const; + uint32_t nativeScanCode() const; #endif #if PLATFORM(WX) diff --git a/WebCore/platform/ScrollView.cpp b/WebCore/platform/ScrollView.cpp index 0e95e22..fac37d7 100644 --- a/WebCore/platform/ScrollView.cpp +++ b/WebCore/platform/ScrollView.cpp @@ -718,27 +718,27 @@ void ScrollView::wheelEvent(PlatformWheelEvent& e) return; } - // Determine how much we want to scroll. If we can move at all, we will accept the event. + // 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 maxScrollDelta = maximumScrollPosition() - scrollPosition(); - if ((e.deltaX() < 0 && maxScrollDelta.width() > 0) || - (e.deltaX() > 0 && scrollOffset().width() > 0) || - (e.deltaY() < 0 && maxScrollDelta.height() > 0) || - (e.deltaY() > 0 && scrollOffset().height() > 0)) { + if ((deltaX < 0 && maxScrollDelta.width() > 0) + || (deltaX > 0 && scrollOffset().width() > 0) + || (deltaY < 0 && maxScrollDelta.height() > 0) + || (deltaY > 0 && scrollOffset().height() > 0)) { e.accept(); - float deltaX = e.deltaX(); - float deltaY = e.deltaY(); if (e.granularity() == ScrollByPageWheelEvent) { - ASSERT(deltaX == 0); + 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; } - // Should we fall back on scrollBy() if there is no scrollbar for a non-zero delta? - if (deltaY && m_verticalScrollbar) + if (deltaY) m_verticalScrollbar->scroll(ScrollUp, ScrollByPixel, deltaY); - if (deltaX && m_horizontalScrollbar) + if (deltaX) m_horizontalScrollbar->scroll(ScrollLeft, ScrollByPixel, deltaX); } } @@ -837,7 +837,7 @@ void ScrollView::paintScrollbars(GraphicsContext* context, const IntRect& rect) void ScrollView::paintPanScrollIcon(GraphicsContext* context) { static Image* panScrollIcon = Image::loadPlatformResource("panIcon").releaseRef(); - context->drawImage(panScrollIcon, DeviceColorSpace, m_panScrollIconPoint); + context->drawImage(panScrollIcon, ColorSpaceDeviceRGB, m_panScrollIconPoint); } void ScrollView::paint(GraphicsContext* context, const IntRect& rect) diff --git a/WebCore/platform/ScrollbarTheme.h b/WebCore/platform/ScrollbarTheme.h index c5c2094..0efaf7a 100644 --- a/WebCore/platform/ScrollbarTheme.h +++ b/WebCore/platform/ScrollbarTheme.h @@ -73,7 +73,7 @@ public: virtual void invalidatePart(Scrollbar*, ScrollbarPart) {} - virtual void paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect) { context->fillRect(cornerRect, Color::white, DeviceColorSpace); } + virtual void paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect) { context->fillRect(cornerRect, Color::white, ColorSpaceDeviceRGB); } virtual bool shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent&) { return false; } virtual bool shouldSnapBackToDragOrigin(Scrollbar*, const PlatformMouseEvent&) { return false; } diff --git a/WebCore/platform/ScrollbarThemeComposite.cpp b/WebCore/platform/ScrollbarThemeComposite.cpp index fdac14d..bf00ebb 100644 --- a/WebCore/platform/ScrollbarThemeComposite.cpp +++ b/WebCore/platform/ScrollbarThemeComposite.cpp @@ -298,7 +298,7 @@ void ScrollbarThemeComposite::paintScrollCorner(ScrollView* view, GraphicsContex Page* page = frameView->frame() ? frameView->frame()->page() : 0; if (page && page->settings()->shouldPaintCustomScrollbars() && page->chrome()->client()->paintCustomScrollCorner(context, cornerRect)) return; - context->fillRect(cornerRect, Color::white, DeviceColorSpace); + context->fillRect(cornerRect, Color::white, ColorSpaceDeviceRGB); } } diff --git a/WebCore/platform/Widget.h b/WebCore/platform/Widget.h index 089a1e9..85612ed 100644 --- a/WebCore/platform/Widget.h +++ b/WebCore/platform/Widget.h @@ -232,6 +232,10 @@ public: const String edjeThemeRecursive() const; #endif +#if PLATFORM(CHROMIUM) + virtual bool isPluginContainer() const { return false; } +#endif + // Virtual methods to convert points to/from the containing ScrollView virtual IntRect convertToContainingView(const IntRect&) const; virtual IntRect convertFromContainingView(const IntRect&) const; diff --git a/WebCore/platform/android/FileSystemAndroid.cpp b/WebCore/platform/android/FileSystemAndroid.cpp index 3513958..5ab8929 100644 --- a/WebCore/platform/android/FileSystemAndroid.cpp +++ b/WebCore/platform/android/FileSystemAndroid.cpp @@ -28,8 +28,11 @@ #include "config.h" #include "FileSystem.h" +<<<<<<< HEAD #include "PlatformBridge.h" #include "StringBuilder.h" +======= +>>>>>>> webkit.org at r70209 #include "cutils/log.h" #include <dirent.h> #include <dlfcn.h> @@ -37,6 +40,7 @@ #include <fnmatch.h> #include <sys/stat.h> #include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> namespace WebCore { diff --git a/WebCore/platform/audio/AudioResampler.cpp b/WebCore/platform/audio/AudioResampler.cpp new file mode 100644 index 0000000..7f8221e --- /dev/null +++ b/WebCore/platform/audio/AudioResampler.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "AudioResampler.h" + +#include "AudioBus.h" +#include <algorithm> +#include <math.h> + +using namespace std; + +namespace WebCore { + +const double AudioResampler::MaxRate = 8.0; + +AudioResampler::AudioResampler() + : m_rate(1.0) +{ + m_kernels.append(adoptPtr(new AudioResamplerKernel(this))); + m_sourceBus = adoptPtr(new AudioBus(1, 0, false)); +} + +AudioResampler::AudioResampler(unsigned numberOfChannels) + : m_rate(1.0) +{ + for (unsigned i = 0; i < numberOfChannels; ++i) + m_kernels.append(adoptPtr(new AudioResamplerKernel(this))); + + m_sourceBus = adoptPtr(new AudioBus(numberOfChannels, 0, false)); +} + +void AudioResampler::configureChannels(unsigned numberOfChannels) +{ + unsigned currentSize = m_kernels.size(); + if (numberOfChannels == currentSize) + return; // already setup + + // First deal with adding or removing kernels. + if (numberOfChannels > currentSize) { + for (unsigned i = currentSize; i < numberOfChannels; ++i) + m_kernels.append(adoptPtr(new AudioResamplerKernel(this))); + } else + m_kernels.resize(numberOfChannels); + + // Reconfigure our source bus to the new channel size. + m_sourceBus = adoptPtr(new AudioBus(numberOfChannels, 0, false)); +} + +void AudioResampler::process(AudioSourceProvider* provider, AudioBus* destinationBus, size_t framesToProcess) +{ + ASSERT(provider); + if (!provider) + return; + + unsigned numberOfChannels = m_kernels.size(); + + // Make sure our configuration matches the bus we're rendering to. + bool channelsMatch = (destinationBus && destinationBus->numberOfChannels() == numberOfChannels); + ASSERT(channelsMatch); + if (!channelsMatch) + return; + + // Setup the source bus. + for (unsigned i = 0; i < numberOfChannels; ++i) { + // Figure out how many frames we need to get from the provider, and a pointer to the buffer. + size_t framesNeeded; + float* fillPointer = m_kernels[i]->getSourcePointer(framesToProcess, &framesNeeded); + ASSERT(fillPointer); + if (!fillPointer) + return; + + m_sourceBus->setChannelMemory(i, fillPointer, framesNeeded); + } + + // Ask the provider to supply the desired number of source frames. + provider->provideInput(m_sourceBus.get(), m_sourceBus->length()); + + // Now that we have the source data, resample each channel into the destination bus. + // FIXME: optimize for the common stereo case where it's faster to process both left/right channels in the same inner loop. + for (unsigned i = 0; i < numberOfChannels; ++i) { + float* destination = destinationBus->channel(i)->data(); + m_kernels[i]->process(destination, framesToProcess); + } +} + +void AudioResampler::setRate(double rate) +{ + if (isnan(rate) || isinf(rate) || rate <= 0.0) + return; + + m_rate = min(AudioResampler::MaxRate, rate); +} + +void AudioResampler::reset() +{ + unsigned numberOfChannels = m_kernels.size(); + for (unsigned i = 0; i < numberOfChannels; ++i) + m_kernels[i]->reset(); +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/AudioResampler.h b/WebCore/platform/audio/AudioResampler.h new file mode 100644 index 0000000..ed352b8 --- /dev/null +++ b/WebCore/platform/audio/AudioResampler.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 AudioResampler_h +#define AudioResampler_h + +#include "AudioBus.h" +#include "AudioResamplerKernel.h" +#include "AudioSourceProvider.h" +#include <wtf/OwnPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +// AudioResampler resamples the audio stream from an AudioSourceProvider. +// The audio stream may be single or multi-channel. +// The default constructor defaults to single-channel (mono). + +class AudioResampler { +public: + AudioResampler(); + AudioResampler(unsigned numberOfChannels); + ~AudioResampler() { } + + // Given an AudioSourceProvider, process() resamples the source stream into destinationBus. + void process(AudioSourceProvider*, AudioBus* destinationBus, size_t framesToProcess); + + // Resets the processing state. + void reset(); + + void configureChannels(unsigned numberOfChannels); + + // 0 < rate <= MaxRate + void setRate(double rate); + double rate() const { return m_rate; } + + static const double MaxRate; + +private: + double m_rate; + Vector<OwnPtr<AudioResamplerKernel> > m_kernels; + OwnPtr<AudioBus> m_sourceBus; +}; + +} // namespace WebCore + +#endif // AudioResampler_h diff --git a/WebCore/platform/audio/AudioUtilities.cpp b/WebCore/platform/audio/AudioUtilities.cpp new file mode 100644 index 0000000..32f4335 --- /dev/null +++ b/WebCore/platform/audio/AudioUtilities.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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. + */ + +#include "config.h" + +#include "AudioUtilities.h" + +#include <math.h> + +namespace WebCore { + +namespace AudioUtilities { + +double decibelsToLinear(double decibels) +{ + return pow(10.0, 0.05 * decibels); +} + +double linearToDecibels(double linear) +{ + // It's not possible to calculate decibels for a zero linear value since it would be -Inf. + // -1000.0 dB represents a very tiny linear value in case we ever reach this case. + ASSERT(linear); + if (!linear) + return -1000.0; + + return 20.0 * log10(linear); +} + +double discreteTimeConstantForSampleRate(double timeConstant, double sampleRate) +{ + return 1.0 - pow(1.0 / M_E, 1.0 / (sampleRate * timeConstant)); +} + +} // AudioUtilites + +} // WebCore diff --git a/WebCore/platform/audio/AudioUtilities.h b/WebCore/platform/audio/AudioUtilities.h new file mode 100644 index 0000000..7cf44ce --- /dev/null +++ b/WebCore/platform/audio/AudioUtilities.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 AudioUtilities_h +#define AudioUtilities_h + +namespace WebCore { + +namespace AudioUtilities { + +// Standard functions for converting to and from decibel values from linear. +double linearToDecibels(double); +double decibelsToLinear(double); + +// timeConstant is the time it takes a first-order linear time-invariant system +// to reach the value 1 - 1/e (around 63.2%) given a step input response. +// discreteTimeConstantForSampleRate() will return the discrete time-constant for the specific sampleRate. +double discreteTimeConstantForSampleRate(double timeConstant, double sampleRate); + +} // AudioUtilites + +} // WebCore + +#endif // AudioUtilities_h diff --git a/WebCore/platform/audio/EqualPowerPanner.cpp b/WebCore/platform/audio/EqualPowerPanner.cpp new file mode 100644 index 0000000..2e4e10f --- /dev/null +++ b/WebCore/platform/audio/EqualPowerPanner.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "EqualPowerPanner.h" + +#include "AudioBus.h" +#include "AudioUtilities.h" +#include <math.h> + +// Use a 50ms smoothing / de-zippering time-constant. +const double SmoothingTimeConstant = 0.050; + +namespace WebCore { + +EqualPowerPanner::EqualPowerPanner(double sampleRate) + : Panner(PanningModelEqualPower) + , m_isFirstRender(true) + , m_gainL(0.0) + , m_gainR(0.0) +{ + m_smoothingConstant = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, sampleRate); +} + +void EqualPowerPanner::pan(double azimuth, double /*elevation*/, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) +{ + // FIXME: implement stereo sources + bool isInputSafe = inputBus && inputBus->numberOfChannels() == 1 && framesToProcess <= inputBus->length(); + ASSERT(isInputSafe); + if (!isInputSafe) + return; + + bool isOutputSafe = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length(); + ASSERT(isOutputSafe); + if (!isOutputSafe) + return; + + AudioChannel* channel = inputBus->channel(0); + float* sourceP = channel->data(); + float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->data(); + float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->data(); + + if (!sourceP || !destinationL || !destinationR) + return; + + // Pan smoothly from left to right with azimuth going from -30 -> +30 degrees. + double desiredPanPosition; + if (azimuth > 30.0) + desiredPanPosition = 1.0; + else if (azimuth < -30.0) + desiredPanPosition = 0.0; + else + desiredPanPosition = (azimuth + 30.0) / 60.0; + + double desiredGainL = 0.5 * cos(M_PI * desiredPanPosition) + 0.5; + double desiredGainR = sqrt(1.0 - desiredGainL*desiredGainL); + + // Don't de-zipper on first render call. + if (m_isFirstRender) { + m_isFirstRender = false; + m_gainL = desiredGainL; + m_gainR = desiredGainR; + } + + // Cache in local variables. + double gainL = m_gainL; + double gainR = m_gainR; + + // Get local copy of smoothing constant. + const double SmoothingConstant = m_smoothingConstant; + + int n = framesToProcess; + + while (n--) { + float input = *sourceP++; + gainL += (desiredGainL - gainL) * SmoothingConstant; + gainR += (desiredGainR - gainR) * SmoothingConstant; + *destinationL++ = static_cast<float>(input * gainL); + *destinationR++ = static_cast<float>(input * gainR); + } + + m_gainL = gainL; + m_gainR = gainR; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/EqualPowerPanner.h b/WebCore/platform/audio/EqualPowerPanner.h new file mode 100644 index 0000000..f20617e --- /dev/null +++ b/WebCore/platform/audio/EqualPowerPanner.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 EqualPowerPanner_h +#define EqualPowerPanner_h + +#include "Panner.h" + +namespace WebCore { + +// Common type of stereo panner as found in normal audio mixing equipment. + +class EqualPowerPanner : public Panner { +public: + EqualPowerPanner(double sampleRate); + + virtual void pan(double azimuth, double elevation, AudioBus* inputBus, AudioBus* outputBuf, size_t framesToProcess); + + virtual void reset() { m_isFirstRender = true; } + +private: + // For smoothing / de-zippering + bool m_isFirstRender; + double m_smoothingConstant; + + double m_gainL; + double m_gainR; +}; + +} // namespace WebCore + +#endif // EqualPowerPanner_h diff --git a/WebCore/platform/audio/HRTFPanner.cpp b/WebCore/platform/audio/HRTFPanner.cpp new file mode 100644 index 0000000..56f06f1 --- /dev/null +++ b/WebCore/platform/audio/HRTFPanner.cpp @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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. + */ + +#include "config.h" + +#if ENABLE(WEB_AUDIO) + +#include "HRTFPanner.h" + +#include "AudioBus.h" +#include "FFTConvolver.h" +#include "HRTFDatabase.h" +#include "HRTFDatabaseLoader.h" +#include <algorithm> +#include <math.h> +#include <wtf/RefPtr.h> + +using namespace std; + +namespace WebCore { + +// The value of 2 milliseconds is larger than the largest delay which exists in any HRTFKernel from the default HRTFDatabase (0.0136 seconds). +// We ASSERT the delay values used in process() with this value. +const double MaxDelayTimeSeconds = 0.002; + +HRTFPanner::HRTFPanner(double sampleRate) + : Panner(PanningModelHRTF) + , m_sampleRate(sampleRate) + , m_isFirstRender(true) + , m_azimuthIndex(0) + , m_convolverL(fftSizeForSampleRate(sampleRate)) + , m_convolverR(fftSizeForSampleRate(sampleRate)) + , m_delayLineL(MaxDelayTimeSeconds, sampleRate) + , m_delayLineR(MaxDelayTimeSeconds, sampleRate) +{ +} + +HRTFPanner::~HRTFPanner() +{ +} + +size_t HRTFPanner::fftSizeForSampleRate(double sampleRate) +{ + // The HRTF impulse responses (loaded as audio resources) are 512 sample-frames @44.1KHz. + // Currently, we truncate the impulse responses to half this size, but an FFT-size of twice impulse response size is needed (for convolution). + // So for sample rates around 44.1KHz an FFT size of 512 is good. We double that size for higher sample rates. + ASSERT(sampleRate >= 44100 && sampleRate <= 96000.0); + return (sampleRate <= 48000.0) ? 512 : 1024; +} + +void HRTFPanner::reset() +{ + m_isFirstRender = true; + m_convolverL.reset(); + m_convolverR.reset(); + m_delayLineL.reset(); + m_delayLineR.reset(); +} + +static bool wrapDistance(int i, int j, int length) +{ + int directDistance = abs(i - j); + int indirectDistance = length - directDistance; + + return indirectDistance < directDistance; +} + +int HRTFPanner::calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend) +{ + // Convert the azimuth angle from the range -180 -> +180 into the range 0 -> 360. + // The azimuth index may then be calculated from this positive value. + if (azimuth < 0) + azimuth += 360.0; + + HRTFDatabase* database = HRTFDatabaseLoader::defaultHRTFDatabase(); + ASSERT(database); + + int numberOfAzimuths = database->numberOfAzimuths(); + const double angleBetweenAzimuths = 360.0 / numberOfAzimuths; + + // Calculate the azimuth index and the blend (0 -> 1) for interpolation. + double desiredAzimuthIndexFloat = azimuth / angleBetweenAzimuths; + int desiredAzimuthIndex = static_cast<int>(desiredAzimuthIndexFloat); + azimuthBlend = desiredAzimuthIndexFloat - static_cast<double>(desiredAzimuthIndex); + + // We don't immediately start using this azimuth index, but instead approach this index from the last index we rendered at. + // This minimizes the clicks and graininess for moving sources which occur otherwise. + desiredAzimuthIndex = max(0, desiredAzimuthIndex); + desiredAzimuthIndex = min(numberOfAzimuths - 1, desiredAzimuthIndex); + return desiredAzimuthIndex; +} + +void HRTFPanner::pan(double desiredAzimuth, double elevation, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess) +{ + unsigned numInputChannels = inputBus ? inputBus->numberOfChannels() : 0; + + bool isInputGood = inputBus && numInputChannels >= 1 && numInputChannels <= 2; + ASSERT(isInputGood); + + bool isOutputGood = outputBus && outputBus->numberOfChannels() == 2 && framesToProcess <= outputBus->length(); + ASSERT(isOutputGood); + + if (!isInputGood || !isOutputGood) { + if (outputBus) + outputBus->zero(); + return; + } + + // This code only runs as long as the context is alive and after database has been loaded. + HRTFDatabase* database = HRTFDatabaseLoader::defaultHRTFDatabase(); + ASSERT(database); + if (!database) { + outputBus->zero(); + return; + } + + // IRCAM HRTF azimuths values from the loaded database is reversed from the panner's notion of azimuth. + double azimuth = -desiredAzimuth; + + bool isAzimuthGood = azimuth >= -180.0 && azimuth <= 180.0; + ASSERT(isAzimuthGood); + if (!isAzimuthGood) { + outputBus->zero(); + return; + } + + // Normally, we'll just be dealing with mono sources. + // If we have a stereo input, implement stereo panning with left source processed by left HRTF, and right source by right HRTF. + AudioChannel* inputChannelL = inputBus->channelByType(AudioBus::ChannelLeft); + AudioChannel* inputChannelR = numInputChannels > 1 ? inputBus->channelByType(AudioBus::ChannelRight) : 0; + + // Get source and destination pointers. + float* sourceL = inputChannelL->data(); + float* sourceR = numInputChannels > 1 ? inputChannelR->data() : sourceL; + float* destinationL = outputBus->channelByType(AudioBus::ChannelLeft)->data(); + float* destinationR = outputBus->channelByType(AudioBus::ChannelRight)->data(); + + double azimuthBlend; + int desiredAzimuthIndex = calculateDesiredAzimuthIndexAndBlend(azimuth, azimuthBlend); + + // This algorithm currently requires that we process in power-of-two size chunks at least 128. + ASSERT(1UL << static_cast<int>(log2(framesToProcess)) == framesToProcess); + ASSERT(framesToProcess >= 128); + + const unsigned framesPerSegment = 128; + const unsigned numberOfSegments = framesToProcess / framesPerSegment; + + for (unsigned segment = 0; segment < numberOfSegments; ++segment) { + if (m_isFirstRender) { + // Snap exactly to desired position (first time and after reset()). + m_azimuthIndex = desiredAzimuthIndex; + m_isFirstRender = false; + } else { + // Each segment renders with an azimuth index closer by one to the desired azimuth index. + // Because inter-aural time delay is mostly a factor of azimuth and the delay is where the clicks and graininess come from, + // we don't bother smoothing the elevations. + int numberOfAzimuths = database->numberOfAzimuths(); + bool wrap = wrapDistance(m_azimuthIndex, desiredAzimuthIndex, numberOfAzimuths); + if (wrap) { + if (m_azimuthIndex < desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex - 1 + numberOfAzimuths) % numberOfAzimuths; + else if (m_azimuthIndex > desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex + 1) % numberOfAzimuths; + } else { + if (m_azimuthIndex < desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex + 1) % numberOfAzimuths; + else if (m_azimuthIndex > desiredAzimuthIndex) + m_azimuthIndex = (m_azimuthIndex - 1 + numberOfAzimuths) % numberOfAzimuths; + } + } + + // Get the HRTFKernels and interpolated delays. + HRTFKernel* kernelL; + HRTFKernel* kernelR; + double frameDelayL; + double frameDelayR; + database->getKernelsFromAzimuthElevation(azimuthBlend, m_azimuthIndex, elevation, kernelL, kernelR, frameDelayL, frameDelayR); + + ASSERT(kernelL && kernelR); + if (!kernelL || !kernelR) { + outputBus->zero(); + return; + } + + ASSERT(frameDelayL / sampleRate() < MaxDelayTimeSeconds && frameDelayR / sampleRate() < MaxDelayTimeSeconds); + + // Calculate the source and destination pointers for the current segment. + unsigned offset = segment * framesPerSegment; + float* segmentSourceL = sourceL + offset; + float* segmentSourceR = sourceR + offset; + float* segmentDestinationL = destinationL + offset; + float* segmentDestinationR = destinationR + offset; + + // First run through delay lines for inter-aural time difference. + m_delayLineL.setDelayFrames(frameDelayL); + m_delayLineR.setDelayFrames(frameDelayR); + m_delayLineL.process(segmentSourceL, segmentDestinationL, framesPerSegment); + m_delayLineR.process(segmentSourceR, segmentDestinationR, framesPerSegment); + + // Now do the convolutions in-place. + m_convolverL.process(kernelL->fftFrame(), segmentDestinationL, segmentDestinationL, framesPerSegment); + m_convolverR.process(kernelR->fftFrame(), segmentDestinationR, segmentDestinationR, framesPerSegment); + } +} + +} // namespace WebCore + +#endif // ENABLE(WEB_AUDIO) diff --git a/WebCore/platform/audio/HRTFPanner.h b/WebCore/platform/audio/HRTFPanner.h new file mode 100644 index 0000000..6c13d48 --- /dev/null +++ b/WebCore/platform/audio/HRTFPanner.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 HRTFPanner_h +#define HRTFPanner_h + +#include "DelayDSPKernel.h" +#include "FFTConvolver.h" +#include "Panner.h" + +namespace WebCore { + +class HRTFPanner : public Panner { +public: + explicit HRTFPanner(double sampleRate); + virtual ~HRTFPanner(); + + // Panner + virtual void pan(double azimuth, double elevation, AudioBus* inputBus, AudioBus* outputBus, size_t framesToProcess); + virtual void reset(); + + size_t fftSize() { return fftSizeForSampleRate(m_sampleRate); } + static size_t fftSizeForSampleRate(double sampleRate); + + double sampleRate() const { return m_sampleRate; } + +private: + // Given an azimuth angle in the range -180 -> +180, returns the corresponding azimuth index for the database, + // and azimuthBlend which is an interpolation value from 0 -> 1. + int calculateDesiredAzimuthIndexAndBlend(double azimuth, double& azimuthBlend); + + double m_sampleRate; + + // m_isFirstRender and m_azimuthIndex are used to avoid harshly changing from rendering at one azimuth angle to another angle very far away. + // Changing the azimuth gradually produces a smoother sound. + bool m_isFirstRender; + int m_azimuthIndex; + + FFTConvolver m_convolverL; + FFTConvolver m_convolverR; + DelayDSPKernel m_delayLineL; + DelayDSPKernel m_delayLineR; +}; + +} // namespace WebCore + +#endif // HRTFPanner_h diff --git a/WebCore/platform/brew/ClipboardBrew.cpp b/WebCore/platform/brew/ClipboardBrew.cpp index 10025d6..c0504bc 100644 --- a/WebCore/platform/brew/ClipboardBrew.cpp +++ b/WebCore/platform/brew/ClipboardBrew.cpp @@ -42,7 +42,7 @@ PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy, DragData*, Frame* } ClipboardBrew::ClipboardBrew(ClipboardAccessPolicy policy, ClipboardType clipboardType) - : Clipboard(clipboardType, isForDragging) + : Clipboard(policy, clipboardType) { } diff --git a/WebCore/platform/brew/FileSystemBrew.cpp b/WebCore/platform/brew/FileSystemBrew.cpp index a723e63..e207b55 100644 --- a/WebCore/platform/brew/FileSystemBrew.cpp +++ b/WebCore/platform/brew/FileSystemBrew.cpp @@ -30,24 +30,23 @@ #include "FileSystem.h" #include "NotImplemented.h" -#include "PlatformString.h" -#include "StringBuilder.h" #include <AEEAppGen.h> #include <AEEFile.h> #include <AEEStdLib.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> #include <wtf/RandomNumber.h> +#include <wtf/brew/RefPtrBrew.h> #include <wtf/brew/ShellBrew.h> #include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> namespace WebCore { bool getFileSize(const String& path, long long& result) { - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); FileInfo info; if (IFILEMGR_GetInfo(fileMgr.get(), path.utf8().data(), &info) == SUCCESS) { @@ -67,21 +66,21 @@ bool getFileModificationTime(const String& path, time_t& result) bool fileExists(const String& path) { - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); return (IFILEMGR_Test(fileMgr.get(), path.utf8().data()) == SUCCESS); } bool deleteFile(const String& path) { - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); return (IFILEMGR_Remove(fileMgr.get(), path.utf8().data()) == SUCCESS); } bool deleteEmptyDirectory(const String& path) { - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); return (IFILEMGR_RmDir(fileMgr.get(), path.utf8().data()) == SUCCESS); } @@ -110,7 +109,7 @@ CString fileSystemRepresentation(const String& path) static String canonicalPath(const String& path) { - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); // Get the buffer size required to resolve the path. int canonPathLen; @@ -163,7 +162,7 @@ static bool makeAllDirectories(IFileMgr* fileManager, const String& path) bool makeAllDirectories(const String& path) { - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); return makeAllDirectories(fileMgr.get(), canonicalPath(path)); } @@ -193,7 +192,7 @@ CString openTemporaryFile(const char* prefix, PlatformFileHandle& handle) // use "fs:/~/tmp" as our temporary directory. String tempPath("fs:/~/tmp"); - OwnPtr<IFileMgr> fileMgr = createInstance<IFileMgr>(AEECLSID_FILEMGR); + PlatformRefPtr<IFileMgr> fileMgr = createRefPtrInstance<IFileMgr>(AEECLSID_FILEMGR); // Create the temporary directory if it does not exist. IFILEMGR_MkDir(fileMgr.get(), tempPath.utf8().data()); diff --git a/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp b/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp index 3384a4f..fc6a753 100644 --- a/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp +++ b/WebCore/platform/brew/PlatformKeyboardEventBrew.cpp @@ -30,9 +30,14 @@ #include "WindowsKeyboardCodes.h" #include <AEEEvent.h> +#include <AEEIKeysMapping.h> +#include <AEEKeysMapping.bid> #include <AEEStdDef.h> #include <AEEVCodes.h> +#include <wtf/brew/RefPtrBrew.h> +#include <wtf/brew/ShellBrew.h> + namespace WebCore { static String keyIdentifierForBrewKeyCode(uint16 keyCode) @@ -143,23 +148,93 @@ static int windowsKeyCodeForKeyEvent(uint16 code) static inline String singleCharacterString(UChar c) { - return String(&c, 1); + UChar text; + + // Some key codes are not mapped to Unicode characters. Convert them to Unicode characters here. + switch (c) { + case AVK_0: + text = VK_0; + break; + case AVK_1: + text = VK_1; + break; + case AVK_2: + text = VK_2; + break; + case AVK_3: + text = VK_3; + break; + case AVK_4: + text = VK_4; + break; + case AVK_5: + text = VK_5; + break; + case AVK_6: + text = VK_6; + break; + case AVK_7: + text = VK_7; + break; + case AVK_8: + text = VK_8; + break; + case AVK_9: + text = VK_9; + break; + case AVK_STAR: + text = '*'; + break; + case AVK_POUND: + text = '#'; + break; + case AVK_FUNCTION1: + text = '='; + break; + case AVK_FUNCTION2: + text = '/'; + break; + case AVK_FUNCTION3: + text = '_'; + break; + case AVK_PUNC1: + text = ','; + break; + case AVK_PUNC2: + text = '.'; + break; + case AVK_SPACE: + text = VK_SPACE; + break; + default: + text = c; + break; + } + + return String(&text, 1); } PlatformKeyboardEvent::PlatformKeyboardEvent(AEEEvent event, uint16 code, uint32 modifiers, Type type) : m_type(type) - , m_text((type == Char) ? singleCharacterString(code) : String()) - , m_unmodifiedText((type == Char) ? singleCharacterString(code) : String()) - , m_keyIdentifier((type == Char) ? String() : keyIdentifierForBrewKeyCode(code)) - , m_autoRepeat(modifiers & KB_AUTOREPEAT) - , m_windowsVirtualKeyCode((type == RawKeyDown || type == KeyUp) ? windowsKeyCodeForKeyEvent(code) : 0) - , m_nativeVirtualKeyCode(code) , m_isKeypad(false) - , m_shiftKey(modifiers & (KB_LSHIFT | KB_RSHIFT)) - , m_ctrlKey(modifiers & (KB_LCTRL | KB_RCTRL)) - , m_altKey(modifiers & (KB_LALT | KB_RALT)) , m_metaKey(false) + , m_windowsVirtualKeyCode((type == RawKeyDown || type == KeyUp) ? windowsKeyCodeForKeyEvent(code) : 0) { + if ((m_type == Char) && modifiers) { + PlatformRefPtr<IKeysMapping> keysMapping = createRefPtrInstance<IKeysMapping>(AEECLSID_KeysMapping); + int result = IKeysMapping_GetMapping(keysMapping.get(), code, modifiers, reinterpret_cast<AECHAR*>(&code)); + if (result == AEE_SUCCESS) // Reset the modifier when key code is successfully mapped. + modifiers = 0; + } + + m_text = (type == Char) ? singleCharacterString(code) : String(); + m_unmodifiedText = (type == Char) ? singleCharacterString(code) : String(); + m_keyIdentifier = (type == Char) ? String() : keyIdentifierForBrewKeyCode(code); + m_nativeVirtualKeyCode = code; + m_autoRepeat = modifiers & KB_AUTOREPEAT; + m_shiftKey = modifiers & (KB_LSHIFT | KB_RSHIFT); + m_ctrlKey = modifiers & (KB_LCTRL | KB_RCTRL); + m_altKey = modifiers & (KB_LALT | KB_RALT); } void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool) diff --git a/WebCore/platform/brew/ScreenBrew.cpp b/WebCore/platform/brew/ScreenBrew.cpp index b141b34..53e53d0 100644 --- a/WebCore/platform/brew/ScreenBrew.cpp +++ b/WebCore/platform/brew/ScreenBrew.cpp @@ -36,6 +36,7 @@ #include <AEEAppGen.h> #include <AEEStdLib.h> +#include <wtf/brew/RefPtrBrew.h> namespace WebCore { @@ -48,17 +49,14 @@ struct DisplayInfo { static void getDisplayInfo(DisplayInfo& info) { IDisplay* display = reinterpret_cast<AEEApplet*>(GETAPPINSTANCE())->m_pIDisplay; - IBitmap* bitmap = IDisplay_GetDestination(display); - ASSERT(bitmap); + PlatformRefPtr<IBitmap> bitmap = adoptPlatformRef(IDisplay_GetDestination(display)); AEEBitmapInfo bitmapInfo; - IBitmap_GetInfo(bitmap, &bitmapInfo, sizeof(AEEBitmapInfo)); + IBitmap_GetInfo(bitmap.get(), &bitmapInfo, sizeof(AEEBitmapInfo)); info.width = bitmapInfo.cx; info.height = bitmapInfo.cy; info.depth = bitmapInfo.nDepth; - - IBitmap_Release(bitmap); } FloatRect screenRect(Widget*) diff --git a/WebCore/platform/chromium/ChromiumBridge.h b/WebCore/platform/chromium/ChromiumBridge.h index 121ec4b..74bad04 100644 --- a/WebCore/platform/chromium/ChromiumBridge.h +++ b/WebCore/platform/chromium/ChromiumBridge.h @@ -154,9 +154,10 @@ namespace WebCore { // Forms -------------------------------------------------------------- static void notifyFormStateChanged(const Document*); +#if !ENABLE(CLIENT_BASED_GEOLOCATION) // Geolocation -------------------------------------------------------- static GeolocationServiceBridge* createGeolocationServiceBridge(GeolocationServiceChromium*); - +#endif // Databases ---------------------------------------------------------- // Returns a handle to the DB file and ooptionally a handle to its containing directory static PlatformFileHandle databaseOpenFile(const String& vfsFleName, int desiredFlags); @@ -254,6 +255,45 @@ namespace WebCore { GraphicsContext*, int part, int state, int classicState, const IntRect&); static void paintProgressBar( GraphicsContext*, const IntRect& barRect, const IntRect& valueRect, bool determinate, double animatedSeconds); +#elif OS(LINUX) + // The UI part which is being accessed. + enum ThemePart { + PartScrollbarDownArrow, + PartScrollbarLeftArrow, + PartScrollbarRightArrow, + PartScrollbarUpArrow, + PartScrollbarHorizontalThumb, + PartScrollbarVerticalThumb, + PartScrollbarHoriztonalTrack, + PartScrollbarVerticalTrack, + }; + + // The current state of the associated Part. + enum ThemePaintState { + StateDisabled, + StateHover, + StateNormal, + StatePressed, + }; + + struct ScrollbarTrackExtraParams { + // The bounds of the entire track, as opposed to the part being painted. + int trackX; + int trackY; + int trackWidth; + int trackHeight; + }; + + union ThemePaintExtraParams { + ScrollbarTrackExtraParams scrollbarTrack; + }; + + // Gets the size of the given theme part. For variable sized items + // like vertical scrollbar thumbs, the width will be the required width of + // the track while the height will be the minimum height. + static IntSize getThemePartSize(ThemePart); + // Paint the given the given theme part. + static void paintThemePart(GraphicsContext*, ThemePart, ThemePaintState, const IntRect&, const ThemePaintExtraParams*); #endif // Trace Event -------------------------------------------------------- diff --git a/WebCore/platform/chromium/ChromiumDataObject.cpp b/WebCore/platform/chromium/ChromiumDataObject.cpp index 8352669..78b794b 100644 --- a/WebCore/platform/chromium/ChromiumDataObject.cpp +++ b/WebCore/platform/chromium/ChromiumDataObject.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -33,54 +33,187 @@ namespace WebCore { -void ChromiumDataObject::clear() +ChromiumDataObject::ChromiumDataObject(PassRefPtr<ChromiumDataObjectLegacy> data) + : RefCounted<ChromiumDataObject>() + , m_legacyData(data) +{ +} + +ChromiumDataObject::ChromiumDataObject(PassRefPtr<ReadableDataObject> data) + : RefCounted<ChromiumDataObject>() + , m_readableData(data) +{ +} + +ChromiumDataObject::ChromiumDataObject(PassRefPtr<WritableDataObject> data) + : RefCounted<ChromiumDataObject>() + , m_writableData(data) +{ +} + +PassRefPtr<ChromiumDataObject> ChromiumDataObject::create(PassRefPtr<ChromiumDataObjectLegacy> data) +{ + return adoptRef(new ChromiumDataObject(data)); +} + +PassRefPtr<ChromiumDataObject> ChromiumDataObject::createReadable(Clipboard::ClipboardType clipboardType) +{ + return adoptRef(new ChromiumDataObject(ReadableDataObject::create(clipboardType))); +} + +PassRefPtr<ChromiumDataObject> ChromiumDataObject::createWritable(Clipboard::ClipboardType clipboardType) +{ + return adoptRef(new ChromiumDataObject(WritableDataObject::create(clipboardType))); +} + +void ChromiumDataObject::clearData(const String& type) +{ + if (m_legacyData) + m_legacyData->clearData(type); + else + m_writableData->clearData(type); +} + +void ChromiumDataObject::clearAll() { - clearAllExceptFiles(); - filenames.clear(); + if (m_legacyData) + m_legacyData->clearAll(); + else + m_writableData->clearAll(); } void ChromiumDataObject::clearAllExceptFiles() { - url = KURL(); - urlTitle = ""; - uriList.clear(); - downloadMetadata = ""; - fileExtension = ""; - plainText = ""; - textHtml = ""; - htmlBaseUrl = KURL(); - fileContentFilename = ""; - if (fileContent) - fileContent->clear(); + if (m_legacyData) + m_legacyData->clearAllExceptFiles(); + else + m_writableData->clearAllExceptFiles(); } bool ChromiumDataObject::hasData() const { - return !url.isEmpty() - || !uriList.isEmpty() - || !downloadMetadata.isEmpty() - || !fileExtension.isEmpty() - || !filenames.isEmpty() - || !plainText.isEmpty() - || !textHtml.isEmpty() - || fileContent; + if (m_legacyData) + return m_legacyData->hasData(); + return m_readableData->hasData(); +} + +HashSet<String> ChromiumDataObject::types() const +{ + if (m_legacyData) + return m_legacyData->types(); + return m_readableData->types(); +} + +String ChromiumDataObject::getData(const String& type, bool& success) +{ + if (m_legacyData) + return m_legacyData->getData(type, success); + return m_readableData->getData(type, success); +} + +bool ChromiumDataObject::setData(const String& type, const String& data) +{ + if (m_legacyData) + return m_legacyData->setData(type, data); + return m_writableData->setData(type, data); +} + +String ChromiumDataObject::urlTitle() const +{ + if (m_legacyData) + return m_legacyData->urlTitle(); + return m_readableData->urlTitle(); +} + +void ChromiumDataObject::setUrlTitle(const String& urlTitle) +{ + if (m_legacyData) + m_legacyData->setUrlTitle(urlTitle); + else + m_writableData->setUrlTitle(urlTitle); +} + +KURL ChromiumDataObject::htmlBaseUrl() const +{ + if (m_legacyData) + return m_legacyData->htmlBaseUrl(); + return m_readableData->htmlBaseUrl(); +} + +void ChromiumDataObject::setHtmlBaseUrl(const KURL& url) +{ + if (m_legacyData) + m_legacyData->setHtmlBaseUrl(url); + else + m_writableData->setHtmlBaseUrl(url); +} + +bool ChromiumDataObject::containsFilenames() const +{ + if (m_legacyData) + return m_legacyData->containsFilenames(); + return m_readableData->containsFilenames(); +} + +Vector<String> ChromiumDataObject::filenames() const +{ + if (m_legacyData) + return m_legacyData->filenames(); + return m_readableData->filenames(); +} + +void ChromiumDataObject::setFilenames(const Vector<String>& filenames) +{ + if (m_legacyData) + m_legacyData->setFilenames(filenames); + else + ASSERT_NOT_REACHED(); +} + +String ChromiumDataObject::fileExtension() const +{ + if (m_legacyData) + return m_legacyData->fileExtension(); + return m_writableData->fileExtension(); +} + +void ChromiumDataObject::setFileExtension(const String& fileExtension) +{ + if (m_legacyData) + m_legacyData->setFileExtension(fileExtension); + else + m_writableData->setFileExtension(fileExtension); +} + +String ChromiumDataObject::fileContentFilename() const +{ + if (m_legacyData) + return m_legacyData->fileContentFilename(); + return m_writableData->fileContentFilename(); +} + +void ChromiumDataObject::setFileContentFilename(const String& fileContentFilename) +{ + if (m_legacyData) + m_legacyData->setFileContentFilename(fileContentFilename); + else + m_writableData->setFileContentFilename(fileContentFilename); +} + +PassRefPtr<SharedBuffer> ChromiumDataObject::fileContent() const +{ + if (m_legacyData) + return m_legacyData->fileContent(); + return m_writableData->fileContent(); +} + +void ChromiumDataObject::setFileContent(PassRefPtr<SharedBuffer> fileContent) +{ + if (m_legacyData) + m_legacyData->setFileContent(fileContent); + else + m_writableData->setFileContent(fileContent); +} + } -ChromiumDataObject::ChromiumDataObject(const ChromiumDataObject& other) - : RefCounted<ChromiumDataObject>() - , urlTitle(other.urlTitle) - , downloadMetadata(other.downloadMetadata) - , fileExtension(other.fileExtension) - , filenames(other.filenames) - , plainText(other.plainText) - , textHtml(other.textHtml) - , htmlBaseUrl(other.htmlBaseUrl) - , fileContentFilename(other.fileContentFilename) - , url(other.url) - , uriList(other.uriList) -{ - if (other.fileContent.get()) - fileContent = other.fileContent->copy(); -} - -} // namespace WebCore diff --git a/WebCore/platform/chromium/ChromiumDataObject.h b/WebCore/platform/chromium/ChromiumDataObject.h index af0a3fa..4aac5c9 100644 --- a/WebCore/platform/chromium/ChromiumDataObject.h +++ b/WebCore/platform/chromium/ChromiumDataObject.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * Copyright (c) 2010, Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are @@ -31,85 +31,58 @@ #ifndef ChromiumDataObject_h #define ChromiumDataObject_h -#include "KURL.h" -#include "PlatformString.h" -#include "SharedBuffer.h" +#include "ChromiumDataObjectLegacy.h" +#include "ReadableDataObject.h" +#include "WritableDataObject.h" #include <wtf/RefPtr.h> -#include <wtf/Vector.h> namespace WebCore { - // A data object for holding data that would be in a clipboard or moved - // during a drag-n-drop operation. This is the data that WebCore is aware - // of and is not specific to a platform. - class ChromiumDataObject : public RefCounted<ChromiumDataObject> { - public: - static PassRefPtr<ChromiumDataObject> create() - { - return adoptRef(new ChromiumDataObject); - } - - PassRefPtr<ChromiumDataObject> copy() const - { - return adoptRef(new ChromiumDataObject(*this)); - } - - void clear(); - void clearAllExceptFiles(); - bool hasData() const; - - void clearURL() - { - url = KURL(); - uriList.clear(); - urlTitle = ""; - } - - bool hasValidURL() const - { - return url.isValid(); - } - - KURL getURL() const - { - return url; - } - - void setURL(const KURL& newURL) - { - url = newURL; - uriList.clear(); - if (newURL.isEmpty()) - return; - uriList.append(newURL.string()); - } - - String urlTitle; - - String downloadMetadata; - - String fileExtension; - Vector<String> filenames; - - String plainText; - - String textHtml; - KURL htmlBaseUrl; - - String fileContentFilename; - RefPtr<SharedBuffer> fileContent; - - private: - // URL and uri-list are linked, so they should not be accessed individually. - KURL url; - Vector<String> uriList; - - ChromiumDataObject() {} - ChromiumDataObject(const ChromiumDataObject&); - - friend class ClipboardChromium; - }; - -} // namespace WebCore +class ChromiumDataObject : public RefCounted<ChromiumDataObject> { +public: + static PassRefPtr<ChromiumDataObject> create(PassRefPtr<ChromiumDataObjectLegacy> data); + static PassRefPtr<ChromiumDataObject> createReadable(Clipboard::ClipboardType); + static PassRefPtr<ChromiumDataObject> createWritable(Clipboard::ClipboardType); + + void clearData(const String& type); + void clearAll(); + void clearAllExceptFiles(); + + bool hasData() const; + + HashSet<String> types() const; + String getData(const String& type, bool& success); + bool setData(const String& type, const String& data); + + // Special handlers for URL/HTML metadata. + String urlTitle() const; + void setUrlTitle(const String& urlTitle); + KURL htmlBaseUrl() const; + void setHtmlBaseUrl(const KURL& url); + + // Used to handle files being dragged in. + bool containsFilenames() const; + Vector<String> filenames() const; + void setFilenames(const Vector<String>& filenames); + + // Used to handle files (images) being dragged out. + String fileExtension() const; + void setFileExtension(const String& fileExtension); + String fileContentFilename() const; + void setFileContentFilename(const String& fileContentFilename); + PassRefPtr<SharedBuffer> fileContent() const; + void setFileContent(PassRefPtr<SharedBuffer> fileContent); + +private: + ChromiumDataObject(PassRefPtr<ChromiumDataObjectLegacy>); + ChromiumDataObject(PassRefPtr<ReadableDataObject>); + ChromiumDataObject(PassRefPtr<WritableDataObject>); + + RefPtr<ChromiumDataObjectLegacy> m_legacyData; + RefPtr<ReadableDataObject> m_readableData; + RefPtr<WritableDataObject> m_writableData; +}; + +} #endif diff --git a/WebCore/platform/chromium/ChromiumDataObjectLegacy.cpp b/WebCore/platform/chromium/ChromiumDataObjectLegacy.cpp new file mode 100644 index 0000000..a2952c0 --- /dev/null +++ b/WebCore/platform/chromium/ChromiumDataObjectLegacy.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "ChromiumDataObjectLegacy.h" + +#include "ChromiumBridge.h" +#include "ClipboardMimeTypes.h" +#include "Pasteboard.h" + +namespace WebCore { + +// Per RFC 2483, the line separator for "text/..." MIME types is CR-LF. +static char const* const textMIMETypeLineSeparator = "\r\n"; + +void ChromiumDataObjectLegacy::clearData(const String& type) +{ + if (type == mimeTypeTextPlain) { + m_plainText = ""; + return; + } + + if (type == mimeTypeURL || type == mimeTypeTextURIList) { + m_uriList = ""; + m_url = KURL(); + m_urlTitle = ""; + return; + } + + if (type == mimeTypeTextHTML) { + m_textHtml = ""; + m_htmlBaseUrl = KURL(); + return; + } + + if (type == mimeTypeDownloadURL) { + m_downloadMetadata = ""; + return; + } +} + +void ChromiumDataObjectLegacy::clearAll() +{ + clearAllExceptFiles(); + m_filenames.clear(); +} + +void ChromiumDataObjectLegacy::clearAllExceptFiles() +{ + m_urlTitle = ""; + m_url = KURL(); + m_uriList = ""; + m_downloadMetadata = ""; + m_fileExtension = ""; + m_plainText = ""; + m_textHtml = ""; + m_htmlBaseUrl = KURL(); + m_fileContentFilename = ""; + if (m_fileContent) + m_fileContent->clear(); +} + +bool ChromiumDataObjectLegacy::hasData() const +{ + return !m_url.isEmpty() + || !m_uriList.isEmpty() + || !m_downloadMetadata.isEmpty() + || !m_fileExtension.isEmpty() + || !m_filenames.isEmpty() + || !m_plainText.isEmpty() + || !m_textHtml.isEmpty() + || m_fileContent; +} + +HashSet<String> ChromiumDataObjectLegacy::types() const +{ + // This is currently broken for pasteboard events, and always has been. + HashSet<String> results; + + if (!m_plainText.isEmpty()) { + results.add(mimeTypeText); + results.add(mimeTypeTextPlain); + } + + if (m_url.isValid()) + results.add(mimeTypeURL); + + if (!m_uriList.isEmpty()) + results.add(mimeTypeTextURIList); + + if (!m_textHtml.isEmpty()) + results.add(mimeTypeTextHTML); + + if (!m_filenames.isEmpty()) + results.add("Files"); + + return results; +} + +String ChromiumDataObjectLegacy::getData(const String& type, bool& success) +{ + if (type == mimeTypeTextPlain) { + if (m_clipboardType == Clipboard::CopyAndPaste) { + PasteboardPrivate::ClipboardBuffer buffer = + Pasteboard::generalPasteboard()->isSelectionMode() ? + PasteboardPrivate::SelectionBuffer : + PasteboardPrivate::StandardBuffer; + String text = ChromiumBridge::clipboardReadPlainText(buffer); + success = !text.isEmpty(); + return text; + } + success = !m_plainText.isEmpty(); + return m_plainText; + } + + if (type == mimeTypeURL) { + success = !m_url.isEmpty(); + return m_url.string(); + } + + if (type == mimeTypeTextURIList) { + success = !m_uriList.isEmpty(); + return m_uriList; + } + + if (type == mimeTypeTextHTML) { + if (m_clipboardType == Clipboard::CopyAndPaste) { + PasteboardPrivate::ClipboardBuffer buffer = + Pasteboard::generalPasteboard()->isSelectionMode() ? + PasteboardPrivate::SelectionBuffer : + PasteboardPrivate::StandardBuffer; + String htmlText; + KURL sourceURL; + ChromiumBridge::clipboardReadHTML(buffer, &htmlText, &sourceURL); + success = !htmlText.isEmpty(); + return htmlText; + } + success = !m_textHtml.isEmpty(); + return m_textHtml; + } + + if (type == mimeTypeDownloadURL) { + success = !m_downloadMetadata.isEmpty(); + return m_downloadMetadata; + } + + success = false; + return String(); +} + +bool ChromiumDataObjectLegacy::setData(const String& type, const String& data) +{ + if (type == mimeTypeTextPlain) { + m_plainText = data; + return true; + } + + if (type == mimeTypeURL || type == mimeTypeTextURIList) { + m_url = KURL(); + Vector<String> uriList; + // Line separator is \r\n per RFC 2483 - however, for compatibility + // reasons we also allow just \n here. + data.split('\n', uriList); + // Process the input and copy the first valid URL into the url member. + // In case no URLs can be found, subsequent calls to getData("URL") + // will get an empty string. This is in line with the HTML5 spec (see + // "The DragEvent and DataTransfer interfaces"). + for (size_t i = 0; i < uriList.size(); ++i) { + String& line = uriList[i]; + line = line.stripWhiteSpace(); + if (line.isEmpty()) + continue; + if (line[0] == '#') + continue; + KURL url = KURL(ParsedURLString, line); + if (url.isValid()) { + m_url = url; + break; + } + } + m_uriList = data; + return true; + } + + if (type == mimeTypeTextHTML) { + m_textHtml = data; + m_htmlBaseUrl = KURL(); + return true; + } + + if (type == mimeTypeDownloadURL) { + m_downloadMetadata = data; + return true; + } + + return false; +} + +ChromiumDataObjectLegacy::ChromiumDataObjectLegacy(Clipboard::ClipboardType clipboardType) + : m_clipboardType(clipboardType) +{ +} + +ChromiumDataObjectLegacy::ChromiumDataObjectLegacy(const ChromiumDataObjectLegacy& other) + : RefCounted<ChromiumDataObjectLegacy>() + , m_clipboardType(other.m_clipboardType) + , m_urlTitle(other.m_urlTitle) + , m_downloadMetadata(other.m_downloadMetadata) + , m_fileExtension(other.m_fileExtension) + , m_filenames(other.m_filenames) + , m_plainText(other.m_plainText) + , m_textHtml(other.m_textHtml) + , m_htmlBaseUrl(other.m_htmlBaseUrl) + , m_fileContentFilename(other.m_fileContentFilename) + , m_url(other.m_url) + , m_uriList(other.m_uriList) +{ + if (other.m_fileContent.get()) + m_fileContent = other.m_fileContent->copy(); +} + +} // namespace WebCore diff --git a/WebCore/platform/chromium/ChromiumDataObjectLegacy.h b/WebCore/platform/chromium/ChromiumDataObjectLegacy.h new file mode 100644 index 0000000..55be7aa --- /dev/null +++ b/WebCore/platform/chromium/ChromiumDataObjectLegacy.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2008, 2009, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ChromiumDataObjectLegacy_h +#define ChromiumDataObjectLegacy_h + +#include "Clipboard.h" +#include "KURL.h" +#include "PlatformString.h" +#include "SharedBuffer.h" +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> +#include <wtf/text/StringHash.h> + +namespace WebCore { + +// A data object for holding data that would be in a clipboard or moved +// during a drag-n-drop operation. This is the data that WebCore is aware +// of and is not specific to a platform. +class ChromiumDataObjectLegacy : public RefCounted<ChromiumDataObjectLegacy> { +public: + static PassRefPtr<ChromiumDataObjectLegacy> create(Clipboard::ClipboardType clipboardType) + { + return adoptRef(new ChromiumDataObjectLegacy(clipboardType)); + } + + PassRefPtr<ChromiumDataObjectLegacy> copy() const + { + return adoptRef(new ChromiumDataObjectLegacy(*this)); + } + + void clearData(const String& type); + void clearAll(); + void clearAllExceptFiles(); + + bool hasData() const; + + HashSet<String> types() const; + String getData(const String& type, bool& success); + bool setData(const String& type, const String& data); + + // Special handlers for URL/HTML metadata. + String urlTitle() const { return m_urlTitle; } + void setUrlTitle(const String& urlTitle) { m_urlTitle = urlTitle; } + KURL htmlBaseUrl() const { return m_htmlBaseUrl; } + void setHtmlBaseUrl(const KURL& url) { m_htmlBaseUrl = url; } + + // Used to handle files being dragged in. + bool containsFilenames() const { return !m_filenames.isEmpty(); } + Vector<String> filenames() const { return m_filenames; } + void setFilenames(const Vector<String>& filenames) { m_filenames = filenames; } + + // Used to handle files (images) being dragged out. + String fileExtension() const { return m_fileExtension; } + void setFileExtension(const String& fileExtension) { m_fileExtension = fileExtension; } + String fileContentFilename() const { return m_fileContentFilename; } + void setFileContentFilename(const String& fileContentFilename) { m_fileContentFilename = fileContentFilename; } + PassRefPtr<SharedBuffer> fileContent() const { return m_fileContent; } + void setFileContent(PassRefPtr<SharedBuffer> fileContent) { m_fileContent = fileContent; } + +private: + ChromiumDataObjectLegacy(Clipboard::ClipboardType); + ChromiumDataObjectLegacy(const ChromiumDataObjectLegacy&); + + Clipboard::ClipboardType m_clipboardType; + + String m_urlTitle; + + String m_downloadMetadata; + + String m_fileExtension; + Vector<String> m_filenames; + + String m_plainText; + + String m_textHtml; + KURL m_htmlBaseUrl; + + String m_fileContentFilename; + RefPtr<SharedBuffer> m_fileContent; + + // These two are linked. Setting m_url will set m_uriList to the same + // string value; setting m_uriList will cause its contents to be parsed + // according to RFC 2483 and the first URL found will be set in m_url. + KURL m_url; + String m_uriList; +}; + +} // namespace WebCore + +#endif diff --git a/WebCore/platform/chromium/ClipboardChromium.cpp b/WebCore/platform/chromium/ClipboardChromium.cpp index c2ec80c..46b4339 100644 --- a/WebCore/platform/chromium/ClipboardChromium.cpp +++ b/WebCore/platform/chromium/ClipboardChromium.cpp @@ -28,8 +28,8 @@ #include "ClipboardChromium.h" #include "CachedImage.h" -#include "ChromiumBridge.h" #include "ChromiumDataObject.h" +#include "ClipboardMimeTypes.h" #include "ClipboardUtilitiesChromium.h" #include "Document.h" #include "DragData.h" @@ -37,17 +37,18 @@ #include "FileList.h" #include "Frame.h" #include "HTMLNames.h" +#include "HTMLParserIdioms.h" #include "Image.h" #include "MIMETypeRegistry.h" #include "NamedNodeMap.h" -#include "Pasteboard.h" -#include "PlatformString.h" #include "Range.h" #include "RenderImage.h" #include "ScriptExecutionContext.h" -#include "StringBuilder.h" #include "markup.h" +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> + namespace WebCore { using namespace HTMLNames; @@ -55,40 +56,12 @@ using namespace HTMLNames; // We provide the IE clipboard types (URL and Text), and the clipboard types specified in the WHATWG Web Applications 1.0 draft // see http://www.whatwg.org/specs/web-apps/current-work/ Section 6.3.5.3 -enum ClipboardDataType { - ClipboardDataTypeNone, - - ClipboardDataTypeURL, - ClipboardDataTypeURIList, - ClipboardDataTypeDownloadURL, - ClipboardDataTypePlainText, - ClipboardDataTypeHTML, - - ClipboardDataTypeOther, -}; - -// Per RFC 2483, the line separator for "text/..." MIME types is CR-LF. -static char const* const textMIMETypeLineSeparator = "\r\n"; - -static ClipboardDataType clipboardTypeFromMIMEType(const String& type) +static String normalizeType(const String& type) { String cleanType = type.stripWhiteSpace().lower(); - if (cleanType.isEmpty()) - return ClipboardDataTypeNone; - - // Includes two special cases for IE compatibility. - if (cleanType == "text" || cleanType == "text/plain" || cleanType.startsWith("text/plain;")) - return ClipboardDataTypePlainText; - if (cleanType == "url") - return ClipboardDataTypeURL; - if (cleanType == "text/uri-list") - return ClipboardDataTypeURIList; - if (cleanType == "downloadurl") - return ClipboardDataTypeDownloadURL; - if (cleanType == "text/html") - return ClipboardDataTypeHTML; - - return ClipboardDataTypeOther; + if (cleanType == mimeTypeText || cleanType.startsWith(mimeTypeTextPlainEtc)) + return mimeTypeTextPlain; + return cleanType; } PassRefPtr<Clipboard> Clipboard::create(ClipboardAccessPolicy policy, DragData* dragData, Frame* frame) @@ -112,41 +85,22 @@ PassRefPtr<ClipboardChromium> ClipboardChromium::create(ClipboardType clipboardT return adoptRef(new ClipboardChromium(clipboardType, dataObject, policy, frame)); } +PassRefPtr<ClipboardChromium> ClipboardChromium::create(ClipboardType clipboardType, + ClipboardAccessPolicy policy, Frame* frame) +{ + RefPtr<ChromiumDataObject> dataObject = + policy == ClipboardWritable ? + ChromiumDataObject::createWritable(clipboardType) : + ChromiumDataObject::createReadable(clipboardType); + return adoptRef(new ClipboardChromium(clipboardType, dataObject, policy, frame)); +} + void ClipboardChromium::clearData(const String& type) { if (policy() != ClipboardWritable || !m_dataObject) return; - ClipboardDataType dataType = clipboardTypeFromMIMEType(type); - switch (dataType) { - case ClipboardDataTypeNone: - // If called with no arguments, everything except the file list must be cleared. - // (See HTML5 spec, "The DragEvent and DataTransfer interfaces") - m_dataObject->clearAllExceptFiles(); - return; - - case ClipboardDataTypeURL: - case ClipboardDataTypeURIList: - m_dataObject->clearURL(); - return; - - case ClipboardDataTypeDownloadURL: - m_dataObject->downloadMetadata = ""; - return; - - case ClipboardDataTypePlainText: - m_dataObject->plainText = ""; - return; - - case ClipboardDataTypeHTML: - m_dataObject->textHtml = ""; - m_dataObject->htmlBaseUrl = KURL(); - return; - - case ClipboardDataTypeOther: - // Not yet implemented, see https://bugs.webkit.org/show_bug.cgi?id=34410 - return; - } + m_dataObject->clearData(normalizeType(type)); ASSERT_NOT_REACHED(); } @@ -156,7 +110,7 @@ void ClipboardChromium::clearAllData() if (policy() != ClipboardWritable) return; - m_dataObject->clear(); + m_dataObject->clearAll(); } String ClipboardChromium::getData(const String& type, bool& success) const @@ -165,80 +119,7 @@ String ClipboardChromium::getData(const String& type, bool& success) const if (policy() != ClipboardReadable || !m_dataObject) return String(); - ClipboardDataType dataType = clipboardTypeFromMIMEType(type); - switch (dataType) { - case ClipboardDataTypeNone: - return String(); - - // Hack for URLs. file URLs are used internally for drop's default action, but we don't want - // to expose them to the page, so we filter them out here. - case ClipboardDataTypeURIList: - { - String text; - for (size_t i = 0; i < m_dataObject->uriList.size(); ++i) { - const String& uri = m_dataObject->uriList[i]; - if (protocolIs(uri, "file")) - continue; - ASSERT(!uri.isEmpty()); - if (!text.isEmpty()) - text.append(textMIMETypeLineSeparator); - // URIs have already been canonicalized, so copy everything verbatim. - text.append(uri); - } - success = !text.isEmpty(); - return text; - } - - case ClipboardDataTypeURL: - // In case of a previous setData('text/uri-list'), setData() has already - // prepared the 'url' member, so we can just retrieve it here. - if (!m_dataObject->url.isEmpty() && !m_dataObject->url.isLocalFile()) { - success = true; - return m_dataObject->url.string(); - } - return String(); - - case ClipboardDataTypeDownloadURL: - success = !m_dataObject->downloadMetadata.isEmpty(); - return m_dataObject->downloadMetadata; - - case ClipboardDataTypePlainText: - if (isForCopyAndPaste()) { - PasteboardPrivate::ClipboardBuffer buffer = - Pasteboard::generalPasteboard()->isSelectionMode() ? - PasteboardPrivate::SelectionBuffer : - PasteboardPrivate::StandardBuffer; - String text = ChromiumBridge::clipboardReadPlainText(buffer); - success = !text.isEmpty(); - return text; - } - // Otherwise return whatever is stored in plainText. - success = !m_dataObject->plainText.isEmpty(); - return m_dataObject->plainText; - - case ClipboardDataTypeHTML: - if (isForCopyAndPaste()) { - PasteboardPrivate::ClipboardBuffer buffer = - Pasteboard::generalPasteboard()->isSelectionMode() ? - PasteboardPrivate::SelectionBuffer : - PasteboardPrivate::StandardBuffer; - String htmlText; - KURL sourceURL; - ChromiumBridge::clipboardReadHTML(buffer, &htmlText, &sourceURL); - success = !htmlText.isEmpty(); - return htmlText; - } - // Otherwise return whatever is stored in textHtml. - success = !m_dataObject->textHtml.isEmpty(); - return m_dataObject->textHtml; - - case ClipboardDataTypeOther: - // not yet implemented, see https://bugs.webkit.org/show_bug.cgi?id=34410 - return String(); - } - - ASSERT_NOT_REACHED(); - return String(); + return m_dataObject->getData(normalizeType(type), success); } bool ClipboardChromium::setData(const String& type, const String& data) @@ -246,69 +127,7 @@ bool ClipboardChromium::setData(const String& type, const String& data) if (policy() != ClipboardWritable) return false; - ClipboardDataType dataType = clipboardTypeFromMIMEType(type); - switch (dataType) { - case ClipboardDataTypeNone: - return false; - - case ClipboardDataTypeURL: - // For setData(), "URL" must be treated as "text/uri-list". - // (See HTML5 spec, "The DragEvent and DataTransfer interfaces") - case ClipboardDataTypeURIList: - m_dataObject->url = KURL(); - // Line separator is \r\n per RFC 2483 - however, for compatibility reasons - // we also allow just \n here. - data.split('\n', m_dataObject->uriList); - // Strip white space on all lines, including trailing \r from above split. - // If this leaves a line empty, remove it completely. - // - // Also, copy the first valid URL into the 'url' member as well. - // In case no entry is a valid URL (i.e., remarks only), then we leave 'url' empty. - // I.e., in that case subsequent calls to getData("URL") will get an empty string. - // This is in line with the HTML5 spec (see "The DragEvent and DataTransfer interfaces"). - for (size_t i = 0; i < m_dataObject->uriList.size(); /**/) { - String& line = m_dataObject->uriList[i]; - line = line.stripWhiteSpace(); - if (line.isEmpty()) { - m_dataObject->uriList.remove(i); - continue; - } - ++i; - // Only copy the first valid URL. - if (m_dataObject->url.isValid()) - continue; - // Skip remarks. - if (line[0] == '#') - continue; - KURL url = KURL(ParsedURLString, line); - if (url.isValid()) - m_dataObject->url = url; - } - if (m_dataObject->uriList.isEmpty()) { - ASSERT(m_dataObject->url.isEmpty()); - return data.isEmpty(); - } - return true; - - case ClipboardDataTypeDownloadURL: - m_dataObject->downloadMetadata = data; - return true; - - case ClipboardDataTypePlainText: - m_dataObject->plainText = data; - return true; - - case ClipboardDataTypeHTML: - m_dataObject->textHtml = data; - return true; - - case ClipboardDataTypeOther: - // Not yet implemented, see https://bugs.webkit.org/show_bug.cgi?id=34410 - return false; - } - - ASSERT_NOT_REACHED(); - return false; + return m_dataObject->setData(normalizeType(type), data); } // extensions beyond IE's API @@ -321,33 +140,7 @@ HashSet<String> ClipboardChromium::types() const if (!m_dataObject) return results; - if (!m_dataObject->filenames.isEmpty()) - results.add("Files"); - - // Hack for URLs. file URLs are used internally for drop's default action, but we don't want - // to expose them to the page, so we filter them out here. - if (m_dataObject->url.isValid() && !m_dataObject->url.isLocalFile()) { - ASSERT(!m_dataObject->uriList.isEmpty()); - results.add("URL"); - } - - if (!m_dataObject->uriList.isEmpty()) { - // Verify that the URI list contains at least one non-file URL. - for (Vector<String>::const_iterator it = m_dataObject->uriList.begin(); - it != m_dataObject->uriList.end(); ++it) { - if (!protocolIs(*it, "file")) { - // Note that even if the URI list is not empty, it may not actually - // contain a valid URL, so we can't return "URL" here. - results.add("text/uri-list"); - break; - } - } - } - - if (!m_dataObject->plainText.isEmpty()) { - results.add("Text"); - results.add("text/plain"); - } + results = m_dataObject->types(); return results; } @@ -357,12 +150,13 @@ PassRefPtr<FileList> ClipboardChromium::files() const if (policy() != ClipboardReadable) return FileList::create(); - if (!m_dataObject || m_dataObject->filenames.isEmpty()) + if (!m_dataObject) return FileList::create(); + const Vector<String>& filenames = m_dataObject->filenames(); RefPtr<FileList> fileList = FileList::create(); - for (size_t i = 0; i < m_dataObject->filenames.size(); ++i) - fileList->append(File::create(m_dataObject->filenames.at(i))); + for (size_t i = 0; i < filenames.size(); ++i) + fileList->append(File::create(filenames.at(i))); return fileList.release(); } @@ -412,7 +206,7 @@ static String imageToMarkup(const String& url, Element* element) StringBuilder markup; markup.append("<img src=\""); markup.append(url); - markup.append("\""); + markup.append('"'); // Copy over attributes. If we are dragging an image, we expect things like // the id to be copied as well. NamedNodeMap* attrs = element->attributes(); @@ -421,13 +215,13 @@ static String imageToMarkup(const String& url, Element* element) Attribute* attr = attrs->attributeItem(i); if (attr->localName() == "src") continue; - markup.append(" "); + markup.append(' '); markup.append(attr->localName()); markup.append("=\""); String escapedAttr = attr->value(); escapedAttr.replace("\"", """); markup.append(escapedAttr); - markup.append("\""); + markup.append('"'); } markup.append("/>"); @@ -461,7 +255,7 @@ static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* elem if (!imageBuffer || !imageBuffer->size()) return; - dataObject->fileContent = imageBuffer; + dataObject->setFileContent(imageBuffer); // Determine the filename for the file contents of the image. We try to // use the alt tag if one exists, otherwise we fall back on the suggested @@ -469,13 +263,13 @@ static void writeImageToDataObject(ChromiumDataObject* dataObject, Element* elem // in the URL. String extension = MIMETypeRegistry::getPreferredExtensionForMIMEType( cachedImage->response().mimeType()); - dataObject->fileExtension = extension.isEmpty() ? "" : "." + extension; + dataObject->setFileExtension(extension.isEmpty() ? "" : "." + extension); String title = element->getAttribute(altAttr); if (title.isEmpty()) title = cachedImage->response().suggestedFilename(); title = ClipboardChromium::validateFileName(title, dataObject); - dataObject->fileContentFilename = title + dataObject->fileExtension; + dataObject->setFileContentFilename(title + dataObject->fileExtension()); } void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& url, const String& title, Frame* frame) @@ -483,8 +277,8 @@ void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& u if (!m_dataObject) return; - m_dataObject->url = url; - m_dataObject->urlTitle = title; + m_dataObject->setData(mimeTypeURL, url); + m_dataObject->setUrlTitle(title); // Write the bytes in the image to the file format. writeImageToDataObject(m_dataObject.get(), element, url); @@ -493,12 +287,12 @@ void ClipboardChromium::declareAndWriteDragImage(Element* element, const KURL& u if (imageURL.isEmpty()) return; - String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)); + String fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)); if (fullURL.isEmpty()) return; // Put img tag on the clipboard referencing the image - m_dataObject->textHtml = imageToMarkup(fullURL, element); + m_dataObject->setData(mimeTypeTextHTML, imageToMarkup(fullURL, element)); } void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*) @@ -506,17 +300,15 @@ void ClipboardChromium::writeURL(const KURL& url, const String& title, Frame*) if (!m_dataObject) return; ASSERT(!url.isEmpty()); - m_dataObject->url = url; - m_dataObject->urlTitle = title; - m_dataObject->uriList.clear(); - m_dataObject->uriList.append(url); + m_dataObject->setData(mimeTypeURL, url); + m_dataObject->setUrlTitle(title); // The URL can also be used as plain text. - m_dataObject->plainText = url.string(); + m_dataObject->setData(mimeTypeTextPlain, url.string()); // The URL can also be used as an HTML fragment. - m_dataObject->textHtml = urlToMarkup(url, title); - m_dataObject->htmlBaseUrl = url; + m_dataObject->setData(mimeTypeTextHTML, urlToMarkup(url, title)); + m_dataObject->setHtmlBaseUrl(url); } void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame) @@ -525,15 +317,15 @@ void ClipboardChromium::writeRange(Range* selectedRange, Frame* frame) if (!m_dataObject) return; - m_dataObject->textHtml = createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs); - m_dataObject->htmlBaseUrl = frame->document()->url(); + m_dataObject->setData(mimeTypeTextHTML, createMarkup(selectedRange, 0, AnnotateForInterchange, false, AbsoluteURLs)); + m_dataObject->setHtmlBaseUrl(frame->document()->url()); String str = frame->editor()->selectedText(); #if OS(WINDOWS) replaceNewlinesWithWindowsStyleNewlines(str); #endif replaceNBSPWithSpace(str); - m_dataObject->plainText = str; + m_dataObject->setData(mimeTypeTextPlain, str); } void ClipboardChromium::writePlainText(const String& text) @@ -546,7 +338,7 @@ void ClipboardChromium::writePlainText(const String& text) replaceNewlinesWithWindowsStyleNewlines(str); #endif replaceNBSPWithSpace(str); - m_dataObject->plainText = str; + m_dataObject->setData(mimeTypeTextPlain, str); } bool ClipboardChromium::hasData() diff --git a/WebCore/platform/chromium/ClipboardChromium.h b/WebCore/platform/chromium/ClipboardChromium.h index 14f59e9..1d69921 100644 --- a/WebCore/platform/chromium/ClipboardChromium.h +++ b/WebCore/platform/chromium/ClipboardChromium.h @@ -48,6 +48,9 @@ namespace WebCore { static PassRefPtr<ClipboardChromium> create( ClipboardType, PassRefPtr<ChromiumDataObject>, ClipboardAccessPolicy, Frame*); + static PassRefPtr<ClipboardChromium> create( + ClipboardType, ClipboardAccessPolicy, Frame*); + // Returns the file name (not including the extension). This removes any // invalid file system characters as well as making sure the // path + extension is not bigger than allowed by the file system. diff --git a/WebCore/platform/chromium/ClipboardChromiumWin.cpp b/WebCore/platform/chromium/ClipboardChromiumWin.cpp index b4a2c21..d9bbeb5 100644 --- a/WebCore/platform/chromium/ClipboardChromiumWin.cpp +++ b/WebCore/platform/chromium/ClipboardChromiumWin.cpp @@ -44,11 +44,11 @@ String ClipboardChromium::validateFileName(const String& title, ChromiumDataObje { // Remove any invalid file system characters. String result = title.removeCharacters(&isInvalidFileCharacter); - if (result.length() + dataObject->fileExtension.length() + 1 >= MAX_PATH) { - if (dataObject->fileExtension.length() + 1 >= MAX_PATH) - dataObject->fileExtension = ""; - if (result.length() + dataObject->fileExtension.length() + 1 >= MAX_PATH) - result = result.substring(0, MAX_PATH - dataObject->fileExtension.length() - 1); + if (result.length() + dataObject->fileExtension().length() + 1 >= MAX_PATH) { + if (dataObject->fileExtension().length() + 1 >= MAX_PATH) + dataObject->setFileExtension(""); + if (result.length() + dataObject->fileExtension().length() + 1 >= MAX_PATH) + result = result.substring(0, MAX_PATH - dataObject->fileExtension().length() - 1); } return result; } diff --git a/WebCore/platform/chromium/ClipboardMimeTypes.cpp b/WebCore/platform/chromium/ClipboardMimeTypes.cpp index b95744f8..f689e0e 100644 --- a/WebCore/platform/chromium/ClipboardMimeTypes.cpp +++ b/WebCore/platform/chromium/ClipboardMimeTypes.cpp @@ -33,8 +33,12 @@ namespace WebCore { -const char textPlainType[] = "text/plain"; -const char textHtmlType[] = "text/html"; -const char textUriListType[] = "text/uri-list"; +const char mimeTypeText[] = "text"; +const char mimeTypeTextPlain[] = "text/plain"; +const char mimeTypeTextPlainEtc[] = "text/plain;"; +const char mimeTypeTextHTML[] = "text/html"; +const char mimeTypeURL[] = "url"; +const char mimeTypeTextURIList[] = "text/uri-list"; +const char mimeTypeDownloadURL[] = "downloadurl"; } // namespace WebCore diff --git a/WebCore/platform/chromium/ClipboardMimeTypes.h b/WebCore/platform/chromium/ClipboardMimeTypes.h index d7468f2..9bdccfe 100644 --- a/WebCore/platform/chromium/ClipboardMimeTypes.h +++ b/WebCore/platform/chromium/ClipboardMimeTypes.h @@ -33,9 +33,13 @@ namespace WebCore { -extern const char textPlainType[]; -extern const char textHtmlType[]; -extern const char textUriListType[]; +extern const char mimeTypeText[]; +extern const char mimeTypeTextPlain[]; +extern const char mimeTypeTextPlainEtc[]; +extern const char mimeTypeTextHTML[]; +extern const char mimeTypeURL[]; +extern const char mimeTypeTextURIList[]; +extern const char mimeTypeDownloadURL[]; } // namespace WebCore diff --git a/WebCore/platform/chromium/DragDataChromium.cpp b/WebCore/platform/chromium/DragDataChromium.cpp index 674d34d..2b04523 100644 --- a/WebCore/platform/chromium/DragDataChromium.cpp +++ b/WebCore/platform/chromium/DragDataChromium.cpp @@ -32,6 +32,7 @@ #include "ChromiumBridge.h" #include "ChromiumDataObject.h" +#include "ClipboardMimeTypes.h" #include "DocumentFragment.h" #include "FileSystem.h" #include "KURL.h" @@ -43,50 +44,50 @@ namespace WebCore { static bool containsHTML(const ChromiumDataObject* dropData) { - return dropData->textHtml.length() > 0; + return dropData->types().contains(mimeTypeTextHTML); } bool DragData::containsURL(FilenameConversionPolicy filenamePolicy) const { - return !asURL(filenamePolicy).isEmpty(); + return m_platformDragData->types().contains(mimeTypeURL) + || (filenamePolicy == ConvertFilenames && m_platformDragData->containsFilenames()); } String DragData::asURL(FilenameConversionPolicy filenamePolicy, String* title) const { String url; - if (m_platformDragData->hasValidURL()) - url = m_platformDragData->getURL().string(); - else if (filenamePolicy == ConvertFilenames && !m_platformDragData->filenames.isEmpty()) { - String fileName = m_platformDragData->filenames[0]; - fileName = ChromiumBridge::getAbsolutePath(fileName); - url = ChromiumBridge::filePathToURL(fileName).string(); + if (m_platformDragData->types().contains(mimeTypeURL)) { + bool ignoredSuccess; + url = m_platformDragData->getData(mimeTypeURL, ignoredSuccess); + if (title) + *title = m_platformDragData->urlTitle(); + } else if (filenamePolicy == ConvertFilenames && containsFiles()) { + url = ChromiumBridge::filePathToURL(ChromiumBridge::getAbsolutePath(m_platformDragData->filenames()[0])); } - - // |title| can be NULL - if (title) - *title = m_platformDragData->urlTitle; return url; } bool DragData::containsFiles() const { - return !m_platformDragData->filenames.isEmpty(); + return m_platformDragData->containsFilenames(); } void DragData::asFilenames(Vector<String>& result) const { - for (size_t i = 0; i < m_platformDragData->filenames.size(); ++i) - result.append(m_platformDragData->filenames[i]); + const Vector<String>& filenames = m_platformDragData->filenames(); + for (size_t i = 0; i < filenames.size(); ++i) + result.append(filenames[i]); } bool DragData::containsPlainText() const { - return !m_platformDragData->plainText.isEmpty(); + return m_platformDragData->types().contains(mimeTypeTextPlain); } String DragData::asPlainText() const { - return m_platformDragData->plainText; + bool ignoredSuccess; + return m_platformDragData->getData(mimeTypeTextPlain, ignoredSuccess); } bool DragData::containsColor() const @@ -101,8 +102,8 @@ bool DragData::canSmartReplace() const // This is allowed whenever the drag data contains a 'range' (ie., // ClipboardWin::writeRange is called). For example, dragging a link // should not result in a space being added. - return !m_platformDragData->plainText.isEmpty() - && !m_platformDragData->hasValidURL(); + return m_platformDragData->types().contains(mimeTypeTextPlain) + && !m_platformDragData->types().contains(mimeTypeURL); } bool DragData::containsCompatibleContent() const @@ -134,9 +135,10 @@ PassRefPtr<DocumentFragment> DragData::asFragment(Document* doc) const // return fragment; } - if (!m_platformDragData->textHtml.isEmpty()) { + if (m_platformDragData->types().contains(mimeTypeTextHTML)) { + bool ignoredSuccess; RefPtr<DocumentFragment> fragment = createFragmentFromMarkup(doc, - m_platformDragData->textHtml, m_platformDragData->htmlBaseUrl, FragmentScriptingNotAllowed); + m_platformDragData->getData(mimeTypeTextHTML, ignoredSuccess), m_platformDragData->htmlBaseUrl(), FragmentScriptingNotAllowed); return fragment.release(); } diff --git a/WebCore/platform/chromium/GeolocationServiceChromium.cpp b/WebCore/platform/chromium/GeolocationServiceChromium.cpp index b64f5eb..c5e73ae 100644 --- a/WebCore/platform/chromium/GeolocationServiceChromium.cpp +++ b/WebCore/platform/chromium/GeolocationServiceChromium.cpp @@ -33,6 +33,10 @@ #include "ChromiumBridge.h" +#if ENABLE(CLIENT_BASED_GEOLOCATION) +#error "This file should not be compiled when ENABLE(CLIENT_BASED_GEOLOCATION)" +#endif // ENABLE(CLIENT_BASED_GEOLOCATION) + namespace WebCore { GeolocationServiceBridge::~GeolocationServiceBridge() diff --git a/WebCore/platform/chromium/Language.cpp b/WebCore/platform/chromium/LanguageChromium.cpp index 2612af4..69fe372 100644 --- a/WebCore/platform/chromium/Language.cpp +++ b/WebCore/platform/chromium/LanguageChromium.cpp @@ -36,7 +36,7 @@ namespace WebCore { -String defaultLanguage() +String platformDefaultLanguage() { static String computedDefaultLanguage; if (computedDefaultLanguage.isEmpty()) diff --git a/WebCore/platform/chromium/PasteboardChromium.cpp b/WebCore/platform/chromium/PasteboardChromium.cpp index ba69b00..907a4b9 100644 --- a/WebCore/platform/chromium/PasteboardChromium.cpp +++ b/WebCore/platform/chromium/PasteboardChromium.cpp @@ -38,6 +38,7 @@ #include "Element.h" #include "Frame.h" #include "HTMLNames.h" +#include "HTMLParserIdioms.h" #include "Image.h" #include "KURL.h" #include "markup.h" @@ -147,7 +148,7 @@ void Pasteboard::writeImage(Node* node, const KURL&, const String& title) Element* element = static_cast<Element*>(node); urlString = element->getAttribute(element->imageSourceAttributeName()); } - KURL url = urlString.isEmpty() ? KURL() : node->document()->completeURL(deprecatedParseURL(urlString)); + KURL url = urlString.isEmpty() ? KURL() : node->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(urlString)); ChromiumBridge::clipboardWriteImage(bitmap, url, title); } diff --git a/WebCore/platform/chromium/PopupMenuChromium.cpp b/WebCore/platform/chromium/PopupMenuChromium.cpp index 9c73a9c..3c807ba 100644 --- a/WebCore/platform/chromium/PopupMenuChromium.cpp +++ b/WebCore/platform/chromium/PopupMenuChromium.cpp @@ -513,7 +513,7 @@ void PopupContainer::paintBorder(GraphicsContext* gc, const IntRect& rect) Color borderColor(127, 157, 185); gc->setStrokeStyle(NoStroke); - gc->setFillColor(borderColor, DeviceColorSpace); + gc->setFillColor(borderColor, ColorSpaceDeviceRGB); int tx = x(); int ty = y(); @@ -857,7 +857,7 @@ void PopupListBox::paint(GraphicsContext* gc, const IntRect& rect) // Special case for an empty popup. if (numItems() == 0) - gc->fillRect(r, Color::white, DeviceColorSpace); + gc->fillRect(r, Color::white, ColorSpaceDeviceRGB); gc->restore(); @@ -894,23 +894,23 @@ void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd // If we have a transparent background, make sure it has a color to blend // against. if (backColor.hasAlpha()) - gc->fillRect(rowRect, Color::white, DeviceColorSpace); + gc->fillRect(rowRect, Color::white, ColorSpaceDeviceRGB); - gc->fillRect(rowRect, backColor, DeviceColorSpace); + gc->fillRect(rowRect, backColor, ColorSpaceDeviceRGB); if (m_popupClient->itemIsSeparator(rowIndex)) { IntRect separatorRect( rowRect.x() + separatorPadding, rowRect.y() + (rowRect.height() - separatorHeight) / 2, rowRect.width() - 2 * separatorPadding, separatorHeight); - gc->fillRect(separatorRect, textColor, DeviceColorSpace); + gc->fillRect(separatorRect, textColor, ColorSpaceDeviceRGB); return; } if (!style.isVisible()) return; - gc->setFillColor(textColor, DeviceColorSpace); + gc->setFillColor(textColor, ColorSpaceDeviceRGB); Font itemFont = getRowFont(rowIndex); // FIXME: http://crbug.com/19872 We should get the padding of individual option @@ -973,7 +973,7 @@ void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd remainingWidth -= (imageRect.width() + kLabelToIconPadding); imageRect.setX(rowRect.width() - rightPadding - imageRect.width()); imageRect.setY(rowRect.y() + (rowRect.height() - imageRect.height()) / 2); - gc->drawImage(image.get(), DeviceColorSpace, imageRect); + gc->drawImage(image.get(), ColorSpaceDeviceRGB, imageRect); } // Draw the the label if applicable. @@ -985,7 +985,7 @@ void PopupListBox::paintRow(GraphicsContext* gc, const IntRect& rect, int rowInd else textX = remainingWidth - itemFont.width(labelTextRun); - gc->setFillColor(labelColor, DeviceColorSpace); + gc->setFillColor(labelColor, ColorSpaceDeviceRGB); gc->drawBidiText(itemFont, labelTextRun, IntPoint(textX, textY)); } diff --git a/WebCore/platform/chromium/ReadableDataObject.cpp b/WebCore/platform/chromium/ReadableDataObject.cpp index 1a333bb..97c481b 100644 --- a/WebCore/platform/chromium/ReadableDataObject.cpp +++ b/WebCore/platform/chromium/ReadableDataObject.cpp @@ -33,21 +33,23 @@ #include "ChromiumBridge.h" #include "ClipboardMimeTypes.h" +#include "Pasteboard.h" +#include "PasteboardPrivate.h" namespace WebCore { -static PasteboardPrivate::ClipboardBuffer clipboardBuffer(bool isForDragging) +static PasteboardPrivate::ClipboardBuffer clipboardBuffer(Clipboard::ClipboardType clipboardType) { - return isForDragging ? PasteboardPrivate::DragBuffer : PasteboardPrivate::StandardBuffer; + return clipboardType == Clipboard::DragAndDrop ? PasteboardPrivate::DragBuffer : PasteboardPrivate::StandardBuffer; } -PassRefPtr<ReadableDataObject> ReadableDataObject::create(bool isForDragging) +PassRefPtr<ReadableDataObject> ReadableDataObject::create(Clipboard::ClipboardType clipboardType) { - return adoptRef(new ReadableDataObject(isForDragging)); + return adoptRef(new ReadableDataObject(clipboardType)); } -ReadableDataObject::ReadableDataObject(bool isForDragging) - : m_isForDragging(isForDragging) +ReadableDataObject::ReadableDataObject(Clipboard::ClipboardType clipboardType) + : m_clipboardType(clipboardType) , m_containsFilenames(false) , m_isTypeCacheInitialized(false) { @@ -69,34 +71,51 @@ String ReadableDataObject::getData(const String& type, bool& succeeded) const { String data; String ignoredMetadata; + // Since the Chromium-side bridge isn't complete yet, we special case this + // for copy-and-paste, since that code path no longer uses + // ChromiumDataObjectLegacy. + if (m_clipboardType == Clipboard::CopyAndPaste) { + if (type == mimeTypeTextPlain) { + PasteboardPrivate::ClipboardBuffer buffer = + Pasteboard::generalPasteboard()->isSelectionMode() ? + PasteboardPrivate::SelectionBuffer : + PasteboardPrivate::StandardBuffer; + data = ChromiumBridge::clipboardReadPlainText(buffer); + } else if (type == mimeTypeTextHTML) { + PasteboardPrivate::ClipboardBuffer buffer = + Pasteboard::generalPasteboard()->isSelectionMode() ? + PasteboardPrivate::SelectionBuffer : + PasteboardPrivate::StandardBuffer; + KURL ignoredSourceURL; + ChromiumBridge::clipboardReadHTML(buffer, &data, &ignoredSourceURL); + } + succeeded = !data.isEmpty(); + return data; + } succeeded = ChromiumBridge::clipboardReadData( - clipboardBuffer(m_isForDragging), type, data, ignoredMetadata); + clipboardBuffer(m_clipboardType), type, data, ignoredMetadata); return data; } -String ReadableDataObject::getURL(String* title) const +String ReadableDataObject::urlTitle() const { - String url; - String ignoredTitle; - if (!title) - title = &ignoredTitle; + String ignoredData; + String urlTitle; ChromiumBridge::clipboardReadData( - clipboardBuffer(m_isForDragging), textUriListType, url, *title); - return url; + clipboardBuffer(m_clipboardType), mimeTypeTextURIList, ignoredData, urlTitle); + return urlTitle; } -String ReadableDataObject::getHTML(String* baseURL) const +KURL ReadableDataObject::htmlBaseUrl() const { - String html; - String ignoredBaseURL; - if (!baseURL) - baseURL = &ignoredBaseURL; + String ignoredData; + String htmlBaseUrl; ChromiumBridge::clipboardReadData( - clipboardBuffer(m_isForDragging), textHtmlType, html, *baseURL); - return html; + clipboardBuffer(m_clipboardType), mimeTypeTextHTML, ignoredData, htmlBaseUrl); + return KURL(ParsedURLString, htmlBaseUrl); } -bool ReadableDataObject::hasFilenames() const +bool ReadableDataObject::containsFilenames() const { ensureTypeCacheInitialized(); return m_containsFilenames; @@ -104,7 +123,7 @@ bool ReadableDataObject::hasFilenames() const Vector<String> ReadableDataObject::filenames() const { - return ChromiumBridge::clipboardReadFilenames(clipboardBuffer(m_isForDragging)); + return ChromiumBridge::clipboardReadFilenames(clipboardBuffer(m_clipboardType)); } void ReadableDataObject::ensureTypeCacheInitialized() const @@ -113,7 +132,7 @@ void ReadableDataObject::ensureTypeCacheInitialized() const return; m_types = ChromiumBridge::clipboardReadAvailableTypes( - clipboardBuffer(m_isForDragging), &m_containsFilenames); + clipboardBuffer(m_clipboardType), &m_containsFilenames); m_isTypeCacheInitialized = true; } diff --git a/WebCore/platform/chromium/ReadableDataObject.h b/WebCore/platform/chromium/ReadableDataObject.h index 60f6d45..027e0ed 100644 --- a/WebCore/platform/chromium/ReadableDataObject.h +++ b/WebCore/platform/chromium/ReadableDataObject.h @@ -31,6 +31,7 @@ #ifndef ReadableDataObject_h #define ReadableDataObject_h +#include "Clipboard.h" #include "PlatformString.h" #include <wtf/HashSet.h> #include <wtf/RefCounted.h> @@ -43,26 +44,25 @@ namespace WebCore { // browser to the renderer. class ReadableDataObject : public RefCounted<ReadableDataObject> { public: - static PassRefPtr<ReadableDataObject> create(bool isForDragging); + static PassRefPtr<ReadableDataObject> create(Clipboard::ClipboardType); - virtual bool hasData() const; - virtual HashSet<String> types() const; - virtual String getData(const String& type, bool& succeeded) const; + bool hasData() const; + HashSet<String> types() const; + String getData(const String& type, bool& succeeded) const; - virtual String getURL(String* title) const; - virtual String getHTML(String* baseURL) const; + String urlTitle() const; + KURL htmlBaseUrl() const; - virtual bool hasFilenames() const; - virtual Vector<String> filenames() const; + bool containsFilenames() const; + Vector<String> filenames() const; private: - explicit ReadableDataObject(bool isForDragging); + explicit ReadableDataObject(Clipboard::ClipboardType); // This isn't always const... but most of the time it is. void ensureTypeCacheInitialized() const; - - bool m_isForDragging; + Clipboard::ClipboardType m_clipboardType; // To avoid making a lot of IPC calls for each drag event, we cache some // values in the renderer. diff --git a/WebCore/platform/chromium/ScrollbarThemeChromium.cpp b/WebCore/platform/chromium/ScrollbarThemeChromium.cpp index cf3ca6f..234d0ee 100644 --- a/WebCore/platform/chromium/ScrollbarThemeChromium.cpp +++ b/WebCore/platform/chromium/ScrollbarThemeChromium.cpp @@ -131,7 +131,7 @@ void ScrollbarThemeChromium::paintTickmarks(GraphicsContext* context, Scrollbar* const int yPos = rect.topLeft().y() + (rect.height() * percent); IntPoint tick(scrollbar->x(), yPos); - context->drawImage(dash.get(), DeviceColorSpace, tick); + context->drawImage(dash.get(), ColorSpaceDeviceRGB, tick); } context->restore(); diff --git a/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp b/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp index d8f2c79..46e6993 100644 --- a/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp +++ b/WebCore/platform/chromium/ScrollbarThemeChromiumLinux.cpp @@ -31,17 +31,12 @@ #include "config.h" #include "ScrollbarThemeChromiumLinux.h" -#include "PlatformContextSkia.h" +#include "ChromiumBridge.h" #include "PlatformMouseEvent.h" -#include "PlatformThemeChromiumGtk.h" #include "Scrollbar.h" -#include "TransformationMatrix.h" namespace WebCore { -static const int scrollbarThicknessValue = 15; -static const int buttonLength = 14; - ScrollbarTheme* ScrollbarTheme::nativeTheme() { static ScrollbarThemeChromiumLinux theme; @@ -50,136 +45,79 @@ ScrollbarTheme* ScrollbarTheme::nativeTheme() int ScrollbarThemeChromiumLinux::scrollbarThickness(ScrollbarControlSize controlSize) { - return scrollbarThicknessValue; -} - -static void drawVertLine(SkCanvas* canvas, int x, int y1, int y2, const SkPaint& paint) -{ - SkIRect skrect; - skrect.set(x, y1, x + 1, y2 + 1); - canvas->drawIRect(skrect, paint); -} - -static void drawHorizLine(SkCanvas* canvas, int x1, int x2, int y, const SkPaint& paint) -{ - SkIRect skrect; - skrect.set(x1, y, x2 + 1, y + 1); - canvas->drawIRect(skrect, paint); -} - -static void drawBox(SkCanvas* canvas, const IntRect& rect, const SkPaint& paint) -{ - const int right = rect.x() + rect.width() - 1; - const int bottom = rect.y() + rect.height() - 1; - drawHorizLine(canvas, rect.x(), right, rect.y(), paint); - drawVertLine(canvas, right, rect.y(), bottom, paint); - drawHorizLine(canvas, rect.x(), right, bottom, paint); - drawVertLine(canvas, rect.x(), rect.y(), bottom, paint); + // Horiz and Vert scrollbars are the same thickness. + IntSize scrollbarSize = ChromiumBridge::getThemePartSize(ChromiumBridge::PartScrollbarVerticalTrack); + return scrollbarSize.width(); } void ScrollbarThemeChromiumLinux::paintTrackPiece(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart partType) { - SkCanvas* const canvas = gc->platformContext()->canvas(); - SkPaint paint; - SkIRect skrect; - - skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); - SkScalar trackHSV[3]; - SkColorToHSV(PlatformThemeChromiumGtk::trackColor(), trackHSV); - paint.setColor(PlatformThemeChromiumGtk::saturateAndBrighten(trackHSV, 0, 0)); - canvas->drawIRect(skrect, paint); - - SkScalar thumbHSV[3]; - SkColorToHSV(PlatformThemeChromiumGtk::thumbInactiveColor(), - thumbHSV); - - paint.setColor(PlatformThemeChromiumGtk::outlineColor(trackHSV, thumbHSV)); - drawBox(canvas, rect, paint); + ChromiumBridge::ThemePaintState state = scrollbar->hoveredPart() == partType ? ChromiumBridge::StateHover : ChromiumBridge::StateNormal; + IntRect alignRect = trackRect(scrollbar, false); + ChromiumBridge::ThemePaintExtraParams extraParams; + extraParams.scrollbarTrack.trackX = alignRect.x(); + extraParams.scrollbarTrack.trackY = alignRect.y(); + extraParams.scrollbarTrack.trackWidth = alignRect.width(); + extraParams.scrollbarTrack.trackHeight = alignRect.height(); + ChromiumBridge::paintThemePart( + gc, + scrollbar->orientation() == HorizontalScrollbar ? ChromiumBridge::PartScrollbarHoriztonalTrack : ChromiumBridge::PartScrollbarVerticalTrack, + state, + rect, + &extraParams); } void ScrollbarThemeChromiumLinux::paintButton(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) { - PlatformThemeChromiumGtk::ArrowDirection direction; + ChromiumBridge::ThemePart paintPart; + ChromiumBridge::ThemePaintState state = ChromiumBridge::StateNormal; + bool checkMin = false; + bool checkMax = false; if (scrollbar->orientation() == HorizontalScrollbar) { - if (part == BackButtonStartPart) - direction = PlatformThemeChromiumGtk::West; - else - direction = PlatformThemeChromiumGtk::East; + if (part == BackButtonStartPart) { + paintPart = ChromiumBridge::PartScrollbarLeftArrow; + checkMin = true; + } else { + paintPart = ChromiumBridge::PartScrollbarRightArrow; + checkMax = true; + } } else { - if (part == BackButtonStartPart) - direction = PlatformThemeChromiumGtk::North; - else - direction = PlatformThemeChromiumGtk::South; + if (part == BackButtonStartPart) { + paintPart = ChromiumBridge::PartScrollbarUpArrow; + checkMin = true; + } else { + paintPart = ChromiumBridge::PartScrollbarDownArrow; + checkMax = true; + } } - - ControlStates states = 0; - // Determine if the button can be pressed. - if (((direction == PlatformThemeChromiumGtk::West || direction == PlatformThemeChromiumGtk::North) && scrollbar->currentPos()) - || ((direction == PlatformThemeChromiumGtk::East || direction == PlatformThemeChromiumGtk::South) && scrollbar->currentPos() != scrollbar->maximum())) - states |= EnabledState; - - if (states & EnabledState) { + if ((checkMin && (scrollbar->currentPos() <= 0)) + || (checkMax && scrollbar->currentPos() == scrollbar->maximum())) { + state = ChromiumBridge::StateDisabled; + } else { if (part == scrollbar->pressedPart()) - states |= PressedState; + state = ChromiumBridge::StatePressed; else if (part == scrollbar->hoveredPart()) - states |= HoverState; + state = ChromiumBridge::StateHover; } - - PlatformThemeChromiumGtk::paintArrowButton(gc, rect, direction, states); + ChromiumBridge::paintThemePart(gc, paintPart, state, rect, 0); } void ScrollbarThemeChromiumLinux::paintThumb(GraphicsContext* gc, Scrollbar* scrollbar, const IntRect& rect) { - const bool hovered = scrollbar->hoveredPart() == ThumbPart; - const int midx = rect.x() + rect.width() / 2; - const int midy = rect.y() + rect.height() / 2; - const bool vertical = scrollbar->orientation() == VerticalScrollbar; - SkCanvas* const canvas = gc->platformContext()->canvas(); - - SkScalar thumb[3]; - SkColorToHSV(hovered - ? PlatformThemeChromiumGtk::thumbActiveColor() - : PlatformThemeChromiumGtk::thumbInactiveColor(), - thumb); - - SkPaint paint; - paint.setColor(PlatformThemeChromiumGtk::saturateAndBrighten(thumb, 0, 0.02)); - - SkIRect skrect; - if (vertical) - skrect.set(rect.x(), rect.y(), midx + 1, rect.y() + rect.height()); - else - skrect.set(rect.x(), rect.y(), rect.x() + rect.width(), midy + 1); - - canvas->drawIRect(skrect, paint); - - paint.setColor(PlatformThemeChromiumGtk::saturateAndBrighten(thumb, 0, -0.02)); + ChromiumBridge::ThemePaintState state; - if (vertical) - skrect.set(midx + 1, rect.y(), rect.x() + rect.width(), rect.y() + rect.height()); + if (scrollbar->pressedPart() == ThumbPart) + state = ChromiumBridge::StatePressed; + else if (scrollbar->hoveredPart() == ThumbPart) + state = ChromiumBridge::StateHover; else - skrect.set(rect.x(), midy + 1, rect.x() + rect.width(), rect.y() + rect.height()); - - canvas->drawIRect(skrect, paint); - - SkScalar track[3]; - SkColorToHSV(PlatformThemeChromiumGtk::trackColor(), track); - paint.setColor(PlatformThemeChromiumGtk::outlineColor(track, thumb)); - drawBox(canvas, rect, paint); - - if (rect.height() > 10 && rect.width() > 10) { - const int grippyHalfWidth = 2; - const int interGrippyOffset = 3; - if (vertical) { - drawHorizLine(canvas, midx - grippyHalfWidth, midx + grippyHalfWidth, midy - interGrippyOffset, paint); - drawHorizLine(canvas, midx - grippyHalfWidth, midx + grippyHalfWidth, midy, paint); - drawHorizLine(canvas, midx - grippyHalfWidth, midx + grippyHalfWidth, midy + interGrippyOffset, paint); - } else { - drawVertLine(canvas, midx - interGrippyOffset, midy - grippyHalfWidth, midy + grippyHalfWidth, paint); - drawVertLine(canvas, midx, midy - grippyHalfWidth, midy + grippyHalfWidth, paint); - drawVertLine(canvas, midx + interGrippyOffset, midy - grippyHalfWidth, midy + grippyHalfWidth, paint); - } - } + state = ChromiumBridge::StateNormal; + ChromiumBridge::paintThemePart( + gc, + scrollbar->orientation() == HorizontalScrollbar ? ChromiumBridge::PartScrollbarHorizontalThumb : ChromiumBridge::PartScrollbarVerticalThumb, + state, + rect, + 0); } bool ScrollbarThemeChromiumLinux::shouldCenterOnThumb(Scrollbar*, const PlatformMouseEvent& evt) @@ -189,17 +127,25 @@ bool ScrollbarThemeChromiumLinux::shouldCenterOnThumb(Scrollbar*, const Platform IntSize ScrollbarThemeChromiumLinux::buttonSize(Scrollbar* scrollbar) { - if (scrollbar->orientation() == VerticalScrollbar) - return IntSize(scrollbarThicknessValue, buttonLength); + if (scrollbar->orientation() == VerticalScrollbar) { + IntSize size = ChromiumBridge::getThemePartSize(ChromiumBridge::PartScrollbarUpArrow); + return IntSize(size.width(), scrollbar->height() < 2 * size.height() ? scrollbar->height() / 2 : size.height()); + } // HorizontalScrollbar - return IntSize(buttonLength, scrollbarThicknessValue); + IntSize size = ChromiumBridge::getThemePartSize(ChromiumBridge::PartScrollbarLeftArrow); + return IntSize(scrollbar->width() < 2 * size.width() ? scrollbar->width() / 2 : size.width(), size.height()); } int ScrollbarThemeChromiumLinux::minimumThumbLength(Scrollbar* scrollbar) { - // This matches Firefox on Linux. - return 2 * scrollbarThickness(scrollbar->controlSize()); + if (scrollbar->orientation() == VerticalScrollbar) { + IntSize size = ChromiumBridge::getThemePartSize(ChromiumBridge::PartScrollbarVerticalThumb); + return size.height(); + } + + IntSize size = ChromiumBridge::getThemePartSize(ChromiumBridge::PartScrollbarHorizontalThumb); + return size.width(); } } // namespace WebCore diff --git a/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm b/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm index bafc96e..b47e998 100644 --- a/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm +++ b/WebCore/platform/chromium/ScrollbarThemeChromiumMac.mm @@ -417,8 +417,8 @@ bool ScrollbarThemeChromiumMac::paint(Scrollbar* scrollbar, GraphicsContext* con if (scrollbar->orientation() == VerticalScrollbar && tickmarks.size()) { drawingContext->save(); drawingContext->setShouldAntialias(false); - drawingContext->setStrokeColor(Color(0xCC, 0xAA, 0x00, 0xFF), DeviceColorSpace); - drawingContext->setFillColor(Color(0xFF, 0xDD, 0x00, 0xFF), DeviceColorSpace); + drawingContext->setStrokeColor(Color(0xCC, 0xAA, 0x00, 0xFF), ColorSpaceDeviceRGB); + drawingContext->setFillColor(Color(0xFF, 0xDD, 0x00, 0xFF), ColorSpaceDeviceRGB); IntRect thumbArea = trackRect(scrollbar, false); if (!canDrawDirectly) { @@ -453,7 +453,7 @@ bool ScrollbarThemeChromiumMac::paint(Scrollbar* scrollbar, GraphicsContext* con } if (!canDrawDirectly) - context->drawImageBuffer(imageBuffer.get(), DeviceColorSpace, scrollbar->frameRect().location()); + context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, scrollbar->frameRect().location()); return true; } diff --git a/WebCore/platform/chromium/ThemeChromiumMac.mm b/WebCore/platform/chromium/ThemeChromiumMac.mm index 68fd7b7..5e457f3 100644 --- a/WebCore/platform/chromium/ThemeChromiumMac.mm +++ b/WebCore/platform/chromium/ThemeChromiumMac.mm @@ -376,6 +376,7 @@ static void paintCheckbox(ControlStates states, GraphicsContext* context, const // Determine the width and height needed for the control and prepare the cell for painting. NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor); + LocalCurrentGraphicsContext localContext(context); context->save(); @@ -456,6 +457,7 @@ static void paintRadio(ControlStates states, GraphicsContext* context, const Int { // Determine the width and height needed for the control and prepare the cell for painting. NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor); + LocalCurrentGraphicsContext localContext(context); context->save(); diff --git a/WebCore/platform/chromium/WritableDataObject.cpp b/WebCore/platform/chromium/WritableDataObject.cpp index 6e7c283..7cbf42c 100644 --- a/WebCore/platform/chromium/WritableDataObject.cpp +++ b/WebCore/platform/chromium/WritableDataObject.cpp @@ -36,22 +36,22 @@ namespace WebCore { -PassRefPtr<WritableDataObject> WritableDataObject::create(bool isForDragging) +PassRefPtr<WritableDataObject> WritableDataObject::create(Clipboard::ClipboardType clipboardType) { - return adoptRef(new WritableDataObject(isForDragging)); + return adoptRef(new WritableDataObject(clipboardType)); } -WritableDataObject::WritableDataObject(bool isForDragging) - : m_isForDragging(isForDragging) +WritableDataObject::WritableDataObject(Clipboard::ClipboardType clipboardType) + : m_clipboardType(clipboardType) { } void WritableDataObject::clearData(const String& type) { m_dataMap.remove(type); - if (type == textUriListType) + if (type == mimeTypeTextURIList) m_urlTitle = ""; - else if (type == textHtmlType) + else if (type == mimeTypeTextHTML) m_htmlBaseURL = KURL(); } @@ -76,77 +76,19 @@ void WritableDataObject::clearAll() bool WritableDataObject::setData(const String& type, const String& data) { - if (!m_isForDragging) { + if (m_clipboardType == Clipboard::CopyAndPaste) { + // FIXME: This is currently unimplemented on the Chromium-side. This is + // "okay" for now since the original implementation didn't support it + // anyway. Going forward, this is something we'll need to fix though. ChromiumBridge::clipboardWriteData(type, data, ""); return true; } m_dataMap.set(type, data); - if (type == textUriListType) + if (type == mimeTypeTextURIList) m_urlTitle = ""; - else if (type == textHtmlType) + else if (type == mimeTypeTextHTML) m_htmlBaseURL = KURL(); return true; } -void WritableDataObject::setURL(const String& url, const String& title) -{ - setData(textUriListType, url); - m_urlTitle = title; -} - -void WritableDataObject::setHTML(const String& html, const KURL& baseURL) -{ - setData(textHtmlType, html); - m_htmlBaseURL = baseURL; -} - -// Accessors used when transferring drag data from the renderer to the -// browser. -HashMap<String, String> WritableDataObject::dataMap() const -{ - return m_dataMap; -} - -String WritableDataObject::urlTitle() const -{ - return m_urlTitle; -} - -KURL WritableDataObject::htmlBaseURL() const -{ - return m_htmlBaseURL; -} - -// Used for transferring file data from the renderer to the browser. -String WritableDataObject::fileExtension() const -{ - return m_fileExtension; -} - -String WritableDataObject::fileContentFilename() const -{ - return m_fileContentFilename; -} - -PassRefPtr<SharedBuffer> WritableDataObject::fileContent() const -{ - return m_fileContent; -} - -void WritableDataObject::setFileExtension(const String& fileExtension) -{ - m_fileExtension = fileExtension; -} - -void WritableDataObject::setFileContentFilename(const String& fileContentFilename) -{ - m_fileContentFilename = fileContentFilename; -} - -void WritableDataObject::setFileContent(PassRefPtr<SharedBuffer> fileContent) -{ - m_fileContent = fileContent; -} - - } // namespace WebCore diff --git a/WebCore/platform/chromium/WritableDataObject.h b/WebCore/platform/chromium/WritableDataObject.h index 71e2e26..c475d15 100644 --- a/WebCore/platform/chromium/WritableDataObject.h +++ b/WebCore/platform/chromium/WritableDataObject.h @@ -31,6 +31,7 @@ #ifndef WritableDataObject_h #define WritableDataObject_h +#include "Clipboard.h" #include "KURL.h" #include "PlatformString.h" #include "SharedBuffer.h" @@ -47,32 +48,32 @@ namespace WebCore { // atomically. class WritableDataObject : public RefCounted<WritableDataObject> { public: - static PassRefPtr<WritableDataObject> create(bool isForDragging); + static PassRefPtr<WritableDataObject> create(Clipboard::ClipboardType); - virtual void clearData(const String& type); - virtual void clearAllExceptFiles(); - virtual void clearAll(); - virtual bool setData(const String& type, const String& data); + void clearData(const String& type); + void clearAllExceptFiles(); + void clearAll(); + bool setData(const String& type, const String& data); - virtual void setURL(const String& url, const String& title); - virtual void setHTML(const String& html, const KURL& baseURL); + void setUrlTitle(const String& title) { m_urlTitle = title; } + void setHtmlBaseUrl(const KURL& baseURL) { m_htmlBaseURL = baseURL; } // Used for transferring drag data from the renderer to the browser. - virtual HashMap<String, String> dataMap() const; - virtual String urlTitle() const; - virtual KURL htmlBaseURL() const; + HashMap<String, String> dataMap() const { return m_dataMap; } + String urlTitle() const { return m_urlTitle; } + KURL htmlBaseURL() const { return m_htmlBaseURL; } - virtual String fileExtension() const; - virtual String fileContentFilename() const; - virtual PassRefPtr<SharedBuffer> fileContent() const; - virtual void setFileExtension(const String&); - virtual void setFileContentFilename(const String&); - virtual void setFileContent(PassRefPtr<SharedBuffer>); + String fileExtension() const { return m_fileExtension; } + String fileContentFilename() const { return m_fileContentFilename; } + PassRefPtr<SharedBuffer> fileContent() const { return m_fileContent; } + void setFileExtension(const String& fileExtension) { m_fileExtension = fileExtension; } + void setFileContentFilename(const String& fileContentFilename) { m_fileContentFilename = fileContentFilename; } + void setFileContent(PassRefPtr<SharedBuffer> fileContent) { m_fileContent = fileContent; } private: - explicit WritableDataObject(bool isForDragging); + explicit WritableDataObject(Clipboard::ClipboardType); - bool m_isForDragging; + Clipboard::ClipboardType m_clipboardType; HashMap<String, String> m_dataMap; String m_urlTitle; diff --git a/WebCore/platform/cocoa/KeyEventCocoa.mm b/WebCore/platform/cocoa/KeyEventCocoa.mm index cf83d93..a2b5c9c 100644 --- a/WebCore/platform/cocoa/KeyEventCocoa.mm +++ b/WebCore/platform/cocoa/KeyEventCocoa.mm @@ -471,6 +471,33 @@ int windowsKeyCodeForKeyCode(uint16_t keyCode) int windowsKeyCodeForCharCode(unichar charCode) { switch (charCode) { +#if PLATFORM(IOS) + case 8: case 0x7F: return VK_BACK; + case 9: return VK_TAB; + case 0xD: case 3: return VK_RETURN; + case 0x1B: return VK_ESCAPE; + case ' ': return VK_SPACE; + case NSHomeFunctionKey: return VK_HOME; + case NSEndFunctionKey: return VK_END; + case NSPageUpFunctionKey: return VK_PRIOR; + case NSPageDownFunctionKey: return VK_NEXT; + case NSUpArrowFunctionKey: return VK_UP; + case NSDownArrowFunctionKey: return VK_DOWN; + case NSLeftArrowFunctionKey: return VK_LEFT; + case NSRightArrowFunctionKey: return VK_RIGHT; + case NSDeleteFunctionKey: return VK_DELETE; + + case '0': case ')': return VK_0; + case '1': case '!': return VK_1; + case '2': case '@': return VK_2; + case '3': case '#': return VK_3; + case '4': case '$': return VK_4; + case '5': case '%': return VK_5; + case '6': case '^': return VK_6; + case '7': case '&': return VK_7; + case '8': case '*': return VK_8; + case '9': case '(': return VK_9; +#endif case 'a': case 'A': return VK_A; case 'b': case 'B': return VK_B; case 'c': case 'C': return VK_C; @@ -505,6 +532,30 @@ int windowsKeyCodeForCharCode(unichar charCode) case NSExecuteFunctionKey: return VK_EXECUTE; case NSPrintScreenFunctionKey: return VK_SNAPSHOT; case NSInsertFunctionKey: return VK_INSERT; +#if PLATFORM(IOS) + case NSHelpFunctionKey: return VK_INSERT; + + case NSF1FunctionKey: return VK_F1; + case NSF2FunctionKey: return VK_F2; + case NSF3FunctionKey: return VK_F3; + case NSF4FunctionKey: return VK_F4; + case NSF5FunctionKey: return VK_F5; + case NSF6FunctionKey: return VK_F6; + case NSF7FunctionKey: return VK_F7; + case NSF8FunctionKey: return VK_F8; + case NSF9FunctionKey: return VK_F9; + case NSF10FunctionKey: return VK_F10; + case NSF11FunctionKey: return VK_F11; + case NSF12FunctionKey: return VK_F12; + case NSF13FunctionKey: return VK_F13; + case NSF14FunctionKey: return VK_F14; + case NSF15FunctionKey: return VK_F15; + case NSF16FunctionKey: return VK_F16; + case NSF17FunctionKey: return VK_F17; + case NSF18FunctionKey: return VK_F18; + case NSF19FunctionKey: return VK_F19; + case NSF20FunctionKey: return VK_F20; +#endif case NSF21FunctionKey: return VK_F21; case NSF22FunctionKey: return VK_F22; case NSF23FunctionKey: return VK_F23; diff --git a/WebCore/platform/efl/Language.cpp b/WebCore/platform/efl/LanguageEfl.cpp index 1da7925..4b765a6 100644 --- a/WebCore/platform/efl/Language.cpp +++ b/WebCore/platform/efl/LanguageEfl.cpp @@ -34,7 +34,7 @@ namespace WebCore { -String defaultLanguage() +String platformDefaultLanguage() { notImplemented(); return String(); diff --git a/WebCore/platform/efl/PlatformKeyboardEventEfl.cpp b/WebCore/platform/efl/PlatformKeyboardEventEfl.cpp index fd84b15..2888b22 100644 --- a/WebCore/platform/efl/PlatformKeyboardEventEfl.cpp +++ b/WebCore/platform/efl/PlatformKeyboardEventEfl.cpp @@ -39,7 +39,7 @@ #include <Evas.h> #include <stdio.h> #include <wtf/HashMap.h> -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/text/StringHash.h> namespace WebCore { @@ -53,7 +53,7 @@ static WindowsKeyMap gWindowsKeyMap; static void createKeyMap() { for (unsigned int i = 1; i < 25; i++) { - String key = String::format("F%d", i); + String key = makeString('F', String::number(i)); gKeyMap.set(key, key); } gKeyMap.set("Alt_L", "Alt"); @@ -131,15 +131,15 @@ static void createWindowsKeyMap() gWindowsKeyMap.set("quotedbl", VK_OEM_7); // Alphabet - String alphabet = "abcdefghijklmnopqrstuvwxyz"; + const char* alphabet = "abcdefghijklmnopqrstuvwxyz"; for (unsigned int i = 0; i < 26; i++) { - String key = String::format("%c", alphabet[i]); + String key(alphabet + i, 1); gWindowsKeyMap.set(key, VK_A + i); } // Digits for (unsigned int i = 0; i < 10; i++) { - String key = String::format("%d", i); + String key = String::number(i); gWindowsKeyMap.set(key, VK_0 + i); } @@ -161,7 +161,7 @@ static void createWindowsKeyMap() // F_XX for (unsigned int i = 1; i < 25; i++) { - String key = String::format("F%d", i); + String key = makeString('F', String::number(i)); gWindowsKeyMap.set(key, VK_F1 + i); } } diff --git a/WebCore/platform/efl/RenderThemeEfl.cpp b/WebCore/platform/efl/RenderThemeEfl.cpp index 53997be..6076747 100644 --- a/WebCore/platform/efl/RenderThemeEfl.cpp +++ b/WebCore/platform/efl/RenderThemeEfl.cpp @@ -35,6 +35,7 @@ #include "Page.h" #include "RenderBox.h" #include "RenderObject.h" +#include "RenderProgress.h" #include "RenderSlider.h" #include <wtf/text/CString.h> @@ -296,6 +297,25 @@ bool RenderThemeEfl::paintThemePart(RenderObject* o, FormType type, const PaintI msg->val[0] = static_cast<float>(value) / static_cast<float>(max); msg->val[1] = 0.1; edje_object_message_send(ce->o, EDJE_MESSAGE_FLOAT_SET, 0, msg); +#if ENABLE(PROGRESS_TAG) + } else if (type == ProgressBar) { + RenderProgress* renderProgress = toRenderProgress(o); + Edje_Message_Float_Set* msg; + int max; + double value; + + msg = static_cast<Edje_Message_Float_Set*>(alloca(sizeof(Edje_Message_Float_Set) + sizeof(float))); + max = rect.width(); + value = renderProgress->position(); + + msg->count = 2; + if (o->style()->direction() == RTL) + msg->val[0] = (1.0 - value) * max; + else + msg->val[0] = 0; + msg->val[1] = value; + edje_object_message_send(ce->o, EDJE_MESSAGE_FLOAT_SET, 0, msg); +#endif } edje_object_calc_force(ce->o); @@ -562,6 +582,9 @@ const char* RenderThemeEfl::edjeGroupFromFormType(FormType type) const W("entry"), W("checkbox"), W("combo"), +#if ENABLE(PROGRESS_TAG) + W("progressbar"), +#endif W("search/field"), W("search/decoration"), W("search/results_button"), @@ -1008,4 +1031,16 @@ void RenderThemeEfl::systemFont(int propId, FontDescription& fontDescription) co fontDescription.setItalic(false); } +#if ENABLE(PROGRESS_TAG) +void RenderThemeEfl::adjustProgressBarStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + style->setBoxShadow(0); +} + +bool RenderThemeEfl::paintProgressBar(RenderObject* o, const PaintInfo& i, const IntRect& rect) +{ + return paintThemePart(o, ProgressBar, i, rect); +} +#endif + } diff --git a/WebCore/platform/efl/RenderThemeEfl.h b/WebCore/platform/efl/RenderThemeEfl.h index 478dfc5..087e2aa 100644 --- a/WebCore/platform/efl/RenderThemeEfl.h +++ b/WebCore/platform/efl/RenderThemeEfl.h @@ -45,6 +45,9 @@ enum FormType { // KEEP IN SYNC WITH edjeGroupFromFormType() TextField, CheckBox, ComboBox, +#if ENABLE(PROGRESS_TAG) + ProgressBar, +#endif SearchField, SearchFieldDecoration, SearchFieldResultsButton, @@ -145,6 +148,11 @@ public: static void setDefaultFontSize(int size); +#if ENABLE(PROGRESS_TAG) + virtual void adjustProgressBarStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintProgressBar(RenderObject*, const PaintInfo&, const IntRect&); +#endif + protected: static float defaultFontSize; diff --git a/WebCore/platform/efl/SharedBufferEfl.cpp b/WebCore/platform/efl/SharedBufferEfl.cpp index 1025e33..23769ab 100644 --- a/WebCore/platform/efl/SharedBufferEfl.cpp +++ b/WebCore/platform/efl/SharedBufferEfl.cpp @@ -56,7 +56,7 @@ PassRefPtr<SharedBuffer> SharedBuffer::createWithContentsOfFile(const String& fi result = SharedBuffer::create(); result->m_buffer.resize(fileStat.st_size); - if (result->m_buffer.size() != fileStat.st_size) { + if (result->m_buffer.size() != static_cast<unsigned>(fileStat.st_size)) { fclose(file); return 0; } diff --git a/WebCore/platform/efl/SharedTimerEfl.cpp b/WebCore/platform/efl/SharedTimerEfl.cpp index 437de64..990d0c8 100644 --- a/WebCore/platform/efl/SharedTimerEfl.cpp +++ b/WebCore/platform/efl/SharedTimerEfl.cpp @@ -30,43 +30,97 @@ #include "SharedTimer.h" #include <Ecore.h> +#include <pthread.h> #include <stdio.h> #include <wtf/Assertions.h> #include <wtf/CurrentTime.h> +#include <wtf/MainThread.h> namespace WebCore { -static Ecore_Timer *g_sharedTimer = 0; +static pthread_mutex_t timerMutex = PTHREAD_MUTEX_INITIALIZER; +static Ecore_Timer *_sharedTimer = 0; +static Ecore_Pipe *_pipe = 0; -static void (*g_timerFunction)(); +static void (*_timerFunction)(); + +struct timerOp { + double time; + unsigned char op; // 0 - add a timer; 1 - del a timer; +}; void setSharedTimerFiredFunction(void (*func)()) { - g_timerFunction = func; + _timerFunction = func; } static Eina_Bool timerEvent(void*) { - if (g_timerFunction) - g_timerFunction(); + if (_timerFunction) + _timerFunction(); + + _sharedTimer = 0; return ECORE_CALLBACK_CANCEL; } -void stopSharedTimer() +void processTimers(struct timerOp *tOp) { - if (g_sharedTimer) { - ecore_timer_del(g_sharedTimer); - g_sharedTimer = 0; + if (_sharedTimer) { + ecore_timer_del(_sharedTimer); + _sharedTimer = 0; } + + if (tOp->op == 1) + return; + + double interval = tOp->time - currentTime(); + + if (interval <= ecore_animator_frametime_get()) { + if (_timerFunction) + _timerFunction(); + return; + } + + _sharedTimer = ecore_timer_add(interval, timerEvent, 0); } -void setSharedTimerFireTime(double fireTime) +void pipeHandlerCb(void *data, void *buffer, unsigned int nbyte) +{ + ASSERT(nbyte == sizeof(struct timerOp)); + + struct timerOp *tOp = (struct timerOp *)buffer; + processTimers(tOp); +} + +void stopSharedTimer() { - double interval = fireTime - currentTime(); + struct timerOp tOp; + pthread_mutex_lock(&timerMutex); + if (!_pipe) + _pipe = ecore_pipe_add(pipeHandlerCb, 0); + pthread_mutex_unlock(&timerMutex); - stopSharedTimer(); - g_sharedTimer = ecore_timer_add(interval, timerEvent, 0); + tOp.op = 1; + ecore_pipe_write(_pipe, &tOp, sizeof(tOp)); +} + +void addNewTimer(double fireTime) +{ + struct timerOp tOp; + pthread_mutex_lock(&timerMutex); + if (!_pipe) + _pipe = ecore_pipe_add(pipeHandlerCb, 0); + pthread_mutex_unlock(&timerMutex); + + tOp.time = fireTime; + tOp.op = 0; + ecore_pipe_write(_pipe, &tOp, sizeof(tOp)); +} + +void setSharedTimerFireTime(double fireTime) +{ + addNewTimer(fireTime); } } diff --git a/WebCore/platform/efl/WidgetEfl.cpp b/WebCore/platform/efl/WidgetEfl.cpp index d82e99e..640e6e3 100644 --- a/WebCore/platform/efl/WidgetEfl.cpp +++ b/WebCore/platform/efl/WidgetEfl.cpp @@ -206,7 +206,7 @@ void Widget::setFocus(bool focused) void Widget::applyFallbackCursor() { -#if HAVE_ECORE_X +#ifdef HAVE_ECORE_X if (m_data->m_isUsingEcoreX && !m_data->m_cursorGroup.isNull()) { int shape = cursorStringMap.cursor(m_data->m_cursorGroup.utf8().data()); diff --git a/WebCore/platform/graphics/ANGLEWebKitBridge.cpp b/WebCore/platform/graphics/ANGLEWebKitBridge.cpp index 9a14820..64f19c4 100644 --- a/WebCore/platform/graphics/ANGLEWebKitBridge.cpp +++ b/WebCore/platform/graphics/ANGLEWebKitBridge.cpp @@ -44,8 +44,6 @@ ANGLEWebKitBridge::~ANGLEWebKitBridge() ShDestruct(m_fragmentCompiler); ShDestruct(m_vertexCompiler); } - - ShFinalize(); } bool ANGLEWebKitBridge::validateShaderSource(const char* shaderSource, ANGLEShaderType shaderType, String& translatedShaderSource, String& shaderValidationLog) diff --git a/WebCore/platform/graphics/Color.cpp b/WebCore/platform/graphics/Color.cpp index f28d51c..fa7346e 100644 --- a/WebCore/platform/graphics/Color.cpp +++ b/WebCore/platform/graphics/Color.cpp @@ -175,8 +175,7 @@ Color::Color(const char* name) m_valid = parseHexColor(&name[1], m_color); else { const NamedColor* foundColor = findColor(name, strlen(name)); - m_color = foundColor ? foundColor->RGBValue : 0; - m_color |= 0xFF000000; + m_color = foundColor ? foundColor->ARGBValue : 0; m_valid = foundColor; } } @@ -219,8 +218,7 @@ static inline const NamedColor* findNamedColor(const String& name) void Color::setNamedColor(const String& name) { const NamedColor* foundColor = findNamedColor(name); - m_color = foundColor ? foundColor->RGBValue : 0; - m_color |= 0xFF000000; + m_color = foundColor ? foundColor->ARGBValue : 0; m_valid = foundColor; } diff --git a/WebCore/platform/graphics/Color.h b/WebCore/platform/graphics/Color.h index 22a8a8f..276e69f 100644 --- a/WebCore/platform/graphics/Color.h +++ b/WebCore/platform/graphics/Color.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-6 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 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 @@ -31,6 +31,7 @@ #include <wtf/unicode/Unicode.h> #if PLATFORM(CG) +#include "ColorSpace.h" typedef struct CGColor* CGColorRef; #endif @@ -180,7 +181,7 @@ Color colorFromPremultipliedARGB(unsigned); unsigned premultipliedARGBFromColor(const Color&); #if PLATFORM(CG) -CGColorRef createCGColor(const Color&); +CGColorRef cachedCGColor(const Color&, ColorSpace); #endif } // namespace WebCore diff --git a/WebCore/platform/graphics/ColorSpace.h b/WebCore/platform/graphics/ColorSpace.h index 1bad58c..7622c47 100644 --- a/WebCore/platform/graphics/ColorSpace.h +++ b/WebCore/platform/graphics/ColorSpace.h @@ -28,7 +28,11 @@ namespace WebCore { - enum ColorSpace { DeviceColorSpace, sRGBColorSpace }; +enum ColorSpace { + ColorSpaceDeviceRGB, + ColorSpaceSRGB, + ColorSpaceLinearRGB +}; } // namespace WebCore diff --git a/WebCore/platform/graphics/ContextShadow.cpp b/WebCore/platform/graphics/ContextShadow.cpp index 1007962..87a1c5c 100644 --- a/WebCore/platform/graphics/ContextShadow.cpp +++ b/WebCore/platform/graphics/ContextShadow.cpp @@ -39,18 +39,20 @@ namespace WebCore { ContextShadow::ContextShadow() : m_type(NoShadow) - , m_blurRadius(0) + , m_blurDistance(0) + , m_layerContext(0) { } ContextShadow::ContextShadow(const Color& color, float radius, const FloatSize& offset) : m_color(color) - , m_blurRadius(round(radius)) + , m_blurDistance(round(radius)) , m_offset(offset) + , m_layerContext(0) { // See comments in http://webkit.org/b/40793, it seems sensible // to follow Skia's limit of 128 pixels of blur radius - m_blurRadius = min(m_blurRadius, 128); + m_blurDistance = min(m_blurDistance, 128); // The type of shadow is decided by the blur radius, shadow offset, and shadow color. if (!m_color.isValid() || !color.alpha()) { @@ -71,7 +73,7 @@ void ContextShadow::clear() { m_type = NoShadow; m_color = Color(); - m_blurRadius = 0; + m_blurDistance = 0; m_offset = FloatSize(); } @@ -85,8 +87,9 @@ static const int BlurSumShift = 15; void ContextShadow::blurLayerImage(unsigned char* imageData, const IntSize& size, int rowStride) { int channels[4] = { 3, 0, 1, 3 }; - int dmax = m_blurRadius >> 1; - int dmin = dmax - 1 + (m_blurRadius & 1); + int d = max(2, static_cast<int>(floorf((2 / 3.f) * m_blurDistance))); + int dmax = d >> 1; + int dmin = dmax - 1 + (d & 1); if (dmin < 0) dmin = 0; @@ -153,8 +156,8 @@ void ContextShadow::calculateLayerBoundingRect(const FloatRect& layerArea, const destinationRect.move(m_offset); m_layerRect = enclosingIntRect(destinationRect); - // We expand the area by the blur radius * 2 to give extra space for the blur transition. - m_layerRect.inflate((m_type == BlurShadow) ? ceil(m_blurRadius * 2) : 0); + // We expand the area by the blur radius to give extra space for the blur transition. + m_layerRect.inflate(m_type == BlurShadow ? m_blurDistance : 0); if (!clipRect.contains(m_layerRect)) { // No need to have the buffer larger than the clip. @@ -167,7 +170,7 @@ void ContextShadow::calculateLayerBoundingRect(const FloatRect& layerArea, const // We adjust again because the pixels at the borders are still // potentially affected by the pixels outside the buffer. if (m_type == BlurShadow) - m_layerRect.inflate((m_type == BlurShadow) ? ceil(m_blurRadius * 2) : 0); + m_layerRect.inflate(m_type == BlurShadow ? m_blurDistance : 0); } } diff --git a/WebCore/platform/graphics/ContextShadow.h b/WebCore/platform/graphics/ContextShadow.h index ede9336..0160f8a 100644 --- a/WebCore/platform/graphics/ContextShadow.h +++ b/WebCore/platform/graphics/ContextShadow.h @@ -31,6 +31,7 @@ #include "Color.h" #include "FloatRect.h" +#include "GraphicsContext.h" #include "IntRect.h" #include "RefCounted.h" @@ -65,7 +66,7 @@ public: } m_type; Color m_color; - int m_blurRadius; + int m_blurDistance; FloatSize m_offset; ContextShadow(); @@ -98,6 +99,9 @@ public: PlatformContext beginShadowLayer(PlatformContext, const FloatRect& layerArea); void endShadowLayer(PlatformContext); static void purgeScratchBuffer(); +#if PLATFORM(CAIRO) + void drawRectShadow(GraphicsContext* context, const IntRect& rect, const IntSize& topLeftRadius = IntSize(), const IntSize& topRightRadius = IntSize(), const IntSize& bottomLeftRadius = IntSize(), const IntSize& bottomRightRadius = IntSize()); +#endif #if PLATFORM(QT) QPointF offset() { return QPointF(m_offset.width(), m_offset.height()); } @@ -111,6 +115,9 @@ private: void blurLayerImage(unsigned char*, const IntSize& imageSize, int stride); void calculateLayerBoundingRect(const FloatRect& layerArea, const IntRect& clipRect); +#if PLATFORM(CAIRO) + void drawRectShadowWithoutTiling(PlatformContext context, const IntRect& shadowRect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius, float alpha); +#endif }; } // namespace WebCore diff --git a/WebCore/platform/graphics/FloatRect.h b/WebCore/platform/graphics/FloatRect.h index e387927..10ad838 100644 --- a/WebCore/platform/graphics/FloatRect.h +++ b/WebCore/platform/graphics/FloatRect.h @@ -59,6 +59,10 @@ class BRect; struct SkRect; #endif +#if PLATFORM(CAIRO) +typedef struct _cairo_rectangle cairo_rectangle_t; +#endif + namespace WebCore { #if PLATFORM(OPENVG) @@ -172,6 +176,11 @@ public: operator VGRect() const; #endif +#if PLATFORM(CAIRO) + FloatRect(const cairo_rectangle_t&); + operator cairo_rectangle_t() const; +#endif + private: FloatPoint m_location; FloatSize m_size; diff --git a/WebCore/platform/graphics/GraphicsContext.cpp b/WebCore/platform/graphics/GraphicsContext.cpp index 3dfdb20..ec15d26 100644 --- a/WebCore/platform/graphics/GraphicsContext.cpp +++ b/WebCore/platform/graphics/GraphicsContext.cpp @@ -219,7 +219,7 @@ void GraphicsContext::setStrokePattern(PassRefPtr<Pattern> pattern) { ASSERT(pattern); if (!pattern) { - setStrokeColor(Color::black, DeviceColorSpace); + setStrokeColor(Color::black, ColorSpaceDeviceRGB); return; } m_common->state.strokeGradient.clear(); @@ -231,7 +231,7 @@ void GraphicsContext::setFillPattern(PassRefPtr<Pattern> pattern) { ASSERT(pattern); if (!pattern) { - setFillColor(Color::black, DeviceColorSpace); + setFillColor(Color::black, ColorSpaceDeviceRGB); return; } m_common->state.fillGradient.clear(); @@ -243,7 +243,7 @@ void GraphicsContext::setStrokeGradient(PassRefPtr<Gradient> gradient) { ASSERT(gradient); if (!gradient) { - setStrokeColor(Color::black, DeviceColorSpace); + setStrokeColor(Color::black, ColorSpaceDeviceRGB); return; } m_common->state.strokeGradient = gradient; @@ -255,7 +255,7 @@ void GraphicsContext::setFillGradient(PassRefPtr<Gradient> gradient) { ASSERT(gradient); if (!gradient) { - setFillColor(Color::black, DeviceColorSpace); + setFillColor(Color::black, ColorSpaceDeviceRGB); return; } m_common->state.fillGradient = gradient; @@ -500,7 +500,9 @@ void GraphicsContext::addRoundedRectClip(const IntRect& rect, const IntSize& top if (paintingDisabled()) return; - clip(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); + Path path; + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + clip(path); } void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, @@ -509,7 +511,9 @@ void GraphicsContext::clipOutRoundedRect(const IntRect& rect, const IntSize& top if (paintingDisabled()) return; - clipOut(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); + Path path; + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + clipOut(path); } void GraphicsContext::clipToImageBuffer(ImageBuffer* buffer, const FloatRect& rect) diff --git a/WebCore/platform/graphics/GraphicsContext.h b/WebCore/platform/graphics/GraphicsContext.h index fd3bf2c..b1fa48a 100644 --- a/WebCore/platform/graphics/GraphicsContext.h +++ b/WebCore/platform/graphics/GraphicsContext.h @@ -41,6 +41,9 @@ typedef struct CGContext PlatformGraphicsContext; #elif PLATFORM(CAIRO) #include "PlatformRefPtrCairo.h" +namespace WebCore { +class ContextShadow; +} typedef struct _cairo PlatformGraphicsContext; #elif PLATFORM(OPENVG) namespace WebCore { @@ -333,11 +336,6 @@ namespace WebCore { void setAlpha(float); #if PLATFORM(CAIRO) float getAlpha(); - void applyPlatformShadow(PassOwnPtr<ImageBuffer> buffer, const Color& shadowColor, const FloatRect& shadowRect, float radius); - PlatformRefPtr<cairo_surface_t> createShadowMask(PassOwnPtr<ImageBuffer>, const FloatRect&, float radius); - - static void calculateShadowBufferDimensions(IntSize& shadowBufferSize, FloatRect& shadowRect, float& radius, const FloatRect& sourceRect, const FloatSize& shadowOffset, float shadowBlur); - void drawTiledShadow(const IntRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, ColorSpace colorSpace); #endif void setCompositeOperation(CompositeOperator); @@ -438,6 +436,9 @@ namespace WebCore { void pushTransparencyLayerInternal(const QRect &rect, qreal opacity, QPixmap& alphaMask); QPen pen(); static QPainter::CompositionMode toQtCompositionMode(CompositeOperator op); +#endif + +#if PLATFORM(QT) || PLATFORM(CAIRO) ContextShadow* contextShadow(); #endif diff --git a/WebCore/platform/graphics/GraphicsContext3D.cpp b/WebCore/platform/graphics/GraphicsContext3D.cpp index 86e9569..d2e9057 100644 --- a/WebCore/platform/graphics/GraphicsContext3D.cpp +++ b/WebCore/platform/graphics/GraphicsContext3D.cpp @@ -31,11 +31,27 @@ #include "GraphicsContext3D.h" #include "ArrayBufferView.h" +#include "DrawingBuffer.h" #include "Image.h" #include "ImageData.h" namespace WebCore { +static uint8_t convertColor16LittleTo8(uint16_t value) +{ + return value >> 8; +} + +static uint8_t convertColor16BigTo8(uint16_t value) +{ + return static_cast<uint8_t>(value & 0x00FF); +} + +PassRefPtr<DrawingBuffer> GraphicsContext3D::createDrawingBuffer(const IntSize& size) +{ + return DrawingBuffer::create(this, size); +} + bool GraphicsContext3D::computeFormatAndTypeParameters(unsigned int format, unsigned int type, unsigned long* componentsPerPixel, @@ -247,6 +263,22 @@ void unpackRGBA8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = source[3]; } +void unpackRGBA16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[0]); + destination[1] = convertColor16LittleTo8(source[1]); + destination[2] = convertColor16LittleTo8(source[2]); + destination[3] = convertColor16LittleTo8(source[3]); +} + +void unpackRGBA16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[0]); + destination[1] = convertColor16BigTo8(source[1]); + destination[2] = convertColor16BigTo8(source[2]); + destination[3] = convertColor16BigTo8(source[3]); +} + void unpackRGB8ToRGBA8(const uint8_t* source, uint8_t* destination) { destination[0] = source[0]; @@ -255,6 +287,22 @@ void unpackRGB8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = 0xFF; } +void unpackRGB16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[0]); + destination[1] = convertColor16LittleTo8(source[1]); + destination[2] = convertColor16LittleTo8(source[2]); + destination[3] = 0xFF; +} + +void unpackRGB16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[0]); + destination[1] = convertColor16BigTo8(source[1]); + destination[2] = convertColor16BigTo8(source[2]); + destination[3] = 0xFF; +} + void unpackARGB8ToRGBA8(const uint8_t* source, uint8_t* destination) { destination[0] = source[1]; @@ -263,6 +311,22 @@ void unpackARGB8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = source[0]; } +void unpackARGB16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[1]); + destination[1] = convertColor16LittleTo8(source[2]); + destination[2] = convertColor16LittleTo8(source[3]); + destination[3] = convertColor16LittleTo8(source[0]); +} + +void unpackARGB16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[1]); + destination[1] = convertColor16BigTo8(source[2]); + destination[2] = convertColor16BigTo8(source[3]); + destination[3] = convertColor16BigTo8(source[0]); +} + void unpackBGRA8ToRGBA8(const uint8_t* source, uint8_t* destination) { destination[0] = source[2]; @@ -271,6 +335,22 @@ void unpackBGRA8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = source[3]; } +void unpackBGRA16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[2]); + destination[1] = convertColor16LittleTo8(source[1]); + destination[2] = convertColor16LittleTo8(source[0]); + destination[3] = convertColor16LittleTo8(source[3]); +} + +void unpackBGRA16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[2]); + destination[1] = convertColor16BigTo8(source[1]); + destination[2] = convertColor16BigTo8(source[0]); + destination[3] = convertColor16BigTo8(source[3]); +} + void unpackRGBA5551ToRGBA8(const uint16_t* source, uint8_t* destination) { uint16_t packedValue = source[0]; @@ -316,6 +396,22 @@ void unpackR8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = 0xFF; } +void unpackR16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[0]); + destination[1] = convertColor16LittleTo8(source[0]); + destination[2] = convertColor16LittleTo8(source[0]); + destination[3] = 0xFF; +} + +void unpackR16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[0]); + destination[1] = convertColor16BigTo8(source[0]); + destination[2] = convertColor16BigTo8(source[0]); + destination[3] = 0xFF; +} + void unpackRA8ToRGBA8(const uint8_t* source, uint8_t* destination) { destination[0] = source[0]; @@ -324,6 +420,22 @@ void unpackRA8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = source[1]; } +void unpackRA16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[0]); + destination[1] = convertColor16LittleTo8(source[0]); + destination[2] = convertColor16LittleTo8(source[0]); + destination[3] = convertColor16LittleTo8(source[1]); +} + +void unpackRA16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[0]); + destination[1] = convertColor16BigTo8(source[0]); + destination[2] = convertColor16BigTo8(source[0]); + destination[3] = convertColor16BigTo8(source[1]); +} + void unpackAR8ToRGBA8(const uint8_t* source, uint8_t* destination) { destination[0] = source[1]; @@ -332,6 +444,22 @@ void unpackAR8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = source[0]; } +void unpackAR16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16LittleTo8(source[1]); + destination[1] = convertColor16LittleTo8(source[1]); + destination[2] = convertColor16LittleTo8(source[1]); + destination[3] = convertColor16LittleTo8(source[0]); +} + +void unpackAR16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = convertColor16BigTo8(source[1]); + destination[1] = convertColor16BigTo8(source[1]); + destination[2] = convertColor16BigTo8(source[1]); + destination[3] = convertColor16BigTo8(source[0]); +} + void unpackA8ToRGBA8(const uint8_t* source, uint8_t* destination) { destination[0] = 0x0; @@ -340,6 +468,22 @@ void unpackA8ToRGBA8(const uint8_t* source, uint8_t* destination) destination[3] = source[0]; } +void unpackA16LittleToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = 0x0; + destination[1] = 0x0; + destination[2] = 0x0; + destination[3] = convertColor16LittleTo8(source[0]); +} + +void unpackA16BigToRGBA8(const uint16_t* source, uint8_t* destination) +{ + destination[0] = 0x0; + destination[1] = 0x0; + destination[2] = 0x0; + destination[3] = convertColor16BigTo8(source[0]); +} + //---------------------------------------------------------------------- // Pixel packing routines. // @@ -602,6 +746,7 @@ static void computeIncrementParameters(unsigned int width, { unsigned int elementSizeInBytes = sizeof(SourceType); ASSERT(elementSizeInBytes <= bytesPerPixel); + ASSERT(!(bytesPerPixel % elementSizeInBytes)); unsigned int validRowBytes = width * bytesPerPixel; unsigned int totalRowBytes = validRowBytes; if (unpackAlignment) { @@ -644,24 +789,72 @@ static void doPacking(const void* sourceData, } break; } + case GraphicsContext3D::kSourceFormatRGBA16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 8, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackRGBA16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatRGBA16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 8, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackRGBA16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatRGB8: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint8_t>(width, 3, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); doUnpackingAndPacking<uint8_t, DestType, unpackRGB8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatRGB16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 6, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackRGB16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatRGB16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 6, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackRGB16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatARGB8: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint8_t>(width, 4, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); doUnpackingAndPacking<uint8_t, DestType, unpackARGB8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatARGB16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 8, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackARGB16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatARGB16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 8, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackARGB16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatBGRA8: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint8_t>(width, 4, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); doUnpackingAndPacking<uint8_t, DestType, unpackBGRA8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatBGRA16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 8, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackBGRA16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatBGRA16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 8, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackBGRA16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatRGBA5551: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint16_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); @@ -686,24 +879,72 @@ static void doPacking(const void* sourceData, doUnpackingAndPacking<uint8_t, DestType, unpackR8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatR16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackR16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatR16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackR16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatRA8: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint8_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); doUnpackingAndPacking<uint8_t, DestType, unpackRA8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatRA16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 4, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackRA16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatRA16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 4, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackRA16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatAR8: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint8_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); doUnpackingAndPacking<uint8_t, DestType, unpackAR8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatAR16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 4, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackAR16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatAR16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 4, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackAR16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } case GraphicsContext3D::kSourceFormatA8: { unsigned int sourceElementsPerPixel, sourceElementsPerRow; computeIncrementParameters<uint8_t>(width, 1, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); doUnpackingAndPacking<uint8_t, DestType, unpackA8ToRGBA8, packingFunc>(static_cast<const uint8_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); break; } + case GraphicsContext3D::kSourceFormatA16Little: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackA16LittleToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } + case GraphicsContext3D::kSourceFormatA16Big: { + unsigned int sourceElementsPerPixel, sourceElementsPerRow; + computeIncrementParameters<uint16_t>(width, 2, sourceUnpackAlignment, &sourceElementsPerPixel, &sourceElementsPerRow); + doUnpackingAndPacking<uint16_t, DestType, unpackA16BigToRGBA8, packingFunc>(static_cast<const uint16_t*>(sourceData), width, height, sourceElementsPerPixel, sourceElementsPerRow, destinationData, destinationElementsPerPixel); + break; + } } } diff --git a/WebCore/platform/graphics/GraphicsContext3D.h b/WebCore/platform/graphics/GraphicsContext3D.h index a12b1c4..d74c97c 100644 --- a/WebCore/platform/graphics/GraphicsContext3D.h +++ b/WebCore/platform/graphics/GraphicsContext3D.h @@ -26,16 +26,12 @@ #ifndef GraphicsContext3D_h #define GraphicsContext3D_h -#if PLATFORM(MAC) -#include "ANGLEWebKitBridge.h" -#endif #include "GraphicsLayer.h" #include "PlatformString.h" #include <wtf/HashMap.h> #include <wtf/ListHashSet.h> #include <wtf/Noncopyable.h> -#include <wtf/PassOwnPtr.h> // FIXME: Find a better way to avoid the name confliction for NO_ERROR. #if ((PLATFORM(CHROMIUM) && OS(WINDOWS)) || PLATFORM(WIN) || (PLATFORM(QT) && OS(WINDOWS))) @@ -43,6 +39,7 @@ #endif #if PLATFORM(MAC) +#include "ANGLEWebKitBridge.h" #include <OpenGL/OpenGL.h> #include <wtf/RetainPtr.h> @@ -78,6 +75,7 @@ const Platform3DObject NullPlatform3DObject = 0; namespace WebCore { class CanvasRenderingContext; +class DrawingBuffer; class HostWindow; class Image; class ImageData; @@ -94,7 +92,7 @@ struct ActiveInfo { class GraphicsContext3DInternal; #endif -class GraphicsContext3D : public Noncopyable { +class GraphicsContext3D : public RefCounted<GraphicsContext3D> { public: enum WebGLEnumType { DEPTH_BUFFER_BIT = 0x00000100, @@ -409,7 +407,13 @@ public: // GL_CHROMIUM_map_sub (enums inherited from GL_ARB_vertex_buffer_object) READ_ONLY = 0x88B8, - WRITE_ONLY = 0x88B9 + WRITE_ONLY = 0x88B9, + + // GL_ARB_robustness enums + GUILTY_CONTEXT_RESET_ARB = 0x8253, + INNOCENT_CONTEXT_RESET_ARB = 0x8254, + UNKNOWN_CONTEXT_RESET_ARB = 0x8255 + }; // Context creation attributes. @@ -435,8 +439,8 @@ public: RenderDirectlyToHostWindow }; - static PassOwnPtr<GraphicsContext3D> create(Attributes attrs, HostWindow* hostWindow, RenderStyle renderStyle = RenderOffscreen); - virtual ~GraphicsContext3D(); + static PassRefPtr<GraphicsContext3D> create(Attributes, HostWindow*, RenderStyle = RenderOffscreen); + ~GraphicsContext3D(); #if PLATFORM(MAC) PlatformGraphicsContext3D platformGraphicsContext3D() const { return m_contextObj; } @@ -463,6 +467,8 @@ public: #endif void makeContextCurrent(); + PassRefPtr<DrawingBuffer> createDrawingBuffer(const IntSize& = IntSize()); + #if PLATFORM(MAC) || PLATFORM(CHROMIUM) // With multisampling on, blit from multisampleFBO to regular FBO. void prepareTexture(); @@ -535,16 +541,32 @@ public: // by non-member functions. enum SourceDataFormat { kSourceFormatRGBA8, + kSourceFormatRGBA16Little, + kSourceFormatRGBA16Big, kSourceFormatRGB8, + kSourceFormatRGB16Little, + kSourceFormatRGB16Big, kSourceFormatBGRA8, + kSourceFormatBGRA16Little, + kSourceFormatBGRA16Big, kSourceFormatARGB8, + kSourceFormatARGB16Little, + kSourceFormatARGB16Big, kSourceFormatRGBA5551, kSourceFormatRGBA4444, kSourceFormatRGB565, kSourceFormatR8, + kSourceFormatR16Little, + kSourceFormatR16Big, kSourceFormatRA8, + kSourceFormatRA16Little, + kSourceFormatRA16Big, kSourceFormatAR8, - kSourceFormatA8 + kSourceFormatAR16Little, + kSourceFormatAR16Big, + kSourceFormatA8, + kSourceFormatA16Little, + kSourceFormatA16Big }; //---------------------------------------------------------------------- @@ -777,6 +799,9 @@ public: bool supportsCopyTextureToParentTextureCHROMIUM(); void copyTextureToParentTextureCHROMIUM(unsigned texture, unsigned parentTexture); + // GL_ARB_robustness + int getGraphicsResetStatusARB(); + private: GraphicsContext3D(Attributes attrs, HostWindow* hostWindow, bool renderDirectlyToHostWindow); diff --git a/WebCore/platform/graphics/GraphicsContextPrivate.h b/WebCore/platform/graphics/GraphicsContextPrivate.h index 903c7e3..985cad9 100644 --- a/WebCore/platform/graphics/GraphicsContextPrivate.h +++ b/WebCore/platform/graphics/GraphicsContextPrivate.h @@ -39,10 +39,10 @@ namespace WebCore { , strokeStyle(SolidStroke) , strokeThickness(0) , strokeColor(Color::black) - , strokeColorSpace(DeviceColorSpace) + , strokeColorSpace(ColorSpaceDeviceRGB) , fillRule(RULE_NONZERO) , fillColor(Color::black) - , fillColorSpace(DeviceColorSpace) + , fillColorSpace(ColorSpaceDeviceRGB) , shouldAntialias(true) , paintingDisabled(false) , shadowBlur(0) diff --git a/WebCore/platform/graphics/GraphicsLayer.cpp b/WebCore/platform/graphics/GraphicsLayer.cpp index 412f06d..08b79ab 100644 --- a/WebCore/platform/graphics/GraphicsLayer.cpp +++ b/WebCore/platform/graphics/GraphicsLayer.cpp @@ -33,6 +33,7 @@ #include "RotateTransformOperation.h" #include "TextStream.h" #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #ifndef NDEBUG #include <stdio.h> @@ -249,7 +250,7 @@ void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const I String GraphicsLayer::animationNameForTransition(AnimatedPropertyID property) { // | is not a valid identifier character in CSS, so this can never conflict with a keyframe identifier. - return String::format("-|transition%c-", property); + return makeString("-|transition", static_cast<char>(property), '-'); } void GraphicsLayer::suspendAnimations(double) diff --git a/WebCore/platform/graphics/ImageBuffer.cpp b/WebCore/platform/graphics/ImageBuffer.cpp index 71b8189..4a76be4 100644 --- a/WebCore/platform/graphics/ImageBuffer.cpp +++ b/WebCore/platform/graphics/ImageBuffer.cpp @@ -32,17 +32,17 @@ namespace WebCore { -void ImageBuffer::transformColorSpace(ImageColorSpace srcColorSpace, ImageColorSpace dstColorSpace) +void ImageBuffer::transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace) { if (srcColorSpace == dstColorSpace) return; // only sRGB <-> linearRGB are supported at the moment - if ((srcColorSpace != LinearRGB && srcColorSpace != DeviceRGB) || - (dstColorSpace != LinearRGB && dstColorSpace != DeviceRGB)) + if ((srcColorSpace != ColorSpaceLinearRGB && srcColorSpace != ColorSpaceDeviceRGB) + || (dstColorSpace != ColorSpaceLinearRGB && dstColorSpace != ColorSpaceDeviceRGB)) return; - if (dstColorSpace == LinearRGB) { + if (dstColorSpace == ColorSpaceLinearRGB) { if (m_linearRgbLUT.isEmpty()) { for (unsigned i = 0; i < 256; i++) { float color = i / 255.0f; @@ -53,7 +53,7 @@ void ImageBuffer::transformColorSpace(ImageColorSpace srcColorSpace, ImageColorS } } platformTransformColorSpace(m_linearRgbLUT); - } else if (dstColorSpace == DeviceRGB) { + } else if (dstColorSpace == ColorSpaceDeviceRGB) { if (m_deviceRgbLUT.isEmpty()) { for (unsigned i = 0; i < 256; i++) { float color = i / 255.0f; diff --git a/WebCore/platform/graphics/ImageBuffer.h b/WebCore/platform/graphics/ImageBuffer.h index 3c0508e..822a0ff 100644 --- a/WebCore/platform/graphics/ImageBuffer.h +++ b/WebCore/platform/graphics/ImageBuffer.h @@ -29,6 +29,7 @@ #define ImageBuffer_h #include "AffineTransform.h" +#include "ColorSpace.h" #include "FloatRect.h" #include "Image.h" #include "IntSize.h" @@ -44,13 +45,6 @@ namespace WebCore { class ImageData; class IntPoint; class IntRect; - - enum ImageColorSpace { - Unknown, - DeviceRGB, // like sRGB - GrayScale, - LinearRGB - }; enum Multiply { Premultiplied, @@ -60,7 +54,7 @@ namespace WebCore { class ImageBuffer : public Noncopyable { public: // Will return a null pointer on allocation failure. - static PassOwnPtr<ImageBuffer> create(const IntSize& size, ImageColorSpace colorSpace = DeviceRGB) + static PassOwnPtr<ImageBuffer> create(const IntSize& size, ColorSpace colorSpace = ColorSpaceDeviceRGB) { bool success = false; OwnPtr<ImageBuffer> buf(new ImageBuffer(size, colorSpace, success)); @@ -89,7 +83,7 @@ namespace WebCore { String toDataURL(const String& mimeType, const double* quality = 0) const; #if !PLATFORM(CG) AffineTransform baseTransform() const { return AffineTransform(); } - void transformColorSpace(ImageColorSpace srcColorSpace, ImageColorSpace dstColorSpace); + void transformColorSpace(ColorSpace srcColorSpace, ColorSpace dstColorSpace); void platformTransformColorSpace(const Vector<int>&); #else AffineTransform baseTransform() const { return AffineTransform(1, 0, 0, -1, 0, m_size.height()); } @@ -119,7 +113,7 @@ namespace WebCore { // This constructor will place its success into the given out-variable // so that create() knows when it should return failure. - ImageBuffer(const IntSize&, ImageColorSpace colorSpace, bool& success); + ImageBuffer(const IntSize&, ColorSpace colorSpace, bool& success); }; } // namespace WebCore diff --git a/WebCore/platform/graphics/IntRect.h b/WebCore/platform/graphics/IntRect.h index c5990ef..c8d7c71 100644 --- a/WebCore/platform/graphics/IntRect.h +++ b/WebCore/platform/graphics/IntRect.h @@ -56,6 +56,8 @@ typedef cairo_rectangle_int_t GdkRectangle; #endif #elif PLATFORM(HAIKU) class BRect; +#elif PLATFORM(EFL) +typedef struct _Eina_Rectangle Eina_Rectangle; #endif #if PLATFORM(WX) @@ -158,6 +160,9 @@ public: #elif PLATFORM(HAIKU) explicit IntRect(const BRect&); operator BRect() const; +#elif PLATFORM(EFL) + explicit IntRect(const Eina_Rectangle&); + operator Eina_Rectangle() const; #endif #if PLATFORM(CG) diff --git a/WebCore/platform/graphics/Path.cpp b/WebCore/platform/graphics/Path.cpp index 4e2de53..55760b1 100644 --- a/WebCore/platform/graphics/Path.cpp +++ b/WebCore/platform/graphics/Path.cpp @@ -35,8 +35,9 @@ #include <math.h> #include <wtf/MathExtras.h> -static const float QUARTER = 0.552f; // approximation of control point positions on a bezier - // to simulate a quarter of a circle. +// Approximation of control point positions on a bezier to simulate a quarter of a circle. +static const float gCircleControlPoint = 0.448f; + namespace WebCore { #if !PLATFORM(OPENVG) && !PLATFORM(QT) @@ -47,7 +48,7 @@ static void pathLengthApplierFunction(void* info, const PathElement* element) return; traversalState.m_previous = traversalState.m_current; FloatPoint* points = element->points; - float segmentLength = 0.0f; + float segmentLength = 0; switch (element->type) { case PathElementMoveToPoint: segmentLength = traversalState.moveTo(points[0]); @@ -75,10 +76,8 @@ static void pathLengthApplierFunction(void* info, const PathElement* element) if (traversalState.m_action == PathTraversalState::TraversalPointAtLength) { float offset = traversalState.m_desiredLength - traversalState.m_totalLength; traversalState.m_current.move(offset * cosf(slope), offset * sinf(slope)); - } else { - static const float rad2deg = 180.0f / piFloat; - traversalState.m_normalAngle = slope * rad2deg; - } + } else + traversalState.m_normalAngle = rad2deg(slope); traversalState.m_success = true; } @@ -110,167 +109,83 @@ float Path::normalAngleAtLength(float length, bool& ok) } #endif -Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& roundingRadii) +void Path::addRoundedRect(const FloatRect& rect, const FloatSize& roundingRadii) { - Path path; - float x = rectangle.x(); - float y = rectangle.y(); - float width = rectangle.width(); - float height = rectangle.height(); - float rx = roundingRadii.width(); - float ry = roundingRadii.height(); - if (width <= 0.0f || height <= 0.0f) - return path; + if (rect.isEmpty()) + return; + + FloatSize radius(roundingRadii); + FloatSize halfSize(rect.width() / 2, rect.height() / 2); - float dx = rx, dy = ry; // If rx is greater than half of the width of the rectangle // then set rx to half of the width (required in SVG spec) - if (dx > width * 0.5f) - dx = width * 0.5f; + if (radius.width() > halfSize.width()) + radius.setWidth(halfSize.width()); // If ry is greater than half of the height of the rectangle // then set ry to half of the height (required in SVG spec) - if (dy > height * 0.5f) - dy = height * 0.5f; + if (radius.height() > halfSize.height()) + radius.setHeight(halfSize.height()); - path.moveTo(FloatPoint(x + dx, y)); + moveTo(FloatPoint(rect.x() + radius.width(), rect.y())); - if (dx < width * 0.5f) - path.addLineTo(FloatPoint(x + width - rx, y)); + if (radius.width() < halfSize.width()) + addLineTo(FloatPoint(rect.x() + rect.width() - roundingRadii.width(), rect.y())); - path.addBezierCurveTo(FloatPoint(x + width - dx * (1 - QUARTER), y), FloatPoint(x + width, y + dy * (1 - QUARTER)), FloatPoint(x + width, y + dy)); + addBezierCurveTo(FloatPoint(rect.x() + rect.width() - radius.width() * gCircleControlPoint, rect.y()), FloatPoint(rect.x() + rect.width(), rect.y() + radius.height() * gCircleControlPoint), FloatPoint(rect.x() + rect.width(), rect.y() + radius.height())); - if (dy < height * 0.5) - path.addLineTo(FloatPoint(x + width, y + height - dy)); + if (radius.height() < halfSize.height()) + addLineTo(FloatPoint(rect.x() + rect.width(), rect.y() + rect.height() - radius.height())); - path.addBezierCurveTo(FloatPoint(x + width, y + height - dy * (1 - QUARTER)), FloatPoint(x + width - dx * (1 - QUARTER), y + height), FloatPoint(x + width - dx, y + height)); + addBezierCurveTo(FloatPoint(rect.x() + rect.width(), rect.y() + rect.height() - radius.height() * gCircleControlPoint), FloatPoint(rect.x() + rect.width() - radius.width() * gCircleControlPoint, rect.y() + rect.height()), FloatPoint(rect.x() + rect.width() - radius.width(), rect.y() + rect.height())); - if (dx < width * 0.5) - path.addLineTo(FloatPoint(x + dx, y + height)); + if (radius.width() < halfSize.width()) + addLineTo(FloatPoint(rect.x() + radius.width(), rect.y() + rect.height())); - path.addBezierCurveTo(FloatPoint(x + dx * (1 - QUARTER), y + height), FloatPoint(x, y + height - dy * (1 - QUARTER)), FloatPoint(x, y + height - dy)); + addBezierCurveTo(FloatPoint(rect.x() + radius.width() * gCircleControlPoint, rect.y() + rect.height()), FloatPoint(rect.x(), rect.y() + rect.height() - radius.height() * gCircleControlPoint), FloatPoint(rect.x(), rect.y() + rect.height() - radius.height())); - if (dy < height * 0.5) - path.addLineTo(FloatPoint(x, y + dy)); + if (radius.height() < halfSize.height()) + addLineTo(FloatPoint(rect.x(), rect.y() + radius.height())); - path.addBezierCurveTo(FloatPoint(x, y + dy * (1 - QUARTER)), FloatPoint(x + dx * (1 - QUARTER), y), FloatPoint(x + dx, y)); + addBezierCurveTo(FloatPoint(rect.x(), rect.y() + radius.height() * gCircleControlPoint), FloatPoint(rect.x() + radius.width() * gCircleControlPoint, rect.y()), FloatPoint(rect.x() + radius.width(), rect.y())); - path.closeSubpath(); - - return path; + closeSubpath(); } -Path Path::createRoundedRectangle(const FloatRect& rectangle, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) +void Path::addRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius) { - Path path; - - float width = rectangle.width(); - float height = rectangle.height(); - if (width <= 0.0 || height <= 0.0) - return path; + if (rect.isEmpty()) + return; - if (width < topLeftRadius.width() + topRightRadius.width() - || width < bottomLeftRadius.width() + bottomRightRadius.width() - || height < topLeftRadius.height() + bottomLeftRadius.height() - || height < topRightRadius.height() + bottomRightRadius.height()) + if (rect.width() < topLeftRadius.width() + topRightRadius.width() + || rect.width() < bottomLeftRadius.width() + bottomRightRadius.width() + || rect.height() < topLeftRadius.height() + bottomLeftRadius.height() + || rect.height() < topRightRadius.height() + bottomRightRadius.height()) { // If all the radii cannot be accommodated, return a rect. - return createRectangle(rectangle); - - float x = rectangle.x(); - float y = rectangle.y(); - - path.moveTo(FloatPoint(x + topLeftRadius.width(), y)); - - path.addLineTo(FloatPoint(x + width - topRightRadius.width(), y)); - - path.addBezierCurveTo(FloatPoint(x + width - topRightRadius.width() * (1 - QUARTER), y), FloatPoint(x + width, y + topRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width, y + topRightRadius.height())); - - path.addLineTo(FloatPoint(x + width, y + height - bottomRightRadius.height())); - - path.addBezierCurveTo(FloatPoint(x + width, y + height - bottomRightRadius.height() * (1 - QUARTER)), FloatPoint(x + width - bottomRightRadius.width() * (1 - QUARTER), y + height), FloatPoint(x + width - bottomRightRadius.width(), y + height)); - - path.addLineTo(FloatPoint(x + bottomLeftRadius.width(), y + height)); - - path.addBezierCurveTo(FloatPoint(x + bottomLeftRadius.width() * (1 - QUARTER), y + height), FloatPoint(x, y + height - bottomLeftRadius.height() * (1 - QUARTER)), FloatPoint(x, y + height - bottomLeftRadius.height())); - - path.addLineTo(FloatPoint(x, y + topLeftRadius.height())); - - path.addBezierCurveTo(FloatPoint(x, y + topLeftRadius.height() * (1 - QUARTER)), FloatPoint(x + topLeftRadius.width() * (1 - QUARTER), y), FloatPoint(x + topLeftRadius.width(), y)); - - path.closeSubpath(); - - return path; -} - -Path Path::createRectangle(const FloatRect& rectangle) -{ - Path path; - float x = rectangle.x(); - float y = rectangle.y(); - float width = rectangle.width(); - float height = rectangle.height(); - if (width <= 0.0f || height <= 0.0f) - return path; - - path.moveTo(FloatPoint(x, y)); - path.addLineTo(FloatPoint(x + width, y)); - path.addLineTo(FloatPoint(x + width, y + height)); - path.addLineTo(FloatPoint(x, y + height)); - path.closeSubpath(); - - return path; -} - -Path Path::createEllipse(const FloatPoint& center, float rx, float ry) -{ - float cx = center.x(); - float cy = center.y(); - Path path; - if (rx <= 0.0f || ry <= 0.0f) - return path; - - float x = cx; - float y = cy; - - unsigned step = 0, num = 100; - bool running = true; - while (running) - { - if (step == num) - { - running = false; - break; - } - - float angle = static_cast<float>(step) / static_cast<float>(num) * 2.0f * piFloat; - x = cx + cosf(angle) * rx; - y = cy + sinf(angle) * ry; - - step++; - if (step == 1) - path.moveTo(FloatPoint(x, y)); - else - path.addLineTo(FloatPoint(x, y)); + addRect(rect); + return; } - path.closeSubpath(); - - return path; -} - -Path Path::createCircle(const FloatPoint& center, float r) -{ - return createEllipse(center, r, r); -} - -Path Path::createLine(const FloatPoint& start, const FloatPoint& end) -{ - Path path; - - path.moveTo(start); - path.addLineTo(end); - - return path; + moveTo(FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); + + addLineTo(FloatPoint(rect.x() + rect.width() - topRightRadius.width(), rect.y())); + addBezierCurveTo(FloatPoint(rect.x() + rect.width() - topRightRadius.width() * gCircleControlPoint, rect.y()), + FloatPoint(rect.x() + rect.width(), rect.y() + topRightRadius.height() * gCircleControlPoint), + FloatPoint(rect.x() + rect.width(), rect.y() + topRightRadius.height())); + addLineTo(FloatPoint(rect.x() + rect.width(), rect.y() + rect.height() - bottomRightRadius.height())); + addBezierCurveTo(FloatPoint(rect.x() + rect.width(), rect.y() + rect.height() - bottomRightRadius.height() * gCircleControlPoint), + FloatPoint(rect.x() + rect.width() - bottomRightRadius.width() * gCircleControlPoint, rect.y() + rect.height()), + FloatPoint(rect.x() + rect.width() - bottomRightRadius.width(), rect.y() + rect.height())); + addLineTo(FloatPoint(rect.x() + bottomLeftRadius.width(), rect.y() + rect.height())); + addBezierCurveTo(FloatPoint(rect.x() + bottomLeftRadius.width() * gCircleControlPoint, rect.y() + rect.height()), + FloatPoint(rect.x(), rect.y() + rect.height() - bottomLeftRadius.height() * gCircleControlPoint), + FloatPoint(rect.x(), rect.y() + rect.height() - bottomLeftRadius.height())); + addLineTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height())); + addBezierCurveTo(FloatPoint(rect.x(), rect.y() + topLeftRadius.height() * gCircleControlPoint), + FloatPoint(rect.x() + topLeftRadius.width() * gCircleControlPoint, rect.y()), + FloatPoint(rect.x() + topLeftRadius.width(), rect.y())); + + closeSubpath(); } } diff --git a/WebCore/platform/graphics/Path.h b/WebCore/platform/graphics/Path.h index 43ba889..86ba831 100644 --- a/WebCore/platform/graphics/Path.h +++ b/WebCore/platform/graphics/Path.h @@ -137,20 +137,13 @@ namespace WebCore { void addArc(const FloatPoint&, float radius, float startAngle, float endAngle, bool anticlockwise); void addRect(const FloatRect&); void addEllipse(const FloatRect&); + void addRoundedRect(const FloatRect&, const FloatSize& roundingRadii); + void addRoundedRect(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); void translate(const FloatSize&); - String debugString() const; - PlatformPathPtr platformPath() const { return m_path; } - static Path createRoundedRectangle(const FloatRect&, const FloatSize& roundingRadii); - static Path createRoundedRectangle(const FloatRect&, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius); - static Path createRectangle(const FloatRect&); - static Path createEllipse(const FloatPoint& center, float rx, float ry); - static Path createCircle(const FloatPoint& center, float r); - static Path createLine(const FloatPoint&, const FloatPoint&); - void apply(void* info, PathApplierFunction) const; void transform(const AffineTransform&); diff --git a/WebCore/platform/graphics/brew/ImageBrew.cpp b/WebCore/platform/graphics/brew/ImageBrew.cpp index f5c855d..b574b0a 100644 --- a/WebCore/platform/graphics/brew/ImageBrew.cpp +++ b/WebCore/platform/graphics/brew/ImageBrew.cpp @@ -36,13 +36,13 @@ #include "SharedBuffer.h" #include <wtf/text/CString.h> -#include <wtf/text/WTFString.h> +#include <wtf/text/StringConcatenate.h> namespace WebCore { PassRefPtr<Image> Image::loadPlatformResource(const char *name) { - String resourcePath = homeDirectoryPath() + String::format("res/%s.png", name); + String resourcePath = makeString(homeDirectoryPath(), "res/", name, ".png"); RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(resourcePath.utf8().data()); if (!buffer) diff --git a/WebCore/platform/graphics/cairo/CairoUtilities.cpp b/WebCore/platform/graphics/cairo/CairoUtilities.cpp index 8c2049f..7af5577 100644 --- a/WebCore/platform/graphics/cairo/CairoUtilities.cpp +++ b/WebCore/platform/graphics/cairo/CairoUtilities.cpp @@ -26,8 +26,15 @@ #include "config.h" #include "CairoUtilities.h" +#include "AffineTransform.h" +#include "CairoPath.h" #include "Color.h" -#include <cairo.h> +#include "FloatPoint.h" +#include "FloatRect.h" +#include "IntRect.h" +#include "OwnPtrCairo.h" +#include "Path.h" +#include "PlatformRefPtrCairo.h" #include <wtf/Vector.h> namespace WebCore { @@ -56,4 +63,96 @@ void setSourceRGBAFromColor(cairo_t* context, const Color& color) cairo_set_source_rgba(context, red, green, blue, alpha); } +void appendPathToCairoContext(cairo_t* to, cairo_t* from) +{ + OwnPtr<cairo_path_t> cairoPath(cairo_copy_path(from)); + cairo_append_path(to, cairoPath.get()); +} + +void setPathOnCairoContext(cairo_t* to, cairo_t* from) +{ + cairo_new_path(to); + appendPathToCairoContext(to, from); +} + +void appendWebCorePathToCairoContext(cairo_t* context, const Path& path) +{ + appendPathToCairoContext(context, path.platformPath()->context()); +} + +cairo_operator_t toCairoOperator(CompositeOperator op) +{ + switch (op) { + case CompositeClear: + return CAIRO_OPERATOR_CLEAR; + case CompositeCopy: + return CAIRO_OPERATOR_SOURCE; + case CompositeSourceOver: + return CAIRO_OPERATOR_OVER; + case CompositeSourceIn: + return CAIRO_OPERATOR_IN; + case CompositeSourceOut: + return CAIRO_OPERATOR_OUT; + case CompositeSourceAtop: + return CAIRO_OPERATOR_ATOP; + case CompositeDestinationOver: + return CAIRO_OPERATOR_DEST_OVER; + case CompositeDestinationIn: + return CAIRO_OPERATOR_DEST_IN; + case CompositeDestinationOut: + return CAIRO_OPERATOR_DEST_OUT; + case CompositeDestinationAtop: + return CAIRO_OPERATOR_DEST_ATOP; + case CompositeXOR: + return CAIRO_OPERATOR_XOR; + case CompositePlusDarker: + return CAIRO_OPERATOR_SATURATE; + case CompositeHighlight: + // There is no Cairo equivalent for CompositeHighlight. + return CAIRO_OPERATOR_OVER; + case CompositePlusLighter: + return CAIRO_OPERATOR_ADD; + default: + return CAIRO_OPERATOR_SOURCE; + } +} + +void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, + const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect) +{ + // Avoid NaN + if (!isfinite(phase.x()) || !isfinite(phase.y())) + return; + + cairo_save(cr); + + PlatformRefPtr<cairo_surface_t> clippedImageSurface = 0; + if (tileRect.size() != imageSize) { + IntRect imageRect = enclosingIntRect(tileRect); + clippedImageSurface = adoptPlatformRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, imageRect.width(), imageRect.height())); + PlatformRefPtr<cairo_t> clippedImageContext(cairo_create(clippedImageSurface.get())); + cairo_set_source_surface(clippedImageContext.get(), image, -tileRect.x(), -tileRect.y()); + cairo_paint(clippedImageContext.get()); + image = clippedImageSurface.get(); + } + + cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image); + cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); + + cairo_matrix_t patternMatrix = cairo_matrix_t(patternTransform); + cairo_matrix_t phaseMatrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()}; + cairo_matrix_t combined; + cairo_matrix_multiply(&combined, &patternMatrix, &phaseMatrix); + cairo_matrix_invert(&combined); + cairo_pattern_set_matrix(pattern, &combined); + + cairo_set_operator(cr, op); + cairo_set_source(cr, pattern); + cairo_pattern_destroy(pattern); + cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height()); + cairo_fill(cr); + + cairo_restore(cr); +} + } // namespace WebCore diff --git a/WebCore/platform/graphics/cairo/CairoUtilities.h b/WebCore/platform/graphics/cairo/CairoUtilities.h index 0675b90..d8fff8d 100644 --- a/WebCore/platform/graphics/cairo/CairoUtilities.h +++ b/WebCore/platform/graphics/cairo/CairoUtilities.h @@ -26,13 +26,25 @@ #ifndef CairoUtilities_h #define CairoUtilities_h -typedef struct _cairo cairo_t; +#include <GraphicsTypes.h> +#include <cairo.h> namespace WebCore { +class AffineTransform; class Color; +class FloatRect; +class FloatPoint; +class IntSize; +class Path; void copyContextProperties(cairo_t* srcCr, cairo_t* dstCr); void setSourceRGBAFromColor(cairo_t*, const Color&); +void appendPathToCairoContext(cairo_t* to, cairo_t* from); +void setPathOnCairoContext(cairo_t* to, cairo_t* from); +void appendWebCorePathToCairoContext(cairo_t* context, const Path& path); +cairo_operator_t toCairoOperator(CompositeOperator op); +void drawPatternToCairoContext(cairo_t* cr, cairo_surface_t* image, const IntSize& imageSize, const FloatRect& tileRect, + const AffineTransform& patternTransform, const FloatPoint& phase, cairo_operator_t op, const FloatRect& destRect); } // namespace WebCore diff --git a/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp b/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp index 4b94cb3..8299b6a 100644 --- a/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp +++ b/WebCore/platform/graphics/cairo/ContextShadowCairo.cpp @@ -29,10 +29,15 @@ #include "config.h" #include "ContextShadow.h" +#include "AffineTransform.h" #include "CairoUtilities.h" +#include "OwnPtrCairo.h" +#include "Path.h" #include "Timer.h" #include <cairo.h> +using WTF::max; + namespace WebCore { static cairo_surface_t* scratchBuffer = 0; @@ -105,10 +110,13 @@ void ContextShadow::endShadowLayer(cairo_t* cr) cairo_destroy(m_layerContext); m_layerContext = 0; - if (m_type == BlurShadow) + if (m_type == BlurShadow) { + cairo_surface_flush(m_layerImage); blurLayerImage(cairo_image_surface_get_data(m_layerImage), IntSize(cairo_image_surface_get_width(m_layerImage), cairo_image_surface_get_height(m_layerImage)), cairo_image_surface_get_stride(m_layerImage)); + cairo_surface_mark_dirty(m_layerImage); + } cairo_save(cr); setSourceRGBAFromColor(cr, m_color); @@ -119,4 +127,223 @@ void ContextShadow::endShadowLayer(cairo_t* cr) scheduleScratchBufferPurge(); } +void ContextShadow::drawRectShadowWithoutTiling(PlatformContext context, const IntRect& shadowRect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius, float alpha) +{ + beginShadowLayer(context, shadowRect); + + if (!m_layerContext) + return; + + Path path; + path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + + appendWebCorePathToCairoContext(m_layerContext, path); + cairo_set_source_rgba(m_layerContext, 0, 0, 0, alpha); + cairo_fill(m_layerContext); + + endShadowLayer(context); +} + +static inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile) +{ + FloatPoint phase = dest.location(); + phase.move(-tile.x(), -tile.y()); + + return phase; +} + +/* + This function uses 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 part with solid color to complete the + shadow. + */ +void ContextShadow::drawRectShadow(GraphicsContext* context, const IntRect& rect, const IntSize& topLeftRadius, const IntSize& topRightRadius, const IntSize& bottomLeftRadius, const IntSize& bottomRightRadius) +{ + + // drawShadowedRect still does not work with rotations. + // https://bugs.webkit.org/show_bug.cgi?id=45042 + float radiusTwice = m_blurDistance * 2; + cairo_t* cr = context->platformContext(); + if ((!context->getCTM().isIdentityOrTranslationOrFlipped()) || (radiusTwice > rect.width()) + || (radiusTwice > rect.height()) || (m_type != BlurShadow)) { + drawRectShadowWithoutTiling(cr, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, context->getAlpha()); + return; + } + + // Calculate size of the template shadow buffer. + IntSize shadowBufferSize = IntSize(rect.width() + radiusTwice, rect.height() + radiusTwice); + + // Determine dimensions of shadow rect. + FloatRect shadowRect = FloatRect(rect.location(), shadowBufferSize); + shadowRect.move(- m_blurDistance, - m_blurDistance); + + // Size of the tiling side. + int sideTileWidth = 1; + + // Find the extra space needed from the curve of the corners. + int extraWidthFromCornerRadii = radiusTwice + max(topLeftRadius.width(), bottomLeftRadius.width()) + + radiusTwice + max(topRightRadius.width(), bottomRightRadius.width()); + int extraHeightFromCornerRadii = radiusTwice + max(topLeftRadius.height(), topRightRadius.height()) + + radiusTwice + max(bottomLeftRadius.height(), bottomRightRadius.height()); + + // The length of a side of the buffer is the enough space for four blur radii, + // the radii of the corners, and then 1 pixel to draw the side tiles. + IntSize shadowTemplateSize = IntSize(sideTileWidth + extraWidthFromCornerRadii, + sideTileWidth + extraHeightFromCornerRadii); + + // Reduce the size of what we have to draw with the clip area. + double x1, x2, y1, y2; + cairo_clip_extents(cr, &x1, &y1, &x2, &y2); + calculateLayerBoundingRect(shadowRect, IntRect(x1, y1, x2 - x1, y2 - y1)); + + if ((shadowTemplateSize.width() * shadowTemplateSize.height() > m_layerRect.width() * m_layerRect.height())) { + drawRectShadowWithoutTiling(cr, rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius, context->getAlpha()); + return; + } + + shadowRect.move(m_offset.width(), m_offset.height()); + + m_layerImage = getScratchBuffer(shadowTemplateSize); + + // Draw shadow into a new ImageBuffer. + m_layerContext = cairo_create(m_layerImage); + + // Clear the surface first. + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_CLEAR); + cairo_paint(m_layerContext); + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_OVER); + + // Draw the rectangle. + IntRect templateRect = IntRect(m_blurDistance, m_blurDistance, shadowTemplateSize.width() - radiusTwice, shadowTemplateSize.height() - radiusTwice); + Path path; + path.addRoundedRect(templateRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + appendWebCorePathToCairoContext(m_layerContext, path); + + cairo_set_source_rgba(m_layerContext, 0, 0, 0, context->getAlpha()); + cairo_fill(m_layerContext); + + // Blur the image. + cairo_surface_flush(m_layerImage); + blurLayerImage(cairo_image_surface_get_data(m_layerImage), shadowTemplateSize, cairo_image_surface_get_stride(m_layerImage)); + cairo_surface_mark_dirty(m_layerImage); + + // Mask the image with the shadow color. + cairo_set_operator(m_layerContext, CAIRO_OPERATOR_IN); + setSourceRGBAFromColor(m_layerContext, m_color); + cairo_paint(m_layerContext); + + cairo_destroy(m_layerContext); + m_layerContext = 0; + + // Fill the internal part of the shadow. + shadowRect.inflate(-radiusTwice); + if (!shadowRect.isEmpty()) { + cairo_save(cr); + path.clear(); + path.addRoundedRect(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius); + appendWebCorePathToCairoContext(cr, path); + setSourceRGBAFromColor(cr, m_color); + cairo_fill(cr); + cairo_restore(cr); + } + shadowRect.inflate(radiusTwice); + + // Draw top side. + FloatRect tileRect = FloatRect(radiusTwice + topLeftRadius.width(), 0, sideTileWidth, radiusTwice); + FloatRect destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y()); + destRect.setWidth(shadowRect.width() - topLeftRadius.width() - topRightRadius.width() - m_blurDistance * 4); + FloatPoint phase = getPhase(destRect, tileRect); + AffineTransform patternTransform; + patternTransform.makeIdentity(); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the bottom side. + tileRect = FloatRect(radiusTwice + bottomLeftRadius.width(), shadowTemplateSize.height() - radiusTwice, sideTileWidth, radiusTwice); + destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y() + radiusTwice + rect.height() - shadowTemplateSize.height()); + destRect.setWidth(shadowRect.width() - bottomLeftRadius.width() - bottomRightRadius.width() - m_blurDistance * 4); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the right side. + tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice, radiusTwice + topRightRadius.height(), radiusTwice, sideTileWidth); + destRect = tileRect; + destRect.move(shadowRect.x() + radiusTwice + rect.width() - shadowTemplateSize.width(), shadowRect.y()); + destRect.setHeight(shadowRect.height() - topRightRadius.height() - bottomRightRadius.height() - m_blurDistance * 4); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the left side. + tileRect = FloatRect(0, radiusTwice + topLeftRadius.height(), radiusTwice, sideTileWidth); + destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y()); + destRect.setHeight(shadowRect.height() - topLeftRadius.height() - bottomLeftRadius.height() - m_blurDistance * 4); + phase = FloatPoint(destRect.x() - tileRect.x(), destRect.y() - tileRect.y()); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the top left corner. + tileRect = FloatRect(0, 0, radiusTwice + topLeftRadius.width(), radiusTwice + topLeftRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y()); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the top right corner. + tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - topRightRadius.width(), 0, radiusTwice + topRightRadius.width(), + radiusTwice + topRightRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice, shadowRect.y()); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the bottom right corner. + tileRect = FloatRect(shadowTemplateSize.width() - radiusTwice - bottomRightRadius.width(), + shadowTemplateSize.height() - radiusTwice - bottomRightRadius.height(), + radiusTwice + bottomRightRadius.width(), radiusTwice + bottomRightRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x() + rect.width() - shadowTemplateSize.width() + radiusTwice, + shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Draw the bottom left corner. + tileRect = FloatRect(0, shadowTemplateSize.height() - radiusTwice - bottomLeftRadius.height(), + radiusTwice + bottomLeftRadius.width(), radiusTwice + bottomLeftRadius.height()); + destRect = tileRect; + destRect.move(shadowRect.x(), shadowRect.y() + rect.height() - shadowTemplateSize.height() + radiusTwice); + phase = getPhase(destRect, tileRect); + drawPatternToCairoContext(cr, m_layerImage, shadowTemplateSize, tileRect, patternTransform, phase, CAIRO_OPERATOR_OVER, destRect); + + // Schedule a purge of the scratch buffer. + scheduleScratchBufferPurge(); +} + } diff --git a/WebCore/platform/graphics/cairo/FloatRectCairo.cpp b/WebCore/platform/graphics/cairo/FloatRectCairo.cpp new file mode 100644 index 0000000..9f86f74 --- /dev/null +++ b/WebCore/platform/graphics/cairo/FloatRectCairo.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2010 Igalia S.L. + * + * 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 "FloatRect.h" + +#include <cairo.h> + +namespace WebCore { + +FloatRect::FloatRect(const cairo_rectangle_t& r) + : m_location(r.x, r.y) + , m_size(r.width, r.height) +{ +} + +FloatRect::operator cairo_rectangle_t() const +{ + cairo_rectangle_t r = { x(), y(), width(), height() }; + return r; +} + +} // namespace WebCore diff --git a/WebCore/platform/graphics/cairo/FontCacheFreeType.cpp b/WebCore/platform/graphics/cairo/FontCacheFreeType.cpp index febad12..5dca010 100644 --- a/WebCore/platform/graphics/cairo/FontCacheFreeType.cpp +++ b/WebCore/platform/graphics/cairo/FontCacheFreeType.cpp @@ -87,12 +87,12 @@ void FontCache::getTraitsInFamily(const AtomicString& familyName, Vector<unsigne { } -static CString getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription& fontDescription, const AtomicString& family) +static String getFamilyNameStringFromFontDescriptionAndFamily(const FontDescription& fontDescription, const AtomicString& family) { // If we're creating a fallback font (e.g. "-webkit-monospace"), convert the name into // the fallback name (like "monospace") that fontconfig understands. if (family.length() && !family.startsWith("-webkit-")) - return family.string().utf8(); + return family.string(); switch (fontDescription.genericFamily()) { case FontDescription::StandardFamily: @@ -112,23 +112,14 @@ static CString getFamilyNameStringFromFontDescriptionAndFamily(const FontDescrip } } - -static bool isFallbackFontAllowed(const CString& familyName) -{ - return !strcasecmp(familyName.data(), "sans") - || !strcasecmp(familyName.data(), "sans-serif") - || !strcasecmp(familyName.data(), "serif") - || !strcasecmp(familyName.data(), "monospace"); -} - FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontDescription, const AtomicString& family) { // The CSS font matching algorithm (http://www.w3.org/TR/css3-fonts/#font-matching-algorithm) // says that we must find an exact match for font family, slant (italic or oblique can be used) // and font weight (we only match bold/non-bold here). PlatformRefPtr<FcPattern> pattern = adoptPlatformRef(FcPatternCreate()); - CString familyNameString = getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family); - if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.data()))) + String familyNameString(getFamilyNameStringFromFontDescriptionAndFamily(fontDescription, family)); + if (!FcPatternAddString(pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(familyNameString.utf8().data()))) return 0; bool italic = fontDescription.italic(); @@ -140,53 +131,39 @@ FontPlatformData* FontCache::createFontPlatformData(const FontDescription& fontD if (!FcPatternAddDouble(pattern.get(), FC_PIXEL_SIZE, fontDescription.computedPixelSize())) return 0; - // The following comment and strategy are originally from Skia (src/ports/SkFontHost_fontconfig.cpp): - // Font matching: - // CSS often specifies a fallback list of families: - // font-family: a, b, c, serif; - // However, fontconfig will always do its best to find *a* font when asked - // for something so we need a way to tell if the match which it has found is - // "good enough" for us. Otherwise, we can return null which gets piped up - // and lets WebKit know to try the next CSS family name. However, fontconfig - // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we - // wish to support that. - // - // Thus, if a specific family is requested we set @family_requested. Then we - // record two strings: the family name after config processing and the - // family name after resolving. If the two are equal, it's a good match. - // - // So consider the case where a user has mapped Arial to Helvetica in their - // config. - // requested family: "Arial" - // post_config_family: "Helvetica" - // post_match_family: "Helvetica" - // -> good match - // - // and for a missing font: - // requested family: "Monaco" - // post_config_family: "Monaco" - // post_match_family: "Times New Roman" - // -> BAD match - // + // The strategy is originally from Skia (src/ports/SkFontHost_fontconfig.cpp): + + // Allow Fontconfig to do pre-match substitution. Unless we are accessing a "fallback" + // family like "sans," this is the only time we allow Fontconfig to substitute one + // family name for another (i.e. if the fonts are aliased to each other). FcConfigSubstitute(0, pattern.get(), FcMatchPattern); FcDefaultSubstitute(pattern.get()); - FcChar8* familyNameAfterConfiguration; - FcPatternGetString(pattern.get(), FC_FAMILY, 0, &familyNameAfterConfiguration); + FcChar8* fontConfigFamilyNameAfterConfiguration; + FcPatternGetString(pattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterConfiguration); + String familyNameAfterConfiguration = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterConfiguration)); FcResult fontConfigResult; PlatformRefPtr<FcPattern> resultPattern = adoptPlatformRef(FcFontMatch(0, pattern.get(), &fontConfigResult)); if (!resultPattern) // No match. return 0; - // Properly handle the situation where Fontconfig gives us a font that has a different family than we requested. - FcChar8* familyNameAfterMatching; - FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &familyNameAfterMatching); - if (strcasecmp(reinterpret_cast<char*>(familyNameAfterConfiguration), - reinterpret_cast<char*>(familyNameAfterMatching)) && !isFallbackFontAllowed(familyNameString)) - return 0; - - return new FontPlatformData(resultPattern.get(), fontDescription); + FcChar8* fontConfigFamilyNameAfterMatching; + FcPatternGetString(resultPattern.get(), FC_FAMILY, 0, &fontConfigFamilyNameAfterMatching); + String familyNameAfterMatching = String::fromUTF8(reinterpret_cast<char*>(fontConfigFamilyNameAfterMatching)); + if (equalIgnoringCase(familyNameAfterConfiguration, familyNameAfterMatching)) + return new FontPlatformData(resultPattern.get(), fontDescription); + + // If Fontconfig gave use a different font family than the one we requested, we should ignore it + // and allow WebCore to give us the next font on the CSS fallback list. The only exception is if + // this family name is a commonly used generic family. + if (equalIgnoringCase(familyNameString, "sans") || equalIgnoringCase(familyNameString, "sans-serif") + || equalIgnoringCase(familyNameString, "serif") || equalIgnoringCase(familyNameString, "monospace") + || equalIgnoringCase(familyNameString, "fantasy") || equalIgnoringCase(familyNameString, "cursive")) + return new FontPlatformData(resultPattern.get(), fontDescription); + + // Fontconfig did not return a good match. + return 0; } } diff --git a/WebCore/platform/graphics/cairo/FontCairo.cpp b/WebCore/platform/graphics/cairo/FontCairo.cpp index cd5d362..3d55c70 100644 --- a/WebCore/platform/graphics/cairo/FontCairo.cpp +++ b/WebCore/platform/graphics/cairo/FontCairo.cpp @@ -31,6 +31,8 @@ #include "Font.h" #include "AffineTransform.h" +#include "CairoUtilities.h" +#include "ContextShadow.h" #include "GlyphBuffer.h" #include "Gradient.h" #include "GraphicsContext.h" @@ -38,18 +40,63 @@ #include "Pattern.h" #include "SimpleFontData.h" -#define SYNTHETIC_OBLIQUE_ANGLE 14 - namespace WebCore { -void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, - int from, int numGlyphs, const FloatPoint& point) const +static void prepareContextForGlyphDrawing(cairo_t* context, const SimpleFontData* font, const FloatPoint& point) { - cairo_t* cr = context->platformContext(); - cairo_save(cr); + static const float syntheticObliqueSkew = -tanf(14 * acosf(0) / 90); + cairo_set_scaled_font(context, font->platformData().scaledFont()); + if (font->platformData().syntheticOblique()) { + cairo_matrix_t mat = {1, 0, syntheticObliqueSkew, 1, point.x(), point.y()}; + cairo_transform(context, &mat); + } else + cairo_translate(context, point.x(), point.y()); +} - cairo_set_scaled_font(cr, font->platformData().scaledFont()); +static void drawGlyphsToContext(cairo_t* context, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs) +{ + cairo_show_glyphs(context, glyphs, numGlyphs); + if (font->syntheticBoldOffset()) { + // We could use cairo_save/cairo_restore here, but two translations are likely faster. + cairo_translate(context, font->syntheticBoldOffset(), 0); + cairo_show_glyphs(context, glyphs, numGlyphs); + cairo_translate(context, -font->syntheticBoldOffset(), 0); + } +} + +static void drawGlyphsShadow(GraphicsContext* graphicsContext, cairo_t* context, const FloatPoint& point, const SimpleFontData* font, GlyphBufferGlyph* glyphs, int numGlyphs) +{ + ContextShadow* shadow = graphicsContext->contextShadow(); + ASSERT(shadow); + + if (!(graphicsContext->textDrawingMode() & cTextFill) || shadow->m_type == ContextShadow::NoShadow) + return; + + if (shadow->m_type == ContextShadow::SolidShadow) { + // Optimize non-blurry shadows, by just drawing text without the ContextShadow. + cairo_save(context); + cairo_translate(context, shadow->m_offset.width(), shadow->m_offset.height()); + setSourceRGBAFromColor(context, shadow->m_color); + prepareContextForGlyphDrawing(context, font, point); + cairo_show_glyphs(context, glyphs, numGlyphs); + cairo_restore(context); + return; + } + + cairo_text_extents_t extents; + cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents); + FloatRect fontExtentsRect(point.x(), point.y() - extents.height, extents.width, extents.height); + cairo_t* shadowContext = shadow->beginShadowLayer(context, fontExtentsRect); + if (shadowContext) { + prepareContextForGlyphDrawing(shadowContext, font, point); + drawGlyphsToContext(shadowContext, font, glyphs, numGlyphs); + shadow->endShadowLayer(context); + } +} +void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, const GlyphBuffer& glyphBuffer, + int from, int numGlyphs, const FloatPoint& point) const +{ GlyphBufferGlyph* glyphs = (GlyphBufferGlyph*)glyphBuffer.glyphs(from); float offset = 0.0f; @@ -59,75 +106,11 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons offset += glyphBuffer.advanceAt(from + i); } - Color fillColor = context->fillColor(); - - // Synthetic Oblique - if(font->platformData().syntheticOblique()) { - cairo_matrix_t mat = {1, 0, -tanf(SYNTHETIC_OBLIQUE_ANGLE * acosf(0) / 90), 1, point.x(), point.y()}; - cairo_transform(cr, &mat); - } else { - cairo_translate(cr, point.x(), point.y()); - } - - // Text shadow, inspired by FontMac - FloatSize shadowOffset; - float shadowBlur = 0; - Color shadowColor; - bool hasShadow = context->textDrawingMode() & cTextFill - && context->getShadow(shadowOffset, shadowBlur, shadowColor); - - // TODO: Blur support - if (hasShadow) { - // Disable graphics context shadows (not yet implemented) and paint them manually - context->clearShadow(); - Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); - cairo_save(cr); - - float red, green, blue, alpha; - shadowFillColor.getRGBA(red, green, blue, alpha); - cairo_set_source_rgba(cr, red, green, blue, alpha); - -#if ENABLE(FILTERS) - cairo_text_extents_t extents; - cairo_scaled_font_glyph_extents(font->platformData().scaledFont(), glyphs, numGlyphs, &extents); - - FloatRect rect(FloatPoint(), FloatSize(extents.width, extents.height)); - IntSize shadowBufferSize; - FloatRect shadowRect; - float radius = 0; - context->calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur); - - // Draw shadow into a new ImageBuffer - OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); - GraphicsContext* shadowContext = shadowBuffer->context(); - cairo_t* shadowCr = shadowContext->platformContext(); - - cairo_translate(shadowCr, radius, extents.height + radius); - - cairo_set_scaled_font(shadowCr, font->platformData().scaledFont()); - cairo_show_glyphs(shadowCr, glyphs, numGlyphs); - if (font->syntheticBoldOffset()) { - cairo_save(shadowCr); - cairo_translate(shadowCr, font->syntheticBoldOffset(), 0); - cairo_show_glyphs(shadowCr, glyphs, numGlyphs); - cairo_restore(shadowCr); - } - cairo_translate(cr, 0.0, -extents.height); - context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius); -#else - cairo_translate(cr, shadowOffset.width(), shadowOffset.height()); - cairo_show_glyphs(cr, glyphs, numGlyphs); - if (font->syntheticBoldOffset()) { - cairo_save(cr); - cairo_translate(cr, font->syntheticBoldOffset(), 0); - cairo_show_glyphs(cr, glyphs, numGlyphs); - cairo_restore(cr); - } -#endif - - cairo_restore(cr); - } + cairo_t* cr = context->platformContext(); + drawGlyphsShadow(context, cr, point, font, glyphs, numGlyphs); + cairo_save(cr); + prepareContextForGlyphDrawing(cr, font, point); if (context->textDrawingMode() & cTextFill) { if (context->fillGradient()) { cairo_set_source(cr, context->fillGradient()->platformGradient()); @@ -148,16 +131,10 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons cairo_pattern_destroy(pattern); } else { float red, green, blue, alpha; - fillColor.getRGBA(red, green, blue, alpha); + context->fillColor().getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); } - cairo_show_glyphs(cr, glyphs, numGlyphs); - if (font->syntheticBoldOffset()) { - cairo_save(cr); - cairo_translate(cr, font->syntheticBoldOffset(), 0); - cairo_show_glyphs(cr, glyphs, numGlyphs); - cairo_restore(cr); - } + drawGlyphsToContext(cr, font, glyphs, numGlyphs); } // Prevent running into a long computation within cairo. If the stroke width is @@ -183,20 +160,15 @@ void Font::drawGlyphs(GraphicsContext* context, const SimpleFontData* font, cons } cairo_pattern_destroy(pattern); } else { - Color strokeColor = context->strokeColor(); float red, green, blue, alpha; - strokeColor.getRGBA(red, green, blue, alpha); + context->strokeColor().getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha * context->getAlpha()); - } + } cairo_glyph_path(cr, glyphs, numGlyphs); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } - // Re-enable the platform shadow we disabled earlier - if (hasShadow) - context->setShadow(shadowOffset, shadowBlur, shadowColor, DeviceColorSpace); - cairo_restore(cr); } diff --git a/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.cpp b/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.cpp index 0617e6c..ba307fa 100644 --- a/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.cpp +++ b/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.cpp @@ -75,40 +75,65 @@ void setCairoFontOptionsFromFontConfigPattern(cairo_font_options_t* options, FcP FcBool booleanResult; int integerResult; - // We will determine if subpixel anti-aliasing is enabled via the FC_RGBA setting. - if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &booleanResult) == FcResultMatch && booleanResult) - cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); - if (FcPatternGetInteger(pattern, FC_RGBA, 0, &integerResult) == FcResultMatch) { + cairo_font_options_set_subpixel_order(options, convertFontConfigSubpixelOrder(integerResult)); + + // Based on the logic in cairo-ft-font.c in the cairo source, a font with + // a subpixel order implies that is uses subpixel antialiasing. if (integerResult != FC_RGBA_NONE) cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_SUBPIXEL); - cairo_font_options_set_subpixel_order(options, convertFontConfigSubpixelOrder(integerResult)); + } + + if (FcPatternGetBool(pattern, FC_ANTIALIAS, 0, &booleanResult) == FcResultMatch) { + // Only override the anti-aliasing setting if was previously turned off. Otherwise + // we'll override the preference which decides between gray anti-aliasing and + // subpixel anti-aliasing. + if (!booleanResult) + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_NONE); + else if (cairo_font_options_get_antialias(options) == CAIRO_ANTIALIAS_NONE) + cairo_font_options_set_antialias(options, CAIRO_ANTIALIAS_GRAY); } if (FcPatternGetInteger(pattern, FC_HINT_STYLE, 0, &integerResult) == FcResultMatch) cairo_font_options_set_hint_style(options, convertFontConfigHintStyle(integerResult)); - if (FcPatternGetBool(pattern, FC_HINTING, 0, &booleanResult) == FcResultMatch && !booleanResult) cairo_font_options_set_hint_style(options, CAIRO_HINT_STYLE_NONE); } +static const cairo_font_options_t* getDefaultFontOptions() +{ + static const cairo_font_options_t* options = cairo_font_options_create(); +#if PLATFORM(GTK) || ENABLE(GLIB_SUPPORT) + if (GdkScreen* screen = gdk_screen_get_default()) { + const cairo_font_options_t* screenOptions = gdk_screen_get_font_options(screen); + if (screenOptions) + options = screenOptions; + } +#endif + return options; +} + FontPlatformData::FontPlatformData(FcPattern* pattern, const FontDescription& fontDescription) : m_pattern(pattern) , m_fallbacks(0) , m_size(fontDescription.computedPixelSize()) , m_syntheticBold(false) , m_syntheticOblique(false) + , m_fixedWidth(false) { - cairo_font_options_t* options = cairo_font_options_create(); - setCairoFontOptionsFromFontConfigPattern(options, pattern); + PlatformRefPtr<cairo_font_face_t> fontFace = adoptPlatformRef(cairo_ft_font_face_create_for_pattern(m_pattern.get())); + initializeWithFontFace(fontFace.get()); - cairo_matrix_t fontMatrix; - cairo_matrix_init_scale(&fontMatrix, m_size, m_size); - cairo_matrix_t ctm; - cairo_matrix_init_identity(&ctm); + int spacing; + if (FcPatternGetInteger(pattern, FC_SPACING, 0, &spacing) == FcResultMatch && spacing == FC_MONO) + m_fixedWidth = true; - PlatformRefPtr<cairo_font_face_t> fontFace = adoptPlatformRef(cairo_ft_font_face_create_for_pattern(m_pattern.get())); - m_scaledFont = adoptPlatformRef(cairo_scaled_font_create(fontFace.get(), &fontMatrix, &ctm, options)); + if (fontDescription.weight() >= FontWeightBold) { + // The FC_EMBOLDEN property instructs us to fake the boldness of the font. + FcBool fontConfigEmbolden; + if (FcPatternGetBool(pattern, FC_EMBOLDEN, 0, &fontConfigEmbolden) == FcResultMatch) + m_syntheticBold = fontConfigEmbolden; + } } FontPlatformData::FontPlatformData(float size, bool bold, bool italic) @@ -116,7 +141,9 @@ FontPlatformData::FontPlatformData(float size, bool bold, bool italic) , m_size(size) , m_syntheticBold(bold) , m_syntheticOblique(italic) + , m_fixedWidth(false) { + // We cannot create a scaled font here. } FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic) @@ -125,24 +152,13 @@ FontPlatformData::FontPlatformData(cairo_font_face_t* fontFace, float size, bool , m_syntheticBold(bold) , m_syntheticOblique(italic) { - cairo_matrix_t fontMatrix; - cairo_matrix_init_scale(&fontMatrix, size, size); - cairo_matrix_t ctm; - cairo_matrix_init_identity(&ctm); - static const cairo_font_options_t* defaultOptions = cairo_font_options_create(); - const cairo_font_options_t* options = NULL; - -#if !PLATFORM(EFL) || ENABLE(GLIB_SUPPORT) - if (GdkScreen* screen = gdk_screen_get_default()) - options = gdk_screen_get_font_options(screen); -#endif - - // gdk_screen_get_font_options() returns NULL if no default options are - // set, so we always have to check. - if (!options) - options = defaultOptions; + initializeWithFontFace(fontFace); - m_scaledFont = adoptPlatformRef(cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options)); + FT_Face fontConfigFace = cairo_ft_scaled_font_lock_face(m_scaledFont.get()); + if (fontConfigFace) { + m_fixedWidth = fontConfigFace->face_flags & FT_FACE_FLAG_FIXED_WIDTH; + cairo_ft_scaled_font_unlock_face(m_scaledFont.get()); + } } FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) @@ -154,6 +170,7 @@ FontPlatformData& FontPlatformData::operator=(const FontPlatformData& other) m_size = other.m_size; m_syntheticBold = other.m_syntheticBold; m_syntheticOblique = other.m_syntheticOblique; + m_fixedWidth = other.m_fixedWidth; m_scaledFont = other.m_scaledFont; m_pattern = other.m_pattern; @@ -172,6 +189,16 @@ FontPlatformData::FontPlatformData(const FontPlatformData& other) *this = other; } +FontPlatformData::FontPlatformData(const FontPlatformData& other, float size) +{ + *this = other; + + // We need to reinitialize the instance, because the difference in size + // necessitates a new scaled font instance. + m_size = size; + initializeWithFontFace(cairo_scaled_font_get_font_face(m_scaledFont.get())); +} + FontPlatformData::~FontPlatformData() { if (m_fallbacks) { @@ -182,14 +209,7 @@ FontPlatformData::~FontPlatformData() bool FontPlatformData::isFixedPitch() { - // TODO: Support isFixedPitch() for custom fonts. - if (!m_pattern) - return false; - - int spacing; - if (FcPatternGetInteger(m_pattern.get(), FC_SPACING, 0, &spacing) == FcResultMatch) - return spacing == FC_MONO; - return false; + return m_fixedWidth; } bool FontPlatformData::operator==(const FontPlatformData& other) const @@ -208,4 +228,37 @@ String FontPlatformData::description() const } #endif +void FontPlatformData::initializeWithFontFace(cairo_font_face_t* fontFace) +{ + cairo_font_options_t* options = cairo_font_options_copy(getDefaultFontOptions()); + + cairo_matrix_t ctm; + cairo_matrix_init_identity(&ctm); + + cairo_matrix_t fontMatrix; + if (!m_pattern) + cairo_matrix_init_scale(&fontMatrix, m_size, m_size); + else { + setCairoFontOptionsFromFontConfigPattern(options, m_pattern.get()); + + // FontConfig may return a list of transformation matrices with the pattern, for instance, + // for fonts that are oblique. We use that to initialize the cairo font matrix. + FcMatrix fontConfigMatrix, *tempFontConfigMatrix; + FcMatrixInit(&fontConfigMatrix); + + // These matrices may be stacked in the pattern, so it's our job to get them all and multiply them. + for (int i = 0; FcPatternGetMatrix(m_pattern.get(), FC_MATRIX, i, &tempFontConfigMatrix) == FcResultMatch; i++) + FcMatrixMultiply(&fontConfigMatrix, &fontConfigMatrix, tempFontConfigMatrix); + cairo_matrix_init(&fontMatrix, fontConfigMatrix.xx, -fontConfigMatrix.yx, + -fontConfigMatrix.xy, fontConfigMatrix.yy, 0, 0); + + // The matrix from FontConfig does not include the scale. + cairo_matrix_scale(&fontMatrix, m_size, m_size); + } + + m_scaledFont = adoptPlatformRef(cairo_scaled_font_create(fontFace, &fontMatrix, &ctm, options)); + cairo_font_options_destroy(options); +} + + } diff --git a/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.h b/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.h index f3488ef..7d3ff99 100644 --- a/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.h +++ b/WebCore/platform/graphics/cairo/FontPlatformDataFreeType.h @@ -57,6 +57,7 @@ public: FontPlatformData(cairo_font_face_t* fontFace, float size, bool bold, bool italic); FontPlatformData(float size, bool bold, bool italic); FontPlatformData(const FontPlatformData&); + FontPlatformData(const FontPlatformData&, float size); ~FontPlatformData(); @@ -89,7 +90,11 @@ public: float m_size; bool m_syntheticBold; bool m_syntheticOblique; + bool m_fixedWidth; PlatformRefPtr<cairo_scaled_font_t> m_scaledFont; + +private: + void initializeWithFontFace(cairo_font_face_t*); }; } diff --git a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp index 05096a9..0847da1 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp +++ b/WebCore/platform/graphics/cairo/GraphicsContextCairo.cpp @@ -36,20 +36,18 @@ #include "AffineTransform.h" #include "CairoPath.h" #include "CairoUtilities.h" -#include "FEGaussianBlur.h" +#include "ContextShadow.h" #include "FloatRect.h" #include "Font.h" +#include "GraphicsContextPlatformPrivateCairo.h" +#include "GraphicsContextPrivate.h" #include "OwnPtrCairo.h" -#include "ImageBuffer.h" -#include "ImageBufferFilter.h" #include "IntRect.h" #include "NotImplemented.h" #include "Path.h" #include "Pattern.h" #include "PlatformRefPtrCairo.h" #include "SimpleFontData.h" -#include "SourceGraphic.h" - #include <cairo.h> #include <math.h> #include <stdio.h> @@ -61,8 +59,6 @@ #elif PLATFORM(WIN) #include <cairo-win32.h> #endif -#include "GraphicsContextPlatformPrivateCairo.h" -#include "GraphicsContextPrivate.h" using namespace std; @@ -132,26 +128,6 @@ static inline void fillRectSourceOver(cairo_t* cr, const FloatRect& rect, const cairo_fill(cr); } -static void appendPathToCairoContext(cairo_t* to, cairo_t* from) -{ - OwnPtr<cairo_path_t> cairoPath(cairo_copy_path(from)); - cairo_append_path(to, cairoPath.get()); -} - -// We apply the pending path built via addPath to the Cairo context -// lazily. This prevents interaction between the path and other routines -// such as fillRect. -static void setPathOnCairoContext(cairo_t* to, cairo_t* from) -{ - cairo_new_path(to); - appendPathToCairoContext(to, from); -} - -static void appendWebCorePathToCairoContext(cairo_t* context, const Path& path) -{ - appendPathToCairoContext(context, path.platformPath()->context()); -} - static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const FloatPoint* points) { cairo_move_to(context, points[0].x(), points[0].y()); @@ -160,83 +136,59 @@ static void addConvexPolygonToContext(cairo_t* context, size_t numPoints, const cairo_close_path(context); } -void GraphicsContext::calculateShadowBufferDimensions(IntSize& shadowBufferSize, FloatRect& shadowRect, float& radius, const FloatRect& sourceRect, const FloatSize& shadowOffset, float shadowBlur) -{ -#if ENABLE(FILTERS) - // limit radius to 128 - radius = min(128.f, max(shadowBlur, 0.f)); - - shadowBufferSize = IntSize(sourceRect.width() + radius * 2, sourceRect.height() + radius * 2); +enum PathDrawingStyle { + Fill = 1, + Stroke = 2, + FillAndStroke = Fill + Stroke +}; - // determine dimensions of shadow rect - shadowRect = FloatRect(sourceRect.location(), shadowBufferSize); - shadowRect.move(shadowOffset.width() - radius, shadowOffset.height() - radius); -#endif -} - -static inline void drawPathShadow(GraphicsContext* context, GraphicsContextPrivate* gcp, bool fillShadow, bool strokeShadow) +static inline void drawPathShadow(GraphicsContext* context, GraphicsContextPrivate* contextPrivate, PathDrawingStyle drawingStyle) { -#if ENABLE(FILTERS) - FloatSize shadowOffset; - float shadowBlur; - Color shadowColor; - if (!context->getShadow(shadowOffset, shadowBlur, shadowColor)) + ContextShadow* shadow = context->contextShadow(); + ASSERT(shadow); + if (shadow->m_type == ContextShadow::NoShadow) return; - - // Calculate filter values to create appropriate shadow. - cairo_t* cr = context->platformContext(); - double x0, x1, y0, y1; - if (strokeShadow) - cairo_stroke_extents(cr, &x0, &y0, &x1, &y1); - else - cairo_fill_extents(cr, &x0, &y0, &x1, &y1); - FloatRect rect(x0, y0, x1 - x0, y1 - y0); - - IntSize shadowBufferSize; - FloatRect shadowRect; - float radius = 0; - GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur); - - cairo_clip_extents(cr, &x0, &y0, &x1, &y1); - FloatRect clipRect(x0, y0, x1 - x0, y1 - y0); - - FloatPoint rectLocation = shadowRect.location(); - - // Reduce the shadow rect using the clip area. - if (!clipRect.contains(shadowRect)) { - shadowRect.intersect(clipRect); - if (shadowRect.isEmpty()) - return; - shadowRect.inflate(radius); - shadowBufferSize = IntSize(shadowRect.width(), shadowRect.height()); - } - shadowOffset = rectLocation - shadowRect.location(); + // Calculate the extents of the rendered solid paths. + cairo_t* cairoContext = context->platformContext(); + cairo_path_t* path = cairo_copy_path(cairoContext); + + FloatRect solidFigureExtents; + double x0 = 0; + double x1 = 0; + double y0 = 0; + double y1 = 0; + if (drawingStyle & Stroke) { + cairo_stroke_extents(cairoContext, &x0, &y0, &x1, &y1); + solidFigureExtents = FloatRect(x0, y0, x1 - x0, y1 - y0); + } + if (drawingStyle & Fill) { + cairo_fill_extents(cairoContext, &x0, &y0, &x1, &y1); + FloatRect fillExtents(x0, y0, x1 - x0, y1 - y0); + solidFigureExtents.unite(fillExtents); + } - // Create suitably-sized ImageBuffer to hold the shadow. - OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); + cairo_t* shadowContext = shadow->beginShadowLayer(cairoContext, solidFigureExtents); + if (!shadowContext) + return; - // Draw shadow into a new ImageBuffer. - cairo_t* shadowContext = shadowBuffer->context()->platformContext(); - copyContextProperties(cr, shadowContext); - cairo_translate(shadowContext, -rect.x() + radius + shadowOffset.width(), -rect.y() + radius + shadowOffset.height()); - cairo_new_path(shadowContext); - OwnPtr<cairo_path_t> path(cairo_copy_path(cr)); - cairo_append_path(shadowContext, path.get()); + // It's important to copy the context properties to the new shadow + // context to preserve things such as the fill rule and stroke width. + copyContextProperties(cairoContext, shadowContext); + cairo_append_path(shadowContext, path); - if (fillShadow) - setPlatformFill(context, shadowContext, gcp); - if (strokeShadow) - setPlatformStroke(context, shadowContext, gcp); + if (drawingStyle & Fill) + setPlatformFill(context, shadowContext, contextPrivate); + if (drawingStyle & Stroke) + setPlatformStroke(context, shadowContext, contextPrivate); - context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius); -#endif + shadow->endShadowLayer(cairoContext); } static void fillCurrentCairoPath(GraphicsContext* context, GraphicsContextPrivate* gcp, cairo_t* cairoContext) { cairo_set_fill_rule(cairoContext, context->fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); - drawPathShadow(context, gcp, true, false); + drawPathShadow(context, gcp, Fill); setPlatformFill(context, cairoContext, gcp); cairo_new_path(cairoContext); @@ -244,7 +196,7 @@ static void fillCurrentCairoPath(GraphicsContext* context, GraphicsContextPrivat static void strokeCurrentCairoPath(GraphicsContext* context, GraphicsContextPrivate* gcp, cairo_t* cairoContext) { - drawPathShadow(context, gcp, false, true); + drawPathShadow(context, gcp, Stroke); setPlatformStroke(context, cairoContext, gcp); cairo_new_path(cairoContext); } @@ -281,12 +233,20 @@ void GraphicsContext::savePlatformState() { cairo_save(m_data->cr); m_data->save(); + m_data->shadowStack.append(m_data->shadow); } void GraphicsContext::restorePlatformState() { cairo_restore(m_data->cr); m_data->restore(); + + if (m_data->shadowStack.isEmpty()) + m_data->shadow = ContextShadow(); + else { + m_data->shadow = m_data->shadowStack.last(); + m_data->shadowStack.removeLast(); + } } // Draws a filled rectangle with a stroked border. @@ -602,7 +562,7 @@ void GraphicsContext::drawPath() setPathOnCairoContext(cr, m_data->m_pendingPath.context()); cairo_set_fill_rule(cr, fillRule() == RULE_EVENODD ? CAIRO_FILL_RULE_EVEN_ODD : CAIRO_FILL_RULE_WINDING); - drawPathShadow(this, m_common, true, true); + drawPathShadow(this, m_common, FillAndStroke); setPlatformFill(this, cr, m_common); setPlatformStroke(this, cr, m_common); @@ -621,49 +581,14 @@ void GraphicsContext::fillRect(const FloatRect& rect) cairo_restore(cr); } -static void drawBorderlessRectShadow(GraphicsContext* context, const FloatRect& rect, const Color& rectColor) -{ -#if ENABLE(FILTERS) - FloatSize shadowOffset; - float shadowBlur; - Color shadowColor; - if (!context->getShadow(shadowOffset, shadowBlur, shadowColor)) - return; - - AffineTransform transform = context->getCTM(); - // drawTiledShadow still does not work with rotations. - if ((transform.isIdentityOrTranslationOrFlipped())) { - cairo_t* cr = context->platformContext(); - cairo_save(cr); - appendWebCorePathToCairoContext(cr, Path::createRectangle(rect)); - FloatSize corner; - IntRect shadowRect(rect); - context->drawTiledShadow(shadowRect, corner, corner, corner, corner, DeviceColorSpace); - cairo_restore(cr); - - return; - } - - IntSize shadowBufferSize; - FloatRect shadowRect; - float radius = 0; - GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, rect, shadowOffset, shadowBlur); - - // Draw shadow into a new ImageBuffer - OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); - GraphicsContext* shadowContext = shadowBuffer->context(); - shadowContext->fillRect(FloatRect(FloatPoint(radius, radius), rect.size()), rectColor, DeviceColorSpace); - - context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius); -#endif -} - -void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace colorSpace) +void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorSpace) { if (paintingDisabled()) return; - drawBorderlessRectShadow(this, rect, color); + if (m_data->hasShadow()) + m_data->shadow.drawRectShadow(this, enclosingIntRect(rect)); + if (color.alpha()) fillRectSourceOver(m_data->cr, rect, color); } @@ -737,8 +662,13 @@ void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int setPlatformStrokeStyle(DottedStroke); #else int radius = (width - 1) / 2; - for (unsigned i = 0; i < rectCount; i++) - appendWebCorePathToCairoContext(cr, Path::createRoundedRectangle(rects[i], FloatSize(radius, radius))); + Path path; + for (unsigned i = 0; i < rectCount; ++i) { + if (i > 0) + path.clear(); + path.addRoundedRect(rects[i], FloatSize(radius, radius)); + appendWebCorePathToCairoContext(cr, path); + } // Force the alpha to 50%. This matches what the Mac does with outline rings. Color ringColor(color.red(), color.green(), color.blue(), 127); @@ -928,57 +858,26 @@ void GraphicsContext::addInnerRoundedRectClip(const IntRect& rect, int thickness cairo_set_fill_rule(cr, savedFillRule); } -void GraphicsContext::setPlatformShadow(FloatSize const& size, float, Color const&, ColorSpace) +void GraphicsContext::setPlatformShadow(FloatSize const& size, float blur, Color const& color, ColorSpace) { - // Cairo doesn't support shadows natively, they are drawn manually in the draw* - // functions - + // Cairo doesn't support shadows natively, they are drawn manually in the draw* functions if (m_common->state.shadowsIgnoreTransforms) { // Meaning that this graphics context is associated with a CanvasRenderingContext // We flip the height since CG and HTML5 Canvas have opposite Y axis m_common->state.shadowOffset = FloatSize(size.width(), -size.height()); - } + m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), -size.height())); + } else + m_data->shadow = ContextShadow(color, blur, FloatSize(size.width(), size.height())); } -void GraphicsContext::applyPlatformShadow(PassOwnPtr<ImageBuffer> buffer, const Color& shadowColor, const FloatRect& shadowRect, float radius) +ContextShadow* GraphicsContext::contextShadow() { -#if ENABLE(FILTERS) - setColor(m_data->cr, shadowColor); - PlatformRefPtr<cairo_surface_t> shadowMask(createShadowMask(buffer, shadowRect, radius)); - cairo_mask_surface(m_data->cr, shadowMask.get(), shadowRect.x(), shadowRect.y()); -#endif -} - -PlatformRefPtr<cairo_surface_t> GraphicsContext::createShadowMask(PassOwnPtr<ImageBuffer> buffer, const FloatRect& shadowRect, float radius) -{ -#if ENABLE(FILTERS) - if (!radius) - return buffer->m_data.m_surface; - - FloatPoint blurRadius = FloatPoint(radius, radius); - float stdDeviation = FEGaussianBlur::calculateStdDeviation(radius); - if (!stdDeviation) - return buffer->m_data.m_surface; - - // create filter - RefPtr<Filter> filter = ImageBufferFilter::create(); - filter->setSourceImage(buffer); - RefPtr<FilterEffect> source = SourceGraphic::create(); - source->setRepaintRectInLocalCoordinates(FloatRect(FloatPoint(), shadowRect.size())); - source->setIsAlphaImage(true); - RefPtr<FilterEffect> blur = FEGaussianBlur::create(stdDeviation, stdDeviation); - FilterEffectVector& inputEffects = blur->inputEffects(); - inputEffects.append(source.get()); - blur->setRepaintRectInLocalCoordinates(FloatRect(FloatPoint(), shadowRect.size())); - blur->apply(filter.get()); - return blur->resultImage()->m_data.m_surface; -#endif + return &m_data->shadow; } - void GraphicsContext::clearPlatformShadow() { - notImplemented(); + m_data->shadow.clear(); } void GraphicsContext::beginTransparencyLayer(float opacity) @@ -1095,43 +994,6 @@ float GraphicsContext::getAlpha() return m_common->state.globalAlpha; } -static inline cairo_operator_t toCairoOperator(CompositeOperator op) -{ - switch (op) { - case CompositeClear: - return CAIRO_OPERATOR_CLEAR; - case CompositeCopy: - return CAIRO_OPERATOR_SOURCE; - case CompositeSourceOver: - return CAIRO_OPERATOR_OVER; - case CompositeSourceIn: - return CAIRO_OPERATOR_IN; - case CompositeSourceOut: - return CAIRO_OPERATOR_OUT; - case CompositeSourceAtop: - return CAIRO_OPERATOR_ATOP; - case CompositeDestinationOver: - return CAIRO_OPERATOR_DEST_OVER; - case CompositeDestinationIn: - return CAIRO_OPERATOR_DEST_IN; - case CompositeDestinationOut: - return CAIRO_OPERATOR_DEST_OUT; - case CompositeDestinationAtop: - return CAIRO_OPERATOR_DEST_ATOP; - case CompositeXOR: - return CAIRO_OPERATOR_XOR; - case CompositePlusDarker: - return CAIRO_OPERATOR_SATURATE; - case CompositeHighlight: - // There is no Cairo equivalent for CompositeHighlight. - return CAIRO_OPERATOR_OVER; - case CompositePlusLighter: - return CAIRO_OPERATOR_ADD; - default: - return CAIRO_OPERATOR_SOURCE; - } -} - void GraphicsContext::setCompositeOperation(CompositeOperator op) { if (paintingDisabled()) @@ -1248,206 +1110,20 @@ static inline FloatPoint getPhase(const FloatRect& dest, const FloatRect& tile) return phase; } -/* - This function uses tiling to improve the performance of the shadow - drawing of rounded rectangles. The code basically does the following - steps: - - 1. Calculate the minimum rectangle size required to create the - tiles - - 2. If that size is smaller than the real rectangle render the new - small 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 small 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 part with solid color to complete the - shadow. - */ -void GraphicsContext::drawTiledShadow(const IntRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius, ColorSpace colorSpace) -{ -#if ENABLE(FILTERS) - FloatSize shadowSize; - float shadowBlur; - Color shadowColor; - if (!getShadow(shadowSize, shadowBlur, shadowColor)) - return; - - // Calculate filter values to create appropriate shadow. - cairo_t* cr = m_data->cr; - - IntSize shadowBufferSize; - FloatRect shadowRect; - float blurRadius = 0; - GraphicsContext::calculateShadowBufferDimensions(shadowBufferSize, shadowRect, blurRadius, rect, shadowSize, shadowBlur); - - // Size of the tiling side. - int sideTileWidth = 1; - float radiusTwice = blurRadius * 2; - - // Find the extra space needed from the curve of the corners. - int extraWidthFromCornerRadii = radiusTwice + max(topLeftRadius.width(), bottomLeftRadius.width()) + - radiusTwice + max(topRightRadius.width(), bottomRightRadius.width()); - int extraHeightFromCornerRadii = radiusTwice + max(topLeftRadius.height(), topRightRadius.height()) + - radiusTwice + max(bottomLeftRadius.height(), bottomRightRadius.height()); - - // The length of a side of the buffer is the enough space for four blur radii, - // the radii of the corners, and then 1 pixel to draw the side tiles. - IntSize smallBufferSize = IntSize(sideTileWidth + extraWidthFromCornerRadii, - sideTileWidth + extraHeightFromCornerRadii); - - if ((smallBufferSize.width() > shadowBufferSize.width()) || (smallBufferSize.height() > shadowBufferSize.height()) || (blurRadius <= 0)) { - // Create suitably-sized ImageBuffer to hold the shadow. - OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); - if (!shadowBuffer) - return; - - // Draw shadow into a new ImageBuffer. - cairo_t* shadowContext = shadowBuffer->context()->platformContext(); - copyContextProperties(cr, shadowContext); - cairo_translate(shadowContext, -rect.x() + blurRadius, -rect.y() + blurRadius); - cairo_new_path(shadowContext); - OwnPtr<cairo_path_t> path(cairo_copy_path(cr)); - cairo_append_path(shadowContext, path.get()); - - setPlatformFill(this, shadowContext, m_common); - - applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, blurRadius); - - return; - } - - OwnPtr<ImageBuffer> smallBuffer = ImageBuffer::create(smallBufferSize); - if (!smallBuffer) - return; - - IntRect smallRect = IntRect(blurRadius, blurRadius, smallBufferSize.width() - radiusTwice, smallBufferSize.height() - radiusTwice); - - // Draw shadow into a new ImageBuffer. - cairo_t* smallBufferContext = smallBuffer->context()->platformContext(); - copyContextProperties(cr, smallBufferContext); - appendWebCorePathToCairoContext(smallBuffer->context()->platformContext(), Path::createRoundedRectangle(smallRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius)); - setPlatformFill(this, smallBufferContext, m_common); - - OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(smallBufferSize); - if (!shadowBuffer) - return; - - smallRect.setSize(smallBufferSize); - - PlatformRefPtr<cairo_surface_t> shadowMask(createShadowMask(smallBuffer.release(), smallRect, blurRadius)); - - cairo_t* shadowContext = shadowBuffer->context()->platformContext(); - setColor(shadowContext, shadowColor); - cairo_mask_surface(shadowContext, shadowMask.get(), 0, 0); - - // Fill the internal part of the shadow. - shadowRect.inflate(-radiusTwice); - if (!shadowRect.isEmpty()) { - cairo_save(cr); - appendWebCorePathToCairoContext(cr, Path::createRoundedRectangle(shadowRect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius)); - setColor(cr, shadowColor); - cairo_fill(cr); - cairo_restore(cr); - } - shadowRect.inflate(radiusTwice); - - // Draw top side. - FloatRect tileRect = FloatRect(radiusTwice + topLeftRadius.width(), 0, sideTileWidth, radiusTwice); - FloatRect destRect = tileRect; - destRect.move(shadowRect.x(), shadowRect.y()); - destRect.setWidth(shadowRect.width() - topLeftRadius.width() - topRightRadius.width() - blurRadius * 4); - FloatPoint phase = getPhase(destRect, tileRect); - AffineTransform patternTransform; - patternTransform.makeIdentity(); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the bottom side. - tileRect = FloatRect(radiusTwice + bottomLeftRadius.width(), smallBufferSize.height() - radiusTwice, sideTileWidth, radiusTwice); - destRect = tileRect; - destRect.move(shadowRect.x(), shadowRect.y() + radiusTwice + rect.height() - smallBufferSize.height()); - destRect.setWidth(shadowRect.width() - bottomLeftRadius.width() - bottomRightRadius.width() - blurRadius * 4); - phase = getPhase(destRect, tileRect); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the right side. - tileRect = FloatRect(smallBufferSize.width() - radiusTwice, radiusTwice + topRightRadius.height(), radiusTwice, sideTileWidth); - destRect = tileRect; - destRect.move(shadowRect.x() + radiusTwice + rect.width() - smallBufferSize.width(), shadowRect.y()); - destRect.setHeight(shadowRect.height() - topRightRadius.height() - bottomRightRadius.height() - blurRadius * 4); - phase = getPhase(destRect, tileRect); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the left side. - tileRect = FloatRect(0, radiusTwice + topLeftRadius.height(), radiusTwice, sideTileWidth); - destRect = tileRect; - destRect.move(shadowRect.x(), shadowRect.y()); - destRect.setHeight(shadowRect.height() - topLeftRadius.height() - bottomLeftRadius.height() - blurRadius * 4); - phase = FloatPoint(destRect.x() - tileRect.x(), destRect.y() - tileRect.y()); - shadowBuffer->drawPattern(this, tileRect, patternTransform, - phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the top left corner. - tileRect = FloatRect(0, 0, radiusTwice + topLeftRadius.width(), radiusTwice + topLeftRadius.height()); - destRect = tileRect; - destRect.move(shadowRect.x(), shadowRect.y()); - phase = getPhase(destRect, tileRect); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the top right corner. - tileRect = FloatRect(smallBufferSize.width() - radiusTwice - topRightRadius.width(), 0, radiusTwice + topRightRadius.width(), radiusTwice + topRightRadius.height()); - destRect = tileRect; - destRect.move(shadowRect.x() + rect.width() - smallBufferSize.width() + radiusTwice, shadowRect.y()); - phase = getPhase(destRect, tileRect); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the bottom right corner. - tileRect = FloatRect(smallBufferSize.width() - radiusTwice - bottomRightRadius.width(), smallBufferSize.height() - radiusTwice - bottomRightRadius.height(), radiusTwice + bottomRightRadius.width(), radiusTwice + bottomRightRadius.height()); - destRect = tileRect; - destRect.move(shadowRect.x() + rect.width() - smallBufferSize.width() + radiusTwice, shadowRect.y() + rect.height() - smallBufferSize.height() + radiusTwice); - phase = getPhase(destRect, tileRect); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); - - // Draw the bottom left corner. - tileRect = FloatRect(0, smallBufferSize.height() - radiusTwice - bottomLeftRadius.height(), radiusTwice + bottomLeftRadius.width(), radiusTwice + bottomLeftRadius.height()); - destRect = tileRect; - destRect.move(shadowRect.x(), shadowRect.y() + rect.height() - smallBufferSize.height() + radiusTwice); - phase = getPhase(destRect, tileRect); - shadowBuffer->drawPattern(this, tileRect, patternTransform, phase, colorSpace, CompositeSourceOver, destRect); -#endif -} - void GraphicsContext::fillRoundedRect(const IntRect& r, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) { if (paintingDisabled()) return; + if (m_data->hasShadow()) + m_data->shadow.drawRectShadow(this, r, topLeft, topRight, bottomLeft, bottomRight); + cairo_t* cr = m_data->cr; cairo_save(cr); - appendWebCorePathToCairoContext(cr, Path::createRoundedRectangle(r, topLeft, topRight, bottomLeft, bottomRight)); + Path path; + path.addRoundedRect(r, topLeft, topRight, bottomLeft, bottomRight); + appendWebCorePathToCairoContext(cr, path); setColor(cr, color); - AffineTransform transform = this->getCTM(); - // drawTiledShadow still does not work with rotations. - if (transform.isIdentityOrTranslationOrFlipped()) - drawTiledShadow(r, topLeft, topRight, bottomLeft, bottomRight, colorSpace); - else - drawPathShadow(this, m_common, true, false); cairo_fill(cr); cairo_restore(cr); } diff --git a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h index 81987ef..527cb72 100644 --- a/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h +++ b/WebCore/platform/graphics/cairo/GraphicsContextPlatformPrivateCairo.h @@ -28,6 +28,7 @@ #include "GraphicsContext.h" #include "CairoPath.h" +#include "ContextShadow.h" #include <cairo.h> #include <math.h> #include <stdio.h> @@ -98,6 +99,10 @@ public: Vector<float> layers; CairoPath m_pendingPath; + ContextShadow shadow; + Vector<ContextShadow> shadowStack; + bool hasShadow() const { return shadow.m_type != ContextShadow::NoShadow; } + #if PLATFORM(GTK) GdkEventExpose* expose; #elif PLATFORM(WIN) diff --git a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp index 976dcb4..d452c3a 100644 --- a/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageBufferCairo.cpp @@ -69,7 +69,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) { } -ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, bool& success) : m_data(size) , m_size(size) { diff --git a/WebCore/platform/graphics/cairo/ImageCairo.cpp b/WebCore/platform/graphics/cairo/ImageCairo.cpp index 904e819..8f7a194 100644 --- a/WebCore/platform/graphics/cairo/ImageCairo.cpp +++ b/WebCore/platform/graphics/cairo/ImageCairo.cpp @@ -31,7 +31,9 @@ #if PLATFORM(CAIRO) #include "AffineTransform.h" +#include "CairoUtilities.h" #include "Color.h" +#include "ContextShadow.h" #include "FloatRect.h" #include "PlatformRefPtrCairo.h" #include "GraphicsContext.h" @@ -133,29 +135,18 @@ void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const Flo cairo_matrix_t matrix = { scaleX, 0, 0, scaleY, srcRect.x(), srcRect.y() }; cairo_pattern_set_matrix(pattern, &matrix); - // Draw the shadow -#if ENABLE(FILTERS) - FloatSize shadowOffset; - float shadowBlur; - Color shadowColor; - if (context->getShadow(shadowOffset, shadowBlur, shadowColor)) { - IntSize shadowBufferSize; - FloatRect shadowRect; - float radius = 0; - context->calculateShadowBufferDimensions(shadowBufferSize, shadowRect, radius, dstRect, shadowOffset, shadowBlur); - shadowColor = colorWithOverrideAlpha(shadowColor.rgb(), (shadowColor.alpha() * context->getAlpha()) / 255.f); - - //draw shadow into a new ImageBuffer - OwnPtr<ImageBuffer> shadowBuffer = ImageBuffer::create(shadowBufferSize); - cairo_t* shadowContext = shadowBuffer->context()->platformContext(); - cairo_set_source(shadowContext, pattern); - cairo_translate(shadowContext, -dstRect.x(), -dstRect.y()); - cairo_rectangle(shadowContext, 0, 0, dstRect.width(), dstRect.height()); - cairo_fill(shadowContext); - - context->applyPlatformShadow(shadowBuffer.release(), shadowColor, shadowRect, radius); + ContextShadow* shadow = context->contextShadow(); + ASSERT(shadow); + if (shadow->m_type != ContextShadow::NoShadow) { + cairo_t* shadowContext = shadow->beginShadowLayer(cr, dstRect); + if (shadowContext) { + cairo_translate(shadowContext, dstRect.x(), dstRect.y()); + cairo_set_source(shadowContext, pattern); + cairo_rectangle(shadowContext, 0, 0, dstRect.width(), dstRect.height()); + cairo_fill(shadowContext); + shadow->endShadowLayer(cr); + } } -#endif // Draw the image. cairo_translate(cr, dstRect.x(), dstRect.y()); @@ -172,46 +163,15 @@ void BitmapImage::draw(GraphicsContext* context, const FloatRect& dst, const Flo } void Image::drawPattern(GraphicsContext* context, const FloatRect& tileRect, const AffineTransform& patternTransform, - const FloatPoint& phase, ColorSpace, CompositeOperator op, const FloatRect& destRect) + const FloatPoint& phase, ColorSpace colorSpace, CompositeOperator op, const FloatRect& destRect) { cairo_surface_t* image = nativeImageForCurrentFrame(); if (!image) // If it's too early we won't have an image yet. return; - // Avoid NaN - if (!isfinite(phase.x()) || !isfinite(phase.y())) - return; - cairo_t* cr = context->platformContext(); - context->save(); - - PlatformRefPtr<cairo_surface_t> clippedImageSurface = 0; - if (tileRect.size() != size()) { - IntRect imageSize = enclosingIntRect(tileRect); - clippedImageSurface = adoptPlatformRef(cairo_image_surface_create(CAIRO_FORMAT_ARGB32, imageSize.width(), imageSize.height())); - PlatformRefPtr<cairo_t> clippedImageContext(cairo_create(clippedImageSurface.get())); - cairo_set_source_surface(clippedImageContext.get(), image, -tileRect.x(), -tileRect.y()); - cairo_paint(clippedImageContext.get()); - image = clippedImageSurface.get(); - } - - cairo_pattern_t* pattern = cairo_pattern_create_for_surface(image); - cairo_pattern_set_extend(pattern, CAIRO_EXTEND_REPEAT); - cairo_matrix_t pattern_matrix = cairo_matrix_t(patternTransform); - cairo_matrix_t phase_matrix = {1, 0, 0, 1, phase.x() + tileRect.x() * patternTransform.a(), phase.y() + tileRect.y() * patternTransform.d()}; - cairo_matrix_t combined; - cairo_matrix_multiply(&combined, &pattern_matrix, &phase_matrix); - cairo_matrix_invert(&combined); - cairo_pattern_set_matrix(pattern, &combined); - - context->setCompositeOperation(op); - cairo_set_source(cr, pattern); - cairo_pattern_destroy(pattern); - cairo_rectangle(cr, destRect.x(), destRect.y(), destRect.width(), destRect.height()); - cairo_fill(cr); - - context->restore(); + drawPatternToCairoContext(cr, image, size(), tileRect, patternTransform, phase, toCairoOperator(op), destRect); if (imageObserver()) imageObserver()->didDraw(this); diff --git a/WebCore/platform/graphics/cairo/PathCairo.cpp b/WebCore/platform/graphics/cairo/PathCairo.cpp index 776bceb..d5045be 100644 --- a/WebCore/platform/graphics/cairo/PathCairo.cpp +++ b/WebCore/platform/graphics/cairo/PathCairo.cpp @@ -337,40 +337,4 @@ void Path::transform(const AffineTransform& trans) cairo_transform(cr, &c_matrix); } -String Path::debugString() const -{ - if (isEmpty()) - return String(); - - String pathString; - OwnPtr<cairo_path_t> path(cairo_copy_path(platformPath()->context())); - cairo_path_data_t* data; - - for (int i = 0; i < path->num_data; i += path->data[i].header.length) { - data = &path->data[i]; - switch (data->header.type) { - case CAIRO_PATH_MOVE_TO: - if (i < (path->num_data - path->data[i].header.length)) - pathString += String::format("M%.2f,%.2f ", - data[1].point.x, data[1].point.y); - break; - case CAIRO_PATH_LINE_TO: - pathString += String::format("L%.2f,%.2f ", - data[1].point.x, data[1].point.y); - break; - case CAIRO_PATH_CURVE_TO: - pathString += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ", - data[1].point.x, data[1].point.y, - data[2].point.x, data[2].point.y, - data[3].point.x, data[3].point.y); - break; - case CAIRO_PATH_CLOSE_PATH: - pathString += "Z "; - break; - } - } - - return pathString.simplifyWhiteSpace(); -} - } // namespace WebCore diff --git a/WebCore/platform/graphics/cg/ColorCG.cpp b/WebCore/platform/graphics/cg/ColorCG.cpp index 6f05c38..c9b05da 100644 --- a/WebCore/platform/graphics/cg/ColorCG.cpp +++ b/WebCore/platform/graphics/cg/ColorCG.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 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 @@ -28,6 +28,7 @@ #if PLATFORM(CG) +#include "GraphicsContextCG.h" #include <wtf/Assertions.h> #include <wtf/RetainPtr.h> #include <ApplicationServices/ApplicationServices.h> @@ -69,37 +70,79 @@ Color::Color(CGColorRef color) m_valid = true; } -#if OS(WINDOWS) +static inline CGColorSpaceRef cachedCGColorSpace(ColorSpace colorSpace) +{ + switch (colorSpace) { + case ColorSpaceDeviceRGB: + return deviceRGBColorSpaceRef(); + case ColorSpaceSRGB: + return sRGBColorSpaceRef(); + case ColorSpaceLinearRGB: + return linearRGBColorSpaceRef(); + } + ASSERT_NOT_REACHED(); + return deviceRGBColorSpaceRef(); +} -CGColorRef createCGColor(const Color& c) +static CGColorRef leakCGColor(const Color& color, ColorSpace colorSpace) { - CGColorRef color = NULL; -#ifdef OBSOLETE_COLORSYNC_API - CMProfileRef prof = NULL; - CMGetSystemProfile(&prof); - RetainPtr<CGColorSpaceRef> rgbSpace(AdoptCF, CGColorSpaceCreateWithPlatformColorSpace(prof)); -#else - ColorSyncProfileRef prof = ColorSyncProfileCreateWithDisplayID(0); - RetainPtr<CGColorSpaceRef> rgbSpace(AdoptCF, CGColorSpaceCreateWithPlatformColorSpace(const_cast<void*>(reinterpret_cast<const void*>(prof)))); -#endif - - if (rgbSpace) { - CGFloat components[4] = { static_cast<CGFloat>(c.red()) / 255, static_cast<CGFloat>(c.green()) / 255, - static_cast<CGFloat>(c.blue()) / 255, static_cast<CGFloat>(c.alpha()) / 255 }; - color = CGColorCreate(rgbSpace.get(), components); + CGFloat components[4]; + color.getRGBA(components[0], components[1], components[2], components[3]); + return CGColorCreate(cachedCGColorSpace(colorSpace), components); +} + +template<ColorSpace colorSpace> static CGColorRef cachedCGColor(const Color& color) +{ + switch (color.rgb()) { + case Color::transparent: { + static CGColorRef transparentCGColor = leakCGColor(color, colorSpace); + return transparentCGColor; + } + case Color::black: { + static CGColorRef blackCGColor = leakCGColor(color, colorSpace); + return blackCGColor; + } + case Color::white: { + static CGColorRef whiteCGColor = leakCGColor(color, colorSpace); + return whiteCGColor; + } } -#ifdef OBSOLETE_COLORSYNC_API - CMCloseProfile(prof); -#else - if (prof) - CFRelease(prof); -#endif + ASSERT(color.rgb()); - return color; + const size_t cacheSize = 32; + static RGBA32 cachedRGBAValues[cacheSize]; + static RetainPtr<CGColorRef>* cachedCGColors = new RetainPtr<CGColorRef>[cacheSize]; + + for (size_t i = 0; i < cacheSize; ++i) { + if (cachedRGBAValues[i] == color.rgb()) + return cachedCGColors[i].get(); + } + + CGColorRef newCGColor = leakCGColor(color, colorSpace); + + static size_t cursor; + cachedRGBAValues[cursor] = color.rgb(); + cachedCGColors[cursor].adoptCF(newCGColor); + if (++cursor == cacheSize) + cursor = 0; + + return newCGColor; } -#endif // OS(WINDOWS) +CGColorRef cachedCGColor(const Color& color, ColorSpace colorSpace) +{ + switch (colorSpace) { + case ColorSpaceDeviceRGB: + return cachedCGColor<ColorSpaceDeviceRGB>(color); + case ColorSpaceSRGB: + return cachedCGColor<ColorSpaceSRGB>(color); + case ColorSpaceLinearRGB: + return cachedCGColor<ColorSpaceLinearRGB>(color); + } + ASSERT_NOT_REACHED(); + return cachedCGColor(color, ColorSpaceDeviceRGB); +} } diff --git a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp index 2a81fd2..fe4fc7f 100644 --- a/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContext3DCG.cpp @@ -64,9 +64,35 @@ bool GraphicsContext3D::getImageData(Image* image, return false; size_t width = CGImageGetWidth(cgImage); size_t height = CGImageGetHeight(cgImage); - if (!width || !height || CGImageGetBitsPerComponent(cgImage) != 8) + if (!width || !height) return false; - size_t componentsPerPixel = CGImageGetBitsPerPixel(cgImage) / 8; + size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage); + size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage); + if (bitsPerComponent != 8 && bitsPerComponent != 16) + return false; + if (bitsPerPixel % bitsPerComponent) + return false; + size_t componentsPerPixel = bitsPerPixel / bitsPerComponent; + bool srcByteOrder16Big = false; + if (bitsPerComponent == 16) { + CGBitmapInfo bitInfo = CGImageGetBitmapInfo(cgImage); + switch (bitInfo & kCGBitmapByteOrderMask) { + case kCGBitmapByteOrder16Big: + srcByteOrder16Big = true; + break; + case kCGBitmapByteOrder16Little: + srcByteOrder16Big = false; + break; + case kCGBitmapByteOrderDefault: + // This is a bug in earlier version of cg where the default endian + // is little whereas the decoded 16-bit png image data is actually + // Big. Later version (10.6.4) no longer returns ByteOrderDefault. + srcByteOrder16Big = true; + break; + default: + return false; + } + } SourceDataFormat srcDataFormat = kSourceFormatRGBA8; AlphaOp neededAlphaOp = kAlphaDoNothing; switch (CGImageGetAlphaInfo(cgImage)) { @@ -79,10 +105,16 @@ bool GraphicsContext3D::getImageData(Image* image, neededAlphaOp = kAlphaDoUnmultiply; switch (componentsPerPixel) { case 2: - srcDataFormat = kSourceFormatAR8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatAR8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatAR16Big : kSourceFormatAR16Little; break; case 4: - srcDataFormat = kSourceFormatARGB8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatARGB8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatARGB16Big : kSourceFormatARGB16Little; break; default: return false; @@ -94,13 +126,22 @@ bool GraphicsContext3D::getImageData(Image* image, neededAlphaOp = kAlphaDoPremultiply; switch (componentsPerPixel) { case 1: - srcDataFormat = kSourceFormatA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatA16Big : kSourceFormatA16Little; break; case 2: - srcDataFormat = kSourceFormatAR8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatAR8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatAR16Big : kSourceFormatAR16Little; break; case 4: - srcDataFormat = kSourceFormatARGB8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatARGB8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatARGB16Big : kSourceFormatARGB16Little; break; default: return false; @@ -110,10 +151,16 @@ bool GraphicsContext3D::getImageData(Image* image, // This path is only accessible for MacOS earlier than 10.6.4. switch (componentsPerPixel) { case 2: - srcDataFormat = kSourceFormatAR8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatAR8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatAR16Big : kSourceFormatAR16Little; break; case 4: - srcDataFormat = kSourceFormatARGB8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatARGB8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatARGB16Big : kSourceFormatARGB16Little; break; default: return false; @@ -127,10 +174,16 @@ bool GraphicsContext3D::getImageData(Image* image, neededAlphaOp = kAlphaDoUnmultiply; switch (componentsPerPixel) { case 2: - srcDataFormat = kSourceFormatRA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRA16Big : kSourceFormatRA16Little; break; case 4: - srcDataFormat = kSourceFormatRGBA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRGBA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRGBA16Big : kSourceFormatRGBA16Little; break; default: return false; @@ -141,13 +194,22 @@ bool GraphicsContext3D::getImageData(Image* image, neededAlphaOp = kAlphaDoPremultiply; switch (componentsPerPixel) { case 1: - srcDataFormat = kSourceFormatA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatA16Big : kSourceFormatA16Little; break; case 2: - srcDataFormat = kSourceFormatRA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRA16Big : kSourceFormatRA16Little; break; case 4: - srcDataFormat = kSourceFormatRGBA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRGBA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRGBA16Big : kSourceFormatRGBA16Little; break; default: return false; @@ -156,10 +218,16 @@ bool GraphicsContext3D::getImageData(Image* image, case kCGImageAlphaNoneSkipLast: switch (componentsPerPixel) { case 2: - srcDataFormat = kSourceFormatRA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRA16Big : kSourceFormatRA16Little; break; case 4: - srcDataFormat = kSourceFormatRGBA8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRGBA8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRGBA16Big : kSourceFormatRGBA16Little; break; default: return false; @@ -168,10 +236,16 @@ bool GraphicsContext3D::getImageData(Image* image, case kCGImageAlphaNone: switch (componentsPerPixel) { case 1: - srcDataFormat = kSourceFormatR8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatR8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatR16Big : kSourceFormatR16Little; break; case 3: - srcDataFormat = kSourceFormatRGB8; + if (bitsPerComponent == 8) + srcDataFormat = kSourceFormatRGB8; + else + srcDataFormat = srcByteOrder16Big ? kSourceFormatRGB16Big : kSourceFormatRGB16Little; break; default: return false; @@ -188,7 +262,7 @@ bool GraphicsContext3D::getImageData(Image* image, outputVector.resize(width * height * 4); unsigned int srcUnpackAlignment = 0; size_t bytesPerRow = CGImageGetBytesPerRow(cgImage); - unsigned int padding = bytesPerRow - componentsPerPixel * width; + unsigned int padding = bytesPerRow - bitsPerPixel / 8 * width; if (padding) { srcUnpackAlignment = padding + 1; while (bytesPerRow % srcUnpackAlignment) diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp index e5079dc..9e0a2f5 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextCG.cpp +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.cpp @@ -26,7 +26,7 @@ #define _USE_MATH_DEFINES 1 #include "config.h" -#include "GraphicsContext.h" +#include "GraphicsContextCG.h" #include "AffineTransform.h" #include "FloatConversion.h" @@ -69,60 +69,14 @@ using namespace std; namespace WebCore { -static CGColorRef createCGColorWithColorSpace(const Color& color, ColorSpace colorSpace) -{ - CGFloat components[4]; - color.getRGBA(components[0], components[1], components[2], components[3]); - - CGColorRef cgColor = 0; - if (colorSpace == sRGBColorSpace) - cgColor = CGColorCreate(sRGBColorSpaceRef(), components); - else - cgColor = CGColorCreate(deviceRGBColorSpaceRef(), components); - - return cgColor; -} - static void setCGFillColor(CGContextRef context, const Color& color, ColorSpace colorSpace) { - CGColorRef cgColor = createCGColorWithColorSpace(color, colorSpace); - CGContextSetFillColorWithColor(context, cgColor); - CFRelease(cgColor); + CGContextSetFillColorWithColor(context, cachedCGColor(color, colorSpace)); } static void setCGStrokeColor(CGContextRef context, const Color& color, ColorSpace colorSpace) { - CGColorRef cgColor = createCGColorWithColorSpace(color, colorSpace); - CGContextSetStrokeColorWithColor(context, cgColor); - CFRelease(cgColor); -} - -static void setCGFillColorSpace(CGContextRef context, ColorSpace colorSpace) -{ - switch (colorSpace) { - case DeviceColorSpace: - break; - case sRGBColorSpace: - CGContextSetFillColorSpace(context, sRGBColorSpaceRef()); - break; - default: - ASSERT_NOT_REACHED(); - break; - } -} - -static void setCGStrokeColorSpace(CGContextRef context, ColorSpace colorSpace) -{ - switch (colorSpace) { - case DeviceColorSpace: - break; - case sRGBColorSpace: - CGContextSetStrokeColorSpace(context, sRGBColorSpaceRef()); - break; - default: - ASSERT_NOT_REACHED(); - break; - } + CGContextSetStrokeColorWithColor(context, cachedCGColor(color, colorSpace)); } CGColorSpaceRef deviceRGBColorSpaceRef() @@ -142,6 +96,17 @@ CGColorSpaceRef sRGBColorSpaceRef() #endif } +CGColorSpaceRef linearRGBColorSpaceRef() +{ + // FIXME: Windows should be able to use kCGColorSpaceGenericRGBLinear, this is tracked by http://webkit.org/b/31363. +#if PLATFORM(WIN) || defined(BUILDING_ON_TIGER) + return deviceRGBColorSpaceRef(); +#else + static CGColorSpaceRef linearRGBSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear); + return linearRGBSpace; +#endif +} + GraphicsContext::GraphicsContext(CGContextRef cgContext) : m_common(createGraphicsContextPrivate()) , m_data(new GraphicsContextPlatformPrivate(cgContext)) @@ -586,9 +551,6 @@ void GraphicsContext::fillPath() CGContextRef context = platformContext(); - // FIXME: Is this helpful and correct in the fillPattern and fillGradient cases? - setCGFillColorSpace(context, m_common->state.fillColorSpace); - if (m_common->state.fillGradient) { CGContextSaveGState(context); if (fillRule() == RULE_EVENODD) @@ -613,9 +575,6 @@ void GraphicsContext::strokePath() CGContextRef context = platformContext(); - // FIXME: Is this helpful and correct in the strokePattern and strokeGradient cases? - setCGStrokeColorSpace(context, m_common->state.strokeColorSpace); - if (m_common->state.strokeGradient) { CGContextSaveGState(context); CGContextReplacePathWithStrokedPath(context); @@ -638,9 +597,6 @@ void GraphicsContext::fillRect(const FloatRect& rect) CGContextRef context = platformContext(); - // FIXME: Is this helpful and correct in the fillPattern and fillGradient cases? - setCGFillColorSpace(context, m_common->state.fillColorSpace); - if (m_common->state.fillGradient) { CGContextSaveGState(context); CGContextClipToRect(context, rect); @@ -659,17 +615,18 @@ void GraphicsContext::fillRect(const FloatRect& rect, const Color& color, ColorS { if (paintingDisabled()) return; + CGContextRef context = platformContext(); Color oldFillColor = fillColor(); ColorSpace oldColorSpace = fillColorSpace(); if (oldFillColor != color || oldColorSpace != colorSpace) - setCGFillColor(context, color, colorSpace); + setCGFillColor(context, color, colorSpace); CGContextFillRect(context, rect); if (oldFillColor != color || oldColorSpace != colorSpace) - setCGFillColor(context, oldFillColor, oldColorSpace); + setCGFillColor(context, oldFillColor, oldColorSpace); } void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLeft, const IntSize& topRight, const IntSize& bottomLeft, const IntSize& bottomRight, const Color& color, ColorSpace colorSpace) @@ -684,7 +641,9 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef if (oldFillColor != color || oldColorSpace != colorSpace) setCGFillColor(context, color, colorSpace); - addPath(Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight)); + Path path; + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + addPath(path); fillPath(); if (oldFillColor != color || oldColorSpace != colorSpace) @@ -821,13 +780,8 @@ void GraphicsContext::setPlatformShadow(const FloatSize& offset, float blur, con // and we should therefore just use the default shadow color. if (!color.isValid()) CGContextSetShadow(context, CGSizeMake(xOffset, yOffset), blurRadius); - else { - RetainPtr<CGColorRef> colorCG(AdoptCF, createCGColorWithColorSpace(color, colorSpace)); - CGContextSetShadowWithColor(context, - CGSizeMake(xOffset, yOffset), - blurRadius, - colorCG.get()); - } + else + CGContextSetShadowWithColor(context, CGSizeMake(xOffset, yOffset), blurRadius, cachedCGColor(color, colorSpace)); } void GraphicsContext::clearPlatformShadow() @@ -865,9 +819,6 @@ void GraphicsContext::strokeRect(const FloatRect& r, float lineWidth) CGContextRef context = platformContext(); - // FIXME: Is this helpful and correct in the strokePattern and strokeGradient cases? - setCGStrokeColorSpace(context, m_common->state.strokeColorSpace); - if (m_common->state.strokeGradient) { CGContextSaveGState(context); setStrokeThickness(lineWidth); diff --git a/WebCore/platform/graphics/cg/GraphicsContextCG.h b/WebCore/platform/graphics/cg/GraphicsContextCG.h new file mode 100644 index 0000000..5de95ef --- /dev/null +++ b/WebCore/platform/graphics/cg/GraphicsContextCG.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2006, 2007, 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 GraphicsContextCG_h +#define GraphicsContextCG_h + +#include "GraphicsContext.h" + +typedef struct CGColorSpace *CGColorSpaceRef; + +namespace WebCore { + +CGColorSpaceRef deviceRGBColorSpaceRef(); +CGColorSpaceRef sRGBColorSpaceRef(); +CGColorSpaceRef linearRGBColorSpaceRef(); + +} + +#endif diff --git a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h index aac4f45..1d0a99f 100644 --- a/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h +++ b/WebCore/platform/graphics/cg/GraphicsContextPlatformPrivateCG.h @@ -28,12 +28,6 @@ namespace WebCore { -// FIXME: This would be in GraphicsContextCG.h if that existed. -CGColorSpaceRef deviceRGBColorSpaceRef(); - -// FIXME: This would be in GraphicsContextCG.h if that existed. -CGColorSpaceRef sRGBColorSpaceRef(); - class GraphicsContextPlatformPrivate { public: GraphicsContextPlatformPrivate(CGContextRef cgContext) diff --git a/WebCore/platform/graphics/cg/ImageBufferCG.cpp b/WebCore/platform/graphics/cg/ImageBufferCG.cpp index ecbcf60..640692a 100644 --- a/WebCore/platform/graphics/cg/ImageBufferCG.cpp +++ b/WebCore/platform/graphics/cg/ImageBufferCG.cpp @@ -31,12 +31,12 @@ #include "Base64.h" #include "BitmapImage.h" #include "GraphicsContext.h" +#include "GraphicsContextCG.h" #include "ImageData.h" #include "MIMETypeRegistry.h" -#include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> #include <wtf/Assertions.h> -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/OwnArrayPtr.h> #include <wtf/RetainPtr.h> #include <wtf/Threading.h> @@ -56,7 +56,7 @@ ImageBufferData::ImageBufferData(const IntSize&) { } -ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace imageColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -65,12 +65,11 @@ ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, b return; unsigned bytesPerRow = size.width(); - if (imageColorSpace != GrayScale) { - // Protect against overflow - if (bytesPerRow > 0x3FFFFFFF) - return; - bytesPerRow *= 4; - } + + // Protect against overflow + if (bytesPerRow > 0x3FFFFFFF) + return; + bytesPerRow *= 4; m_data.m_bytesPerRow = bytesPerRow; size_t dataSize = size.height() * bytesPerRow; @@ -80,27 +79,20 @@ ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, b ASSERT((reinterpret_cast<size_t>(m_data.m_data) & 2) == 0); switch(imageColorSpace) { - case DeviceRGB: - m_data.m_colorSpace.adoptCF(CGColorSpaceCreateDeviceRGB()); - break; - case GrayScale: - m_data.m_colorSpace.adoptCF(CGColorSpaceCreateDeviceGray()); - break; -#if ((PLATFORM(MAC) || PLATFORM(CHROMIUM)) && !defined(BUILDING_ON_TIGER)) - case LinearRGB: - m_data.m_colorSpace.adoptCF(CGColorSpaceCreateWithName(kCGColorSpaceGenericRGBLinear)); - break; - -#endif - default: - m_data.m_colorSpace.adoptCF(CGColorSpaceCreateDeviceRGB()); - break; + case ColorSpaceDeviceRGB: + m_data.m_colorSpace = deviceRGBColorSpaceRef(); + break; + case ColorSpaceSRGB: + m_data.m_colorSpace = sRGBColorSpaceRef(); + break; + case ColorSpaceLinearRGB: + m_data.m_colorSpace = linearRGBColorSpaceRef(); + break; } - m_data.m_grayScale = imageColorSpace == GrayScale; - m_data.m_bitmapInfo = m_data.m_grayScale ? kCGImageAlphaNone : kCGImageAlphaPremultipliedLast; + m_data.m_bitmapInfo = kCGImageAlphaPremultipliedLast; RetainPtr<CGContextRef> cgContext(AdoptCF, CGBitmapContextCreate(m_data.m_data, size.width(), size.height(), 8, bytesPerRow, - m_data.m_colorSpace.get(), m_data.m_bitmapInfo)); + m_data.m_colorSpace, m_data.m_bitmapInfo)); if (!cgContext) return; @@ -135,8 +127,8 @@ PassRefPtr<Image> ImageBuffer::copyImage() const static CGImageRef cgImage(const IntSize& size, const ImageBufferData& data) { - return CGImageCreate(size.width(), size.height(), 8, data.m_grayScale ? 8 : 32, data.m_bytesPerRow, - data.m_colorSpace.get(), data.m_bitmapInfo, data.m_dataProvider.get(), 0, true, kCGRenderingIntentDefault); + return CGImageCreate(size.width(), size.height(), 8, 32, data.m_bytesPerRow, + data.m_colorSpace, data.m_bitmapInfo, data.m_dataProvider.get(), 0, true, kCGRenderingIntentDefault); } void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, const FloatRect& destRect, const FloatRect& srcRect, @@ -145,7 +137,7 @@ void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, if (destContext == context()) { // We're drawing into our own buffer. In order for this to work, we need to copy the source buffer first. RefPtr<Image> copy = copyImage(); - destContext->drawImage(copy.get(), DeviceColorSpace, destRect, srcRect, op, useLowQualityScale); + destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, useLowQualityScale); } else { RefPtr<Image> imageForRendering = BitmapImage::create(cgImage(m_size, m_data)); destContext->drawImage(imageForRendering.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); @@ -371,7 +363,7 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con base64Encode(reinterpret_cast<const char*>(CFDataGetBytePtr(data.get())), CFDataGetLength(data.get()), out); out.append('\0'); - return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data()); + return makeString("data:", mimeType, ";base64,", out.data()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/cg/ImageBufferData.h b/WebCore/platform/graphics/cg/ImageBufferData.h index 2f9d854..456c934 100644 --- a/WebCore/platform/graphics/cg/ImageBufferData.h +++ b/WebCore/platform/graphics/cg/ImageBufferData.h @@ -46,9 +46,8 @@ public: RetainPtr<CGDataProviderRef> m_dataProvider; CGBitmapInfo m_bitmapInfo; - bool m_grayScale; unsigned m_bytesPerRow; - RetainPtr<CGColorSpaceRef> m_colorSpace; + CGColorSpaceRef m_colorSpace; }; } // namespace WebCore diff --git a/WebCore/platform/graphics/cg/ImageCG.cpp b/WebCore/platform/graphics/cg/ImageCG.cpp index 70a80a0..c7ed0c8 100644 --- a/WebCore/platform/graphics/cg/ImageCG.cpp +++ b/WebCore/platform/graphics/cg/ImageCG.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2004, 2005, 2006 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -31,12 +31,12 @@ #include "AffineTransform.h" #include "FloatConversion.h" #include "FloatRect.h" -#include "GraphicsContext.h" -#include "GraphicsContextPlatformPrivateCG.h" +#include "GraphicsContextCG.h" #include "ImageObserver.h" #include "PDFDocumentImage.h" #include "PlatformString.h" #include <ApplicationServices/ApplicationServices.h> +#include <wtf/RetainPtr.h> #if PLATFORM(MAC) || PLATFORM(CHROMIUM) #include "WebCoreSystemInterface.h" @@ -138,11 +138,12 @@ static RetainPtr<CGImageRef> imageWithColorSpace(CGImageRef originalImage, Color return originalImage; switch (colorSpace) { - case DeviceColorSpace: + case ColorSpaceDeviceRGB: return originalImage; - case sRGBColorSpace: - return RetainPtr<CGImageRef>(AdoptCF, CGImageCreateCopyWithColorSpace(originalImage, - sRGBColorSpaceRef())); + case ColorSpaceSRGB: + return RetainPtr<CGImageRef>(AdoptCF, CGImageCreateCopyWithColorSpace(originalImage, sRGBColorSpaceRef())); + case ColorSpaceLinearRGB: + return RetainPtr<CGImageRef>(AdoptCF, CGImageCreateCopyWithColorSpace(originalImage, linearRGBColorSpaceRef())); } ASSERT_NOT_REACHED(); @@ -186,7 +187,7 @@ void BitmapImage::draw(GraphicsContext* ctxt, const FloatRect& destRect, const F // containing only the portion we want to display. We need to do this because high-quality // interpolation smoothes sharp edges, causing pixels from outside the source rect to bleed // into the destination rect. See <rdar://problem/6112909>. - shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && srcRect.size() != destRect.size(); + shouldUseSubimage = (interpolationQuality == kCGInterpolationHigh || interpolationQuality == kCGInterpolationDefault) && (srcRect.size() != destRect.size() || !ctxt->getCTM().isIdentityOrTranslationOrFlipped()); float xScale = srcRect.width() / destRect.width(); float yScale = srcRect.height() / destRect.height(); if (shouldUseSubimage) { diff --git a/WebCore/platform/graphics/cg/PathCG.cpp b/WebCore/platform/graphics/cg/PathCG.cpp index 90d4b8a..b47ed02 100644 --- a/WebCore/platform/graphics/cg/PathCG.cpp +++ b/WebCore/platform/graphics/cg/PathCG.cpp @@ -254,61 +254,9 @@ FloatPoint Path::currentPoint() const return CGPathGetCurrentPoint(m_path); } -static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *element) -{ - CFMutableStringRef string = static_cast<CFMutableStringRef>(info); - - CGPoint* points = element->points; - switch (element->type) { - case kCGPathElementMoveToPoint: - CFStringAppendFormat(string, 0, CFSTR("M%.2f,%.2f "), points[0].x, points[0].y); - break; - case kCGPathElementAddLineToPoint: - CFStringAppendFormat(string, 0, CFSTR("L%.2f,%.2f "), points[0].x, points[0].y); - break; - case kCGPathElementAddQuadCurveToPoint: - CFStringAppendFormat(string, 0, CFSTR("Q%.2f,%.2f,%.2f,%.2f "), - points[0].x, points[0].y, points[1].x, points[1].y); - break; - case kCGPathElementAddCurveToPoint: - CFStringAppendFormat(string, 0, CFSTR("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f "), - points[0].x, points[0].y, points[1].x, points[1].y, - points[2].x, points[2].y); - break; - case kCGPathElementCloseSubpath: - CFStringAppendFormat(string, 0, CFSTR("Z ")); - break; - } -} - -static CFStringRef CFStringFromCGPath(CGPathRef path) -{ - if (!path) - return 0; - - CFMutableStringRef string = CFStringCreateMutable(NULL, 0); - CGPathApply(path, string, CGPathToCFStringApplierFunction); - CFStringTrimWhitespace(string); - - - return string; -} - - #pragma mark - #pragma mark Path Management -String Path::debugString() const -{ - String result; - if (!isEmpty()) { - CFStringRef pathString = CFStringFromCGPath(m_path); - result = String(pathString); - CFRelease(pathString); - } - return result; -} - struct PathApplierInfo { void* info; PathApplierFunction function; diff --git a/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp b/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp index 8dda4d6..eba5349 100644 --- a/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/ContentLayerChromium.cpp @@ -43,8 +43,6 @@ #include "PlatformContextSkia.h" #include "skia/ext/platform_canvas.h" #elif PLATFORM(CG) -#include "LocalCurrentGraphicsContext.h" - #include <CoreGraphics/CGBitmapContext.h> #endif @@ -121,6 +119,7 @@ PassRefPtr<ContentLayerChromium> ContentLayerChromium::create(GraphicsLayerChrom ContentLayerChromium::ContentLayerChromium(GraphicsLayerChromium* owner) : LayerChromium(owner) , m_contentsTexture(0) + , m_skipsDraw(false) { } @@ -129,16 +128,6 @@ ContentLayerChromium::~ContentLayerChromium() cleanupResources(); } -void ContentLayerChromium::setLayerRenderer(LayerRendererChromium* renderer) -{ - // If we're changing layer renderers then we need to free up any resources - // allocated by the old renderer. - if (layerRenderer() && layerRenderer() != renderer) - cleanupResources(); - - LayerChromium::setLayerRenderer(renderer); -} - void ContentLayerChromium::cleanupResources() { if (layerRenderer()) { @@ -149,6 +138,42 @@ void ContentLayerChromium::cleanupResources() } } +bool ContentLayerChromium::requiresClippedUpdateRect() const +{ + // To avoid allocating excessively large textures, switch into "large layer mode" if + // one of the layer's dimensions is larger than 2000 pixels or the current size + // of the visible rect. This is a temporary measure until layer tiling is implemented. + static const int maxLayerSize = 2000; + return (m_bounds.width() > max(maxLayerSize, layerRenderer()->rootLayerContentRect().width()) + || m_bounds.height() > max(maxLayerSize, layerRenderer()->rootLayerContentRect().height()) + || !layerRenderer()->checkTextureSize(m_bounds)); +} + +void ContentLayerChromium::calculateClippedUpdateRect(IntRect& dirtyRect, IntRect& drawRect) const +{ + // For the given layer size and content rect, calculate: + // 1) The minimal texture space rectangle to be uploaded, returned in dirtyRect. + // 2) The content rect-relative rectangle to draw this texture in, returned in drawRect. + + const IntRect clipRect = layerRenderer()->currentScissorRect(); + const TransformationMatrix& transform = drawTransform(); + // The layer's draw transform points to the center of the layer, relative to + // the content rect. layerPos is the distance from the top left of the + // layer to the top left of the content rect. + const IntPoint layerPos(m_bounds.width() / 2 - transform.m41(), + m_bounds.height() / 2 - transform.m42()); + // Transform the contentRect into the space of the layer. + IntRect contentRectInLayerSpace(layerPos, clipRect.size()); + + // Clip the entire layer against the visible region in the content rect + // and use that as the drawable texture, instead of the entire layer. + dirtyRect = IntRect(IntPoint(0, 0), m_bounds); + dirtyRect.intersect(contentRectInLayerSpace); + + // The draw position is relative to the content rect. + drawRect = IntRect(toPoint(dirtyRect.location() - layerPos), dirtyRect.size()); +} + void ContentLayerChromium::updateContents() { RenderLayerBacking* backing = static_cast<RenderLayerBacking*>(m_owner->client()); @@ -159,29 +184,47 @@ void ContentLayerChromium::updateContents() ASSERT(layerRenderer()); - // FIXME: Remove this test when tiled layers are implemented. - m_skipsDraw = false; - if (!layerRenderer()->checkTextureSize(m_bounds)) { - m_skipsDraw = true; - return; - } - void* pixels = 0; - IntRect dirtyRect(m_dirtyRect); + IntRect dirtyRect; + IntRect updateRect; IntSize requiredTextureSize; IntSize bitmapSize; - requiredTextureSize = m_bounds; - IntRect boundsRect(IntPoint(0, 0), m_bounds); - - // If the texture needs to be reallocated then we must redraw the entire - // contents of the layer. - if (requiredTextureSize != m_allocatedTextureSize) - dirtyRect = boundsRect; - else { - // Clip the dirtyRect to the size of the layer to avoid drawing outside - // the bounds of the backing texture. - dirtyRect.intersect(boundsRect); + // FIXME: Remove this test when tiled layers are implemented. + if (requiresClippedUpdateRect()) { + // A layer with 3D transforms could require an arbitrarily large number + // of texels to be repainted, so ignore these layers until tiling is + // implemented. + if (!drawTransform().isIdentityOrTranslation()) { + m_skipsDraw = true; + return; + } + + calculateClippedUpdateRect(dirtyRect, m_largeLayerDrawRect); + if (!layerRenderer()->checkTextureSize(m_largeLayerDrawRect.size())) { + m_skipsDraw = true; + return; + } + if (m_largeLayerDirtyRect == dirtyRect) + return; + + m_largeLayerDirtyRect = dirtyRect; + requiredTextureSize = dirtyRect.size(); + updateRect = IntRect(IntPoint(0, 0), dirtyRect.size()); + } else { + dirtyRect = IntRect(m_dirtyRect); + IntRect boundsRect(IntPoint(0, 0), m_bounds); + requiredTextureSize = m_bounds; + // If the texture needs to be reallocated then we must redraw the entire + // contents of the layer. + if (requiredTextureSize != m_allocatedTextureSize) + dirtyRect = boundsRect; + else { + // Clip the dirtyRect to the size of the layer to avoid drawing + // outside the bounds of the backing texture. + dirtyRect.intersect(boundsRect); + } + updateRect = dirtyRect; } #if PLATFORM(SKIA) @@ -228,7 +271,6 @@ void ContentLayerChromium::updateContents() CGContextScaleCTM(contextCG.get(), 1, -1); GraphicsContext graphicsContext(contextCG.get()); - LocalCurrentGraphicsContext scopedNSGraphicsContext(&graphicsContext); // Translate the graphics context into the coordinate system of the dirty rect. graphicsContext.translate(-dirtyRect.x(), -dirtyRect.y()); @@ -246,7 +288,7 @@ void ContentLayerChromium::updateContents() textureId = layerRenderer()->createLayerTexture(); if (pixels) - updateTextureRect(pixels, bitmapSize, requiredTextureSize, dirtyRect, textureId); + updateTextureRect(pixels, bitmapSize, requiredTextureSize, updateRect, textureId); } void ContentLayerChromium::updateTextureRect(void* pixels, const IntSize& bitmapSize, const IntSize& requiredTextureSize, const IntRect& updateRect, unsigned textureId) @@ -272,7 +314,8 @@ void ContentLayerChromium::updateTextureRect(void* pixels, const IntSize& bitmap } m_dirtyRect.setSize(FloatSize()); - m_contentsDirty = false; + // Large layers always stay dirty, because they need to update when the content rect changes. + m_contentsDirty = requiresClippedUpdateRect(); } void ContentLayerChromium::draw() @@ -288,9 +331,21 @@ void ContentLayerChromium::draw() GLC(context, context->bindTexture(GraphicsContext3D::TEXTURE_2D, m_contentsTexture)); layerRenderer()->useShader(sv->contentShaderProgram()); GLC(context, context->uniform1i(sv->shaderSamplerLocation(), 0)); - drawTexturedQuad(context, layerRenderer()->projectionMatrix(), drawTransform(), - bounds().width(), bounds().height(), drawOpacity(), - sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + + if (requiresClippedUpdateRect()) { + float m43 = drawTransform().m43(); + TransformationMatrix transform; + transform.translate3d(m_largeLayerDrawRect.center().x(), m_largeLayerDrawRect.center().y(), m43); + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), + transform, m_largeLayerDrawRect.width(), + m_largeLayerDrawRect.height(), drawOpacity(), + sv->shaderMatrixLocation(), sv->shaderAlphaLocation()); + } else { + drawTexturedQuad(context, layerRenderer()->projectionMatrix(), + drawTransform(), m_bounds.width(), m_bounds.height(), + drawOpacity(), sv->shaderMatrixLocation(), + sv->shaderAlphaLocation()); + } } } diff --git a/WebCore/platform/graphics/chromium/ContentLayerChromium.h b/WebCore/platform/graphics/chromium/ContentLayerChromium.h index 412ba06..32c2c49 100644 --- a/WebCore/platform/graphics/chromium/ContentLayerChromium.h +++ b/WebCore/platform/graphics/chromium/ContentLayerChromium.h @@ -49,7 +49,6 @@ public: virtual void updateContents(); virtual void draw(); virtual bool drawsContent() { return m_owner && m_owner->drawsContent(); } - virtual void setLayerRenderer(LayerRendererChromium*); // Stores values that are shared between instances of this class that are // associated with the same LayerRendererChromium (and hence the same GL @@ -80,12 +79,17 @@ protected: void updateTextureRect(void* pixels, const IntSize& bitmapSize, const IntSize& requiredTextureSize, const IntRect& updateRect, unsigned textureId); - void cleanupResources(); + virtual void cleanupResources(); + bool requiresClippedUpdateRect() const; unsigned m_contentsTexture; IntSize m_allocatedTextureSize; bool m_skipsDraw; +private: + void calculateClippedUpdateRect(IntRect& dirtyRect, IntRect& drawRect) const; + IntRect m_largeLayerDrawRect; + IntRect m_largeLayerDirtyRect; }; } diff --git a/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp b/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp index 9ce0efe..b54a427 100644 --- a/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp +++ b/WebCore/platform/graphics/chromium/DrawingBufferChromium.cpp @@ -48,7 +48,7 @@ struct DrawingBufferInternal { #endif }; -static unsigned generateColorTexture(SharedGraphicsContext3D* context, const IntSize& size) +static unsigned generateColorTexture(GraphicsContext3D* context, const IntSize& size) { unsigned offscreenColorTexture = context->createTexture(); if (!offscreenColorTexture) @@ -66,13 +66,13 @@ static unsigned generateColorTexture(SharedGraphicsContext3D* context, const Int } -DrawingBuffer::DrawingBuffer(SharedGraphicsContext3D* context, const IntSize& size, unsigned framebuffer) +DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, const IntSize& size) : m_context(context) , m_size(size) - , m_framebuffer(framebuffer) + , m_fbo(context->createFramebuffer()) , m_internal(new DrawingBufferInternal) { - context->bindFramebuffer(framebuffer); + context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); m_internal->offscreenColorTexture = generateColorTexture(context, size); } @@ -82,14 +82,22 @@ DrawingBuffer::~DrawingBuffer() if (m_internal->platformLayer) m_internal->platformLayer->setDrawingBuffer(0); #endif - m_context->bindFramebuffer(m_framebuffer); + + if (!m_context) + return; + + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); m_context->deleteTexture(m_internal->offscreenColorTexture); - m_context->deleteFramebuffer(m_framebuffer); + + clear(); } #if USE(ACCELERATED_COMPOSITING) void DrawingBuffer::publishToPlatformLayer() { + if (!m_context) + return; + if (m_callback) m_callback->willPublish(); unsigned parentTexture = m_internal->platformLayer->textureId(); @@ -106,6 +114,9 @@ void DrawingBuffer::publishToPlatformLayer() void DrawingBuffer::reset(const IntSize& newSize) { + if (!m_context) + return; + if (m_size == newSize) return; m_size = newSize; diff --git a/WebCore/platform/graphics/chromium/FontLinux.cpp b/WebCore/platform/graphics/chromium/FontLinux.cpp index a242523..f38273c 100644 --- a/WebCore/platform/graphics/chromium/FontLinux.cpp +++ b/WebCore/platform/graphics/chromium/FontLinux.cpp @@ -161,62 +161,23 @@ static int truncateFixedPointToInteger(HB_Fixed value) // can call |reset| to start over again. class TextRunWalker { public: - TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) - : m_font(font) - , m_startingX(startingX) - , m_offsetX(m_startingX) - , m_run(getTextRun(run)) - , m_iterateBackwards(m_run.rtl()) - , m_wordSpacingAdjustment(0) - , m_padding(0) - , m_padError(0) - { - // Do not use |run| inside this constructor. Use |m_run| instead. - - memset(&m_item, 0, sizeof(m_item)); - // We cannot know, ahead of time, how many glyphs a given script run - // will produce. We take a guess that script runs will not produce more - // than twice as many glyphs as there are code points plus a bit of - // padding and fallback if we find that we are wrong. - createGlyphArrays((m_run.length() + 2) * 2); - - m_item.log_clusters = new unsigned short[m_run.length()]; - - m_item.face = 0; - m_item.font = allocHarfbuzzFont(); - - m_item.item.bidiLevel = m_run.rtl(); - - int length = m_run.length(); - m_item.stringLength = length; - - if (!m_item.item.bidiLevel) - m_item.string = m_run.characters(); - else { - // Assume mirrored character is in the same Unicode multilingual plane as the original one. - UChar* string = new UChar[length]; - mirrorCharacters(string, m_run.characters(), length); - m_item.string = string; - } - - reset(); - } + TextRunWalker(const TextRun&, unsigned, const Font*); + ~TextRunWalker(); - ~TextRunWalker() - { - fastFree(m_item.font); - deleteGlyphArrays(); - delete[] m_item.log_clusters; - if (m_item.item.bidiLevel) - delete[] m_item.string; - } + bool isWordBreak(unsigned, bool); + // setPadding sets a number of pixels to be distributed across the TextRun. + // WebKit uses this to justify text. + void setPadding(int); + void reset(); + void setBackwardsIteration(bool); + // Advance to the next script run, returning false when the end of the + // TextRun has been reached. + bool nextScriptRun(); + float widthOfFullRun(); // setWordSpacingAdjustment sets a delta (in pixels) which is applied at // each word break in the TextRun. - void setWordSpacingAdjustment(int wordSpacingAdjustment) - { - m_wordSpacingAdjustment = wordSpacingAdjustment; - } + void setWordSpacingAdjustment(int wordSpacingAdjustment) { m_wordSpacingAdjustment = wordSpacingAdjustment; } // setLetterSpacingAdjustment sets an additional number of pixels that is // added to the advance after each output cluster. This matches the behaviour @@ -224,393 +185,431 @@ public: // // (NOTE: currently does nothing because I don't know how to get the // cluster information from Harfbuzz.) - void setLetterSpacingAdjustment(int letterSpacingAdjustment) - { - m_letterSpacing = letterSpacingAdjustment; - } - - bool isWordBreak(unsigned i, bool isRTL) - { - if (!isRTL) - return i && isCodepointSpace(m_item.string[i]) && !isCodepointSpace(m_item.string[i - 1]); - return i != m_item.stringLength - 1 && isCodepointSpace(m_item.string[i]) && !isCodepointSpace(m_item.string[i + 1]); - } - - // setPadding sets a number of pixels to be distributed across the TextRun. - // WebKit uses this to justify text. - void setPadding(int padding) - { - m_padding = padding; - if (!m_padding) - return; - - // If we have padding to distribute, then we try to give an equal - // amount to each space. The last space gets the smaller amount, if - // any. - unsigned numWordBreaks = 0; - bool isRTL = m_iterateBackwards; - - for (unsigned i = 0; i < m_item.stringLength; i++) { - if (isWordBreak(i, isRTL)) - numWordBreaks++; - } - - if (numWordBreaks) - m_padPerWordBreak = m_padding / numWordBreaks; - else - m_padPerWordBreak = 0; - } - - void reset() - { - if (m_iterateBackwards) - m_indexOfNextScriptRun = m_run.length() - 1; - else - m_indexOfNextScriptRun = 0; - m_offsetX = m_startingX; - } + void setLetterSpacingAdjustment(int letterSpacingAdjustment) { m_letterSpacing = letterSpacingAdjustment; } // Set the x offset for the next script run. This affects the values in // |xPositions| - void setXOffsetToZero() - { - m_offsetX = 0; - } - - bool rtl() const - { - return m_run.rtl(); - } - - void setBackwardsIteration(bool isBackwards) - { - m_iterateBackwards = isBackwards; - reset(); - } - - // Advance to the next script run, returning false when the end of the - // TextRun has been reached. - bool nextScriptRun() - { - if (m_iterateBackwards) { - // In right-to-left mode we need to render the shaped glyph backwards and - // also render the script runs themselves backwards. So given a TextRun: - // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) - // we render: - // TTTTTTCAAAAAAA - // (and the glyphs in each A, C and T section are backwards too) - if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) - return false; - } else { - if (!hb_utf16_script_run_next(&m_numCodePoints, &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. - // Other WebKit code (e.g. Mac) segments complex text just by finding - // the longest span of text covered by a single font. - // But we currently need to call hb_utf16_script_run_next anyway to fill - // in the harfbuzz data structures to e.g. pick the correct script's shaper. - // So we allow that to run first, then do a second pass over the range it - // found and take the largest subregion that stays within a single font. - const FontData* glyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; - unsigned endOfRun; - for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) { - const FontData* nextGlyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false, false).fontData; - if (nextGlyphData != glyphData) - break; - } - m_item.item.length = endOfRun; - m_indexOfNextScriptRun = m_item.item.pos + endOfRun; - } - - setupFontForScriptRun(); - shapeGlyphs(); - setGlyphXPositions(rtl()); - - return true; - } - - const uint16_t* glyphs() const - { - return m_glyphs16; - } + void setXOffsetToZero() { m_offsetX = 0; } + bool rtl() const { return m_run.rtl(); } + const uint16_t* glyphs() const { return m_glyphs16; } // Return the length of the array returned by |glyphs| - const unsigned length() const - { - return m_item.num_glyphs; - } + const unsigned length() const { return m_item.num_glyphs; } // Return the x offset for each of the glyphs. Note that this is translated // by the current x offset and that the x offset is updated for each script // run. - const SkScalar* xPositions() const - { - return m_xPositions; - } + const SkScalar* xPositions() const { return m_xPositions; } // Get the advances (widths) for each glyph. - const HB_Fixed* advances() const - { - return m_item.advances; - } + const HB_Fixed* advances() const { return m_item.advances; } // Return the width (in px) of the current script run. - const unsigned width() const - { - return m_pixelWidth; - } + const unsigned width() const { return m_pixelWidth; } // Return the cluster log for the current script run. For example: // script run: f i a n c é (fi gets ligatured) // log clutrs: 0 0 1 2 3 4 // So, for each input code point, the log tells you which output glyph was // generated for it. - const unsigned short* logClusters() const - { - return m_item.log_clusters; - } + 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_numCodePoints; } - const FontPlatformData* fontPlatformDataForScriptRun() - { - return reinterpret_cast<FontPlatformData*>(m_item.font->userData); - } + const FontPlatformData* fontPlatformDataForScriptRun() { return reinterpret_cast<FontPlatformData*>(m_item.font->userData); } - float widthOfFullRun() - { - float widthSum = 0; - while (nextScriptRun()) - widthSum += width(); +private: + const TextRun& getTextRun(const TextRun&); + const TextRun& getNormalizedTextRun(const TextRun&); + void setupFontForScriptRun(); + HB_FontRec* allocHarfbuzzFont(); + void deleteGlyphArrays(); + void createGlyphArrays(int); + void resetGlyphArrays(); + void shapeGlyphs(); + void setGlyphXPositions(bool); + void mirrorCharacters(UChar*, const UChar*, int) const; + + // This matches the logic in RenderBlock::findNextLineBreak + static bool isCodepointSpace(HB_UChar16 c) { return c == ' ' || c == '\t'; } - return widthSum; - } + const Font* const m_font; + HB_ShaperItem m_item; + uint16_t* m_glyphs16; // A vector of 16-bit glyph ids. + SkScalar* m_xPositions; // A vector of x positions for each glyph. + ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. + const unsigned m_startingX; // Offset in pixels of the first script 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. -private: - const TextRun& getTextRun(const TextRun& originalRun) - { - // Normalize the text run in two ways: - // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks - // (U+0300..) are used in the run. This conversion is necessary since most OpenType - // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in - // their GSUB tables. - // - // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since - // the API returns FALSE (= not normalized) for complex runs that don't require NFC - // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, - // Harfbuzz will do the same thing for us using the GSUB table. - // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs - // for characters like '\n' otherwise. - for (int i = 0; i < originalRun.length(); ++i) { - UChar ch = originalRun[i]; - UBlockCode block = ::ublock_getCode(ch); - if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) { - return getNormalizedTextRun(originalRun); - } - } - return originalRun; - } + OwnPtr<TextRun> m_normalizedRun; + OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run. + const TextRun& m_run; + bool m_iterateBackwards; + int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break. + float m_padding; // pixels to be distributed over the line at word breaks. + float m_padPerWordBreak; // pixels to be added to each word break. + float m_padError; // |m_padPerWordBreak| might have a fractional component. + // Since we only add a whole number of padding pixels at + // each word break we accumulate error. This is the + // number of pixels that we are behind so far. + unsigned m_letterSpacing; // pixels to be added after each glyph. +}; - const TextRun& getNormalizedTextRun(const TextRun& originalRun) - { - icu::UnicodeString normalizedString; - UErrorCode error = U_ZERO_ERROR; - icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error); - if (U_FAILURE(error)) - return originalRun; - - m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]); - normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error); - ASSERT(U_SUCCESS(error)); - - for (int i = 0; i < normalizedString.length(); ++i) { - if (Font::treatAsSpace(m_normalizedBuffer[i])) - m_normalizedBuffer[i] = ' '; - } - m_normalizedRun.set(new TextRun(originalRun)); - m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length()); - return *m_normalizedRun; - } +TextRunWalker::TextRunWalker(const TextRun& run, unsigned startingX, const Font* font) + : m_font(font) + , m_startingX(startingX) + , m_offsetX(m_startingX) + , m_run(getTextRun(run)) + , m_iterateBackwards(m_run.rtl()) + , m_wordSpacingAdjustment(0) + , m_padding(0) + , m_padError(0) +{ + // Do not use |run| inside this constructor. Use |m_run| instead. - void setupFontForScriptRun() - { - const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; - const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); - m_item.face = platformData.harfbuzzFace(); - void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); - m_item.font->userData = opaquePlatformData; - } + memset(&m_item, 0, sizeof(m_item)); + // We cannot know, ahead of time, how many glyphs a given script run + // will produce. We take a guess that script runs will not produce more + // than twice as many glyphs as there are code points plus a bit of + // padding and fallback if we find that we are wrong. + createGlyphArrays((m_run.length() + 2) * 2); - HB_FontRec* allocHarfbuzzFont() - { - HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); - 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; - } + m_item.log_clusters = new unsigned short[m_run.length()]; + + m_item.face = 0; + m_item.font = allocHarfbuzzFont(); + + m_item.item.bidiLevel = m_run.rtl(); - void deleteGlyphArrays() - { - delete[] m_item.glyphs; - delete[] m_item.attributes; - delete[] m_item.advances; - delete[] m_item.offsets; - delete[] m_glyphs16; - delete[] m_xPositions; + int length = m_run.length(); + m_item.stringLength = length; + + if (!m_item.item.bidiLevel) + m_item.string = m_run.characters(); + else { + // Assume mirrored character is in the same Unicode multilingual plane as the original one. + UChar* string = new UChar[length]; + mirrorCharacters(string, m_run.characters(), length); + m_item.string = string; } - void createGlyphArrays(int size) - { - m_item.glyphs = new HB_Glyph[size]; - memset(m_item.glyphs, 0, size * sizeof(HB_Glyph)); - m_item.attributes = new HB_GlyphAttributes[size]; - memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes)); - m_item.advances = new HB_Fixed[size]; - memset(m_item.advances, 0, size * sizeof(HB_Fixed)); - m_item.offsets = new HB_FixedPoint[size]; - memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint)); - - m_glyphs16 = new uint16_t[size]; - m_xPositions = new SkScalar[size]; - - m_item.num_glyphs = size; + reset(); +} + +TextRunWalker::~TextRunWalker() +{ + fastFree(m_item.font); + deleteGlyphArrays(); + delete[] m_item.log_clusters; + if (m_item.item.bidiLevel) + delete[] m_item.string; +} + +bool TextRunWalker::isWordBreak(unsigned index, bool isRTL) +{ + if (!isRTL) + return index && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index - 1]); + return index != m_item.stringLength - 1 && isCodepointSpace(m_item.string[index]) && !isCodepointSpace(m_item.string[index + 1]); +} + +// setPadding sets a number of pixels to be distributed across the TextRun. +// WebKit uses this to justify text. +void TextRunWalker::setPadding(int padding) +{ + m_padding = padding; + if (!m_padding) + return; + + // If we have padding to distribute, then we try to give an equal + // amount to each space. The last space gets the smaller amount, if + // any. + unsigned numWordBreaks = 0; + bool isRTL = m_iterateBackwards; + + for (unsigned i = 0; i < m_item.stringLength; i++) { + if (isWordBreak(i, isRTL)) + numWordBreaks++; } - void shapeGlyphs() - { - for (;;) { - if (HB_ShapeItem(&m_item)) - break; + if (numWordBreaks) + m_padPerWordBreak = m_padding / numWordBreaks; + else + m_padPerWordBreak = 0; +} + +void TextRunWalker::reset() +{ + if (m_iterateBackwards) + m_indexOfNextScriptRun = m_run.length() - 1; + else + m_indexOfNextScriptRun = 0; + m_offsetX = m_startingX; +} - // We overflowed our arrays. Resize and retry. - // HB_ShapeItem fills in m_item.num_glyphs with the needed size. - deleteGlyphArrays(); - // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer - // shaper (at least) can fail because of insufficient glyph buffers - // and request 0 additional glyphs: throwing us into an infinite - // loop. - createGlyphArrays(m_item.num_glyphs + 1); +void TextRunWalker::setBackwardsIteration(bool isBackwards) +{ + m_iterateBackwards = isBackwards; + reset(); +} + +// Advance to the next script run, returning false when the end of the +// TextRun has been reached. +bool TextRunWalker::nextScriptRun() +{ + if (m_iterateBackwards) { + // In right-to-left mode we need to render the shaped glyph backwards and + // also render the script runs themselves backwards. So given a TextRun: + // AAAAAAACTTTTTTT (A = Arabic, C = Common, T = Thai) + // we render: + // TTTTTTCAAAAAAA + // (and the glyphs in each A, C and T section are backwards too) + if (!hb_utf16_script_run_prev(&m_numCodePoints, &m_item.item, m_run.characters(), m_run.length(), &m_indexOfNextScriptRun)) + return false; + } else { + if (!hb_utf16_script_run_next(&m_numCodePoints, &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. + // Other WebKit code (e.g. Mac) segments complex text just by finding + // the longest span of text covered by a single font. + // But we currently need to call hb_utf16_script_run_next anyway to fill + // in the harfbuzz data structures to e.g. pick the correct script's shaper. + // So we allow that to run first, then do a second pass over the range it + // found and take the largest subregion that stays within a single font. + const FontData* glyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; + unsigned endOfRun; + for (endOfRun = 1; endOfRun < m_item.item.length; ++endOfRun) { + const FontData* nextGlyphData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos + endOfRun], false, false).fontData; + if (nextGlyphData != glyphData) + break; } + m_item.item.length = endOfRun; + m_indexOfNextScriptRun = m_item.item.pos + endOfRun; } - void setGlyphXPositions(bool isRTL) - { - double position = 0; - // logClustersIndex indexes logClusters for the first (or last when - // RTL) codepoint of the current glyph. Each time we advance a glyph, - // we skip over all the codepoints that contributed to the current - // glyph. - unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0; - - for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) { - // Glyphs are stored in logical order, but for layout purposes we - // always go left to right. - int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; - - m_glyphs16[i] = m_item.glyphs[i]; - double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); - m_xPositions[i] = m_offsetX + position + offsetX; - - double advance = truncateFixedPointToInteger(m_item.advances[i]); - // The first half of the conjuction works around the case where - // output glyphs aren't associated with any codepoints by the - // clusters log. - if (logClustersIndex < m_item.item.length - && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { - advance += m_wordSpacingAdjustment; - - if (m_padding > 0) { - unsigned toPad = roundf(m_padPerWordBreak + m_padError); - m_padError += m_padPerWordBreak - toPad; - - if (m_padding < toPad) - toPad = m_padding; - m_padding -= toPad; - advance += toPad; - } - } + setupFontForScriptRun(); + shapeGlyphs(); + setGlyphXPositions(rtl()); - // We would like to add m_letterSpacing after each cluster, but I - // don't know where the cluster information is. This is typically - // fine for Roman languages, but breaks more complex languages - // terribly. - // advance += m_letterSpacing; - - if (isRTL) { - while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) - logClustersIndex--; - } else { - while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i) - logClustersIndex++; - } + return true; +} - position += advance; - } +float TextRunWalker::widthOfFullRun() +{ + float widthSum = 0; + while (nextScriptRun()) + widthSum += width(); - m_pixelWidth = position; - m_offsetX += m_pixelWidth; + return widthSum; +} + +const TextRun& TextRunWalker::getTextRun(const TextRun& originalRun) +{ + // Normalize the text run in two ways: + // 1) Convert the |originalRun| to NFC normalized form if combining diacritical marks + // (U+0300..) are used in the run. This conversion is necessary since most OpenType + // fonts (e.g., Arial) don't have substitution rules for the diacritical marks in + // their GSUB tables. + // + // Note that we don't use the icu::Normalizer::isNormalized(UNORM_NFC) API here since + // the API returns FALSE (= not normalized) for complex runs that don't require NFC + // normalization (e.g., Arabic text). Unless the run contains the diacritical marks, + // Harfbuzz will do the same thing for us using the GSUB table. + // 2) Convert spacing characters into plain spaces, as some fonts will provide glyphs + // for characters like '\n' otherwise. + for (int i = 0; i < originalRun.length(); ++i) { + UChar ch = originalRun[i]; + UBlockCode block = ::ublock_getCode(ch); + if (block == UBLOCK_COMBINING_DIACRITICAL_MARKS || (Font::treatAsSpace(ch) && ch != ' ')) + return getNormalizedTextRun(originalRun); + } + return originalRun; +} + +const TextRun& TextRunWalker::getNormalizedTextRun(const TextRun& originalRun) +{ + icu::UnicodeString normalizedString; + UErrorCode error = U_ZERO_ERROR; + icu::Normalizer::normalize(icu::UnicodeString(originalRun.characters(), originalRun.length()), UNORM_NFC, 0 /* no options */, normalizedString, error); + if (U_FAILURE(error)) + return originalRun; + + m_normalizedBuffer.set(new UChar[normalizedString.length() + 1]); + normalizedString.extract(m_normalizedBuffer.get(), normalizedString.length() + 1, error); + ASSERT(U_SUCCESS(error)); + + for (int i = 0; i < normalizedString.length(); ++i) { + if (Font::treatAsSpace(m_normalizedBuffer[i])) + m_normalizedBuffer[i] = ' '; } - static bool isCodepointSpace(HB_UChar16 c) - { - // This matches the logic in RenderBlock::findNextLineBreak - return c == ' ' || c == '\t'; + m_normalizedRun.set(new TextRun(originalRun)); + m_normalizedRun->setText(m_normalizedBuffer.get(), normalizedString.length()); + return *m_normalizedRun; +} + +void TextRunWalker::setupFontForScriptRun() +{ + const FontData* fontData = m_font->glyphDataForCharacter(m_item.string[m_item.item.pos], false, false).fontData; + const FontPlatformData& platformData = fontData->fontDataForCharacter(' ')->platformData(); + m_item.face = platformData.harfbuzzFace(); + void* opaquePlatformData = const_cast<FontPlatformData*>(&platformData); + m_item.font->userData = opaquePlatformData; +} + +HB_FontRec* TextRunWalker::allocHarfbuzzFont() +{ + HB_FontRec* font = reinterpret_cast<HB_FontRec*>(fastMalloc(sizeof(HB_FontRec))); + 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; +} + +void TextRunWalker::deleteGlyphArrays() +{ + delete[] m_item.glyphs; + delete[] m_item.attributes; + delete[] m_item.advances; + delete[] m_item.offsets; + delete[] m_glyphs16; + delete[] m_xPositions; +} + +void TextRunWalker::createGlyphArrays(int size) +{ + m_item.glyphs = new HB_Glyph[size]; + m_item.attributes = new HB_GlyphAttributes[size]; + m_item.advances = new HB_Fixed[size]; + m_item.offsets = new HB_FixedPoint[size]; + + m_glyphs16 = new uint16_t[size]; + m_xPositions = new SkScalar[size]; + + m_item.num_glyphs = size; + m_glyphsArrayCapacity = size; // Save the GlyphArrays size. + resetGlyphArrays(); +} + +void TextRunWalker::resetGlyphArrays() +{ + int size = m_item.num_glyphs; + // All the types here don't have pointers. It is safe to reset to + // zero unless Harfbuzz breaks the compatibility in the future. + memset(m_item.glyphs, 0, size * sizeof(HB_Glyph)); + memset(m_item.attributes, 0, size * sizeof(HB_GlyphAttributes)); + memset(m_item.advances, 0, size * sizeof(HB_Fixed)); + memset(m_item.offsets, 0, size * sizeof(HB_FixedPoint)); + memset(m_glyphs16, 0, size * sizeof(uint16_t)); + memset(m_xPositions, 0, size * sizeof(SkScalar)); +} + +void TextRunWalker::shapeGlyphs() +{ + // HB_ShapeItem() resets m_item.num_glyphs. If the previous call to + // HB_ShapeItem() used less space than was available, the capacity of + // the array may be larger than the current value of m_item.num_glyphs. + // So, we need to reset the num_glyphs to the capacity of the array. + m_item.num_glyphs = m_glyphsArrayCapacity; + resetGlyphArrays(); + while (!HB_ShapeItem(&m_item)) { + // We overflowed our arrays. Resize and retry. + // HB_ShapeItem fills in m_item.num_glyphs with the needed size. + deleteGlyphArrays(); + // The |+ 1| here is a workaround for a bug in Harfbuzz: the Khmer + // shaper (at least) can fail because of insufficient glyph buffers + // and request 0 additional glyphs: throwing us into an infinite + // loop. + createGlyphArrays(m_item.num_glyphs + 1); } +} + +void TextRunWalker::setGlyphXPositions(bool isRTL) +{ + double position = 0; + // logClustersIndex indexes logClusters for the first (or last when + // RTL) codepoint of the current glyph. Each time we advance a glyph, + // we skip over all the codepoints that contributed to the current + // glyph. + unsigned logClustersIndex = isRTL ? m_item.num_glyphs - 1 : 0; + + for (unsigned iter = 0; iter < m_item.num_glyphs; ++iter) { + // Glyphs are stored in logical order, but for layout purposes we + // always go left to right. + int i = isRTL ? m_item.num_glyphs - iter - 1 : iter; + + m_glyphs16[i] = m_item.glyphs[i]; + double offsetX = truncateFixedPointToInteger(m_item.offsets[i].x); + m_xPositions[i] = m_offsetX + position + offsetX; + + double advance = truncateFixedPointToInteger(m_item.advances[i]); + // The first half of the conjuction works around the case where + // output glyphs aren't associated with any codepoints by the + // clusters log. + if (logClustersIndex < m_item.item.length + && isWordBreak(m_item.item.pos + logClustersIndex, isRTL)) { + advance += m_wordSpacingAdjustment; + + if (m_padding > 0) { + unsigned toPad = roundf(m_padPerWordBreak + m_padError); + m_padError += m_padPerWordBreak - toPad; + + if (m_padding < toPad) + toPad = m_padding; + m_padding -= toPad; + advance += toPad; + } + } - void mirrorCharacters(UChar* destination, const UChar* source, int length) const - { - int position = 0; - bool error = false; - // Iterate characters in source and mirror character if needed. - while (position < length) { - UChar32 character; - int nextPosition = position; - U16_NEXT(source, nextPosition, length, character); - character = u_charMirror(character); - U16_APPEND(destination, position, length, character, error); - ASSERT(!error); - position = nextPosition; + // We would like to add m_letterSpacing after each cluster, but I + // don't know where the cluster information is. This is typically + // fine for Roman languages, but breaks more complex languages + // terribly. + // advance += m_letterSpacing; + + if (isRTL) { + while (logClustersIndex > 0 && logClusters()[logClustersIndex] == i) + logClustersIndex--; + } else { + while (logClustersIndex < m_item.item.length && logClusters()[logClustersIndex] == i) + logClustersIndex++; } + + position += advance; } - const Font* const m_font; - HB_ShaperItem m_item; - uint16_t* m_glyphs16; // A vector of 16-bit glyph ids. - SkScalar* m_xPositions; // A vector of x positions for each glyph. - ssize_t m_indexOfNextScriptRun; // Indexes the script run in |m_run|. - const unsigned m_startingX; // Offset in pixels of the first script 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. + m_pixelWidth = position; + m_offsetX += m_pixelWidth; +} - OwnPtr<TextRun> m_normalizedRun; - OwnArrayPtr<UChar> m_normalizedBuffer; // A buffer for normalized run. - const TextRun& m_run; - bool m_iterateBackwards; - int m_wordSpacingAdjustment; // delta adjustment (pixels) for each word break. - float m_padding; // pixels to be distributed over the line at word breaks. - float m_padPerWordBreak; // pixels to be added to each word break. - float m_padError; // |m_padPerWordBreak| might have a fractional component. - // Since we only add a whole number of padding pixels at - // each word break we accumulate error. This is the - // number of pixels that we are behind so far. - unsigned m_letterSpacing; // pixels to be added after each glyph. -}; +void TextRunWalker::mirrorCharacters(UChar* destination, const UChar* source, int length) const +{ + int position = 0; + bool error = false; + // Iterate characters in source and mirror character if needed. + while (position < length) { + UChar32 character; + int nextPosition = position; + U16_NEXT(source, nextPosition, length, character); + character = u_charMirror(character); + U16_APPEND(destination, position, length, character, error); + ASSERT(!error); + position = nextPosition; + } +} static void setupForTextPainting(SkPaint* paint, SkColor color) { diff --git a/WebCore/platform/graphics/chromium/GLES2Canvas.cpp b/WebCore/platform/graphics/chromium/GLES2Canvas.cpp index 46aecf4..697cf5e 100644 --- a/WebCore/platform/graphics/chromium/GLES2Canvas.cpp +++ b/WebCore/platform/graphics/chromium/GLES2Canvas.cpp @@ -97,7 +97,7 @@ void GLES2Canvas::clearRect(const FloatRect& rect) } else { save(); setCompositeOperation(CompositeClear); - fillRect(rect, Color(RGBA32(0)), DeviceColorSpace); + fillRect(rect, Color(RGBA32(0)), ColorSpaceDeviceRGB); restore(); } } @@ -120,7 +120,7 @@ void GLES2Canvas::fillRect(const FloatRect& rect, const Color& color, ColorSpace void GLES2Canvas::fillRect(const FloatRect& rect) { - fillRect(rect, m_state->m_fillColor, DeviceColorSpace); + fillRect(rect, m_state->m_fillColor, ColorSpaceDeviceRGB); } void GLES2Canvas::setFillColor(const Color& color, ColorSpace colorSpace) diff --git a/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp b/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp index c97be82..afcc98c 100644 --- a/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/ImageLayerChromium.cpp @@ -75,6 +75,14 @@ void ImageLayerChromium::updateContents() { ASSERT(layerRenderer()); + // FIXME: Remove this test when tiled layers are implemented. + if (requiresClippedUpdateRect()) { + // Use the base version of updateContents which draws a subset of the + // image to a bitmap, as the pixel contents can't be uploaded directly. + ContentLayerChromium::updateContents(); + return; + } + void* pixels = 0; IntSize requiredTextureSize; IntSize bitmapSize; @@ -136,12 +144,6 @@ void ImageLayerChromium::updateContents() #else #error "Need to implement for your platform." #endif - // FIXME: Remove this test when tiled layers are implemented. - m_skipsDraw = false; - if (!layerRenderer()->checkTextureSize(requiredTextureSize)) { - m_skipsDraw = true; - return; - } unsigned textureId = m_contentsTexture; if (!textureId) diff --git a/WebCore/platform/graphics/chromium/LayerChromium.cpp b/WebCore/platform/graphics/chromium/LayerChromium.cpp index 5dba58d..79f18f0 100644 --- a/WebCore/platform/graphics/chromium/LayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/LayerChromium.cpp @@ -174,6 +174,11 @@ LayerChromium::~LayerChromium() void LayerChromium::setLayerRenderer(LayerRendererChromium* renderer) { + // If we're changing layer renderers then we need to free up any resources + // allocated by the old renderer. + if (layerRenderer() && layerRenderer() != renderer) + cleanupResources(); + m_layerRenderer = renderer; } @@ -446,15 +451,12 @@ void LayerChromium::drawDebugBorder() GLC(context, context->drawElements(GraphicsContext3D::LINE_LOOP, 4, GraphicsContext3D::UNSIGNED_SHORT, 6 * sizeof(unsigned short))); } -const FloatRect LayerChromium::getDrawRect() const +const IntRect LayerChromium::getDrawRect() const { // Form the matrix used by the shader to map the corners of the layer's // bounds into the view space. - TransformationMatrix renderMatrix = drawTransform(); - renderMatrix.scale3d(bounds().width(), bounds().height(), 1); - - FloatRect layerRect(-0.5, -0.5, 1, 1); - FloatRect mappedRect = renderMatrix.mapRect(layerRect); + FloatRect layerRect(-0.5 * bounds().width(), -0.5 * bounds().height(), bounds().width(), bounds().height()); + IntRect mappedRect = enclosingIntRect(drawTransform().mapRect(layerRect)); return mappedRect; } diff --git a/WebCore/platform/graphics/chromium/LayerChromium.h b/WebCore/platform/graphics/chromium/LayerChromium.h index 0a66318..3956e28 100644 --- a/WebCore/platform/graphics/chromium/LayerChromium.h +++ b/WebCore/platform/graphics/chromium/LayerChromium.h @@ -156,7 +156,7 @@ public: bool contentsDirty() { return m_contentsDirty; } // Returns the rect containtaining this layer in the current view's coordinate system. - const FloatRect getDrawRect() const; + const IntRect getDrawRect() const; // These methods typically need to be overwritten by derived classes. virtual bool drawsContent() { return false; } @@ -202,6 +202,11 @@ protected: GraphicsLayerChromium* m_owner; LayerChromium(GraphicsLayerChromium* owner); + // This is called to clean up resources being held in the same context as + // layerRendererContext(). Subclasses should override this method if they + // hold context-dependent resources such as textures. + virtual void cleanupResources() { } + LayerRendererChromium* layerRenderer() const { return m_layerRenderer.get(); } GraphicsContext3D* layerRendererContext() const; diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp index c4031e5..e93e296 100644 --- a/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp +++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.cpp @@ -73,7 +73,7 @@ static inline bool compareLayerZ(const LayerChromium* a, const LayerChromium* b) return transformA.m43() < transformB.m43(); } -PassRefPtr<LayerRendererChromium> LayerRendererChromium::create(PassOwnPtr<GraphicsContext3D> context) +PassRefPtr<LayerRendererChromium> LayerRendererChromium::create(PassRefPtr<GraphicsContext3D> context) { if (!context) return 0; @@ -85,7 +85,7 @@ PassRefPtr<LayerRendererChromium> LayerRendererChromium::create(PassOwnPtr<Graph return layerRenderer.release(); } -LayerRendererChromium::LayerRendererChromium(PassOwnPtr<GraphicsContext3D> context) +LayerRendererChromium::LayerRendererChromium(PassRefPtr<GraphicsContext3D> context) : m_rootLayerTextureId(0) , m_rootLayerTextureWidth(0) , m_rootLayerTextureHeight(0) @@ -127,7 +127,6 @@ void LayerRendererChromium::setRootLayerCanvasSize(const IntSize& size) // the old ones. m_rootLayerCanvas = new skia::PlatformCanvas(size.width(), size.height(), false); m_rootLayerSkiaContext = new PlatformContextSkia(m_rootLayerCanvas.get()); - m_rootLayerSkiaContext->setDrawingToImageBuffer(true); m_rootLayerGraphicsContext = new GraphicsContext(reinterpret_cast<PlatformGraphicsContext*>(m_rootLayerSkiaContext.get())); #elif PLATFORM(CG) // Release the previous CGBitmapContext before reallocating the backing store as a precaution. @@ -204,6 +203,8 @@ void LayerRendererChromium::prepareToDrawLayers(const IntRect& visibleRect, cons GLC(m_context, m_context->disable(GraphicsContext3D::CULL_FACE)); GLC(m_context, m_context->depthFunc(GraphicsContext3D::LEQUAL)); GLC(m_context, m_context->clearStencil(0)); + // Blending disabled by default. Root layer alpha channel on Windows is incorrect when Skia uses ClearType. + GLC(m_context, m_context->disable(GraphicsContext3D::BLEND)); if (m_scrollPosition == IntPoint(-1, -1)) { m_scrollPosition = scrollPosition; @@ -284,11 +285,15 @@ void LayerRendererChromium::drawLayers(const IntRect& visibleRect, const IntRect const ContentLayerChromium::SharedValues* contentLayerValues = contentLayerSharedValues(); useShader(contentLayerValues->contentShaderProgram()); GLC(m_context, m_context->uniform1i(contentLayerValues->shaderSamplerLocation(), 0)); + // Mask out writes to alpha channel: ClearType via Skia results in invalid + // zero alpha values on text glyphs. The root layer is always opaque. + GLC(m_context, m_context->colorMask(true, true, true, false)); TransformationMatrix layerMatrix; layerMatrix.translate3d(visibleRect.width() * 0.5f, visibleRect.height() * 0.5f, 0); LayerChromium::drawTexturedQuad(m_context.get(), m_projectionMatrix, layerMatrix, visibleRect.width(), visibleRect.height(), 1, contentLayerValues->shaderMatrixLocation(), contentLayerValues->shaderAlphaLocation()); + GLC(m_context, m_context->colorMask(true, true, true, true)); // If culling is enabled then we will cull the backface. GLC(m_context, m_context->cullFace(GraphicsContext3D::BACK)); @@ -302,8 +307,9 @@ void LayerRendererChromium::drawLayers(const IntRect& visibleRect, const IntRect GLC(m_context, m_context->enable(GraphicsContext3D::BLEND)); GLC(m_context, m_context->blendFunc(GraphicsContext3D::ONE, GraphicsContext3D::ONE_MINUS_SRC_ALPHA)); - // Set the rootVisibleRect --- used by subsequent drawLayers calls + // Set the root visible/content rects --- used by subsequent drawLayers calls. m_rootVisibleRect = visibleRect; + m_rootContentRect = contentRect; // Traverse the layer tree and update the layer transforms. float opacity = 1; @@ -315,7 +321,7 @@ void LayerRendererChromium::drawLayers(const IntRect& visibleRect, const IntRect // Enable scissoring to avoid rendering composited layers over the scrollbars. GLC(m_context, m_context->enable(GraphicsContext3D::SCISSOR_TEST)); - FloatRect scissorRect(contentRect); + IntRect scissorRect(contentRect); // The scissorRect should not include the scroll offset. scissorRect.move(-m_scrollPosition.x(), -m_scrollPosition.y()); @@ -328,9 +334,10 @@ void LayerRendererChromium::drawLayers(const IntRect& visibleRect, const IntRect // Traverse the layer tree one more time to draw the layers. for (size_t i = 0; i < sublayers.size(); i++) - drawLayersRecursive(sublayers[i].get(), scissorRect); + drawLayersRecursive(sublayers[i].get()); GLC(m_context, m_context->disable(GraphicsContext3D::SCISSOR_TEST)); + GLC(m_context, m_context->disable(GraphicsContext3D::BLEND)); } void LayerRendererChromium::finish() @@ -494,14 +501,14 @@ void LayerRendererChromium::drawLayerIntoStencilBuffer(LayerChromium* layer, boo } // Recursively walk the layer tree and draw the layers. -void LayerRendererChromium::drawLayersRecursive(LayerChromium* layer, const FloatRect& scissorRect) +void LayerRendererChromium::drawLayersRecursive(LayerChromium* layer) { static bool depthTestEnabledForSubtree = false; static int currentStencilValue = 0; // Check if the layer falls within the visible bounds of the page. - FloatRect layerRect = layer->getDrawRect(); - bool isLayerVisible = scissorRect.intersects(layerRect); + IntRect layerRect = layer->getDrawRect(); + bool isLayerVisible = m_currentScissorRect.intersects(layerRect); // Enable depth testing for this layer and all its descendants if preserves3D is set. bool mustClearDepth = false; @@ -520,15 +527,16 @@ void LayerRendererChromium::drawLayersRecursive(LayerChromium* layer, const Floa // FIXME: We should check here if the layer has descendants that draw content // before we setup for clipping. - FloatRect currentScissorRect = scissorRect; + IntRect previousScissorRect = m_currentScissorRect; bool mustResetScissorRect = false; bool didStencilDraw = false; if (layer->masksToBounds()) { // If the layer isn't rotated then we can use scissoring otherwise we need // to clip using the stencil buffer. if (layer->drawTransform().isIdentityOrTranslation()) { + IntRect currentScissorRect = previousScissorRect; currentScissorRect.intersect(layerRect); - if (currentScissorRect != scissorRect) { + if (currentScissorRect != previousScissorRect) { scissorToRect(currentScissorRect); mustResetScissorRect = true; } @@ -573,11 +581,11 @@ void LayerRendererChromium::drawLayersRecursive(LayerChromium* layer, const Floa std::stable_sort(sublayerList.begin(), sublayerList.end(), compareLayerZ); for (i = 0; i < sublayerList.size(); i++) - drawLayersRecursive(sublayerList[i], currentScissorRect); + drawLayersRecursive(sublayerList[i]); } else { const Vector<RefPtr<LayerChromium> >& sublayers = layer->getSublayers(); for (size_t i = 0; i < sublayers.size(); i++) - drawLayersRecursive(sublayers[i].get(), currentScissorRect); + drawLayersRecursive(sublayers[i].get()); } if (didStencilDraw) { @@ -593,7 +601,7 @@ void LayerRendererChromium::drawLayersRecursive(LayerChromium* layer, const Floa } if (mustResetScissorRect) { - scissorToRect(scissorRect); + scissorToRect(previousScissorRect); } if (mustClearDepth) { @@ -629,11 +637,12 @@ void LayerRendererChromium::drawLayer(LayerChromium* layer) // Sets the scissor region to the given rectangle. The coordinate system for the // scissorRect has its origin at the top left corner of the current visible rect. -void LayerRendererChromium::scissorToRect(const FloatRect& scissorRect) +void LayerRendererChromium::scissorToRect(const IntRect& scissorRect) { // Compute the lower left corner of the scissor rect. - float bottom = std::max((float)m_rootVisibleRect.height() - scissorRect.bottom(), 0.f); + int bottom = std::max(m_rootVisibleRect.height() - scissorRect.bottom(), 0); GLC(m_context, m_context->scissor(scissorRect.x(), bottom, scissorRect.width(), scissorRect.height())); + m_currentScissorRect = scissorRect; } bool LayerRendererChromium::makeContextCurrent() diff --git a/WebCore/platform/graphics/chromium/LayerRendererChromium.h b/WebCore/platform/graphics/chromium/LayerRendererChromium.h index b714584..6a06105 100644 --- a/WebCore/platform/graphics/chromium/LayerRendererChromium.h +++ b/WebCore/platform/graphics/chromium/LayerRendererChromium.h @@ -59,9 +59,8 @@ class GraphicsContext3D; // Class that handles drawing of composited render layers using GL. class LayerRendererChromium : public RefCounted<LayerRendererChromium> { public: - static PassRefPtr<LayerRendererChromium> create(PassOwnPtr<GraphicsContext3D> graphicsContext3D); + static PassRefPtr<LayerRendererChromium> create(PassRefPtr<GraphicsContext3D> graphicsContext3D); - LayerRendererChromium(PassOwnPtr<GraphicsContext3D> graphicsContext3D); ~LayerRendererChromium(); GraphicsContext3D* context(); @@ -93,6 +92,8 @@ public: unsigned createLayerTexture(); void deleteLayerTexture(unsigned); + IntRect currentScissorRect() const { return m_currentScissorRect; } + static void debugGLCall(GraphicsContext3D*, const char* command, const char* file, int line); const TransformationMatrix& projectionMatrix() const { return m_projectionMatrix; } @@ -109,12 +110,15 @@ public: void resizeOnscreenContent(const IntSize&); IntSize rootLayerTextureSize() const { return IntSize(m_rootLayerTextureWidth, m_rootLayerTextureHeight); } + IntRect rootLayerContentRect() const { return m_rootContentRect; } void getFramebufferPixels(void *pixels, const IntRect& rect); private: + explicit LayerRendererChromium(PassRefPtr<GraphicsContext3D> graphicsContext3D); + void updateLayersRecursive(LayerChromium* layer, const TransformationMatrix& parentMatrix, float opacity); - void drawLayersRecursive(LayerChromium*, const FloatRect& scissorRect); + void drawLayersRecursive(LayerChromium*); void drawLayer(LayerChromium*); @@ -122,7 +126,7 @@ private: void drawLayerIntoStencilBuffer(LayerChromium*, bool decrement); - void scissorToRect(const FloatRect&); + void scissorToRect(const IntRect&); bool makeContextCurrent(); @@ -160,6 +164,8 @@ private: IntSize m_rootLayerCanvasSize; IntRect m_rootVisibleRect; + IntRect m_rootContentRect; + IntRect m_currentScissorRect; int m_maxTextureSize; @@ -174,7 +180,7 @@ private: OwnPtr<CanvasLayerChromium::SharedValues> m_canvasLayerSharedValues; OwnPtr<VideoLayerChromium::SharedValues> m_videoLayerSharedValues; - OwnPtr<GraphicsContext3D> m_context; + RefPtr<GraphicsContext3D> m_context; }; // Setting DEBUG_GL_CALLS to 1 will call glGetError() after almost every GL diff --git a/WebCore/platform/graphics/chromium/TransparencyWin.cpp b/WebCore/platform/graphics/chromium/TransparencyWin.cpp index 47cc894..4dc2157 100644 --- a/WebCore/platform/graphics/chromium/TransparencyWin.cpp +++ b/WebCore/platform/graphics/chromium/TransparencyWin.cpp @@ -275,7 +275,7 @@ void TransparencyWin::setupLayerForWhiteLayer() if (!m_validLayer) return; - m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white, DeviceColorSpace); + m_drawContext->fillRect(IntRect(IntPoint(0, 0), m_layerSize), Color::white, ColorSpaceDeviceRGB); // Layer rect represents the part of the original layer. } diff --git a/WebCore/platform/graphics/chromium/VideoFrameChromium.h b/WebCore/platform/graphics/chromium/VideoFrameChromium.h index 34e922b..e176b0c 100644 --- a/WebCore/platform/graphics/chromium/VideoFrameChromium.h +++ b/WebCore/platform/graphics/chromium/VideoFrameChromium.h @@ -63,10 +63,7 @@ public: enum SurfaceType { TypeSystemMemory, - TypeOMXBufferHead, - TypeEGLImage, - TypeMFBuffer, - TypeDirect3DSurface + TypeTexture, }; virtual SurfaceType surfaceType() const = 0; @@ -76,6 +73,7 @@ public: virtual unsigned planes() const = 0; virtual int stride(unsigned plane) const = 0; virtual const void* data(unsigned plane) const = 0; + virtual unsigned texture(unsigned plane) const = 0; virtual const IntSize requiredTextureSize(unsigned plane) const = 0; }; diff --git a/WebCore/platform/graphics/chromium/VideoFrameProvider.h b/WebCore/platform/graphics/chromium/VideoFrameProvider.h index f0bad08..c596f87 100644 --- a/WebCore/platform/graphics/chromium/VideoFrameProvider.h +++ b/WebCore/platform/graphics/chromium/VideoFrameProvider.h @@ -37,6 +37,8 @@ namespace WebCore { class VideoFrameProvider { public: + virtual ~VideoFrameProvider() { } + // This function returns a pointer to a VideoFrameChromium, which is // the WebCore wrapper for a video frame in Chromium. getCurrentFrame() // places a lock on the frame in Chromium. Calls to this method should diff --git a/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp b/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp index 26641a9..46c73a1 100644 --- a/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp +++ b/WebCore/platform/graphics/chromium/VideoLayerChromium.cpp @@ -175,16 +175,22 @@ VideoLayerChromium::VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameP , m_skipsDraw(true) , m_frameFormat(VideoFrameChromium::Invalid) , m_provider(provider) + , m_currentFrame(0) { - for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { - m_textures[plane] = 0; - m_textureSizes[plane] = IntSize(); - m_frameSizes[plane] = IntSize(); - } + resetFrameParameters(); } VideoLayerChromium::~VideoLayerChromium() { + cleanupResources(); +} + +void VideoLayerChromium::cleanupResources() +{ + releaseCurrentFrame(); + if (!layerRenderer()) + return; + GraphicsContext3D* context = layerRendererContext(); for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { if (m_textures[plane]) @@ -218,6 +224,14 @@ void VideoLayerChromium::updateContents() return; } + if (frame->surfaceType() == VideoFrameChromium::TypeTexture) { + releaseCurrentFrame(); + saveCurrentFrame(frame); + m_dirtyRect.setSize(FloatSize()); + m_contentsDirty = false; + return; + } + // Allocate textures for planes if they are not allocated already, or // reallocate textures that are the wrong size for the frame. GraphicsContext3D* context = layerRendererContext(); @@ -236,6 +250,7 @@ void VideoLayerChromium::updateContents() m_dirtyRect.setSize(FloatSize()); m_contentsDirty = false; + m_provider->putCurrentFrame(frame); } @@ -319,6 +334,17 @@ void VideoLayerChromium::draw() notImplemented(); break; } + releaseCurrentFrame(); +} + +void VideoLayerChromium::releaseCurrentFrame() +{ + if (!m_currentFrame) + return; + + m_provider->putCurrentFrame(m_currentFrame); + m_currentFrame = 0; + resetFrameParameters(); } void VideoLayerChromium::drawYUV(const SharedValues* sv) @@ -370,6 +396,26 @@ void VideoLayerChromium::drawRGBA(const SharedValues* sv) sv->rgbaShaderMatrixLocation(), sv->rgbaAlphaLocation()); } +void VideoLayerChromium::resetFrameParameters() +{ + for (unsigned plane = 0; plane < VideoFrameChromium::maxPlanes; plane++) { + m_textures[plane] = 0; + m_textureSizes[plane] = IntSize(); + m_frameSizes[plane] = IntSize(); + } +} + +void VideoLayerChromium::saveCurrentFrame(VideoFrameChromium* frame) +{ + ASSERT(!m_currentFrame); + m_currentFrame = frame; + for (unsigned plane = 0; plane < frame->planes(); plane++) { + m_textures[plane] = frame->texture(plane); + m_textureSizes[plane] = frame->requiredTextureSize(plane); + m_frameSizes[plane] = m_textureSizes[plane]; + } +} + } // namespace WebCore #endif // USE(ACCELERATED_COMPOSITING) diff --git a/WebCore/platform/graphics/chromium/VideoLayerChromium.h b/WebCore/platform/graphics/chromium/VideoLayerChromium.h index 620d1a7..05b6578 100644 --- a/WebCore/platform/graphics/chromium/VideoLayerChromium.h +++ b/WebCore/platform/graphics/chromium/VideoLayerChromium.h @@ -49,6 +49,10 @@ public: virtual bool drawsContent() { return true; } virtual void draw(); + // This function is called by VideoFrameProvider. When this method is called + // putCurrentFrame() must be called to return the frame currently held. + void releaseCurrentFrame(); + class SharedValues { public: explicit SharedValues(GraphicsContext3D*); @@ -66,7 +70,7 @@ public: int rgbaAlphaLocation() const { return m_rgbaAlphaLocation; } int rgbaTextureLocation() const { return m_rgbaTextureLocation; } int ccMatrixLocation() const { return m_ccMatrixLocation; } - bool initialized() const { return m_initialized; }; + bool initialized() const { return m_initialized; } private: GraphicsContext3D* m_context; unsigned m_yuvShaderProgram; @@ -85,8 +89,12 @@ public: bool m_initialized; }; +protected: + virtual void cleanupResources(); + private: VideoLayerChromium(GraphicsLayerChromium* owner, VideoFrameProvider*); + static unsigned determineTextureFormat(VideoFrameChromium*); bool allocateTexturesIfNeeded(GraphicsContext3D*, VideoFrameChromium*, unsigned textureFormat); void updateYUVContents(GraphicsContext3D*, const VideoFrameChromium*); @@ -95,12 +103,16 @@ private: void updateTexture(GraphicsContext3D*, unsigned textureId, const IntSize& dimensions, unsigned textureFormat, const void* data); void drawYUV(const SharedValues*); void drawRGBA(const SharedValues*); + void resetFrameParameters(); + void saveCurrentFrame(VideoFrameChromium*); static const float yuv2RGB[9]; bool m_skipsDraw; VideoFrameChromium::Format m_frameFormat; - OwnPtr<VideoFrameProvider> m_provider; + VideoFrameProvider* m_provider; + VideoFrameChromium* m_currentFrame; + unsigned m_textures[3]; IntSize m_textureSizes[3]; IntSize m_frameSizes[3]; diff --git a/WebCore/platform/graphics/efl/ImageEfl.cpp b/WebCore/platform/graphics/efl/ImageEfl.cpp index 112770f..a86ba4e 100644 --- a/WebCore/platform/graphics/efl/ImageEfl.cpp +++ b/WebCore/platform/graphics/efl/ImageEfl.cpp @@ -32,6 +32,7 @@ #include "BitmapImage.h" #include "SharedBuffer.h" +#include <wtf/text/StringConcatenate.h> #include <cairo.h> namespace WebCore { @@ -51,7 +52,7 @@ static PassRefPtr<SharedBuffer> loadResourceSharedBufferFallback() static PassRefPtr<SharedBuffer> loadResourceSharedBuffer(const char* name) { - RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(String::format(DATA_DIR "/webkit-1.0/images/%s.png", name)); + RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(makeString(DATA_DIR "/webkit-1.0/images/", name, ".png")); if (buffer) return buffer.release(); return loadResourceSharedBufferFallback(); diff --git a/WebCore/platform/graphics/filters/ImageBufferFilter.cpp b/WebCore/platform/graphics/efl/IntRectEfl.cpp index 12407f8..0c92f63 100644 --- a/WebCore/platform/graphics/filters/ImageBufferFilter.cpp +++ b/WebCore/platform/graphics/efl/IntRectEfl.cpp @@ -1,6 +1,6 @@ /* - * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> - * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org> + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2010 Samsung Electronics * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -19,25 +19,22 @@ */ #include "config.h" +#include "IntRect.h" -#if ENABLE(FILTERS) -#include "ImageBufferFilter.h" - -#include "FloatSize.h" +#include <Eina.h> namespace WebCore { -ImageBufferFilter::ImageBufferFilter() - : Filter() +IntRect::IntRect(const Eina_Rectangle& r) + : m_location(IntPoint(r.x, r.y)) + , m_size(r.w, r.h) { - setFilterResolution(FloatSize(1.f, 1.f)); } -PassRefPtr<ImageBufferFilter> ImageBufferFilter::create() +IntRect::operator Eina_Rectangle() const { - return adoptRef(new ImageBufferFilter()); + Eina_Rectangle r = {x(), y(), width(), height()}; + return r; } -} // namespace WebCore - -#endif // ENABLE(FILTERS) +} diff --git a/WebCore/platform/graphics/filters/FEBlend.cpp b/WebCore/platform/graphics/filters/FEBlend.cpp index 4185f61..1a40027 100644 --- a/WebCore/platform/graphics/filters/FEBlend.cpp +++ b/WebCore/platform/graphics/filters/FEBlend.cpp @@ -98,13 +98,13 @@ void FEBlend::apply(Filter* filter) if (m_mode == FEBLEND_MODE_UNKNOWN) return; - if (!effectContext()) + if (!effectContext(filter)) return; - IntRect effectADrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArrayA(in->resultImage()->getPremultipliedImageData(effectADrawingRect)->data()); - IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->repaintRectInLocalCoordinates()); + IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArrayB(in2->resultImage()->getPremultipliedImageData(effectBDrawingRect)->data()); IntRect imageRect(IntPoint(), resultImage()->size()); diff --git a/WebCore/platform/graphics/filters/FEColorMatrix.cpp b/WebCore/platform/graphics/filters/FEColorMatrix.cpp index 86c37c2..b41d5ad 100644 --- a/WebCore/platform/graphics/filters/FEColorMatrix.cpp +++ b/WebCore/platform/graphics/filters/FEColorMatrix.cpp @@ -160,11 +160,11 @@ void FEColorMatrix::apply(Filter* filter) if (!in->resultImage()) return; - GraphicsContext* filterContext = effectContext(); + GraphicsContext* filterContext = effectContext(filter); if (!filterContext) return; - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates())); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); IntRect imageRect(IntPoint(), resultImage()->size()); PassRefPtr<ImageData> imageData(resultImage()->getUnmultipliedImageData(imageRect)); diff --git a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp index 6fe38e4..08d0b1f 100644 --- a/WebCore/platform/graphics/filters/FEComponentTransfer.cpp +++ b/WebCore/platform/graphics/filters/FEComponentTransfer.cpp @@ -154,7 +154,7 @@ void FEComponentTransfer::apply(Filter* filter) if (!in->resultImage()) return; - if (!effectContext()) + if (!effectContext(filter)) return; unsigned char rValues[256], gValues[256], bValues[256], aValues[256]; @@ -167,7 +167,7 @@ void FEComponentTransfer::apply(Filter* filter) for (unsigned channel = 0; channel < 4; channel++) (*callEffect[transferFunction[channel].type])(tables[channel], transferFunction[channel]); - IntRect drawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect drawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<ImageData> imageData(in->resultImage()->getUnmultipliedImageData(drawingRect)); CanvasPixelArray* srcPixelArray(imageData->data()); diff --git a/WebCore/platform/graphics/filters/FEComposite.cpp b/WebCore/platform/graphics/filters/FEComposite.cpp index 94e2524..2326966 100644 --- a/WebCore/platform/graphics/filters/FEComposite.cpp +++ b/WebCore/platform/graphics/filters/FEComposite.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) Research In Motion Limited 2010. 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 @@ -112,6 +113,27 @@ inline void arithmetic(const RefPtr<CanvasPixelArray>& srcPixelArrayA, CanvasPix } } } + +void FEComposite::determineAbsolutePaintRect(Filter* filter) +{ + switch (m_type) { + case FECOMPOSITE_OPERATOR_IN: + case FECOMPOSITE_OPERATOR_ATOP: + // For In and Atop the first effect just influences the result of + // the second effect. So just use the absolute paint rect of the second effect here. + setAbsolutePaintRect(inputEffect(1)->absolutePaintRect()); + return; + case FECOMPOSITE_OPERATOR_ARITHMETIC: + // Arithmetic may influnce the compele filter primitive region. So we can't + // optimize the paint region here. + setAbsolutePaintRect(maxEffectRect()); + return; + default: + // Take the union of both input effects. + FilterEffect::determineAbsolutePaintRect(filter); + return; + } +} void FEComposite::apply(Filter* filter) { @@ -122,39 +144,39 @@ void FEComposite::apply(Filter* filter) if (!in->resultImage() || !in2->resultImage()) return; - GraphicsContext* filterContext = effectContext(); + GraphicsContext* filterContext = effectContext(filter); if (!filterContext) return; FloatRect srcRect = FloatRect(0, 0, -1, -1); switch (m_type) { case FECOMPOSITE_OPERATOR_OVER: - filterContext->drawImageBuffer(in2->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in2->repaintRectInLocalCoordinates())); - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates())); + filterContext->drawImageBuffer(in2->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); break; case FECOMPOSITE_OPERATOR_IN: filterContext->save(); - filterContext->clipToImageBuffer(in2->resultImage(), drawingRegionOfInputImage(in2->repaintRectInLocalCoordinates())); - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates())); + filterContext->clipToImageBuffer(in2->resultImage(), drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); filterContext->restore(); break; case FECOMPOSITE_OPERATOR_OUT: - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates())); - filterContext->drawImageBuffer(in2->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in2->repaintRectInLocalCoordinates()), srcRect, CompositeDestinationOut); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); + filterContext->drawImageBuffer(in2->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), srcRect, CompositeDestinationOut); break; case FECOMPOSITE_OPERATOR_ATOP: - filterContext->drawImageBuffer(in2->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in2->repaintRectInLocalCoordinates())); - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates()), srcRect, CompositeSourceAtop); + filterContext->drawImageBuffer(in2->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeSourceAtop); break; case FECOMPOSITE_OPERATOR_XOR: - filterContext->drawImageBuffer(in2->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in2->repaintRectInLocalCoordinates())); - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates()), srcRect, CompositeXOR); + filterContext->drawImageBuffer(in2->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), srcRect, CompositeXOR); break; case FECOMPOSITE_OPERATOR_ARITHMETIC: { - IntRect effectADrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArrayA(in->resultImage()->getPremultipliedImageData(effectADrawingRect)->data()); - IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->repaintRectInLocalCoordinates()); + IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); RefPtr<ImageData> imageData(in2->resultImage()->getPremultipliedImageData(effectBDrawingRect)); CanvasPixelArray* srcPixelArrayB(imageData->data()); diff --git a/WebCore/platform/graphics/filters/FEComposite.h b/WebCore/platform/graphics/filters/FEComposite.h index 82a3b06..ecdb037 100644 --- a/WebCore/platform/graphics/filters/FEComposite.h +++ b/WebCore/platform/graphics/filters/FEComposite.h @@ -61,6 +61,8 @@ public: virtual void apply(Filter*); virtual void dump(); + + virtual void determineAbsolutePaintRect(Filter*); virtual TextStream& externalRepresentation(TextStream&, int indention) const; diff --git a/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp b/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp index dd66c6a..d487a47 100644 --- a/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp +++ b/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp @@ -377,11 +377,11 @@ void FEConvolveMatrix::apply(Filter* filter) if (!in->resultImage()) return; - if (!effectContext()) + if (!effectContext(filter)) return; IntRect imageRect(IntPoint(), resultImage()->size()); - IntRect effectDrawingRect = requestedRegionOfInputImageData(in->filterPrimitiveSubregion()); + IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArray; if (m_preserveAlpha) diff --git a/WebCore/platform/graphics/filters/FEConvolveMatrix.h b/WebCore/platform/graphics/filters/FEConvolveMatrix.h index 2fe634f..8d3439e 100644 --- a/WebCore/platform/graphics/filters/FEConvolveMatrix.h +++ b/WebCore/platform/graphics/filters/FEConvolveMatrix.h @@ -75,6 +75,8 @@ public: virtual void apply(Filter*); virtual void dump(); + virtual void determineAbsolutePaintRect(Filter*) { setAbsolutePaintRect(maxEffectRect()); } + virtual TextStream& externalRepresentation(TextStream&, int indention) const; private: diff --git a/WebCore/platform/graphics/filters/FEDisplacementMap.cpp b/WebCore/platform/graphics/filters/FEDisplacementMap.cpp index 6b5dbaa..0c53241 100644 --- a/WebCore/platform/graphics/filters/FEDisplacementMap.cpp +++ b/WebCore/platform/graphics/filters/FEDisplacementMap.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) Research In Motion Limited 2010. 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 @@ -88,13 +89,13 @@ void FEDisplacementMap::apply(Filter* filter) if (m_xChannelSelector == CHANNEL_UNKNOWN || m_yChannelSelector == CHANNEL_UNKNOWN) return; - if (!effectContext()) + if (!effectContext(filter)) return; - IntRect effectADrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArrayA(in->resultImage()->getPremultipliedImageData(effectADrawingRect)->data()); - IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->repaintRectInLocalCoordinates()); + IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArrayB(in2->resultImage()->getUnmultipliedImageData(effectBDrawingRect)->data()); IntRect imageRect(IntPoint(), resultImage()->size()); @@ -102,10 +103,10 @@ void FEDisplacementMap::apply(Filter* filter) ASSERT(srcPixelArrayA->length() == srcPixelArrayB->length()); - float scaleX = m_scale / 255.f * filter->filterResolution().width(); - float scaleY = m_scale / 255.f * filter->filterResolution().height(); - float scaleAdjustmentX = (0.5f - 0.5f * m_scale) * filter->filterResolution().width(); - float scaleAdjustmentY = (0.5f - 0.5f * m_scale) * filter->filterResolution().height(); + float scaleX = filter->applyHorizontalScale(m_scale / 255); + float scaleY = filter->applyVerticalScale(m_scale / 255); + float scaleAdjustmentX = filter->applyHorizontalScale(0.5f - 0.5f * m_scale); + float scaleAdjustmentY = filter->applyVerticalScale(0.5f - 0.5f * m_scale); int stride = imageRect.width() * 4; for (int y = 0; y < imageRect.height(); ++y) { int line = y * stride; diff --git a/WebCore/platform/graphics/filters/FEDisplacementMap.h b/WebCore/platform/graphics/filters/FEDisplacementMap.h index dc87b90..c5b97a7 100644 --- a/WebCore/platform/graphics/filters/FEDisplacementMap.h +++ b/WebCore/platform/graphics/filters/FEDisplacementMap.h @@ -53,6 +53,8 @@ public: virtual void apply(Filter*); virtual void dump(); + virtual void determineAbsolutePaintRect(Filter*) { setAbsolutePaintRect(maxEffectRect()); } + virtual TextStream& externalRepresentation(TextStream&, int indention) const; private: diff --git a/WebCore/platform/graphics/filters/FEFlood.cpp b/WebCore/platform/graphics/filters/FEFlood.cpp index 7804d89..b51a422 100644 --- a/WebCore/platform/graphics/filters/FEFlood.cpp +++ b/WebCore/platform/graphics/filters/FEFlood.cpp @@ -62,14 +62,14 @@ void FEFlood::setFloodOpacity(float floodOpacity) m_floodOpacity = floodOpacity; } -void FEFlood::apply(Filter*) +void FEFlood::apply(Filter* filter) { - GraphicsContext* filterContext = effectContext(); + GraphicsContext* filterContext = effectContext(filter); if (!filterContext) return; Color color = colorWithOverrideAlpha(floodColor().rgb(), floodOpacity()); - filterContext->fillRect(FloatRect(FloatPoint(), repaintRectInLocalCoordinates().size()), color, DeviceColorSpace); + filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size()), color, ColorSpaceDeviceRGB); } void FEFlood::dump() diff --git a/WebCore/platform/graphics/filters/FEFlood.h b/WebCore/platform/graphics/filters/FEFlood.h index b615531..e6a9574 100644 --- a/WebCore/platform/graphics/filters/FEFlood.h +++ b/WebCore/platform/graphics/filters/FEFlood.h @@ -42,6 +42,8 @@ public: virtual void apply(Filter*); virtual void dump(); + virtual void determineAbsolutePaintRect(Filter*) { setAbsolutePaintRect(maxEffectRect()); } + virtual TextStream& externalRepresentation(TextStream&, int indention) const; private: diff --git a/WebCore/platform/graphics/filters/FEGaussianBlur.cpp b/WebCore/platform/graphics/filters/FEGaussianBlur.cpp index fd9a3d8..1f36ba7 100644 --- a/WebCore/platform/graphics/filters/FEGaussianBlur.cpp +++ b/WebCore/platform/graphics/filters/FEGaussianBlur.cpp @@ -4,6 +4,7 @@ * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2010 Igalia, S.L. + * Copyright (C) Research In Motion Limited 2010. 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 @@ -34,7 +35,8 @@ using std::max; -static const float gGaussianKernelFactor = (3 * sqrtf(2 * piFloat) / 4.f); +static const float gGaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat); +static const unsigned gMaxKernelSize = 1000; namespace WebCore { @@ -97,7 +99,7 @@ static void boxBlur(CanvasPixelArray*& srcPixelArray, CanvasPixelArray*& dstPixe } } -void FEGaussianBlur::kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight) +inline void kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight) { // check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details switch (boxBlur) { @@ -125,6 +127,41 @@ void FEGaussianBlur::kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& } } +inline void calculateKernelSize(Filter* filter, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY) +{ + stdX = filter->applyHorizontalScale(stdX); + stdY = filter->applyVerticalScale(stdY); + + kernelSizeX = 0; + if (stdX) + kernelSizeX = max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gGaussianKernelFactor + 0.5f))); + kernelSizeY = 0; + if (stdY) + kernelSizeY = max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gGaussianKernelFactor + 0.5f))); + + // Limit the kernel size to 1000. A bigger radius won't make a big difference for the result image but + // inflates the absolute paint rect to much. This is compatible with Firefox' behavior. + if (kernelSizeX > gMaxKernelSize) + kernelSizeX = gMaxKernelSize; + if (kernelSizeY > gMaxKernelSize) + kernelSizeY = gMaxKernelSize; +} + +void FEGaussianBlur::determineAbsolutePaintRect(Filter* filter) +{ + FloatRect absolutePaintRect = inputEffect(0)->absolutePaintRect(); + absolutePaintRect.intersect(maxEffectRect()); + + unsigned kernelSizeX = 0; + unsigned kernelSizeY = 0; + calculateKernelSize(filter, kernelSizeX, kernelSizeY, m_stdX, m_stdY); + + // We take the half kernel size and multiply it with three, because we run box blur three times. + absolutePaintRect.inflateX(3 * kernelSizeX * 0.5f); + absolutePaintRect.inflateY(3 * kernelSizeY * 0.5f); + setAbsolutePaintRect(enclosingIntRect(absolutePaintRect)); +} + void FEGaussianBlur::apply(Filter* filter) { FilterEffect* in = inputEffect(0); @@ -132,12 +169,12 @@ void FEGaussianBlur::apply(Filter* filter) if (!in->resultImage()) return; - if (!effectContext()) + if (!effectContext(filter)) return; setIsAlphaImage(in->isAlphaImage()); - IntRect effectDrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<ImageData> srcImageData(in->resultImage()->getPremultipliedImageData(effectDrawingRect)); IntRect imageRect(IntPoint(), resultImage()->size()); @@ -147,12 +184,8 @@ void FEGaussianBlur::apply(Filter* filter) } unsigned kernelSizeX = 0; - if (m_stdX) - kernelSizeX = max(2U, static_cast<unsigned>(floor(m_stdX * filter->filterResolution().width() * gGaussianKernelFactor + 0.5f))); - unsigned kernelSizeY = 0; - if (m_stdY) - kernelSizeY = max(2U, static_cast<unsigned>(floor(m_stdY * filter->filterResolution().height() * gGaussianKernelFactor + 0.5f))); + calculateKernelSize(filter, kernelSizeX, kernelSizeY, m_stdX, m_stdY); CanvasPixelArray* srcPixelArray(srcImageData->data()); RefPtr<ImageData> tmpImageData = ImageData::create(imageRect.width(), imageRect.height()); diff --git a/WebCore/platform/graphics/filters/FEGaussianBlur.h b/WebCore/platform/graphics/filters/FEGaussianBlur.h index 745bcc8..50fc610 100644 --- a/WebCore/platform/graphics/filters/FEGaussianBlur.h +++ b/WebCore/platform/graphics/filters/FEGaussianBlur.h @@ -42,12 +42,13 @@ public: virtual void apply(Filter*); virtual void dump(); + + virtual void determineAbsolutePaintRect(Filter*); virtual TextStream& externalRepresentation(TextStream&, int indention) const; private: FEGaussianBlur(float, float); - static void kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight); float m_stdX; float m_stdY; diff --git a/WebCore/platform/graphics/filters/FELighting.cpp b/WebCore/platform/graphics/filters/FELighting.cpp index f49b67d..e1df580 100644 --- a/WebCore/platform/graphics/filters/FELighting.cpp +++ b/WebCore/platform/graphics/filters/FELighting.cpp @@ -247,12 +247,12 @@ void FELighting::apply(Filter* filter) if (!in->resultImage()) return; - if (!effectContext()) + if (!effectContext(filter)) return; setIsAlphaImage(false); - IntRect effectDrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<ImageData> srcImageData(in->resultImage()->getUnmultipliedImageData(effectDrawingRect)); CanvasPixelArray* srcPixelArray(srcImageData->data()); @@ -261,8 +261,9 @@ void FELighting::apply(Filter* filter) // output for various kernelUnitLengths, and I am not sure they are reliable. // Anyway, feConvolveMatrix should also use the implementation - if (drawLighting(srcPixelArray, effectDrawingRect.width(), effectDrawingRect.height())) - resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint()); + IntSize absolutePaintSize = absolutePaintRect().size(); + if (drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height())) + resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), absolutePaintSize), IntPoint()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/filters/FELighting.h b/WebCore/platform/graphics/filters/FELighting.h index 28c00c4..bd56cee 100644 --- a/WebCore/platform/graphics/filters/FELighting.h +++ b/WebCore/platform/graphics/filters/FELighting.h @@ -44,6 +44,8 @@ class FELighting : public FilterEffect { public: virtual void apply(Filter*); + virtual void determineAbsolutePaintRect(Filter*) { setAbsolutePaintRect(maxEffectRect()); } + protected: enum LightingType { DiffuseLighting, diff --git a/WebCore/platform/graphics/filters/FEMerge.cpp b/WebCore/platform/graphics/filters/FEMerge.cpp index 19c832a..b136af3 100644 --- a/WebCore/platform/graphics/filters/FEMerge.cpp +++ b/WebCore/platform/graphics/filters/FEMerge.cpp @@ -50,13 +50,13 @@ void FEMerge::apply(Filter* filter) return; } - GraphicsContext* filterContext = effectContext(); + GraphicsContext* filterContext = effectContext(filter); if (!filterContext) return; for (unsigned i = 0; i < size; ++i) { FilterEffect* in = inputEffect(i); - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, drawingRegionOfInputImage(in->repaintRectInLocalCoordinates())); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); } } diff --git a/WebCore/platform/graphics/filters/FEMorphology.cpp b/WebCore/platform/graphics/filters/FEMorphology.cpp index 7329e1e..ac26441 100644 --- a/WebCore/platform/graphics/filters/FEMorphology.cpp +++ b/WebCore/platform/graphics/filters/FEMorphology.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) Research In Motion Limited 2010. 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 @@ -74,6 +75,15 @@ float FEMorphology::radiusY() const return m_radiusY; } +void FEMorphology::determineAbsolutePaintRect(Filter* filter) +{ + FloatRect paintRect = inputEffect(0)->absolutePaintRect(); + paintRect.inflateX(filter->applyHorizontalScale(m_radiusX)); + paintRect.inflateY(filter->applyVerticalScale(m_radiusY)); + paintRect.intersect(maxEffectRect()); + setAbsolutePaintRect(enclosingIntRect(paintRect)); +} + void FEMorphology::setRadiusY(float radiusY) { m_radiusY = radiusY; @@ -86,18 +96,18 @@ void FEMorphology::apply(Filter* filter) if (!in->resultImage()) return; - if (!effectContext()) + if (!effectContext(filter)) return; setIsAlphaImage(in->isAlphaImage()); - - int radiusX = static_cast<int>(m_radiusX * filter->filterResolution().width()); - int radiusY = static_cast<int>(m_radiusY * filter->filterResolution().height()); - if (radiusX <= 0 || radiusY <= 0) + if (m_radiusX <= 0 || m_radiusY <= 0) return; + int radiusX = static_cast<int>(floorf(filter->applyHorizontalScale(m_radiusX))); + int radiusY = static_cast<int>(floorf(filter->applyVerticalScale(m_radiusY))); + IntRect imageRect(IntPoint(), resultImage()->size()); - IntRect effectDrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates()); + IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); RefPtr<CanvasPixelArray> srcPixelArray(in->resultImage()->getPremultipliedImageData(effectDrawingRect)->data()); RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); diff --git a/WebCore/platform/graphics/filters/FEMorphology.h b/WebCore/platform/graphics/filters/FEMorphology.h index c8ce058..913671d 100644 --- a/WebCore/platform/graphics/filters/FEMorphology.h +++ b/WebCore/platform/graphics/filters/FEMorphology.h @@ -49,6 +49,8 @@ public: virtual void apply(Filter*); virtual void dump(); + virtual void determineAbsolutePaintRect(Filter*); + virtual TextStream& externalRepresentation(TextStream&, int indention) const; private: diff --git a/WebCore/platform/graphics/filters/FEOffset.cpp b/WebCore/platform/graphics/filters/FEOffset.cpp index ea84cf0..6ca56aa 100644 --- a/WebCore/platform/graphics/filters/FEOffset.cpp +++ b/WebCore/platform/graphics/filters/FEOffset.cpp @@ -3,6 +3,7 @@ * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> * Copyright (C) 2005 Eric Seidel <eric@webkit.org> * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> + * Copyright (C) Research In Motion Limited 2010. 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 @@ -62,6 +63,14 @@ void FEOffset::setDy(float dy) m_dy = dy; } +void FEOffset::determineAbsolutePaintRect(Filter* filter) +{ + FloatRect paintRect = inputEffect(0)->absolutePaintRect(); + paintRect.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); + paintRect.intersect(maxEffectRect()); + setAbsolutePaintRect(enclosingIntRect(paintRect)); +} + void FEOffset::apply(Filter* filter) { FilterEffect* in = inputEffect(0); @@ -69,28 +78,15 @@ void FEOffset::apply(Filter* filter) if (!in->resultImage()) return; - GraphicsContext* filterContext = effectContext(); + GraphicsContext* filterContext = effectContext(filter); if (!filterContext) return; setIsAlphaImage(in->isAlphaImage()); - FloatRect sourceImageRect = filter->sourceImageRect(); - sourceImageRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); - - if (filter->effectBoundingBoxMode()) { - m_dx *= sourceImageRect.width(); - m_dy *= sourceImageRect.height(); - } - m_dx *= filter->filterResolution().width(); - m_dy *= filter->filterResolution().height(); - - FloatRect dstRect = FloatRect(m_dx + in->repaintRectInLocalCoordinates().x() - repaintRectInLocalCoordinates().x(), - m_dy + in->repaintRectInLocalCoordinates().y() - repaintRectInLocalCoordinates().y(), - in->repaintRectInLocalCoordinates().width(), - in->repaintRectInLocalCoordinates().height()); - - filterContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, dstRect); + FloatRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect()); + drawingRegion.move(filter->applyHorizontalScale(m_dx), filter->applyVerticalScale(m_dy)); + filterContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, drawingRegion); } void FEOffset::dump() diff --git a/WebCore/platform/graphics/filters/FEOffset.h b/WebCore/platform/graphics/filters/FEOffset.h index 052ba74..36575c5 100644 --- a/WebCore/platform/graphics/filters/FEOffset.h +++ b/WebCore/platform/graphics/filters/FEOffset.h @@ -40,6 +40,8 @@ public: virtual void apply(Filter*); virtual void dump(); + + virtual void determineAbsolutePaintRect(Filter*); virtual TextStream& externalRepresentation(TextStream&, int indention) const; diff --git a/WebCore/platform/graphics/filters/FETile.cpp b/WebCore/platform/graphics/filters/FETile.cpp index 41abd34..a695d3b 100644 --- a/WebCore/platform/graphics/filters/FETile.cpp +++ b/WebCore/platform/graphics/filters/FETile.cpp @@ -27,6 +27,7 @@ #include "Filter.h" #include "GraphicsContext.h" #include "Pattern.h" +#include "SVGImageBufferTools.h" namespace WebCore { @@ -44,45 +45,51 @@ FloatRect FETile::determineFilterPrimitiveSubregion(Filter* filter) { inputEffect(0)->determineFilterPrimitiveSubregion(filter); - filter->determineFilterPrimitiveSubregion(this, filter->filterRegion()); + filter->determineFilterPrimitiveSubregion(this, filter->filterRegionInUserSpace()); return filterPrimitiveSubregion(); } void FETile::apply(Filter* filter) { +// FIXME: See bug 47315. This is a hack to work around a compile failure, but is incorrect behavior otherwise. +#if ENABLE(SVG) FilterEffect* in = inputEffect(0); in->apply(filter); if (!in->resultImage()) return; - GraphicsContext* filterContext = effectContext(); + GraphicsContext* filterContext = effectContext(filter); if (!filterContext) return; setIsAlphaImage(in->isAlphaImage()); - IntRect tileRect = enclosingIntRect(in->repaintRectInLocalCoordinates()); - // Source input needs more attention. It has the size of the filterRegion but gives the // size of the cutted sourceImage back. This is part of the specification and optimization. - if (in->isSourceInput()) { - FloatRect filterRegion = filter->filterRegion(); - filterRegion.scale(filter->filterResolution().width(), filter->filterResolution().height()); - tileRect = enclosingIntRect(filterRegion); + FloatRect tileRect = in->maxEffectRect(); + FloatPoint inMaxEffectLocation = tileRect.location(); + FloatPoint maxEffectLocation = maxEffectRect().location(); + if (in->filterEffectType() == FilterEffectTypeSourceInput) { + tileRect = filter->filterRegion(); + tileRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); } - OwnPtr<ImageBuffer> tileImage = ImageBuffer::create(tileRect.size()); + OwnPtr<ImageBuffer> tileImage; + if (!SVGImageBufferTools::createImageBuffer(tileRect, tileRect, tileImage, ColorSpaceDeviceRGB)) + return; + GraphicsContext* tileImageContext = tileImage->context(); - tileImageContext->drawImageBuffer(in->resultImage(), DeviceColorSpace, IntPoint()); - RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(), true, true); + tileImageContext->translate(-inMaxEffectLocation.x(), -inMaxEffectLocation.y()); + tileImageContext->drawImageBuffer(in->resultImage(), ColorSpaceDeviceRGB, in->absolutePaintRect().location()); - AffineTransform matrix; - matrix.translate(in->repaintRectInLocalCoordinates().x() - repaintRectInLocalCoordinates().x(), - in->repaintRectInLocalCoordinates().y() - repaintRectInLocalCoordinates().y()); - pattern.get()->setPatternSpaceTransform(matrix); + RefPtr<Pattern> pattern = Pattern::create(tileImage->copyImage(), true, true); + AffineTransform patternTransform; + patternTransform.translate(inMaxEffectLocation.x() - maxEffectLocation.x(), inMaxEffectLocation.y() - maxEffectLocation.y()); + pattern->setPatternSpaceTransform(patternTransform); filterContext->setFillPattern(pattern); - filterContext->fillRect(FloatRect(FloatPoint(), repaintRectInLocalCoordinates().size())); + filterContext->fillRect(FloatRect(FloatPoint(), absolutePaintRect().size())); +#endif } void FETile::dump() @@ -103,4 +110,3 @@ TextStream& FETile::externalRepresentation(TextStream& ts, int indent) const } // namespace WebCore #endif // ENABLE(FILTERS) - diff --git a/WebCore/platform/graphics/filters/FETile.h b/WebCore/platform/graphics/filters/FETile.h index 20efbcd..8562c90 100644 --- a/WebCore/platform/graphics/filters/FETile.h +++ b/WebCore/platform/graphics/filters/FETile.h @@ -35,6 +35,8 @@ public: virtual void apply(Filter*); virtual void dump(); + virtual void determineAbsolutePaintRect(Filter*) { setAbsolutePaintRect(maxEffectRect()); } + virtual TextStream& externalRepresentation(TextStream&, int indention) const; virtual FloatRect determineFilterPrimitiveSubregion(Filter*); diff --git a/WebCore/platform/graphics/filters/FETurbulence.cpp b/WebCore/platform/graphics/filters/FETurbulence.cpp index bb24362..b1494a5 100644 --- a/WebCore/platform/graphics/filters/FETurbulence.cpp +++ b/WebCore/platform/graphics/filters/FETurbulence.cpp @@ -321,7 +321,7 @@ unsigned char FETurbulence::calculateTurbulenceValueForPoint(PaintingData& paint void FETurbulence::apply(Filter* filter) { - if (!effectContext()) + if (!effectContext(filter)) return; IntRect imageRect(IntPoint(), resultImage()->size()); @@ -329,10 +329,10 @@ void FETurbulence::apply(Filter* filter) return; RefPtr<ImageData> imageData = ImageData::create(imageRect.width(), imageRect.height()); - PaintingData paintingData(m_seed, imageRect.size()); + PaintingData paintingData(m_seed, roundedIntSize(filterPrimitiveSubregion().size())); initPaint(paintingData); - FloatRect filterRegion = filter->filterRegion(); + FloatRect filterRegion = absolutePaintRect(); FloatPoint point; point.setY(filterRegion.y()); int indexOfPixelChannel = 0; @@ -342,7 +342,7 @@ void FETurbulence::apply(Filter* filter) for (int x = 0; x < imageRect.width(); ++x) { point.setX(point.x() + 1); for (paintingData.channel = 0; paintingData.channel < 4; ++paintingData.channel, ++indexOfPixelChannel) - imageData->data()->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(paintingData, point)); + imageData->data()->set(indexOfPixelChannel, calculateTurbulenceValueForPoint(paintingData, filter->mapAbsolutePointToLocalPoint(point))); } } resultImage()->putUnmultipliedImageData(imageData.get(), imageRect, IntPoint()); diff --git a/WebCore/platform/graphics/filters/FETurbulence.h b/WebCore/platform/graphics/filters/FETurbulence.h index 1a5a28a..c15d7d1 100644 --- a/WebCore/platform/graphics/filters/FETurbulence.h +++ b/WebCore/platform/graphics/filters/FETurbulence.h @@ -60,6 +60,8 @@ public: virtual void apply(Filter*); virtual void dump(); + + virtual void determineAbsolutePaintRect(Filter*) { setAbsolutePaintRect(maxEffectRect()); } virtual TextStream& externalRepresentation(TextStream&, int indention) const; diff --git a/WebCore/platform/graphics/filters/Filter.h b/WebCore/platform/graphics/filters/Filter.h index bce4be3..121e389 100644 --- a/WebCore/platform/graphics/filters/Filter.h +++ b/WebCore/platform/graphics/filters/Filter.h @@ -44,11 +44,18 @@ namespace WebCore { FloatSize filterResolution() const { return m_filterResolution; } void setFilterResolution(const FloatSize& filterResolution) { m_filterResolution = filterResolution; } + virtual float applyHorizontalScale(float value) const { return value * m_filterResolution.width(); } + virtual float applyVerticalScale(float value) const { return value * m_filterResolution.height(); } + virtual FloatRect sourceImageRect() const = 0; virtual FloatRect filterRegion() const = 0; + + virtual FloatPoint mapAbsolutePointToLocalPoint(const FloatPoint&) const { return FloatPoint(); } // SVG specific virtual void determineFilterPrimitiveSubregion(FilterEffect*, const FloatRect&) { } + + virtual FloatRect filterRegionInUserSpace() const { return FloatRect(); } virtual FloatSize maxImageSize() const = 0; virtual bool effectBoundingBoxMode() const = 0; diff --git a/WebCore/platform/graphics/filters/FilterEffect.cpp b/WebCore/platform/graphics/filters/FilterEffect.cpp index 461b22a..121b921 100644 --- a/WebCore/platform/graphics/filters/FilterEffect.cpp +++ b/WebCore/platform/graphics/filters/FilterEffect.cpp @@ -46,7 +46,7 @@ FloatRect FilterEffect::determineFilterPrimitiveSubregion(Filter* filter) // FETurbulence, FEImage and FEFlood don't have input effects, take the filter region as unite rect. if (!size) - uniteRect = filter->filterRegion(); + uniteRect = filter->filterRegionInUserSpace(); else { for (unsigned i = 0; i < size; ++i) uniteRect.unite(m_inputEffects.at(i)->determineFilterPrimitiveSubregion(filter)); @@ -56,18 +56,29 @@ FloatRect FilterEffect::determineFilterPrimitiveSubregion(Filter* filter) return m_filterPrimitiveSubregion; } -IntRect FilterEffect::requestedRegionOfInputImageData(const FloatRect& effectRect) const +void FilterEffect::determineAbsolutePaintRect(Filter*) +{ + m_absolutePaintRect = IntRect(); + unsigned size = m_inputEffects.size(); + for (unsigned i = 0; i < size; ++i) + m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect()); + + // SVG specification wants us to clip to primitive subregion. + m_absolutePaintRect.intersect(m_maxEffectRect); +} + +IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const { ASSERT(m_effectBuffer); - FloatPoint location = m_repaintRectInLocalCoordinates.location(); + IntPoint location = m_absolutePaintRect.location(); location.move(-effectRect.x(), -effectRect.y()); - return IntRect(roundedIntPoint(location), m_effectBuffer->size()); + return IntRect(location, m_effectBuffer->size()); } -FloatRect FilterEffect::drawingRegionOfInputImage(const FloatRect& srcRect) const +IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const { - return FloatRect(FloatPoint(srcRect.x() - m_repaintRectInLocalCoordinates.x(), - srcRect.y() - m_repaintRectInLocalCoordinates.y()), srcRect.size()); + return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(), + srcRect.y() - m_absolutePaintRect.y()), srcRect.size()); } FilterEffect* FilterEffect::inputEffect(unsigned number) const @@ -76,10 +87,12 @@ FilterEffect* FilterEffect::inputEffect(unsigned number) const return m_inputEffects.at(number).get(); } -GraphicsContext* FilterEffect::effectContext() +GraphicsContext* FilterEffect::effectContext(Filter* filter) { - IntRect bufferRect = enclosingIntRect(m_repaintRectInLocalCoordinates); - m_effectBuffer = ImageBuffer::create(bufferRect.size(), LinearRGB); + determineAbsolutePaintRect(filter); + if (m_absolutePaintRect.isEmpty()) + return 0; + m_effectBuffer = ImageBuffer::create(m_absolutePaintRect.size(), ColorSpaceLinearRGB); if (!m_effectBuffer) return 0; return m_effectBuffer->context(); diff --git a/WebCore/platform/graphics/filters/FilterEffect.h b/WebCore/platform/graphics/filters/FilterEffect.h index ebe1880..a614b59 100644 --- a/WebCore/platform/graphics/filters/FilterEffect.h +++ b/WebCore/platform/graphics/filters/FilterEffect.h @@ -39,6 +39,13 @@ namespace WebCore { typedef Vector<RefPtr<FilterEffect> > FilterEffectVector; +enum FilterEffectType { + FilterEffectTypeUnknown, + FilterEffectTypeImage, + FilterEffectTypeTile, + FilterEffectTypeSourceInput +}; + class FilterEffect : public RefCounted<FilterEffect> { public: virtual ~FilterEffect(); @@ -49,26 +56,31 @@ public: // Creates the ImageBuffer for the current filter primitive result in the size of the // repaintRect. Gives back the GraphicsContext of the own ImageBuffer. - GraphicsContext* effectContext(); + GraphicsContext* effectContext(Filter*); FilterEffectVector& inputEffects() { return m_inputEffects; } FilterEffect* inputEffect(unsigned) const; unsigned numberOfEffectInputs() const { return m_inputEffects.size(); } - FloatRect drawingRegionOfInputImage(const FloatRect&) const; - IntRect requestedRegionOfInputImageData(const FloatRect&) const; + IntRect drawingRegionOfInputImage(const IntRect&) const; + IntRect requestedRegionOfInputImageData(const IntRect&) const; // Solid black image with different alpha values. bool isAlphaImage() const { return m_alphaImage; } void setIsAlphaImage(bool alphaImage) { m_alphaImage = alphaImage; } - FloatRect repaintRectInLocalCoordinates() const { return m_repaintRectInLocalCoordinates; } - void setRepaintRectInLocalCoordinates(const FloatRect& repaintRectInLocalCoordinates) { m_repaintRectInLocalCoordinates = repaintRectInLocalCoordinates; } + IntRect absolutePaintRect() const { return m_absolutePaintRect; } + void setAbsolutePaintRect(const IntRect& absolutePaintRect) { m_absolutePaintRect = absolutePaintRect; } + + IntRect maxEffectRect() const { return m_maxEffectRect; } + void setMaxEffectRect(const IntRect& maxEffectRect) { m_maxEffectRect = maxEffectRect; } virtual void apply(Filter*) = 0; virtual void dump() = 0; - virtual bool isSourceInput() const { return false; } + virtual void determineAbsolutePaintRect(Filter*); + + virtual FilterEffectType filterEffectType() const { return FilterEffectTypeUnknown; } virtual TextStream& externalRepresentation(TextStream&, int indention = 0) const; @@ -87,7 +99,7 @@ public: bool hasHeight() const { return m_hasHeight; } void setHasHeight(bool value) { m_hasHeight = value; } - // FIXME: Pseudo primitives like SourceGraphic and SourceAlpha as well as FETile still need special handling. + // FIXME: FETile still needs special handling. virtual FloatRect determineFilterPrimitiveSubregion(Filter*); FloatRect filterPrimitiveSubregion() const { return m_filterPrimitiveSubregion; } @@ -105,8 +117,11 @@ private: bool m_alphaImage; - // FIXME: Should be the paint region of the filter primitive, instead of the scaled subregion on use of filterRes. - FloatRect m_repaintRectInLocalCoordinates; + IntRect m_absolutePaintRect; + + // The maximum size of a filter primitive. In SVG this is the primitive subregion in absolute coordinate space. + // The absolute paint rect should never be bigger than m_maxEffectRect. + IntRect m_maxEffectRect; private: // The following member variables are SVG specific and will move to RenderSVGResourceFilterPrimitive. diff --git a/WebCore/platform/graphics/filters/ImageBufferFilter.h b/WebCore/platform/graphics/filters/ImageBufferFilter.h deleted file mode 100644 index cd4bc2f..0000000 --- a/WebCore/platform/graphics/filters/ImageBufferFilter.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> - * Copyright (C) 2009 Brent Fulgham <bfulgham@webkit.org> - * - * 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 ImageBufferFilter_h -#define ImageBufferFilter_h - -#if ENABLE(FILTERS) -#include "Filter.h" -#include "FilterEffect.h" -#include "FloatRect.h" -#include "FloatSize.h" - -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - -class ImageBufferFilter : public Filter { -public: - static PassRefPtr<ImageBufferFilter> create(); - - virtual FloatRect filterRegion() const { return FloatRect(); } - virtual FloatRect sourceImageRect() const { return FloatRect(); } - - // SVG specific - virtual bool effectBoundingBoxMode() const { return false; } - - virtual FloatSize maxImageSize() const { return FloatSize(); } - virtual void calculateEffectSubRegion(FilterEffect*) { } - -private: - ImageBufferFilter(); -}; - -} // namespace WebCore - -#endif // ENABLE(FILTERS) - -#endif // ImageBufferFilter_h diff --git a/WebCore/platform/graphics/filters/SourceAlpha.cpp b/WebCore/platform/graphics/filters/SourceAlpha.cpp index beaf2e7..7dc56d9 100644 --- a/WebCore/platform/graphics/filters/SourceAlpha.cpp +++ b/WebCore/platform/graphics/filters/SourceAlpha.cpp @@ -42,31 +42,25 @@ const AtomicString& SourceAlpha::effectName() return s_effectName; } -FloatRect SourceAlpha::determineFilterPrimitiveSubregion(Filter* filter) +void SourceAlpha::determineAbsolutePaintRect(Filter* filter) { - FloatRect clippedSourceRect = filter->sourceImageRect(); - if (filter->sourceImageRect().x() < filter->filterRegion().x()) - clippedSourceRect.setX(filter->filterRegion().x()); - if (filter->sourceImageRect().y() < filter->filterRegion().y()) - clippedSourceRect.setY(filter->filterRegion().y()); - setFilterPrimitiveSubregion(clippedSourceRect); - clippedSourceRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); - setRepaintRectInLocalCoordinates(clippedSourceRect); - return filter->filterRegion(); + FloatRect paintRect = filter->sourceImageRect(); + paintRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); + setAbsolutePaintRect(enclosingIntRect(paintRect)); } void SourceAlpha::apply(Filter* filter) { - GraphicsContext* filterContext = effectContext(); - if (!filterContext) + GraphicsContext* filterContext = effectContext(filter); + if (!filterContext || !filter->sourceImage()) return; setIsAlphaImage(true); - FloatRect imageRect(FloatPoint(), filter->sourceImage()->size()); + FloatRect imageRect(FloatPoint(), absolutePaintRect().size()); filterContext->save(); filterContext->clipToImageBuffer(filter->sourceImage(), imageRect); - filterContext->fillRect(imageRect, Color::black, DeviceColorSpace); + filterContext->fillRect(imageRect, Color::black, ColorSpaceDeviceRGB); filterContext->restore(); } diff --git a/WebCore/platform/graphics/filters/SourceAlpha.h b/WebCore/platform/graphics/filters/SourceAlpha.h index f0fa319..83704e5 100644 --- a/WebCore/platform/graphics/filters/SourceAlpha.h +++ b/WebCore/platform/graphics/filters/SourceAlpha.h @@ -34,12 +34,12 @@ public: static const AtomicString& effectName(); - virtual FloatRect determineFilterPrimitiveSubregion(Filter*); - virtual void apply(Filter*); virtual void dump(); - virtual bool isSourceInput() const { return true; } + virtual void determineAbsolutePaintRect(Filter*); + + virtual FilterEffectType filterEffectType() const { return FilterEffectTypeSourceInput; } virtual TextStream& externalRepresentation(TextStream&, int indention) const; diff --git a/WebCore/platform/graphics/filters/SourceGraphic.cpp b/WebCore/platform/graphics/filters/SourceGraphic.cpp index c014e68..fbb711a 100644 --- a/WebCore/platform/graphics/filters/SourceGraphic.cpp +++ b/WebCore/platform/graphics/filters/SourceGraphic.cpp @@ -41,26 +41,20 @@ const AtomicString& SourceGraphic::effectName() return s_effectName; } -FloatRect SourceGraphic::determineFilterPrimitiveSubregion(Filter* filter) +void SourceGraphic::determineAbsolutePaintRect(Filter* filter) { - FloatRect clippedSourceRect = filter->sourceImageRect(); - if (filter->sourceImageRect().x() < filter->filterRegion().x()) - clippedSourceRect.setX(filter->filterRegion().x()); - if (filter->sourceImageRect().y() < filter->filterRegion().y()) - clippedSourceRect.setY(filter->filterRegion().y()); - setFilterPrimitiveSubregion(clippedSourceRect); - clippedSourceRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); - setRepaintRectInLocalCoordinates(clippedSourceRect); - return filter->filterRegion(); + FloatRect paintRect = filter->sourceImageRect(); + paintRect.scale(filter->filterResolution().width(), filter->filterResolution().height()); + setAbsolutePaintRect(enclosingIntRect(paintRect)); } void SourceGraphic::apply(Filter* filter) { - GraphicsContext* filterContext = effectContext(); - if (!filterContext) + GraphicsContext* filterContext = effectContext(filter); + if (!filterContext || !filter->sourceImage()) return; - filterContext->drawImageBuffer(filter->sourceImage(), DeviceColorSpace, IntPoint()); + filterContext->drawImageBuffer(filter->sourceImage(), ColorSpaceDeviceRGB, IntPoint()); } void SourceGraphic::dump() diff --git a/WebCore/platform/graphics/filters/SourceGraphic.h b/WebCore/platform/graphics/filters/SourceGraphic.h index 2378798..a13337d 100644 --- a/WebCore/platform/graphics/filters/SourceGraphic.h +++ b/WebCore/platform/graphics/filters/SourceGraphic.h @@ -35,12 +35,12 @@ public: static const AtomicString& effectName(); - virtual FloatRect determineFilterPrimitiveSubregion(Filter*); - virtual void apply(Filter*); virtual void dump(); - virtual bool isSourceInput() const { return true; } + virtual void determineAbsolutePaintRect(Filter*); + + virtual FilterEffectType filterEffectType() const { return FilterEffectTypeSourceInput; } virtual TextStream& externalRepresentation(TextStream&, int indention) const; diff --git a/WebCore/platform/graphics/gpu/DrawingBuffer.cpp b/WebCore/platform/graphics/gpu/DrawingBuffer.cpp index dc80954..2dc0517 100644 --- a/WebCore/platform/graphics/gpu/DrawingBuffer.cpp +++ b/WebCore/platform/graphics/gpu/DrawingBuffer.cpp @@ -30,31 +30,40 @@ #include "config.h" -#include "DrawingBuffer.h" +#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(3D_CANVAS) -#include "GraphicsContext3D.h" -#include "SharedGraphicsContext3D.h" +#include "DrawingBuffer.h" namespace WebCore { -PassOwnPtr<DrawingBuffer> DrawingBuffer::create(SharedGraphicsContext3D* context, const IntSize& size) +PassRefPtr<DrawingBuffer> DrawingBuffer::create(GraphicsContext3D* context, const IntSize& size) { - unsigned framebuffer = context->createFramebuffer(); - ASSERT(framebuffer); - if (!framebuffer) - return 0; - return adoptPtr(new DrawingBuffer(context, size, framebuffer)); + RefPtr<DrawingBuffer> drawingBuffer = adoptRef(new DrawingBuffer(context, size)); + return (drawingBuffer->m_context) ? drawingBuffer.release() : 0; } -void DrawingBuffer::bind() +void DrawingBuffer::clear() { - m_context->bindFramebuffer(m_framebuffer); - m_context->setViewport(m_size); + if (!m_context) + return; + + m_context->makeContextCurrent(); + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + m_context->deleteFramebuffer(m_fbo); + m_fbo = 0; + + m_context.clear(); } -void DrawingBuffer::setWillPublishCallback(PassOwnPtr<WillPublishCallback> callback) +void DrawingBuffer::bind() { - m_callback = callback; + if (!m_context) + return; + + m_context->bindFramebuffer(GraphicsContext3D::FRAMEBUFFER, m_fbo); + m_context->viewport(0, 0, m_size.width(), m_size.height()); } } // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/gpu/DrawingBuffer.h b/WebCore/platform/graphics/gpu/DrawingBuffer.h index 23e6f4a..75c7f99 100644 --- a/WebCore/platform/graphics/gpu/DrawingBuffer.h +++ b/WebCore/platform/graphics/gpu/DrawingBuffer.h @@ -31,30 +31,39 @@ #ifndef DrawingBuffer_h #define DrawingBuffer_h +#include "GraphicsContext3D.h" #include "GraphicsLayer.h" #include "IntSize.h" #include <wtf/Noncopyable.h> #include <wtf/OwnPtr.h> #include <wtf/PassOwnPtr.h> +#if PLATFORM(MAC) +#include <wtf/RetainPtr.h> +#endif namespace WebCore { -class SharedGraphicsContext3D; - +#if PLATFORM(CHROMIUM) struct DrawingBufferInternal; +#endif // Manages a rendering target (framebuffer + attachment) for a canvas. Can publish its rendering // results to a PlatformLayer for compositing. -class DrawingBuffer : public Noncopyable { +class DrawingBuffer : public RefCounted<DrawingBuffer> { public: - static PassOwnPtr<DrawingBuffer> create(SharedGraphicsContext3D*, const IntSize&); + friend class GraphicsContext3D; + ~DrawingBuffer(); void reset(const IntSize&); void bind(); IntSize size() const { return m_size; } + // Clear all resources from this object, as well as context. Called when context is destroyed + // to prevent invalid accesses to the resources. + void clear(); + #if USE(ACCELERATED_COMPOSITING) PlatformLayer* platformLayer(); void publishToPlatformLayer(); @@ -62,21 +71,36 @@ public: unsigned getRenderingResultsAsTexture(); +#if PLATFORM(CHROMIUM) class WillPublishCallback : public Noncopyable { public: + virtual ~WillPublishCallback() { } + virtual void willPublish() = 0; }; - void setWillPublishCallback(PassOwnPtr<WillPublishCallback>); + void setWillPublishCallback(PassOwnPtr<WillPublishCallback> callback) { m_callback = callback; } +#endif + + PassRefPtr<GraphicsContext3D> graphicsContext3D() const { return m_context; } + private: - DrawingBuffer(SharedGraphicsContext3D*, const IntSize&, unsigned framebuffer); + static PassRefPtr<DrawingBuffer> create(GraphicsContext3D*, const IntSize&); + + DrawingBuffer(GraphicsContext3D*, const IntSize&); - SharedGraphicsContext3D* m_context; + RefPtr<GraphicsContext3D> m_context; IntSize m_size; - unsigned m_framebuffer; + Platform3DObject m_fbo; +#if PLATFORM(CHROMIUM) OwnPtr<WillPublishCallback> m_callback; OwnPtr<DrawingBufferInternal> m_internal; +#endif + +#if PLATFORM(MAC) + RetainPtr<WebGLLayer> m_platformLayer; +#endif }; } // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp b/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp index e43dc37..672b4d7 100644 --- a/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp +++ b/WebCore/platform/graphics/gpu/LoopBlinnClassifier.cpp @@ -25,6 +25,8 @@ #include "config.h" +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "LoopBlinnClassifier.h" #include "LoopBlinnMathUtils.h" @@ -120,3 +122,5 @@ LoopBlinnClassifier::Result LoopBlinnClassifier::classify(const FloatPoint& c0, } } // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp b/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp index 3b73ff6..1517a67 100644 --- a/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp +++ b/WebCore/platform/graphics/gpu/LoopBlinnLocalTriangulator.cpp @@ -25,6 +25,8 @@ #include "config.h" +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "LoopBlinnLocalTriangulator.h" #include "LoopBlinnMathUtils.h" @@ -273,3 +275,5 @@ bool LoopBlinnLocalTriangulator::isSharedEdge(Vertex* v0, Vertex* v1) } } // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp b/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp index 61ebc9b..5b155a5 100644 --- a/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp +++ b/WebCore/platform/graphics/gpu/LoopBlinnMathUtils.cpp @@ -25,12 +25,13 @@ #include "config.h" +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "LoopBlinnMathUtils.h" #include "FloatPoint.h" -#include "MathExtras.h" #include <algorithm> -#include <string.h> // for memcpy +#include <wtf/MathExtras.h> namespace WebCore { namespace LoopBlinnMathUtils { @@ -563,3 +564,5 @@ int numXRayCrossingsForCubic(const XRay& xRay, const FloatPoint cubic[4], bool& } // namespace LoopBlinnMathUtils } // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp b/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp index ac82637..d272fe1 100644 --- a/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp +++ b/WebCore/platform/graphics/gpu/LoopBlinnTextureCoords.cpp @@ -25,6 +25,8 @@ #include "config.h" +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "LoopBlinnTextureCoords.h" #include <math.h> @@ -169,3 +171,5 @@ LoopBlinnTextureCoords::Result LoopBlinnTextureCoords::compute(const LoopBlinnCl } } // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/gpu/PODInterval.h b/WebCore/platform/graphics/gpu/PODInterval.h index 9df69ba..5c1dcc2 100644 --- a/WebCore/platform/graphics/gpu/PODInterval.h +++ b/WebCore/platform/graphics/gpu/PODInterval.h @@ -27,7 +27,7 @@ #define PODInterval_h #ifndef NDEBUG -#include "StringBuilder.h" +#include <wtf/text/StringBuilder.h> #endif namespace WebCore { @@ -57,14 +57,24 @@ namespace WebCore { // constructor and assignment operator. // // In debug mode, printing of intervals and the data they contain is -// enabled. This requires the following functions to be available: +// enabled. This requires the following template specializations to be +// available: // -// String valueToString(const T&); -// String valueToString(const UserData&); +// template<> struct WebCore::ValueToString<T> { +// static String string(const T& t); +// }; +// template<> struct WebCore::ValueToString<UserData> { +// static String string(const UserData& t); +// }; // // Note that this class requires a copy constructor and assignment // operator in order to be stored in the red-black tree. +#ifndef NDEBUG +template<class T> +struct ValueToString; +#endif + template<class T, class UserData = void*> class PODInterval { public: @@ -131,13 +141,13 @@ public: { StringBuilder builder; builder.append("[PODInterval ("); - builder.append(valueToString(low())); + builder.append(ValueToString<T>::string(low())); builder.append(", "); - builder.append(valueToString(high())); + builder.append(ValueToString<T>::string(high())); builder.append("), data="); - builder.append(valueToString(data())); + builder.append(ValueToString<UserData>::string(data())); builder.append(", maxHigh="); - builder.append(valueToString(maxHigh())); + builder.append(ValueToString<T>::string(maxHigh())); builder.append("]"); return builder.toString(); } diff --git a/WebCore/platform/graphics/gpu/PODIntervalTree.h b/WebCore/platform/graphics/gpu/PODIntervalTree.h index c0a86aa..320ce60 100644 --- a/WebCore/platform/graphics/gpu/PODIntervalTree.h +++ b/WebCore/platform/graphics/gpu/PODIntervalTree.h @@ -35,6 +35,11 @@ namespace WebCore { +#ifndef NDEBUG +template<class T> +struct ValueToString; +#endif + // An interval tree, which is a form of augmented red-black tree. It // supports efficient (O(lg n)) insertion, removal and querying of // intervals in the tree. @@ -191,7 +196,7 @@ private: localMaxValue = node->data().high(); if (!(localMaxValue == node->data().maxHigh())) { #ifndef NDEBUG - String localMaxValueString = valueToString(localMaxValue); + String localMaxValueString = ValueToString<T>::string(localMaxValue); LOG_ERROR("PODIntervalTree verification failed at node 0x%p: localMaxValue=%s and data=%s", node, localMaxValueString.utf8().data(), node->data().toString().utf8().data()); #endif @@ -206,10 +211,12 @@ private: #ifndef NDEBUG // Support for printing PODIntervals at the PODRedBlackTree level. template<class T, class UserData> -String valueToString(const PODInterval<T, UserData>& interval) -{ - return interval.toString(); -} +struct ValueToString<PODInterval<T, UserData> > { + static String string(const PODInterval<T, UserData>& interval) + { + return interval.toString(); + } +}; #endif } // namespace WebCore diff --git a/WebCore/platform/graphics/gpu/PODRedBlackTree.h b/WebCore/platform/graphics/gpu/PODRedBlackTree.h index 9b02037..6d5954c 100644 --- a/WebCore/platform/graphics/gpu/PODRedBlackTree.h +++ b/WebCore/platform/graphics/gpu/PODRedBlackTree.h @@ -42,9 +42,11 @@ // the "<" and "==" operators. // // In debug mode, printing of the data contained in the tree is -// enabled. This requires the following function to be available: +// enabled. This requires the template specialization to be available: // -// String valueToString(const T&); +// template<> struct WebCore::ValueToString<T> { +// static String string(const T& t); +// }; // // Note that when complex types are stored in this red/black tree, it // is possible that single invocations of the "<" and "==" operators @@ -76,13 +78,18 @@ #include <wtf/RefPtr.h> #ifndef NDEBUG #include "Logging.h" -#include "PlatformString.h" -#include "StringBuilder.h" #include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> +#include <wtf/text/WTFString.h> #endif namespace WebCore { +#ifndef NDEBUG +template<class T> +struct ValueToString; +#endif + template<class T> class PODRedBlackTree { public: @@ -723,7 +730,7 @@ private: builder.append("-"); if (node) { builder.append(" "); - builder.append(valueToString(node->data())); + builder.append(ValueToString<T>::string(node->data())); builder.append((node->color() == Black) ? " (black)" : " (red)"); } LOG_ERROR("%s", builder.toString().ascii().data()); diff --git a/WebCore/platform/graphics/gpu/Shader.cpp b/WebCore/platform/graphics/gpu/Shader.cpp index 59c50a7..8983adc 100644 --- a/WebCore/platform/graphics/gpu/Shader.cpp +++ b/WebCore/platform/graphics/gpu/Shader.cpp @@ -29,6 +29,9 @@ */ #include "config.h" + +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "Shader.h" #include "AffineTransform.h" @@ -109,3 +112,5 @@ Shader::~Shader() } } + +#endif diff --git a/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp index 7629735..87a0b69 100644 --- a/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp +++ b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.cpp @@ -30,6 +30,8 @@ #include "config.h" +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "SharedGraphicsContext3D.h" #include "AffineTransform.h" @@ -47,16 +49,26 @@ namespace WebCore { // static -PassRefPtr<SharedGraphicsContext3D> SharedGraphicsContext3D::create(PassOwnPtr<GraphicsContext3D> context) -{ - return adoptRef(new SharedGraphicsContext3D(context)); -} - -SharedGraphicsContext3D::SharedGraphicsContext3D(PassOwnPtr<GraphicsContext3D> context) +PassRefPtr<SharedGraphicsContext3D> SharedGraphicsContext3D::create(HostWindow* hostWindow) +{ + GraphicsContext3D::Attributes attr; + RefPtr<GraphicsContext3D> context = GraphicsContext3D::create(attr, hostWindow); + if (!context) + return 0; + OwnPtr<SolidFillShader> solidFillShader = SolidFillShader::create(context.get()); + if (!solidFillShader) + return 0; + OwnPtr<TexShader> texShader = TexShader::create(context.get()); + if (!texShader) + return 0; + return adoptRef(new SharedGraphicsContext3D(context.release(), solidFillShader.release(), texShader.release())); +} + +SharedGraphicsContext3D::SharedGraphicsContext3D(PassRefPtr<GraphicsContext3D> context, PassOwnPtr<SolidFillShader> solidFillShader, PassOwnPtr<TexShader> texShader) : m_context(context) , m_quadVertices(0) - , m_solidFillShader(SolidFillShader::create(m_context.get())) - , m_texShader(TexShader::create(m_context.get())) + , m_solidFillShader(solidFillShader) + , m_texShader(texShader) { allContexts()->add(this); } @@ -215,8 +227,8 @@ void SharedGraphicsContext3D::removeTexturesFor(NativeImagePtr ptr) // static HashSet<SharedGraphicsContext3D*>* SharedGraphicsContext3D::allContexts() { - static OwnPtr<HashSet<SharedGraphicsContext3D*> > set(new HashSet<SharedGraphicsContext3D*>); - return set.get(); + DEFINE_STATIC_LOCAL(HashSet<SharedGraphicsContext3D*>, allContextsSet, ()); + return &allContextsSet; } @@ -334,3 +346,5 @@ bool SharedGraphicsContext3D::paintsIntoCanvasBuffer() const } } // namespace WebCore + +#endif diff --git a/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h index 3ba3c52..05008c2 100644 --- a/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h +++ b/WebCore/platform/graphics/gpu/SharedGraphicsContext3D.h @@ -47,6 +47,7 @@ class AffineTransform; class Color; class GraphicsContext3D; class FloatRect; +class HostWindow; class IntSize; class SolidFillShader; class TexShader; @@ -55,7 +56,7 @@ typedef HashMap<NativeImagePtr, RefPtr<Texture> > TextureHashMap; class SharedGraphicsContext3D : public RefCounted<SharedGraphicsContext3D> { public: - static PassRefPtr<SharedGraphicsContext3D> create(PassOwnPtr<GraphicsContext3D>); + static PassRefPtr<SharedGraphicsContext3D> create(HostWindow*); ~SharedGraphicsContext3D(); // Functions that delegate directly to GraphicsContext3D, with caching @@ -117,14 +118,16 @@ public: // the texture. PassRefPtr<Texture> createTexture(Texture::Format, int width, int height); + GraphicsContext3D* graphicsContext3D() const { return m_context.get(); } + private: - explicit SharedGraphicsContext3D(PassOwnPtr<GraphicsContext3D> context); + SharedGraphicsContext3D(PassRefPtr<GraphicsContext3D>, PassOwnPtr<SolidFillShader>, PassOwnPtr<TexShader>); // Used to implement removeTexturesFor(), see the comment above. static HashSet<SharedGraphicsContext3D*>* allContexts(); void removeTextureFor(NativeImagePtr); - OwnPtr<GraphicsContext3D> m_context; + RefPtr<GraphicsContext3D> m_context; unsigned m_quadVertices; diff --git a/WebCore/platform/graphics/gpu/SolidFillShader.cpp b/WebCore/platform/graphics/gpu/SolidFillShader.cpp index ff1b1fa..86079be 100644 --- a/WebCore/platform/graphics/gpu/SolidFillShader.cpp +++ b/WebCore/platform/graphics/gpu/SolidFillShader.cpp @@ -29,6 +29,9 @@ */ #include "config.h" + +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "SolidFillShader.h" #include "Color.h" @@ -86,3 +89,5 @@ void SolidFillShader::use(const AffineTransform& transform, const Color& color) } } + +#endif diff --git a/WebCore/platform/graphics/gpu/TexShader.cpp b/WebCore/platform/graphics/gpu/TexShader.cpp index 01f4306..d7ffa17 100644 --- a/WebCore/platform/graphics/gpu/TexShader.cpp +++ b/WebCore/platform/graphics/gpu/TexShader.cpp @@ -29,6 +29,9 @@ */ #include "config.h" + +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "TexShader.h" #include "GraphicsContext3D.h" @@ -93,3 +96,5 @@ void TexShader::use(const AffineTransform& transform, const AffineTransform& tex } } + +#endif diff --git a/WebCore/platform/graphics/gpu/Texture.cpp b/WebCore/platform/graphics/gpu/Texture.cpp index 6023fe9..74807dc 100644 --- a/WebCore/platform/graphics/gpu/Texture.cpp +++ b/WebCore/platform/graphics/gpu/Texture.cpp @@ -30,6 +30,8 @@ #include "config.h" +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "Texture.h" #include "FloatRect.h" @@ -206,3 +208,5 @@ void Texture::bindTile(int tile) } } + +#endif diff --git a/WebCore/platform/graphics/gpu/Texture.h b/WebCore/platform/graphics/gpu/Texture.h index eda475e..92b6d0a 100644 --- a/WebCore/platform/graphics/gpu/Texture.h +++ b/WebCore/platform/graphics/gpu/Texture.h @@ -31,11 +31,11 @@ #ifndef Texture_h #define Texture_h -#include "RefCounted.h" -#include "RefPtr.h" #include "TilingData.h" #include <wtf/OwnPtr.h> #include <wtf/PassOwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> #include <wtf/Vector.h> namespace WebCore { diff --git a/WebCore/platform/graphics/gpu/TilingData.cpp b/WebCore/platform/graphics/gpu/TilingData.cpp index 4da242b..a98add7 100644 --- a/WebCore/platform/graphics/gpu/TilingData.cpp +++ b/WebCore/platform/graphics/gpu/TilingData.cpp @@ -29,6 +29,9 @@ */ #include "config.h" + +#if ENABLE(ACCELERATED_2D_CANVAS) + #include "TilingData.h" #include "FloatRect.h" @@ -220,3 +223,5 @@ void TilingData::intersectDrawQuad(const FloatRect& srcRect, const FloatRect& ds } } + +#endif diff --git a/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm b/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm new file mode 100644 index 0000000..7a8c501 --- /dev/null +++ b/WebCore/platform/graphics/gpu/mac/DrawingBufferMac.mm @@ -0,0 +1,84 @@ +/* + * Copyright (C) 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 INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(ACCELERATED_2D_CANVAS) || ENABLE(3D_CANVAS) + +#include "DrawingBuffer.h" + +#include "WebGLLayer.h" + +#import "BlockExceptions.h" + +namespace WebCore { + +DrawingBuffer::DrawingBuffer(GraphicsContext3D* context, const IntSize& size) + : m_context(context) + , m_size(size) + , m_fbo(context->createFramebuffer()) +{ + ASSERT(m_fbo); + if (!m_fbo) { + clear(); + return; + } + + // Create the WebGLLayer + BEGIN_BLOCK_OBJC_EXCEPTIONS + m_platformLayer.adoptNS([[WebGLLayer alloc] initWithGraphicsContext3D:m_context.get()]); +#ifndef NDEBUG + [m_platformLayer.get() setName:@"DrawingBuffer Layer"]; +#endif + END_BLOCK_OBJC_EXCEPTIONS +} + +DrawingBuffer::~DrawingBuffer() +{ + clear(); +} + +void DrawingBuffer::reset(const IntSize& newSize) +{ + if (!m_context) + return; + + if (m_size == newSize) + return; + m_size = newSize; + + m_context->texImage2D(GraphicsContext3D::TEXTURE_2D, 0, GraphicsContext3D::RGBA, m_size.width(), m_size.height(), 0, GraphicsContext3D::RGBA, GraphicsContext3D::UNSIGNED_BYTE, 0); +} + +#if USE(ACCELERATED_COMPOSITING) +PlatformLayer* DrawingBuffer::platformLayer() +{ + return m_platformLayer.get(); +} +#endif + +} + +#endif diff --git a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp index 539d92a..1cb561e 100644 --- a/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp +++ b/WebCore/platform/graphics/gstreamer/GStreamerGWorld.cpp @@ -194,12 +194,14 @@ void GStreamerGWorld::setWindowOverlay(GstMessage* message) if (g_object_class_find_property(G_OBJECT_GET_CLASS(sink), "force-aspect-ratio")) g_object_set(sink, "force-aspect-ratio", TRUE, NULL); - if (m_videoWindow) + if (m_videoWindow) { + m_videoWindow->prepareForOverlay(message); #if GST_CHECK_VERSION(0, 10, 31) || GST_VERSION_NANO gst_x_overlay_set_window_handle(GST_X_OVERLAY(sink), m_videoWindow->videoWindowId()); #else gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(sink), m_videoWindow->videoWindowId()); #endif + } } } diff --git a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp index da9255b..7012c9f 100644 --- a/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp +++ b/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamer.cpp @@ -1212,7 +1212,7 @@ void MediaPlayerPrivateGStreamer::paint(GraphicsContext* context, const IntRect& if (!gstImage) return; - context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), sRGBColorSpace, + context->drawImage(reinterpret_cast<Image*>(gstImage->image().get()), ColorSpaceSRGB, rect, CompositeCopy, false); } diff --git a/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h b/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h index f3df207..f2a3ff2 100644 --- a/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h +++ b/WebCore/platform/graphics/gstreamer/PlatformVideoWindow.h @@ -25,6 +25,8 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +typedef struct _GstMessage GstMessage; + namespace WebCore { class PlatformVideoWindow : public RefCounted<PlatformVideoWindow> { @@ -34,6 +36,8 @@ class PlatformVideoWindow : public RefCounted<PlatformVideoWindow> { PlatformVideoWindow(); ~PlatformVideoWindow(); + + void prepareForOverlay(GstMessage*); PlatformWidget window() const { return m_window; } unsigned long videoWindowId() const { return m_videoWindowId; } diff --git a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp index c55b9cc..0097716 100644 --- a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp +++ b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowEfl.cpp @@ -35,4 +35,8 @@ PlatformVideoWindow::~PlatformVideoWindow() notImplemented(); } +void PlatformVideoWindow::prepareForOverlay(GstMessage*) +{ +} + #endif // USE(GSTREAMER) diff --git a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp index 77343ae..c2f76cd 100644 --- a/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp +++ b/WebCore/platform/graphics/gstreamer/PlatformVideoWindowGtk.cpp @@ -61,4 +61,9 @@ PlatformVideoWindow::~PlatformVideoWindow() m_videoWindowId = 0; } + +void PlatformVideoWindow::prepareForOverlay(GstMessage*) +{ +} #endif // USE(GSTREAMER) + diff --git a/WebCore/platform/graphics/gtk/FontGtk.cpp b/WebCore/platform/graphics/gtk/FontGtk.cpp index 27f48fc..328ec4a 100644 --- a/WebCore/platform/graphics/gtk/FontGtk.cpp +++ b/WebCore/platform/graphics/gtk/FontGtk.cpp @@ -181,31 +181,15 @@ bool Font::canReturnFallbackFontsForComplexText() return false; } -#ifndef GTK_API_VERSION_2 -static void cairo_region_shrink(cairo_region_t* region, int dx, int dy) +void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { - int nRects = cairo_region_num_rectangles(region); - // Clear region. - cairo_region_subtract(region, region); - - for (int i = 0; i < nRects; i++) { - cairo_rectangle_int_t rect; - cairo_region_get_rectangle(region, i, &rect); - - if (rect.width <= 2 * dx || rect.height <= 2 * dy) - continue; - - rect.x += dx; - rect.y += dy; - rect.width -= 2 * dx; - rect.height -= 2 * dy; - cairo_region_union_rectangle(region, &rect); +#if defined(USE_FREETYPE) + if (!primaryFont()->platformData().m_pattern) { + drawSimpleText(context, run, point, from, to); + return; } -} #endif -void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const -{ cairo_t* cr = context->platformContext(); cairo_save(cr); cairo_translate(cr, point.x(), point.y()); @@ -224,17 +208,13 @@ void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const F #else cairo_region_t* partialRegion = 0; #endif + if (to - from != run.length()) { // Clip the region of the run to be rendered char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); int ranges[] = {start - utf8, end - utf8}; partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1); -#ifdef GTK_API_VERSION_2 - gdk_region_shrink(partialRegion, 0, -pixelSize()); -#else - cairo_region_shrink(partialRegion, 0, -pixelSize()); -#endif } Color fillColor = context->fillColor(); @@ -290,7 +270,7 @@ void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const F // Re-enable the platform shadow we disabled earlier if (hasShadow) - context->setShadow(shadowOffset, shadowBlur, shadowColor, DeviceColorSpace); + context->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); // Pango sometimes leaves behind paths we don't want cairo_new_path(cr); @@ -323,8 +303,13 @@ static PangoLayout* getDefaultPangoLayout(const TextRun& run) return layout; } -float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* /* fallbackFonts */, GlyphOverflow*) const +float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* overflow) const { +#if defined(USE_FREETYPE) + if (!primaryFont()->platformData().m_pattern) + return floatWidthForSimpleText(run, 0, fallbackFonts, overflow); +#endif + if (run.length() == 0) return 0.0f; @@ -345,6 +330,10 @@ float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFon int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const { +#if defined(USE_FREETYPE) + if (!primaryFont()->platformData().m_pattern) + return offsetForPositionForSimpleText(run, xFloat, includePartialGlyphs); +#endif // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. int x = static_cast<int>(xFloat); @@ -369,6 +358,11 @@ int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { +#if defined(USE_FREETYPE) + if (!primaryFont()->platformData().m_pattern) + return selectionRectForSimpleText(run, point, h, from, to); +#endif + PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); diff --git a/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp b/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp index edb26f0..486a317 100644 --- a/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp +++ b/WebCore/platform/graphics/gtk/ImageBufferGtk.cpp @@ -28,6 +28,7 @@ #include <cairo.h> #include <gtk/gtk.h> #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> namespace WebCore { @@ -67,7 +68,7 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con base64Encode(reinterpret_cast<const char*>(buffer.get()), bufferSize, out); out.append('\0'); - return String::format("data:%s;base64,%s", mimeType.utf8().data(), out.data()); + return makeString("data:", mimeType, ";base64,", out.data()); } } diff --git a/WebCore/platform/graphics/haiku/ImageBufferHaiku.cpp b/WebCore/platform/graphics/haiku/ImageBufferHaiku.cpp index c5de485..d1b06f3 100644 --- a/WebCore/platform/graphics/haiku/ImageBufferHaiku.cpp +++ b/WebCore/platform/graphics/haiku/ImageBufferHaiku.cpp @@ -33,6 +33,7 @@ #include "MIMETypeRegistry.h" #include "StillImageHaiku.h" #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <BitmapStream.h> #include <String.h> #include <TranslatorRoster.h> @@ -367,8 +368,7 @@ String ImageBuffer::toDataURL(const String& mimeType, const double*) const base64Encode(reinterpret_cast<const char*>(translatedStream.Buffer()), translatedStream.BufferLength(), encodedBuffer); - return String::format("data:%s;base64,%s", mimeType.utf8().data(), - encodedBuffer.data()); + return makeString("data:", mimeType, ";base64,", encodedBuffer.data()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/haiku/PathHaiku.cpp b/WebCore/platform/graphics/haiku/PathHaiku.cpp index c5b8c98..5377e10 100644 --- a/WebCore/platform/graphics/haiku/PathHaiku.cpp +++ b/WebCore/platform/graphics/haiku/PathHaiku.cpp @@ -144,12 +144,6 @@ bool Path::isEmpty() const return !m_path->Frame().IsValid(); } -String Path::debugString() const -{ - notImplemented(); - return String(); -} - void Path::apply(void* info, PathApplierFunction function) const { notImplemented(); diff --git a/WebCore/platform/graphics/mac/ColorMac.mm b/WebCore/platform/graphics/mac/ColorMac.mm index c8ea9b1..07d6353 100644 --- a/WebCore/platform/graphics/mac/ColorMac.mm +++ b/WebCore/platform/graphics/mac/ColorMac.mm @@ -88,14 +88,15 @@ NSColor *nsColor(const Color& color) static unsigned cachedRGBAValues[cacheSize]; static RetainPtr<NSColor>* cachedColors = new RetainPtr<NSColor>[cacheSize]; - for (int i = 0; i != cacheSize; ++i) + for (int i = 0; i != cacheSize; ++i) { if (cachedRGBAValues[i] == c) return cachedColors[i].get(); + } NSColor *result = [NSColor colorWithDeviceRed:static_cast<CGFloat>(color.red()) / 255 green:static_cast<CGFloat>(color.green()) / 255 blue:static_cast<CGFloat>(color.blue()) / 255 - alpha:static_cast<CGFloat>(color.alpha()) /255]; + alpha:static_cast<CGFloat>(color.alpha()) / 255]; static int cursor; cachedRGBAValues[cursor] = c; @@ -107,24 +108,5 @@ NSColor *nsColor(const Color& color) } } -static CGColorRef CGColorFromNSColor(NSColor *color) -{ - // This needs to always use device colorspace so it can de-calibrate the color for - // CGColor to possibly recalibrate it. - CGFloat components[4]; - NSColor *deviceColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace]; - [deviceColor getRed:&components[0] green:&components[1] blue:&components[2] alpha:&components[3]]; - static CGColorSpaceRef deviceRGBColorSpace = CGColorSpaceCreateDeviceRGB(); - CGColorRef cgColor = CGColorCreate(deviceRGBColorSpace, components); - return cgColor; -} - -CGColorRef createCGColor(const Color& c) -{ - // We could directly create a CGColor here, but that would - // skip any RGB caching the nsColor method does. A direct - // creation could be investigated for a possible performance win. - return CGColorFromNSColor(nsColor(c)); -} } // namespace WebCore diff --git a/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm b/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm index a4919d8..e079b44 100644 --- a/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContext3DMac.mm @@ -77,12 +77,12 @@ static void setPixelFormat(Vector<CGLPixelFormatAttribute>& attribs, int colorBi attribs.append(static_cast<CGLPixelFormatAttribute>(0)); } -PassOwnPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) +PassRefPtr<GraphicsContext3D> GraphicsContext3D::create(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow, GraphicsContext3D::RenderStyle renderStyle) { // This implementation doesn't currently support rendering directly to the HostWindow. if (renderStyle == RenderDirectlyToHostWindow) return 0; - OwnPtr<GraphicsContext3D> context(new GraphicsContext3D(attrs, hostWindow, false)); + RefPtr<GraphicsContext3D> context = adoptRef(new GraphicsContext3D(attrs, hostWindow, false)); return context->m_contextObj ? context.release() : 0; } diff --git a/WebCore/platform/graphics/mac/GraphicsContextMac.mm b/WebCore/platform/graphics/mac/GraphicsContextMac.mm index f3301d8..aa754f2 100644 --- a/WebCore/platform/graphics/mac/GraphicsContextMac.mm +++ b/WebCore/platform/graphics/mac/GraphicsContextMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 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 @@ -30,6 +30,7 @@ #import <AppKit/AppKit.h> #import <wtf/StdLibExtras.h> +#import "LocalCurrentGraphicsContext.h" #import "WebCoreSystemInterface.h" @class NSColor; @@ -43,14 +44,14 @@ namespace WebCore { // calls in this file are all exception-safe, so we don't block // exceptions for those. -static void drawFocusRingToContext(CGContextRef context, RetainPtr<CGPathRef> focusRingPath, RetainPtr<CGColorRef> colorRef, int radius) +static void drawFocusRingToContext(CGContextRef context, CGPathRef focusRingPath, CGColorRef color, int radius) { #ifdef BUILDING_ON_TIGER CGContextBeginTransparencyLayer(context, 0); #endif CGContextBeginPath(context); - CGContextAddPath(context, focusRingPath.get()); - wkDrawFocusRing(context, colorRef.get(), radius); + CGContextAddPath(context, focusRingPath); + wkDrawFocusRing(context, color, radius); #ifdef BUILDING_ON_TIGER CGContextEndTransparencyLayer(context); #endif @@ -63,16 +64,14 @@ void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int of int radius = (width - 1) / 2; offset += radius; - RetainPtr<CGColorRef> colorRef; - if (color.isValid()) - colorRef.adoptCF(createCGColor(color)); - + CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0; + RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable()); unsigned pathCount = paths.size(); for (unsigned i = 0; i < pathCount; i++) CGPathAddPath(focusRingPath.get(), 0, paths[i].platformPath()); - drawFocusRingToContext(platformContext(), focusRingPath, colorRef, radius); + drawFocusRingToContext(platformContext(), focusRingPath.get(), colorRef, radius); } void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) @@ -82,16 +81,14 @@ void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int int radius = (width - 1) / 2; offset += radius; - RetainPtr<CGColorRef> colorRef; - if (color.isValid()) - colorRef.adoptCF(createCGColor(color)); + CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0; RetainPtr<CGMutablePathRef> focusRingPath(AdoptCF, CGPathCreateMutable()); unsigned rectCount = rects.size(); for (unsigned i = 0; i < rectCount; i++) CGPathAddRect(focusRingPath.get(), 0, CGRectInset(rects[i], -offset, -offset)); - drawFocusRingToContext(platformContext(), focusRingPath, colorRef, radius); + drawFocusRingToContext(platformContext(), focusRingPath.get(), colorRef, radius); } #ifdef BUILDING_ON_TIGER // Post-Tiger's setCompositeOperation() is defined in GraphicsContextCG.cpp. @@ -182,6 +179,7 @@ void GraphicsContext::drawLineForTextChecking(const IntPoint& point, int width, // for transforms. // Draw underline. + LocalCurrentGraphicsContext localContext(this); NSGraphicsContext *currentContext = [NSGraphicsContext currentContext]; CGContextRef context = (CGContextRef)[currentContext graphicsPort]; CGContextSaveGState(context); diff --git a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm index d4cd851..c4128ef 100644 --- a/WebCore/platform/graphics/mac/GraphicsLayerCA.mm +++ b/WebCore/platform/graphics/mac/GraphicsLayerCA.mm @@ -38,7 +38,6 @@ #import <QuartzCore/QuartzCore.h> #import "RotateTransformOperation.h" #import "ScaleTransformOperation.h" -#import "StringBuilder.h" #import "SystemTime.h" #import "TranslateTransformOperation.h" #import "WebLayer.h" @@ -48,6 +47,7 @@ #import <wtf/CurrentTime.h> #import <wtf/UnusedParam.h> #import <wtf/RetainPtr.h> +#import <wtf/text/StringConcatenate.h> using namespace std; @@ -248,7 +248,7 @@ static String propertyIdToString(AnimatedPropertyID property) static String animationIdentifier(const String& animationName, AnimatedPropertyID property, int index) { - return animationName + String::format("_%d_%d", property, index); + return makeString(animationName, '_', String::number(property), '_', String::number(index)); } static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction* timingFunction) @@ -265,9 +265,7 @@ static CAMediaTimingFunction* getCAMediaTimingFunction(const TimingFunction* tim static void setLayerBorderColor(PlatformLayer* layer, const Color& color) { - CGColorRef borderColor = createCGColor(color); - [layer setBorderColor:borderColor]; - CGColorRelease(borderColor); + [layer setBorderColor:cachedCGColor(color, ColorSpaceDeviceRGB)]; } static void clearBorderColor(PlatformLayer* layer) @@ -277,9 +275,7 @@ static void clearBorderColor(PlatformLayer* layer) static void setLayerBackgroundColor(PlatformLayer* layer, const Color& color) { - CGColorRef bgColor = createCGColor(color); - [layer setBackgroundColor:bgColor]; - CGColorRelease(bgColor); + [layer setBackgroundColor:cachedCGColor(color, ColorSpaceDeviceRGB)]; } static void clearLayerBackgroundColor(PlatformLayer* layer) diff --git a/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp b/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp index 53d9b86..daf3b12 100644 --- a/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp +++ b/WebCore/platform/graphics/opengl/GraphicsContext3DOpenGL.cpp @@ -1161,11 +1161,11 @@ String GraphicsContext3D::getProgramInfoLog(Platform3DObject program) makeContextCurrent(); GLint length; ::glGetProgramiv((GLuint) program, GL_INFO_LOG_LENGTH, &length); - + if (!length) + return ""; + GLsizei size; GLchar* info = (GLchar*) fastMalloc(length); - if (!info) - return ""; ::glGetProgramInfoLog((GLuint) program, length, &size, info); String s(info); @@ -1227,8 +1227,6 @@ String GraphicsContext3D::getShaderInfoLog(Platform3DObject shader) ASSERT(shader); makeContextCurrent(); - GLint length; - ::glGetShaderiv((GLuint) shader, GL_INFO_LOG_LENGTH, &length); HashMap<Platform3DObject, ShaderSourceEntry>::iterator result = m_shaderSourceMap.find(shader); @@ -1240,21 +1238,19 @@ String GraphicsContext3D::getShaderInfoLog(Platform3DObject shader) if (entry.isValid) { GLint length; ::glGetShaderiv((GLuint) shader, GL_INFO_LOG_LENGTH, &length); + if (!length) + return ""; GLsizei size; GLchar* info = (GLchar*) fastMalloc(length); - if (!info) - return ""; ::glGetShaderInfoLog((GLuint) shader, length, &size, info); String s(info); fastFree(info); return s; - } - else { + } else return entry.log; - } } String GraphicsContext3D::getShaderSource(Platform3DObject shader) @@ -1449,6 +1445,11 @@ void GraphicsContext3D::synthesizeGLError(unsigned long error) m_syntheticErrors.add(error); } +int GraphicsContext3D::getGraphicsResetStatusARB() +{ + return NO_ERROR; +} + } #endif // ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/openvg/PathOpenVG.cpp b/WebCore/platform/graphics/openvg/PathOpenVG.cpp index e74ea57..39a4b06 100644 --- a/WebCore/platform/graphics/openvg/PathOpenVG.cpp +++ b/WebCore/platform/graphics/openvg/PathOpenVG.cpp @@ -436,18 +436,6 @@ bool Path::hasCurrentPoint() const return vgGetParameteri(m_path->vgPath(), VG_PATH_NUM_SEGMENTS) > 0; } -String Path::debugString() const -{ - String debugString = ""; - - // OpenVG provides no means to retrieve path segment information. - // This is a bit unfortunate, we might need to store the segments in - // memory if we want to implement this function properly. - notImplemented(); - - return debugString; -} - void Path::apply(void* info, PathApplierFunction function) const { // OpenVG provides no means to retrieve path segment information. diff --git a/WebCore/platform/graphics/qt/FontQt.cpp b/WebCore/platform/graphics/qt/FontQt.cpp index e7566eb..b049181 100644 --- a/WebCore/platform/graphics/qt/FontQt.cpp +++ b/WebCore/platform/graphics/qt/FontQt.cpp @@ -137,7 +137,7 @@ static void drawTextCommon(GraphicsContext* ctx, const TextRun& run, const Float dy1 = -ctxShadow->offset().y(); // expand the clip rect to include the text shadow as well clip.adjust(dx1, dx2, dy1, dy2); - clip.adjust(-ctxShadow->m_blurRadius, -ctxShadow->m_blurRadius, ctxShadow->m_blurRadius, ctxShadow->m_blurRadius); + clip.adjust(-ctxShadow->m_blurDistance, -ctxShadow->m_blurDistance, ctxShadow->m_blurDistance, ctxShadow->m_blurDistance); } p->save(); p->setClipRect(clip.toRect(), Qt::IntersectClip); diff --git a/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp b/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp index 0756aa7..cda8606 100644 --- a/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp @@ -286,6 +286,11 @@ bool GraphicsContext3D::isErrorGeneratedOnOutOfBoundsAccesses() const return false; } +int GraphicsContext3D::getGraphicsResetStatusARB() +{ + return NO_ERROR; +} + GraphicsContext3DInternal::GraphicsContext3DInternal(GraphicsContext3D::Attributes attrs, HostWindow* hostWindow) : m_attrs(attrs) diff --git a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp index 7e4af40..8b34f51 100644 --- a/WebCore/platform/graphics/qt/GraphicsContextQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsContextQt.cpp @@ -204,6 +204,13 @@ public: return shadow.m_type != ContextShadow::NoShadow; } + inline void clearCurrentPath() + { + if (!currentPath.elementCount()) + return; + currentPath = QPainterPath(); + } + QRectF clipBoundingRect() const { #if QT_VERSION >= QT_VERSION_CHECK(4, 8, 0) @@ -248,8 +255,8 @@ GraphicsContext::GraphicsContext(PlatformGraphicsContext* context) setPaintingDisabled(!context); if (context) { // Make sure the context starts in sync with our state. - setPlatformFillColor(fillColor(), DeviceColorSpace); - setPlatformStrokeColor(strokeColor(), DeviceColorSpace); + setPlatformFillColor(fillColor(), ColorSpaceDeviceRGB); + setPlatformStrokeColor(strokeColor(), ColorSpaceDeviceRGB); // Make sure we start with the correct join mode. setLineJoin(MiterJoin); @@ -533,7 +540,7 @@ void GraphicsContext::fillPath() } else p->fillPath(path, p->brush()); - m_data->currentPath = QPainterPath(); + m_data->clearCurrentPath(); } void GraphicsContext::strokePath() @@ -566,7 +573,7 @@ void GraphicsContext::strokePath() p->strokePath(path, pen); } else p->strokePath(path, pen); - m_data->currentPath = QPainterPath(); + m_data->clearCurrentPath(); } static inline void drawRepeatPattern(QPainter* p, QPixmap* image, const FloatRect& rect, const bool repeatX, const bool repeatY) @@ -722,7 +729,8 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef if (paintingDisabled() || !color.isValid()) return; - Path path = Path::createRoundedRectangle(rect, topLeft, topRight, bottomLeft, bottomRight); + Path path; + path.addRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); QPainter* p = m_data->p(); if (m_data->hasShadow()) { p->translate(m_data->shadow.offset()); @@ -734,7 +742,7 @@ void GraphicsContext::fillRoundedRect(const IntRect& rect, const IntSize& topLef void GraphicsContext::beginPath() { - m_data->currentPath = QPainterPath(); + m_data->clearCurrentPath(); } void GraphicsContext::addPath(const Path& path) @@ -777,7 +785,7 @@ void GraphicsContext::clipPath(WindRule clipRule) QPainter* p = m_data->p(); QPainterPath newPath = m_data->currentPath; newPath.setFillRule(clipRule == RULE_EVENODD ? Qt::OddEvenFill : Qt::WindingFill); - p->setClipPath(newPath); + p->setClipPath(newPath, Qt::IntersectClip); } void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int offset, const Color& color) @@ -835,8 +843,28 @@ void GraphicsContext::drawLineForText(const IntPoint& origin, int width, bool) if (paintingDisabled()) return; + IntPoint startPoint = origin; IntPoint endPoint = origin + IntSize(width, 0); - drawLine(origin, endPoint); + + // If paintengine type is X11 to avoid artifacts + // like bug https://bugs.webkit.org/show_bug.cgi?id=42248 +#if defined(Q_WS_X11) + QPainter* p = m_data->p(); + if (p->paintEngine()->type() == QPaintEngine::X11) { + // If stroke thickness is odd we need decrease Y coordinate by 1 pixel, + // because inside method adjustLineToPixelBoundaries(...), which + // called from drawLine(...), Y coordinate will be increased by 0.5f + // and then inside Qt painting engine will be rounded to next greater + // integer value. + float strokeWidth = strokeThickness(); + if (static_cast<int>(strokeWidth) % 2) { + startPoint.setY(startPoint.y() - 1); + endPoint.setY(endPoint.y() - 1); + } + } +#endif // defined(Q_WS_X11) + + drawLine(startPoint, endPoint); } void GraphicsContext::drawLineForTextChecking(const IntPoint&, int, TextCheckingLineStyle) diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index 079d8ba..e0941f5 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -37,6 +37,7 @@ #include <QtGui/qgraphicseffect.h> #include <QtGui/qgraphicsitem.h> #include <QtGui/qgraphicsscene.h> +#include <QtGui/qgraphicswidget.h> #include <QtGui/qpainter.h> #include <QtGui/qpixmap.h> #include <QtGui/qpixmapcache.h> @@ -320,7 +321,7 @@ GraphicsLayerQtImpl::~GraphicsLayerQtImpl() // our items automatically. const QList<QGraphicsItem*> children = childItems(); QList<QGraphicsItem*>::const_iterator cit; - for (cit = children.begin(); cit != children.end(); ++cit) { + for (cit = children.constBegin(); cit != children.constEnd(); ++cit) { if (QGraphicsItem* item = *cit) { if (scene()) scene()->removeItem(item); @@ -523,7 +524,7 @@ void GraphicsLayerQtImpl::updateTransform() const QList<QGraphicsItem*> children = childItems(); QList<QGraphicsItem*>::const_iterator it; - for (it = children.begin(); it != children.end(); ++it) + for (it = children.constBegin(); it != children.constEnd(); ++it) if (GraphicsLayerQtImpl* layer= toGraphicsLayerQtImpl(*it)) layer->updateTransform(); } @@ -610,13 +611,13 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform if (!m_layer || m_changeMask == NoChanges) goto afterLayerChanges; - if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) { + if (m_changeMask & ParentChange) { // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't // try to snatch that ownership. if (!m_layer->parent() && !parentItem()) setParentItem(0); else if (m_layer && m_layer->parent() && m_layer->parent()->nativeLayer() != parentItem()) - setParentItem(m_layer->parent()->nativeLayer()); + setParentItem(m_layer->parent()->platformLayer()); } if (m_changeMask & ChildrenChange) { @@ -634,13 +635,13 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform const QSet<QGraphicsItem*> childrenToRemove = currentChildren - newChildren; QSet<QGraphicsItem*>::const_iterator it; - for (it = childrenToAdd.begin(); it != childrenToAdd.end(); ++it) { + for (it = childrenToAdd.constBegin(); it != childrenToAdd.constEnd(); ++it) { if (QGraphicsItem* w = *it) w->setParentItem(this); } QSet<QGraphicsItem*>::const_iterator rit; - for (rit = childrenToRemove.begin(); rit != childrenToRemove.end(); ++rit) { + for (rit = childrenToRemove.constBegin(); rit != childrenToRemove.constEnd(); ++rit) { if (GraphicsLayerQtImpl* w = toGraphicsLayerQtImpl(*rit)) w->setParentItem(0); } @@ -680,7 +681,7 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform if (scene()) scene()->update(); - if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | BackfaceVisibilityChange | PositionChange)) { + if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | BackfaceVisibilityChange | PositionChange | ParentChange)) { // Due to the differences between the way WebCore handles transforms and the way Qt handles transforms, // all these elements affect the transforms of all the descendants. forceUpdateTransform = true; @@ -737,6 +738,11 @@ void GraphicsLayerQtImpl::flushChanges(bool recursive, bool forceUpdateTransform const QRect rect(m_layer->contentsRect()); if (m_state.contentsRect != rect) { m_state.contentsRect = rect; + if (m_pendingContent.mediaLayer) { + QGraphicsWidget* widget = qobject_cast<QGraphicsWidget*>(m_pendingContent.mediaLayer.data()); + if (widget) + widget->setGeometry(rect); + } update(); } } @@ -804,7 +810,7 @@ afterLayerChanges: children.append(m_state.maskLayer->platformLayer()); QList<QGraphicsItem*>::const_iterator it; - for (it = children.begin(); it != children.end(); ++it) { + for (it = children.constBegin(); it != children.constEnd(); ++it) { if (QGraphicsItem* item = *it) { if (GraphicsLayerQtImpl* layer = toGraphicsLayerQtImpl(item)) layer->flushChanges(true, forceUpdateTransform); @@ -850,6 +856,20 @@ void GraphicsLayerQt::setNeedsDisplayInRect(const FloatRect& rect) m_impl->notifyChange(GraphicsLayerQtImpl::DisplayChange); } +void GraphicsLayerQt::setContentsNeedsDisplay() +{ + switch (m_impl->m_pendingContent.contentType) { + case GraphicsLayerQtImpl::MediaContentType: + if (!m_impl->m_pendingContent.mediaLayer) + return; + m_impl->m_pendingContent.mediaLayer.data()->update(); + break; + default: + setNeedsDisplay(); + break; + } +} + /* \reimp (GraphicsLayer.h) */ void GraphicsLayerQt::setName(const String& name) diff --git a/WebCore/platform/graphics/qt/GraphicsLayerQt.h b/WebCore/platform/graphics/qt/GraphicsLayerQt.h index 75ca498..ed535eb 100644 --- a/WebCore/platform/graphics/qt/GraphicsLayerQt.h +++ b/WebCore/platform/graphics/qt/GraphicsLayerQt.h @@ -75,6 +75,7 @@ public: virtual void resumeAnimations(); #endif // QT_NO_ANIMATION virtual void setContentsToImage(Image*); + virtual void setContentsNeedsDisplay(); virtual void setContentsToMedia(PlatformLayer*); virtual void setContentsBackgroundColor(const Color&); #if ENABLE(3D_CANVAS) diff --git a/WebCore/platform/graphics/qt/ImageBufferQt.cpp b/WebCore/platform/graphics/qt/ImageBufferQt.cpp index ee01222..0cdc894 100644 --- a/WebCore/platform/graphics/qt/ImageBufferQt.cpp +++ b/WebCore/platform/graphics/qt/ImageBufferQt.cpp @@ -35,6 +35,7 @@ #include "StillImageQt.h" #include "TransparencyLayer.h" #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <QBuffer> #include <QColor> @@ -68,7 +69,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) pen.setColor(Qt::black); pen.setWidth(1); pen.setCapStyle(Qt::FlatCap); - pen.setJoinStyle(Qt::MiterJoin); + pen.setJoinStyle(Qt::SvgMiterJoin); pen.setMiterLimit(10); painter->setPen(pen); QBrush brush = painter->brush(); @@ -79,7 +80,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) m_image = StillImage::createForRendering(&m_pixmap); } -ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -117,7 +118,7 @@ void ImageBuffer::draw(GraphicsContext* destContext, ColorSpace styleColorSpace, if (destContext == context()) { // We're drawing into our own buffer. In order for this to work, we need to copy the source buffer first. RefPtr<Image> copy = copyImage(); - destContext->drawImage(copy.get(), DeviceColorSpace, destRect, srcRect, op, useLowQualityScale); + destContext->drawImage(copy.get(), ColorSpaceDeviceRGB, destRect, srcRect, op, useLowQualityScale); } else destContext->drawImage(m_data.m_image.get(), styleColorSpace, destRect, srcRect, op, useLowQualityScale); } @@ -139,7 +140,7 @@ void ImageBuffer::clip(GraphicsContext* context, const FloatRect& floatRect) con if (!nativeImage) return; - IntRect rect(floatRect); + IntRect rect = enclosingIntRect(floatRect); QPixmap alphaMask = *nativeImage; if (alphaMask.width() != rect.width() || alphaMask.height() != rect.height()) alphaMask = alphaMask.scaled(rect.width(), rect.height()); @@ -216,7 +217,7 @@ PassRefPtr<ImageData> getImageData(const IntRect& rect, const ImageBufferData& i const uchar* bits = image.bits(); #endif - quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() + destx]); + quint32* destRows = reinterpret_cast_ptr<quint32*>(&data[desty * rect.width() * 4 + destx * 4]); if (multiplied == Unmultiplied) { for (int y = 0; y < numRows; ++y) { @@ -401,7 +402,8 @@ String ImageBuffer::toDataURL(const String& mimeType, const double* quality) con } buffer.close(); - return String::format("data:%s;base64,%s", mimeType.utf8().data(), data.toBase64().data()); + + return makeString("data:", mimeType, ";base64,", data.toBase64().data()); } } diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp index 1a31d1e..962c931 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.cpp @@ -27,6 +27,7 @@ #include "HTMLVideoElement.h" #include "NetworkingContext.h" #include "NotImplemented.h" +#include "RenderVideo.h" #include "TimeRanges.h" #include "Widget.h" #include "qwebframe.h" @@ -42,6 +43,7 @@ #include <QPainter> #include <QPoint> #include <QRect> +#include <QStyleOptionGraphicsItem> #include <QTime> #include <QTimer> #include <QUrl> @@ -49,6 +51,10 @@ #include <wtf/HashSet.h> #include <wtf/text/CString.h> +#if USE(ACCELERATED_COMPOSITING) +#include "texmap/TextureMapperPlatformLayer.h" +#endif + using namespace WTF; namespace WebCore { @@ -93,6 +99,8 @@ MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player) , m_videoScene(new QGraphicsScene) , m_networkState(MediaPlayer::Empty) , m_readyState(MediaPlayer::HaveNothing) + , m_currentSize(0, 0) + , m_naturalSize(RenderVideo::defaultSize()) , m_isVisible(false) , m_isSeeking(false) , m_composited(false) @@ -125,8 +133,7 @@ MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player) this, SLOT(nativeSizeChanged(QSizeF))); // Grab the player control - QMediaService* service = m_mediaPlayer->service(); - if (service) { + if (QMediaService* service = m_mediaPlayer->service()) { m_mediaPlayerControl = qobject_cast<QMediaPlayerControl *>( service->requestControl(QMediaPlayerControl_iid)); } @@ -134,6 +141,10 @@ MediaPlayerPrivateQt::MediaPlayerPrivateQt(MediaPlayer* player) MediaPlayerPrivateQt::~MediaPlayerPrivateQt() { + m_mediaPlayer->disconnect(this); + m_mediaPlayer->stop(); + m_mediaPlayer->setMedia(QMediaContent()); + delete m_mediaPlayer; delete m_videoScene; } @@ -330,8 +341,7 @@ float MediaPlayerPrivateQt::duration() const float MediaPlayerPrivateQt::currentTime() const { - float currentTime = m_mediaPlayer->position() / 1000.0f; - return currentTime; + return m_mediaPlayer->position() / 1000.0f; } PassRefPtr<TimeRanges> MediaPlayerPrivateQt::buffered() const @@ -437,8 +447,15 @@ void MediaPlayerPrivateQt::stateChanged(QMediaPlayer::State state) } } -void MediaPlayerPrivateQt::nativeSizeChanged(const QSizeF&) +void MediaPlayerPrivateQt::nativeSizeChanged(const QSizeF& size) { + LOG(Media, "MediaPlayerPrivateQt::naturalSizeChanged(%dx%d)", + size.toSize().width(), size.toSize().height()); + + if (!size.isValid()) + return; + + m_naturalSize = size.toSize(); m_webCorePlayer->sizeChanged(); } @@ -466,7 +483,7 @@ void MediaPlayerPrivateQt::seekTimeout() void MediaPlayerPrivateQt::positionChanged(qint64) { - // Only propogate this event if we are seeking + // Only propagate this event if we are seeking if (m_isSeeking && m_queuedSeek == -1) { m_webCorePlayer->timeChanged(); m_isSeeking = false; @@ -546,6 +563,9 @@ void MediaPlayerPrivateQt::updateStates() void MediaPlayerPrivateQt::setSize(const IntSize& size) { + LOG(Media, "MediaPlayerPrivateQt::setSize(%dx%d)", + size.width(), size.height()); + if (size == m_currentSize) return; @@ -555,10 +575,15 @@ void MediaPlayerPrivateQt::setSize(const IntSize& size) IntSize MediaPlayerPrivateQt::naturalSize() const { - if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) + if (!hasVideo() || m_readyState < MediaPlayer::HaveMetadata) { + LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> 0x0 (!hasVideo || !haveMetaData)"); return IntSize(); + } - return IntSize(m_videoItem->nativeSize().toSize()); + LOG(Media, "MediaPlayerPrivateQt::naturalSize() -> %dx%d (m_naturalSize)", + m_naturalSize.width(), m_naturalSize.height()); + + return m_naturalSize; } void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect) @@ -573,10 +598,7 @@ void MediaPlayerPrivateQt::paint(GraphicsContext* context, const IntRect& rect) if (!m_isVisible) return; - // Grab the painter and widget QPainter* painter = context->platformContext(); - - // Render the video m_videoScene->render(painter, QRectF(QRect(rect)), m_videoItem->sceneBoundingRect()); } @@ -585,7 +607,41 @@ void MediaPlayerPrivateQt::repaint() m_webCorePlayer->repaint(); } -#if USE(ACCELERATED_COMPOSITING) +#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) + +class TextureMapperVideoLayerQt : public virtual TextureMapperVideoLayer { +public: + TextureMapperVideoLayerQt(QGraphicsVideoItem* videoItem) + : m_videoItem(videoItem) + { + } + + virtual void setPlatformLayerClient(TextureMapperLayerClient* client) + { + m_client = client; + } + + virtual void paint(GraphicsContext* context) + { + if (!m_videoItem) + return; + + QStyleOptionGraphicsItem opt; + opt.exposedRect = m_videoItem.data()->sceneBoundingRect(); + opt.rect = opt.exposedRect.toRect(); + m_videoItem.data()->paint(context->platformContext(), &opt); + } + + virtual IntSize size() const + { + return m_videoItem ? IntSize(m_videoItem.data()->size().width(), m_videoItem.data()->size().height()) : IntSize(); + } + + QWeakPointer<QGraphicsVideoItem> m_videoItem; + TextureMapperLayerClient* m_client; +}; + + void MediaPlayerPrivateQt::acceleratedRenderingStateChanged() { MediaPlayerClient* client = m_webCorePlayer->mediaPlayerClient(); @@ -595,14 +651,12 @@ void MediaPlayerPrivateQt::acceleratedRenderingStateChanged() m_composited = composited; if (composited) - m_videoScene->removeItem(m_videoItem); - else - m_videoScene->addItem(m_videoItem); + m_platformLayer = new TextureMapperVideoLayerQt(m_videoItem); } PlatformLayer* MediaPlayerPrivateQt::platformLayer() const { - return m_composited ? m_videoItem : 0; + return m_composited ? m_platformLayer.get() : 0; } #endif diff --git a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h index 179bf2a..93c9d1c 100644 --- a/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h +++ b/WebCore/platform/graphics/qt/MediaPlayerPrivateQt.h @@ -33,6 +33,8 @@ QT_END_NAMESPACE namespace WebCore { +class TextureMapperVideoLayer; + class MediaPlayerPrivateQt : public QObject, public MediaPlayerPrivateInterface { Q_OBJECT @@ -91,12 +93,18 @@ public: bool supportsFullscreen() const { return false; } #if USE(ACCELERATED_COMPOSITING) +#if USE(TEXTURE_MAPPER) // whether accelerated rendering is supported by the media engine for the current media. virtual bool supportsAcceleratedRendering() const { return true; } // called when the rendering system flips the into or out of accelerated rendering mode. virtual void acceleratedRenderingStateChanged(); // returns an object that can be directly composited via GraphicsLayerQt (essentially a QGraphicsItem*) virtual PlatformLayer* platformLayer() const; +#else + virtual bool supportsAcceleratedRendering() const { return false; } + virtual void acceleratedRenderingStateChanged() { } + virtual PlatformLayer* platformLayer() const { return 0; } +#endif #endif virtual PlatformMedia platformMedia() const; @@ -125,11 +133,15 @@ private: QMediaPlayerControl* m_mediaPlayerControl; QGraphicsVideoItem* m_videoItem; QGraphicsScene* m_videoScene; +#if USE(ACCELERATED_COMPOSITING) && USE(TEXTURE_MAPPER) + OwnPtr<TextureMapperVideoLayer> m_platformLayer; +#endif mutable MediaPlayer::NetworkState m_networkState; mutable MediaPlayer::ReadyState m_readyState; IntSize m_currentSize; + IntSize m_naturalSize; bool m_isVisible; bool m_isSeeking; bool m_composited; diff --git a/WebCore/platform/graphics/qt/PathQt.cpp b/WebCore/platform/graphics/qt/PathQt.cpp index b8b9d5e..508ba6a 100644 --- a/WebCore/platform/graphics/qt/PathQt.cpp +++ b/WebCore/platform/graphics/qt/PathQt.cpp @@ -335,6 +335,8 @@ void Path::addEllipse(const FloatRect& r) void Path::clear() { + if (!m_path.elementCount()) + return; m_path = QPainterPath(); } @@ -355,41 +357,6 @@ FloatPoint Path::currentPoint() const return m_path.currentPosition(); } -String Path::debugString() const -{ - QString ret; - for (int i = 0; i < m_path.elementCount(); ++i) { - const QPainterPath::Element &cur = m_path.elementAt(i); - - switch (cur.type) { - case QPainterPath::MoveToElement: - ret += QString(QLatin1String("M%1,%2 ")).arg(cur.x, 0, 'f', 2).arg(cur.y, 0, 'f', 2); - break; - case QPainterPath::LineToElement: - ret += QString(QLatin1String("L%1,%2 ")).arg(cur.x, 0, 'f', 2).arg(cur.y, 0, 'f', 2); - break; - case QPainterPath::CurveToElement: - { - const QPainterPath::Element &c1 = m_path.elementAt(i + 1); - const QPainterPath::Element &c2 = m_path.elementAt(i + 2); - - Q_ASSERT(c1.type == QPainterPath::CurveToDataElement); - Q_ASSERT(c2.type == QPainterPath::CurveToDataElement); - - ret += QString(QLatin1String("C%1,%2,%3,%4,%5,%6 ")).arg(cur.x, 0, 'f', 2).arg(cur.y, 0, 'f', 2).arg(c1.x, 0, 'f', 2) - .arg(c1.y, 0, 'f', 2).arg(c2.x, 0, 'f', 2).arg(c2.y, 0, 'f', 2); - i += 2; - break; - } - case QPainterPath::CurveToDataElement: - Q_ASSERT(false); - break; - } - } - - return ret.trimmed(); -} - void Path::apply(void* info, PathApplierFunction function) const { PathElement pelement; diff --git a/WebCore/platform/graphics/qt/TextureMapperQt.cpp b/WebCore/platform/graphics/qt/TextureMapperQt.cpp new file mode 100644 index 0000000..9236dae --- /dev/null +++ b/WebCore/platform/graphics/qt/TextureMapperQt.cpp @@ -0,0 +1,225 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 "texmap/TextureMapper.h" + +#include <QtCore/qdebug.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/qpixmap.h> + +#ifdef QT_OPENGL_LIB +# include "opengl/TextureMapperGL.h" +#endif + +namespace WebCore { + +class BitmapTextureQt : public BitmapTexture { + friend class TextureMapperQt; +public: + BitmapTextureQt() {} + virtual void destroy(); + virtual IntSize size() const { return IntSize(m_pixmap.width(), m_pixmap.height()); } + virtual void reset(const IntSize&, bool opaque); + virtual PlatformGraphicsContext* beginPaint(const IntRect& dirtyRect); + virtual void endPaint(); + virtual void setContentsToImage(Image*); + virtual bool save(const String& path); + virtual bool isValid() const { return !m_pixmap.isNull(); } + virtual bool allowOfflineTextureUpload() const { return true; } + IntRect sourceRect() const { return IntRect(0, 0, contentSize().width(), contentSize().height()); } +private: + QPainter m_painter; + QPixmap m_pixmap; +}; + +class TextureMapperQt : public TextureMapper { +public: + virtual void drawTexture(const BitmapTexture& texture, const IntRect& targetRect, const TransformationMatrix& matrix, float opacity, const BitmapTexture* maskTexture); + virtual void bindSurface(BitmapTexture* surface); + virtual void setClip(const IntRect&); + virtual bool allowSurfaceForRoot() const { return false; } + TextureMapperQt(GraphicsContext* context); + virtual const char* type() const { return "TextureMapperQt"; } + virtual PassRefPtr<BitmapTexture> createTexture(); + + static void initialize(QPainter* painter) + { + painter->setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform, false); + } + +private: + QPainter* m_painter; + RefPtr<BitmapTextureQt> m_currentSurface; +}; + +void BitmapTextureQt::destroy() +{ + if (m_pixmap.paintingActive()) + qFatal("Destroying an active pixmap"); + m_pixmap = QPixmap(); +} + +void BitmapTextureQt::reset(const IntSize& size, bool isOpaque) +{ + BitmapTexture::reset(size, isOpaque); + + if (size.width() > m_pixmap.size().width() || size.height() > m_pixmap.size().height() || m_pixmap.isNull()) + m_pixmap = QPixmap(size.width(), size.height()); + if (!isOpaque) + m_pixmap.fill(Qt::transparent); +} + +PlatformGraphicsContext* BitmapTextureQt::beginPaint(const IntRect& dirtyRect) +{ + m_painter.begin(&m_pixmap); + TextureMapperQt::initialize(&m_painter); + m_painter.setCompositionMode(QPainter::CompositionMode_Clear); + m_painter.fillRect(QRect(dirtyRect), Qt::transparent); + m_painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + return &m_painter; +} + +void BitmapTextureQt::endPaint() +{ + m_painter.end(); +} + +bool BitmapTextureQt::save(const String& path) +{ + return m_pixmap.save(path, "PNG"); +} + +void BitmapTextureQt::setContentsToImage(Image* image) +{ + if (!image) + return; + const QPixmap* pixmap = image->nativeImageForCurrentFrame(); + if (!pixmap) + return; + BitmapTexture::reset(pixmap->size(), !pixmap->hasAlphaChannel()); + m_pixmap = *pixmap; +} + +void TextureMapperQt::setClip(const IntRect& rect) +{ + QPainter* painter = m_currentSurface ? &m_currentSurface->m_painter : m_painter; + painter->setClipRect(rect); +} + +TextureMapperQt::TextureMapperQt(GraphicsContext* context) + : TextureMapper(context) + , m_painter(context->platformContext()) + , m_currentSurface(0) +{ + TextureMapperQt::initialize(m_painter); +} + +void TextureMapperQt::bindSurface(BitmapTexture* surface) +{ + if (m_currentSurface == surface) + return; + if (m_currentSurface) + m_currentSurface->m_painter.end(); + if (!surface) { + m_currentSurface = 0; + return; + } + BitmapTextureQt* surfaceQt = static_cast<BitmapTextureQt*>(surface); + if (!surfaceQt->m_painter.isActive()) + surfaceQt->m_painter.begin(&surfaceQt->m_pixmap); + m_currentSurface = surfaceQt; +} + + +void TextureMapperQt::drawTexture(const BitmapTexture& texture, const IntRect& targetRect, const TransformationMatrix& matrix, float opacity, const BitmapTexture* maskTexture) +{ + const BitmapTextureQt& textureQt = static_cast<const BitmapTextureQt&>(texture); + QPainter* painter = m_painter; + QPixmap pixmap = textureQt.m_pixmap; + if (m_currentSurface) + painter = &m_currentSurface->m_painter; + + if (maskTexture && maskTexture->isValid()) { + const BitmapTextureQt* mask = static_cast<const BitmapTextureQt*>(maskTexture); + QPixmap intermediatePixmap(pixmap.size()); + intermediatePixmap.fill(Qt::transparent); + QPainter maskPainter(&intermediatePixmap); + maskPainter.setCompositionMode(QPainter::CompositionMode_Source); + maskPainter.drawPixmap(0, 0, pixmap); + maskPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn); + maskPainter.drawPixmap(QRect(0, 0, pixmap.width(), pixmap.height()), mask->m_pixmap, mask->sourceRect()); + maskPainter.end(); + pixmap = intermediatePixmap; + } + + const qreal prevOpacity = painter->opacity(); + const QTransform prevTransform = painter->transform(); + painter->setOpacity(opacity); + painter->setTransform(matrix, true); + painter->drawPixmap(targetRect, pixmap, textureQt.sourceRect()); + painter->setTransform(prevTransform); + painter->setOpacity(prevOpacity); +} + +PassRefPtr<TextureMapper> TextureMapper::create(GraphicsContext* context) +{ +#ifdef QT_OPENGL_LIB + if (context->platformContext()->paintEngine()->type() == QPaintEngine::OpenGL2) + return adoptRef(new TextureMapperGL(context)); +#endif + return adoptRef(new TextureMapperQt(context)); +} + + +PassRefPtr<BitmapTexture> TextureMapperQt::createTexture() +{ + return adoptRef(new BitmapTextureQt()); +} + +#ifdef QT_OPENGL_LIB +class RGBA32PremultimpliedBufferQt : public RGBA32PremultimpliedBuffer { +public: + virtual PlatformGraphicsContext* beginPaint(const IntRect& rect, bool opaque) + { + // m_image is only using during paint, it's safe to override it. + m_image = QImage(rect.size().width(), rect.size().height(), QImage::Format_ARGB32_Premultiplied); + if (!opaque) + m_image.fill(0); + m_painter.begin(&m_image); + TextureMapperQt::initialize(&m_painter); + m_painter.translate(-rect.x(), -rect.y()); + return &m_painter; + } + + virtual void endPaint() { m_painter.end(); } + virtual const void* data() const { return m_image.constBits(); } + +private: + QPainter m_painter; + QImage m_image; +}; + +PassRefPtr<RGBA32PremultimpliedBuffer> RGBA32PremultimpliedBuffer::create() +{ + return adoptRef(new RGBA32PremultimpliedBufferQt()); +} + +#endif +}; diff --git a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp index 2be7dc5..143d667 100644 --- a/WebCore/platform/graphics/skia/ImageBufferSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageBufferSkia.cpp @@ -45,6 +45,8 @@ #include "SkColorPriv.h" #include "SkiaUtils.h" +#include <wtf/text/StringConcatenate.h> + using namespace std; namespace WebCore { @@ -58,7 +60,7 @@ ImageBufferData::ImageBufferData(const IntSize& size) { } -ImageBuffer::ImageBuffer(const IntSize& size, ImageColorSpace imageColorSpace, bool& success) +ImageBuffer::ImageBuffer(const IntSize& size, ColorSpace, bool& success) : m_data(size) , m_size(size) { @@ -290,6 +292,7 @@ void putImageData(ImageData*& source, const IntRect& sourceRect, const IntPoint& void ImageBuffer::putUnmultipliedImageData(ImageData* source, const IntRect& sourceRect, const IntPoint& destPoint) { + context()->platformContext()->prepareForSoftwareDraw(); putImageData<Unmultiplied>(source, sourceRect, destPoint, *context()->platformContext()->bitmap(), m_size); } @@ -311,7 +314,7 @@ String ImageBuffer::toDataURL(const String&, const double*) const base64EncodedData.append('\0'); // And the resulting string. - return String::format("data:image/png;base64,%s", base64EncodedData.data()); + return makeString("data:image/png;base64,", base64EncodedData.data()); } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/ImageSkia.cpp b/WebCore/platform/graphics/skia/ImageSkia.cpp index e123256..23e7be6 100644 --- a/WebCore/platform/graphics/skia/ImageSkia.cpp +++ b/WebCore/platform/graphics/skia/ImageSkia.cpp @@ -143,9 +143,7 @@ static ResamplingMode computeResamplingMode(PlatformContextSkia* platformContext // Everything else gets resampled. // If the platform context permits high quality interpolation, use it. - // High quality interpolation only enabled for scaling and translation. - if (platformContext->interpolationQuality() == InterpolationHigh - && !(platformContext->canvas()->getTotalMatrix().getType() & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask))) + if (platformContext->interpolationQuality() == InterpolationHigh) return RESAMPLE_AWESOME; return RESAMPLE_LINEAR; @@ -175,12 +173,8 @@ static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeIm && srcIRect.height() == bitmap.height(); // We will always draw in integer sizes, so round the destination rect. - // First we need to apply canvas transformation matrix to get desired size of - // resampled image. - SkRect destRectTransformed; - canvas.getTotalMatrix().mapRect(&destRectTransformed, destRect); SkIRect destRectRounded; - destRectTransformed.round(&destRectRounded); + destRect.round(&destRectRounded); SkIRect resizedImageRect = // Represents the size of the resized image. { 0, 0, destRectRounded.width(), destRectRounded.height() }; @@ -194,10 +188,7 @@ static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeIm // Compute the visible portion of our rect. SkRect destBitmapSubsetSk; ClipRectToCanvas(canvas, destRect, &destBitmapSubsetSk); - // Determine size of resampled image based on clipped destination rect. - SkRect destBitmapSubsetSkTransformed; - canvas.getTotalMatrix().mapRect(&destBitmapSubsetSkTransformed, destBitmapSubsetSk); - destBitmapSubsetSkTransformed.offset(-destBitmapSubsetSkTransformed.fLeft, -destBitmapSubsetSkTransformed.fTop); + destBitmapSubsetSk.offset(-destRect.fLeft, -destRect.fTop); // The matrix inverting, etc. could have introduced rounding error which // causes the bounds to be outside of the resized bitmap. We round outward @@ -205,7 +196,7 @@ static void drawResampledBitmap(SkCanvas& canvas, SkPaint& paint, const NativeIm // need, and then clamp to the bitmap bounds so we don't get any invalid // data. SkIRect destBitmapSubsetSkI; - destBitmapSubsetSkTransformed.roundOut(&destBitmapSubsetSkI); + destBitmapSubsetSk.roundOut(&destBitmapSubsetSkI); if (!destBitmapSubsetSkI.intersect(resizedImageRect)) return; // Resized image does not intersect. diff --git a/WebCore/platform/graphics/skia/PathSkia.cpp b/WebCore/platform/graphics/skia/PathSkia.cpp index 12241f8..89323c4 100644 --- a/WebCore/platform/graphics/skia/PathSkia.cpp +++ b/WebCore/platform/graphics/skia/PathSkia.cpp @@ -227,62 +227,6 @@ void Path::transform(const AffineTransform& xform) m_path->transform(xform); } -String Path::debugString() const -{ - String result; - - SkPath::Iter iter(*m_path, false); - SkPoint pts[4]; - - int numPoints = m_path->getPoints(0, 0); - SkPath::Verb verb; - - do { - verb = iter.next(pts); - switch (verb) { - case SkPath::kMove_Verb: - result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY); - numPoints -= 1; - break; - case SkPath::kLine_Verb: - if (!iter.isCloseLine()) { - result += String::format("L%.2f,%.2f ", pts[1].fX, pts[1].fY); - numPoints -= 1; - } - break; - case SkPath::kQuad_Verb: - result += String::format("Q%.2f,%.2f,%.2f,%.2f ", - pts[1].fX, pts[1].fY, - pts[2].fX, pts[2].fY); - numPoints -= 2; - break; - case SkPath::kCubic_Verb: - result += String::format("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f ", - pts[1].fX, pts[1].fY, - pts[2].fX, pts[2].fY, - pts[3].fX, pts[3].fY); - numPoints -= 3; - break; - case SkPath::kClose_Verb: - result += "Z "; - break; - case SkPath::kDone_Verb: - break; - } - } while (verb != SkPath::kDone_Verb); - - // If you have a path that ends with an M, Skia will not iterate the - // trailing M. That's nice of it, but Apple's paths output the trailing M - // and we want out layout dumps to look like theirs - if (numPoints) { - ASSERT(numPoints==1); - m_path->getLastPt(pts); - result += String::format("M%.2f,%.2f ", pts[0].fX, pts[0].fY); - } - - return result.stripWhiteSpace(); -} - // 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) diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp index b469312..3f9e4c1 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.cpp +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.cpp @@ -34,12 +34,10 @@ #include "AffineTransform.h" #include "DrawingBuffer.h" -#include "GLES2Canvas.h" #include "GraphicsContext.h" #include "GraphicsContext3D.h" #include "ImageBuffer.h" #include "NativeImageSkia.h" -#include "SharedGraphicsContext3D.h" #include "SkiaUtils.h" #include "Texture.h" #include "TilingData.h" @@ -56,6 +54,11 @@ #include <wtf/OwnArrayPtr.h> #include <wtf/Vector.h> +#if ENABLE(ACCELERATED_2D_CANVAS) +#include "GLES2Canvas.h" +#include "SharedGraphicsContext3D.h" +#endif + namespace WebCore { extern bool isPathSkiaSafe(const SkMatrix& transform, const SkPath& path); @@ -207,7 +210,9 @@ PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) : m_canvas(canvas) , m_drawingToImageBuffer(false) , m_useGPU(false) +#if ENABLE(ACCELERATED_2D_CANVAS) , m_gpuCanvas(0) +#endif , m_backingStoreState(None) { m_stateStack.append(State()); @@ -216,8 +221,10 @@ PlatformContextSkia::PlatformContextSkia(skia::PlatformCanvas* canvas) PlatformContextSkia::~PlatformContextSkia() { +#if ENABLE(ACCELERATED_2D_CANVAS) if (m_gpuCanvas) m_gpuCanvas->drawingBuffer()->setWillPublishCallback(0); +#endif } void PlatformContextSkia::setCanvas(skia::PlatformCanvas* canvas) @@ -705,6 +712,7 @@ private: void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* context, DrawingBuffer* drawingBuffer, const WebCore::IntSize& size) { +#if ENABLE(ACCELERATED_2D_CANVAS) if (context && drawingBuffer) { m_useGPU = true; m_gpuCanvas = new GLES2Canvas(context, drawingBuffer, size); @@ -716,6 +724,7 @@ void PlatformContextSkia::setSharedGraphicsContext3D(SharedGraphicsContext3D* co m_gpuCanvas.clear(); m_useGPU = false; } +#endif } void PlatformContextSkia::prepareForSoftwareDraw() const @@ -808,6 +817,7 @@ void PlatformContextSkia::markDirtyRect(const IntRect& rect) void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const { +#if ENABLE(ACCELERATED_2D_CANVAS) const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(false); SkAutoLockPixels lock(bitmap); SharedGraphicsContext3D* context = m_gpuCanvas->context(); @@ -816,7 +826,7 @@ void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const m_uploadTexture->updateSubRect(bitmap.getPixels(), m_softwareDirtyRect); AffineTransform identity; - gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), m_softwareDirtyRect, m_softwareDirtyRect, identity, 1.0, DeviceColorSpace, op); + gpuCanvas()->drawTexturedRect(m_uploadTexture.get(), m_softwareDirtyRect, m_softwareDirtyRect, identity, 1.0, ColorSpaceDeviceRGB, op); // Clear out the region of the software canvas we just uploaded. m_canvas->save(); m_canvas->resetMatrix(); @@ -825,10 +835,12 @@ void PlatformContextSkia::uploadSoftwareToHardware(CompositeOperator op) const m_canvas->drawARGB(0, 0, 0, 0, SkXfermode::kClear_Mode); m_canvas->restore(); m_softwareDirtyRect.setWidth(0); // Clear dirty rect. +#endif } void PlatformContextSkia::readbackHardwareToSoftware() const { +#if ENABLE(ACCELERATED_2D_CANVAS) const SkBitmap& bitmap = m_canvas->getDevice()->accessBitmap(true); SkAutoLockPixels lock(bitmap); int width = bitmap.width(), height = bitmap.height(); @@ -850,6 +862,7 @@ void PlatformContextSkia::readbackHardwareToSoftware() const } } m_softwareDirtyRect.unite(IntRect(0, 0, width, height)); // Mark everything as dirty. +#endif } } // namespace WebCore diff --git a/WebCore/platform/graphics/skia/PlatformContextSkia.h b/WebCore/platform/graphics/skia/PlatformContextSkia.h index eb03224..84e5d78 100644 --- a/WebCore/platform/graphics/skia/PlatformContextSkia.h +++ b/WebCore/platform/graphics/skia/PlatformContextSkia.h @@ -183,8 +183,11 @@ public: bool canvasClipApplied() const; bool useGPU() { return m_useGPU; } void setSharedGraphicsContext3D(SharedGraphicsContext3D*, DrawingBuffer*, const IntSize&); +#if ENABLE(ACCELERATED_2D_CANVAS) GLES2Canvas* gpuCanvas() const { return m_gpuCanvas.get(); } - +#else + GLES2Canvas* gpuCanvas() const { return 0; } +#endif // Call these before making a call that manipulates the underlying // skia::PlatformCanvas or WebCore::GLES2Canvas void prepareForSoftwareDraw() const; @@ -224,9 +227,11 @@ private: FloatSize m_imageResamplingHintDstSize; bool m_drawingToImageBuffer; bool m_useGPU; +#if ENABLE(ACCELERATED_2D_CANVAS) OwnPtr<GLES2Canvas> m_gpuCanvas; - mutable enum { None, Software, Mixed, Hardware } m_backingStoreState; mutable RefPtr<Texture> m_uploadTexture; +#endif + mutable enum { None, Software, Mixed, Hardware } m_backingStoreState; mutable IntRect m_softwareDirtyRect; }; diff --git a/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp b/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp new file mode 100644 index 0000000..cf90cb1 --- /dev/null +++ b/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.cpp @@ -0,0 +1,1444 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 "GraphicsLayerTextureMapper.h" + +#include "CurrentTime.h" +#include "FloatRect.h" +#include "GraphicsContext.h" +#include "HashMap.h" +#include "Image.h" +#include "RefCounted.h" +#include "TextureMapper.h" +#include "TextureMapperPlatformLayer.h" +#include "Timer.h" +#include "TransformOperations.h" +#include "TranslateTransformOperation.h" +#include "UnitBezier.h" + +#define DEBUG_TEXMAP_FPS 0 + +namespace WebCore { + +struct TexmapPaintOptions { + BitmapTexture* surface; + TextureMapper* textureMapper; + GraphicsContext* context; + TextureMapperNode* rootLayer; + float opacity; + IntRect scissorRect; + IntRect visibleRect; + bool isSurface; +}; +class TextureMapperCache { +public: + void mark(BitmapTexture* texture); + + class Entry { + public: + RefPtr<BitmapTexture> texture; + Entry() : previousCost(0) { } + inline int calculateCost() const + { + if (!texture || !texture->isValid()) + return 0; + const IntSize textureSize = texture->size(); + // an image's cost in bytes is width * height * bytes per pixel (4). + return textureSize.width() * textureSize.height() * 4; + } + Entry(BitmapTexture* newTexture) + : texture(newTexture) + { + } + bool operator==(const Entry& other) const { return texture == other.texture; } + int previousCost; + }; + + TextureMapperCache() : m_totalCost(0) {} + + void purge(); + Vector<Entry> m_data; + int m_totalCost; +#ifndef TEXMAP_TEXTURE_CACHE_KBS +#define TEXMAP_TEXTURE_CACHE_KBS 24 * 1024 +#endif + static const int MaxCost = TEXMAP_TEXTURE_CACHE_KBS * 1024; + static const int PurgeAmount = MaxCost / 4; +}; + + +void TextureMapperCache::purge() +{ + // If this is in the GL implementation, we need an active GL context, because we might call glDeleteTextures. + const int size = m_data.size(); + + if (m_totalCost <= TextureMapperCache::MaxCost) + return; + + // Ensure that we have the right count. It might be inaccurate if content changed size. + // We only do this when we're actually ready to purge. + m_totalCost = 0; + for (int i = 0; i < size; ++i) + m_totalCost += m_data[i].calculateCost(); + + for (int i = size-1; i >= 0 && m_totalCost > TextureMapperCache::MaxCost - TextureMapperCache::PurgeAmount; --i) { + Entry& entry = m_data[i]; + if (entry.texture->isLocked() || !entry.texture->isValid()) + continue; + m_totalCost -= entry.previousCost; + entry.texture->destroy(); + m_data.remove(i); + } +} + +void TextureMapperCache::mark(BitmapTexture* texture) +{ + if (!texture || !texture->isValid()) + return; + + Entry entry(texture); + size_t index = m_data.find(entry); + if (!index) + return; + + if (index < m_data.size()) + m_data.remove(index); + const int cost = entry.calculateCost(); + m_totalCost -= entry.previousCost; + m_totalCost += (entry.previousCost = cost); + m_data.prepend(entry); +} + +TextureMapperCache gTextureMapperCache; + +class TextureMapperCacheLock { +public: + TextureMapperCacheLock(BitmapTexture* texture) : m_texture(texture) + { + if (m_texture) + m_texture->lock(); + } + ~TextureMapperCacheLock() + { + if (m_texture) + m_texture->unlock(); + } + +private: + RefPtr<BitmapTexture> m_texture; +}; + +class TextureMapperNode : public TextureMapperContentLayer { + +public: + // This set of flags help us defer which properties of the layer have been + // modified by the compositor, so we can know what to look for in the next flush. + enum ChangeMask { + NoChanges = 0, + + ParentChange = (1L << 0), + ChildrenChange = (1L << 1), + MaskLayerChange = (1L << 2), + PositionChange = (1L << 3), + + AnchorPointChange = (1L << 4), + SizeChange = (1L << 5), + TransformChange = (1L << 6), + ContentChange = (1L << 7), + + ContentsOrientationChange = (1L << 9), + OpacityChange = (1L << 10), + ContentsRectChange = (1L << 11), + + Preserves3DChange = (1L << 12), + MasksToBoundsChange = (1L << 13), + DrawsContentChange = (1L << 14), + ContentsOpaqueChange = (1L << 15), + + BackfaceVisibilityChange = (1L << 16), + ChildrenTransformChange = (1L << 17), + DisplayChange = (1L << 18), + BackgroundColorChange = (1L << 19), + + ReplicaLayerChange = (1L << 20) + }; + + // The compositor lets us special-case images and colors, so we try to do so. + enum StaticContentType { HTMLContentType, DirectImageContentType, ColorContentType, MediaContentType, Canvas3DContentType}; + + TextureMapperNode* rootLayer(); + + TextureMapperNode(GraphicsLayerTextureMapper* newLayer); + virtual ~TextureMapperNode(); + + void clearDirectImage(); + void computeTransformations(); + IntSize nearestSurfaceSize() const; + void computeReplicaTransform(); + void computeLayerType(); + void computeLocalTransform(); + void flattenTo2DSpaceIfNecessary(); + void initializeTextureMapper(TextureMapper*); + void invalidateTransform(); + void notifyChange(ChangeMask); + void syncCompositingState(bool recurse); + void performPostSyncOperations(); + void setNeedsDisplay(); + void setNeedsDisplayInRect(IntRect); + virtual void cleanupTextureMapper(); + + void paintRecursive(TexmapPaintOptions options); + void paintSelf(const TexmapPaintOptions& options); + void uploadTextureFromContent(TextureMapper* textureMapper, const IntRect& visibleRect); + + int countDescendantsWithContent() const; + bool hasSurfaceDescendants() const; + + IntSize size() const { return m_size; } + + virtual void setPlatformLayerClient(TextureMapperLayerClient*); + virtual void paint(GraphicsContext*, const IntSize&, const IntRect& targetRect, const IntRect& exposedRect, const TransformationMatrix& transform, float opacity); + + static TextureMapperNode* toTextureMapperNode(GraphicsLayer*); +public: + GraphicsLayerTextureMapper* m_layer; + const char* m_lastTextureMapperType; + RefPtr<TextureMapper> m_lastTextureMapper; + struct TransformData { + TransformationMatrix base, target, replica, forDescendants, perspective, local; + IntRect targetBoundingRect; + float centerZ; + bool dirty, localDirty, perspectiveDirty; + IntRect boundingRectFromRoot; + TransformData() : dirty(true), localDirty(true), perspectiveDirty(true) { } + }; + + TransformData m_transforms; + + enum LayerType { + DefaultLayer, + RootLayer, + ScissorLayer, + ClipLayer, + TransparencyLayer + }; + + LayerType m_layerType; + + struct ContentData { + IntRect needsDisplayRect; + bool needsDisplay; + Color backgroundColor; + + StaticContentType contentType; + RefPtr<Image> image; + TextureMapperVideoLayer* media; + + ContentData() + : needsDisplay(false) + , contentType(HTMLContentType) + , image(0) + , media(0) + { + } + + }; + + inline IntRect targetRect() const + { + return m_currentContent.contentType == HTMLContentType ? entireRect() : m_state.contentsRect; + } + + inline IntRect entireRect() const + { + return IntRect(0, 0, m_size.width(), m_size.height()); + } + + inline IntRect replicaRect() const + { + return m_layerType == TransparencyLayer ? IntRect(0, 0, m_nearestSurfaceSize.width(), m_nearestSurfaceSize.height()) : entireRect(); + } + + RefPtr<BitmapTexture> m_texture; + RefPtr<BitmapTexture> m_surface, m_replicaSurface; + + ContentData m_pendingContent; + ContentData m_currentContent; + + Vector<TextureMapperNode*> m_children; + TextureMapperNode* m_parent; + TextureMapperNode* m_effectTarget; + int m_changeMask; + IntSize m_size, m_nearestSurfaceSize; + String m_name; + TextureMapperLayerClient* m_platformClient; + + struct State { + FloatPoint pos; + FloatPoint3D anchorPoint; + FloatSize size; + TransformationMatrix transform; + TransformationMatrix childrenTransform; + Color backgroundColor; + Color currentColor; + GraphicsLayer::CompositingCoordinatesOrientation geoOrientation; + GraphicsLayer::CompositingCoordinatesOrientation contentsOrientation; + float opacity; + IntRect contentsRect; + int descendantsWithContent; + TextureMapperNode* maskLayer; + TextureMapperNode* replicaLayer; + bool preserves3D; + bool masksToBounds; + bool drawsContent; + bool contentsOpaque; + bool backfaceVisibility; + bool visible; + bool dirty; + bool tiled; + bool hasSurfaceDescendants; + + State() + : opacity(1.f) + , descendantsWithContent(0) + , maskLayer(0) + , replicaLayer(0) + , preserves3D(false) + , masksToBounds(false) + , drawsContent(false) + , contentsOpaque(false) + , backfaceVisibility(false) + , visible(true) + , dirty(true) + , tiled(false) + , hasSurfaceDescendants(false) + { + } + }; + State m_state; +}; + +void TextureMapperNode::setNeedsDisplayInRect(IntRect rect) +{ + if (m_platformClient) { + if (m_state.hasSurfaceDescendants) { + m_platformClient->setNeedsDisplay(); + return; + } + rect.intersect(IntRect(0, 0, m_size.width(), m_size.height())); + if (rect.isEmpty()) + return; + m_platformClient->setNeedsDisplayInRect(rect); + return; + } + + if (!m_parent) + return; + + m_parent->setNeedsDisplayInRect(rect); +} + +void TextureMapperNode::setNeedsDisplay() +{ + if (m_effectTarget) + m_effectTarget->setNeedsDisplay(); + if (m_transforms.targetBoundingRect.isEmpty()) + return; + if (m_state.drawsContent || m_currentContent.contentType != HTMLContentType) + setNeedsDisplayInRect(m_transforms.targetBoundingRect); +} + + +void TextureMapperNode::setPlatformLayerClient(TextureMapperLayerClient* client) +{ + m_platformClient = client; +} + +static int compareGraphicsLayersZValue(const void* a, const void* b) +{ + typedef const TextureMapperNode* NodePtr; + const NodePtr* nodeA = static_cast<const NodePtr*>(a); + const NodePtr* nodeB = static_cast<const NodePtr*>(b); + return int(((*nodeA)->m_transforms.centerZ - (*nodeB)->m_transforms.centerZ) * 1000); +} +inline static void sortByZOrder(Vector<TextureMapperNode* >& array, int first, int last) +{ + qsort(array.data(), array.size(), sizeof(TextureMapperNode*), compareGraphicsLayersZValue); +} + +bool TextureMapperNode::hasSurfaceDescendants() const +{ + if (m_layerType == ClipLayer || m_layerType == TransparencyLayer || m_state.replicaLayer) + return true; + const int size = m_children.size(); + for (int i = 0; i < size; ++i) { + if (TextureMapperNode* child = m_children[i]) { + if (child->hasSurfaceDescendants()) + return true; + } + } + return false; + +} + +void TextureMapperNode::paint(GraphicsContext* context, const IntSize& size, const IntRect& targetRect, const IntRect& exposedRect, const TransformationMatrix& transform, float opacity) +{ + ASSERT(m_layerType == RootLayer); + if (m_size.isEmpty()) + return; + +#if 0 + WTF::StopWatch stopWatch; + ("[TextureMapper] RootPaint!!\n"); +#endif + + RefPtr<TextureMapper> textureMapper = TextureMapper::create(context); + + if (textureMapper->type() != m_lastTextureMapperType) + gTextureMapperCache.m_data.clear(); + + m_lastTextureMapper = textureMapper; + TexmapPaintOptions opt; + opt.opacity = 1; + opt.rootLayer = this; + opt.scissorRect = targetRect; + opt.visibleRect = exposedRect; + opt.textureMapper = textureMapper.get(); + opt.context = textureMapper->graphicsContext(); + opt.surface = 0; + paintRecursive(opt); + + if (textureMapper->allowSurfaceForRoot() || m_state.hasSurfaceDescendants) { + textureMapper->bindSurface(0); + textureMapper->paintToTarget(*m_surface.get(), size, transform, opacity * m_state.opacity, targetRect); + } + gTextureMapperCache.purge(); +} + +int TextureMapperNode::countDescendantsWithContent() const +{ + if (!m_state.visible || m_state.opacity < 0.001) + return 0; + int descendantsWithContent = (m_state.drawsContent || m_currentContent.contentType != HTMLContentType) ? 1 : 0; + + const int size = m_children.size(); + for (int i = 0; i < size; ++i) { + if (TextureMapperNode* child = m_children[i]) + descendantsWithContent += child->countDescendantsWithContent(); + } + + return descendantsWithContent; +} + +inline TextureMapperNode* TextureMapperNode::toTextureMapperNode(GraphicsLayer* layer) +{ + return layer ? static_cast<GraphicsLayerTextureMapper*>(layer)->m_node.get() : 0; +} + +void TextureMapperNode::computeLayerType() +{ + // calculate layer type. A layer can be one of the following: + // RootLayer: the top level. Draws to a framebuffer, and the target texture draws into the viewport. + // only one layer is the root layer. + // ScissorLayer: draws to the current framebuffer, and applies an extra scissor before drawing its children. + // A scissor layer is a layer with children that masks to bounds, is not a transparency layer, and has a rectangular clip. + // ClipLayer: creates a new framebuffer, the size of the layer, and then paints it to the enclosing BitmapTexture with the layer's transform/opacity. + // A clip layer is a layer that masks to bounds, doesn't preserve 3D, has children, and has a transparency/mask or a non-rectangular transform. + // TransparencyLayer: creates a new framebuffer idetical in size to the current framebuffer. Then draws the fb's texture to the current framebuffer with identity transform. + // Used for layers with children and transparency/mask that preserve 3D or don't mask to bounds. + // DefaultLayer: draws itself and its children directly to the current framebuffer. + // any layer that doesn't conform to the other rules is a DefaultLayer. + + const bool selfHasContent = m_state.drawsContent || (m_currentContent.contentType != HTMLContentType); + const bool hasDescendantsWithContent = m_state.descendantsWithContent - (selfHasContent ? 1 : 0); + const bool hasTransparency = m_state.opacity < 0.99 || m_state.maskLayer; + const bool hasReplica = m_state.replicaLayer; + m_layerType = DefaultLayer; + + // Layer has no parent, it must be a root layer. + if (!m_parent && !m_effectTarget) { + m_layerType = RootLayer; + return; + } + + // A layer with no contents is always a default layer. + if (!m_state.descendantsWithContent) + return; + + // A layer with content-descendants and a mask is always a clip layer. + if (hasDescendantsWithContent && m_state.maskLayer) { + m_layerType = ClipLayer; + return; + } + + // A masks-to bounds layer can be a clip or a scissor layer. It's a scissor layer only if it has a trivial clip (identity or translation), or if it has transparency. + // That's because a ClipLayer would create an intermediate drawing surface (FB) - we want to limit it to when it's actually necessary, i.e. transparency or non-trivial clip. + if (m_state.masksToBounds && hasDescendantsWithContent) { + if (hasTransparency || !m_state.transform.isIdentityOrTranslation() || m_parent->m_state.preserves3D) + m_layerType = ClipLayer; + else + m_layerType = ScissorLayer; + return; + } + + // We use a transparency layer when we have two of the following 3: replica, transparency, descendants with contents. + if ((hasReplica && hasDescendantsWithContent) || (hasReplica && hasTransparency) || (hasTransparency && m_state.descendantsWithContent > 1)) + m_layerType = TransparencyLayer; +} +void TextureMapperNode::initializeTextureMapper(TextureMapper* textureMapper) +{ + if (textureMapper->type() == m_lastTextureMapperType) + return; + m_surface = textureMapper->createTexture(); + m_replicaSurface = textureMapper->createTexture(); + m_texture = textureMapper->createTexture(); + gTextureMapperCache.mark(m_texture.get()); + m_lastTextureMapperType = textureMapper->type(); +} + +TextureMapperNode::TextureMapperNode(GraphicsLayerTextureMapper* newLayer) + : m_layer(newLayer) + , m_lastTextureMapperType(0) + , m_lastTextureMapper(0) + , m_layerType(DefaultLayer) + , m_surface(0) + , m_parent(0) + , m_effectTarget(0) + , m_changeMask(NoChanges) + , m_platformClient(0) +{ + +} + +TextureMapperNode* TextureMapperNode::rootLayer() +{ + if (m_effectTarget) + return m_effectTarget->rootLayer(); + if (m_parent) + return m_parent->rootLayer(); + return this; +} + +void TextureMapperNode::invalidateTransform() +{ + m_transforms.dirty = true; + if (m_layerType != ClipLayer) + m_state.dirty = true; + if (m_state.replicaLayer) + m_state.replicaLayer->invalidateTransform(); + const int size = m_children.size(); + for (int i = 0; i < size; ++i) { + if (TextureMapperNode* layer = m_children[i]) + layer->invalidateTransform(); + } +} + +void TextureMapperNode::computeLocalTransform() +{ + if (!m_transforms.localDirty) + return; + const float originX = m_state.anchorPoint.x() * m_size.width(); + const float originY = m_state.anchorPoint.y() * m_size.height(); + m_transforms.local = + TransformationMatrix() + .translate3d(originX + m_state.pos.x(), originY + m_state.pos.y(), m_state.anchorPoint.z()) + .multLeft(m_state.transform) + .translate3d(-originX, -originY, -m_state.anchorPoint.z()); + m_transforms.localDirty = false; +} + +void TextureMapperNode::flattenTo2DSpaceIfNecessary() +{ + if (m_state.preserves3D) + return; + m_transforms.forDescendants.setM13(0); + m_transforms.forDescendants.setM23(0); + m_transforms.forDescendants.setM31(0); + m_transforms.forDescendants.setM32(0); + m_transforms.forDescendants.setM33(1); + m_transforms.forDescendants.setM34(0); + m_transforms.forDescendants.setM43(0); +} + +IntSize TextureMapperNode::nearestSurfaceSize() const +{ + if (m_layerType == ClipLayer || m_layerType == RootLayer) + return m_surface && !m_surface->size().isEmpty() ? m_surface->size() : m_size; + return m_parent->nearestSurfaceSize(); +} + +void TextureMapperNode::computeReplicaTransform() +{ + if (!m_state.replicaLayer) + return; + + m_nearestSurfaceSize = nearestSurfaceSize(); + + if (m_layerType != TransparencyLayer) { + m_transforms.replica = TransformationMatrix(m_transforms.target).multLeft(m_state.replicaLayer->m_transforms.local); + return; + } + + const float originX = m_transforms.target.m41(); + const float originY = m_transforms.target.m42(); + m_transforms.replica = + TransformationMatrix() + .translate(originX, originY) + .multLeft(m_state.replicaLayer->m_transforms.local) + .translate(-originX, -originY); +} + +void TextureMapperNode::computeTransformations() +{ + if (!m_transforms.dirty) + return; + + m_transforms.dirty = false; + if ((m_size.isEmpty() && m_state.masksToBounds)) + return; + + TextureMapperNode* parent = m_parent; + computeLocalTransform(); + + m_transforms.target = TransformationMatrix(parent ? parent->m_transforms.forDescendants : TransformationMatrix()).multLeft(m_transforms.local); + m_transforms.forDescendants = (m_layerType == ClipLayer ? TransformationMatrix() : m_transforms.target); + + if (m_effectTarget) + return; + + m_transforms.targetBoundingRect = IntRect(m_transforms.target.mapRect(entireRect())); + if (m_state.replicaLayer) + m_state.replicaLayer->computeTransformations(); + + flattenTo2DSpaceIfNecessary(); + + if (!m_state.backfaceVisibility && m_transforms.target.inverse().m33() < 0) { + m_state.visible = false; + return; + } + m_state.visible = true; + + if (parent && parent->m_state.preserves3D) + m_transforms.centerZ = m_transforms.target.mapPoint(FloatPoint3D(m_size.width() / 2, m_size.height() / 2, 0)).z(); + + if (!m_children.size()) + return; + + if (m_state.childrenTransform.isIdentity()) + return; + + const FloatPoint centerPoint = FloatPoint(m_size.width() / 2, m_size.height() / 2); + if (m_transforms.perspectiveDirty) + m_transforms.perspective = TransformationMatrix() + .translate(centerPoint.x(), centerPoint.y()) + .multLeft(m_state.childrenTransform) + .translate(-centerPoint.x(), -centerPoint.y()); + m_transforms.perspectiveDirty = false; + m_transforms.forDescendants.multLeft(m_transforms.perspective); +} + +void TextureMapperNode::uploadTextureFromContent(TextureMapper* textureMapper, const IntRect& visibleRect) +{ + if (m_size.isEmpty() || !m_layer) { + m_texture->destroy(); + return; + } + + if (m_currentContent.contentType == DirectImageContentType) { + if (m_currentContent.image) + m_texture->setContentsToImage(m_currentContent.image.get()); + return; + } + + if (m_currentContent.contentType == MediaContentType) { + if (!m_currentContent.media) + return; + m_texture->reset(m_size, true); + PlatformGraphicsContext* platformContext = m_texture->beginPaintMedia(); + GraphicsContext context(platformContext); + m_currentContent.media->paint(&context); + m_texture->endPaint(); + return; + } + + const bool needsReset = (m_texture->contentSize() != m_size) || !m_texture->isValid(); + if ((m_currentContent.contentType != HTMLContentType) + || (!m_currentContent.needsDisplay && m_currentContent.needsDisplayRect.isEmpty() && !needsReset)) + return; + + WTF::StopWatch stopWatch; + IntRect dirtyRect = IntRect(0, 0, m_size.width(), m_size.height()); + if (!needsReset && !m_currentContent.needsDisplay) + dirtyRect.intersect(m_currentContent.needsDisplayRect); + if (needsReset) + m_texture->reset(m_size, m_state.contentsOpaque); + m_pendingContent.needsDisplayRect = IntRect(); + + { + GraphicsContext context(m_texture->beginPaint(dirtyRect)); + if (textureMapper && textureMapper->graphicsContext()) { + GraphicsContext* originalContext = textureMapper->graphicsContext(); + context.setImageInterpolationQuality(originalContext->imageInterpolationQuality()); + context.setTextDrawingMode(originalContext->textDrawingMode()); + } + m_layer->paintGraphicsLayerContents(context, dirtyRect); + } + m_texture->endPaint(); + { +#if 0 + LOG("[TextureMapper] Re-render(%d) layer(%p) %d::%d::%d (%dx%d) [%dms]\n", ++renderCount, this, + needsReset, m_currentContent.needsDisplay, !m_currentContent.needsDisplayRect.isEmpty(), + dirtyRect.width(), dirtyRect.height(), int(stopWatch.elapsed() * 1000)); + static int renderCount = 0; + m_texture->save(String().format("/tmp/layer_%d.png", renderCount)); +#endif + } + m_currentContent.needsDisplay = false; + +} + +void TextureMapperNode::paintSelf(const TexmapPaintOptions& options) +{ + if (!m_layer || m_size.isEmpty() || (!m_state.drawsContent && m_currentContent.contentType == HTMLContentType)) + return; + + RefPtr<BitmapTexture> maskTexture = m_state.maskLayer ? m_state.maskLayer->m_texture : 0; + RefPtr<BitmapTexture> replicaMaskTexture = 0; + if (m_state.replicaLayer && m_state.replicaLayer->m_state.maskLayer) + replicaMaskTexture = m_state.replicaLayer->m_state.maskLayer->m_texture; + + const float opacity = options.isSurface ? 1 : options.opacity; + + uploadTextureFromContent(options.textureMapper, options.visibleRect); + if (m_state.replicaLayer && !options.isSurface) + options.textureMapper->drawTexture(*m_texture.get(), replicaRect(), m_transforms.replica, + opacity * m_state.replicaLayer->m_state.opacity, + replicaMaskTexture ? replicaMaskTexture.get() : maskTexture.get()); + + const IntRect rect = m_layerType == ClipLayer ? entireRect() : targetRect(); + const TransformationMatrix transform = m_layerType == ClipLayer ? TransformationMatrix() : m_transforms.target; + options.textureMapper->drawTexture(*m_texture.get(), rect, transform, opacity, options.isSurface ? 0 : maskTexture.get()); +} + +void TextureMapperNode::paintRecursive(TexmapPaintOptions options) +{ + WTF::StopWatch stopWatch; + + bool isDirty = m_state.dirty; + m_state.dirty = false; + + if ((m_size.isEmpty() && (m_state.masksToBounds + || m_children.isEmpty())) || !m_state.visible || options.opacity < 0.01 || m_state.opacity < 0.01) + return; + + initializeTextureMapper(options.textureMapper); + computeReplicaTransform(); + + if (m_state.maskLayer) { + m_state.maskLayer->initializeTextureMapper(options.textureMapper); + m_state.maskLayer->m_state.dirty = false; + } + + if (m_state.replicaLayer) { + m_state.replicaLayer->initializeTextureMapper(options.textureMapper); + m_state.replicaLayer->m_state.dirty = false; + if (m_state.replicaLayer->m_state.maskLayer) { + m_state.replicaLayer->m_state.maskLayer->initializeTextureMapper(options.textureMapper); + m_state.replicaLayer->m_state.maskLayer->m_state.dirty = false; + } + } + + TextureMapperNode* replica = m_state.replicaLayer; + const bool isSurface = (m_layerType == ClipLayer + || m_layerType == TransparencyLayer + || (m_layerType == RootLayer + && (options.textureMapper->allowSurfaceForRoot() || m_state.hasSurfaceDescendants) + )); + if (isSurface) + uploadTextureFromContent(options.textureMapper, options.visibleRect); + const IntRect boundingRectfromNearestSurface = m_transforms.targetBoundingRect; + + options.opacity *= m_state.opacity; + + TexmapPaintOptions optionsForDescendants(options); + optionsForDescendants.opacity = isSurface ? 1 : options.opacity; + options.isSurface = isSurface; + + if (m_layerType == ClipLayer) { + optionsForDescendants.visibleRect = TransformationMatrix().translate(-boundingRectfromNearestSurface.x(), -boundingRectfromNearestSurface.y()).mapRect(options.visibleRect); + optionsForDescendants.scissorRect = IntRect(0, 0, m_size.width(), m_size.height()); + } + + if (m_layerType == ScissorLayer) + optionsForDescendants.scissorRect.intersect(m_transforms.targetBoundingRect); + options.textureMapper->setClip(optionsForDescendants.scissorRect); + + TextureMapperCacheLock(m_texture.get()); + TextureMapperCacheLock(m_surface.get()); + TextureMapperCacheLock(m_replicaSurface.get()); + + gTextureMapperCache.purge(); + + if (isSurface) { + ASSERT(m_surface); + if (!m_surface->isValid()) + isDirty = true; + if (m_state.tiled) { + m_surface->reset(options.visibleRect.size()); + m_surface->setOffset(options.visibleRect.location()); + } else if (isDirty) + m_surface->reset(m_layerType == TransparencyLayer ? options.surface->size() : m_size); + gTextureMapperCache.mark(m_surface.get()); + options.textureMapper->bindSurface(m_surface.get()); + + optionsForDescendants.surface = m_surface.get(); + } else if (m_surface) + m_surface->destroy(); + + RefPtr<BitmapTexture> maskTexture; + RefPtr<BitmapTexture> replicaMaskTexture; + if (TextureMapperNode* mask = m_state.maskLayer) { + mask->uploadTextureFromContent(options.textureMapper, options.visibleRect); + maskTexture = mask->m_texture; + } + + if (replica && replica->m_state.maskLayer) { + replica->m_state.maskLayer->uploadTextureFromContent(options.textureMapper, options.visibleRect); + replicaMaskTexture = replica->m_state.maskLayer->m_texture; + } + + int childrenSize = m_children.size(); + if (isDirty || !isSurface || m_state.tiled || !m_surface->isValid()) { + bool didPaintSelf = false; + if (!m_state.preserves3D || m_children.isEmpty()) { + paintSelf(options); + didPaintSelf = true; + } + + if (m_children.isEmpty() && !isSurface) + return; + + if (m_layerType == ScissorLayer) + optionsForDescendants.scissorRect.intersect(m_transforms.target.mapRect(IntRect(0, 0, m_size.width(), m_size.height()))); + + for (int i = 0; i < childrenSize; ++i) { + TextureMapperNode* layer = m_children[i]; + if (!layer) + continue; + + if (!didPaintSelf && layer->m_transforms.centerZ >= 0) { + paintSelf(options); + didPaintSelf = true; + } + layer->paintRecursive(optionsForDescendants); + if (isSurface) { + ASSERT(m_surface); + gTextureMapperCache.mark(m_surface.get()); + options.textureMapper->bindSurface(m_surface.get()); + } + } + if (!didPaintSelf) { + paintSelf(options); + didPaintSelf = true; + } + } + + if (m_layerType == RootLayer || m_layerType == DefaultLayer || m_layerType == ScissorLayer) + return; + + ASSERT(m_surface); + BitmapTexture& texture = *m_surface.get(); + if (replica) { + ASSERT(m_replicaSurface); + m_replicaSurface->reset(options.surface->size()); + m_replicaSurface->setOffset(options.surface->offset()); + gTextureMapperCache.mark(m_replicaSurface.get()); + options.textureMapper->bindSurface(m_replicaSurface.get()); + options.textureMapper->drawTexture(texture, replicaRect(), m_transforms.replica, replica->m_state.opacity, replicaMaskTexture ? replicaMaskTexture.get() : maskTexture.get()); + options.textureMapper->drawTexture(texture, IntRect(IntPoint(0, 0), options.surface->size()), TransformationMatrix(), 1.0f, maskTexture.get()); + options.textureMapper->bindSurface(options.surface); + gTextureMapperCache.mark(options.surface); + options.textureMapper->drawTexture(*m_replicaSurface.get(), IntRect(IntPoint(0, 0), options.surface->size()), TransformationMatrix(), options.opacity, 0); + return; + } + + options.textureMapper->bindSurface(options.surface); + options.textureMapper->drawTexture(texture, + m_layerType == TransparencyLayer ? IntRect(IntPoint(0, 0), options.surface->size()) : + targetRect(), + m_layerType == TransparencyLayer ? TransformationMatrix() : m_transforms.target, + options.opacity, maskTexture.get()); + gTextureMapperCache.mark(&texture); +} + +void TextureMapperNode::cleanupTextureMapper() +{ + if (m_texture) + m_texture->destroy(); + if (m_surface) + m_surface->destroy(); + if (m_replicaSurface) + m_replicaSurface->destroy(); + for (int i = 0; i < m_children.size(); ++i) { + if (m_children[i]) + m_children[i]->cleanupTextureMapper(); + } + if (m_lastTextureMapper) + m_lastTextureMapper->cleanup(); +} + +TextureMapperNode::~TextureMapperNode() +{ + setNeedsDisplay(); + { + const int childrenSize = m_children.size(); + for (int i = childrenSize-1; i >= 0; --i) { + ASSERT(m_children[i]->m_parent == this); + m_children[i]->m_parent = 0; + } + } + if (m_parent) + m_parent->m_children.remove(m_parent->m_children.find(this)); +} + +void TextureMapperNode::notifyChange(ChangeMask changeMask) +{ + m_changeMask |= changeMask; + if (!m_layer->client()) + return; + m_layer->client()->notifySyncRequired(m_layer); +} + +void TextureMapperNode::performPostSyncOperations() +{ + const LayerType prevLayerType = m_layerType; + computeLayerType(); + if (prevLayerType != m_layerType) + m_state.dirty = true; + if (m_transforms.dirty) + setNeedsDisplay(); + + computeTransformations(); + if (m_state.maskLayer && !m_state.dirty) + m_state.dirty = m_state.maskLayer->m_state.dirty; + if (m_state.replicaLayer && !m_state.dirty) + m_state.dirty = m_state.replicaLayer->m_state.dirty; + + const int size = m_children.size(); + + for (int i = size - 1; i >= 0; --i) { + TextureMapperNode* layer = m_children[i]; + + layer->performPostSyncOperations(); + if (!m_state.dirty) + m_state.dirty = layer->m_state.dirty; + } + m_state.hasSurfaceDescendants = hasSurfaceDescendants(); + if (m_state.dirty) + m_state.descendantsWithContent = countDescendantsWithContent(); + + if (m_state.preserves3D) + sortByZOrder(m_children, 0, size); + if (m_state.dirty) + setNeedsDisplay(); +} + +void TextureMapperNode::syncCompositingState(bool recurse) +{ + bool needsToInvalidateTransform = false; + + if (!m_layer) + return; + + if (m_changeMask == NoChanges) + goto afterCurrentLayerSync; + + setNeedsDisplay(); + if (m_parent) + m_parent->m_state.dirty = true; + + if (m_currentContent.contentType == HTMLContentType && (m_changeMask & ParentChange)) { + // The WebCore compositor manages item ownership. We have to make sure graphicsview doesn't + // try to snatch that ownership. + + if (!m_layer->parent()) + m_parent = 0; + else + m_parent = toTextureMapperNode(m_layer->parent()); + + if (!m_layer->parent() && m_parent) { + size_t index = m_parent->m_children.find(this); + m_parent->m_children.remove(index); + } + + } + + if (m_changeMask & ChildrenChange) { + m_children.clear(); + for (size_t i = 0; i < m_layer->children().size(); ++i) { + if (TextureMapperNode* child = toTextureMapperNode(m_layer->children()[i])) { + if (!child) + continue; + m_children.append(child); + child->m_parent = this; + } + } + m_state.dirty = true; + } + + if (m_changeMask & (SizeChange | ContentsRectChange)) { + IntSize wantedSize = IntSize(m_layer->size().width(), m_layer->size().height()); + if (wantedSize.isEmpty() && m_pendingContent.contentType == HTMLContentType) + wantedSize = IntSize(m_layer->contentsRect().width(), m_layer->contentsRect().height()); + + if (wantedSize != m_size) { + m_size = IntSize(wantedSize.width(), wantedSize.height()); + if (m_platformClient) + m_platformClient->setSizeChanged(m_size); + const bool needsTiling = m_size.width() > 2000 || m_size.height() > 2000; + if (m_state.tiled != needsTiling) + m_state.tiled = needsTiling; + m_state.dirty = true; + } + } + + if (m_changeMask & MaskLayerChange) { + if (TextureMapperNode* layer = toTextureMapperNode(m_layer->maskLayer())) + layer->m_effectTarget = this; + } + + if (m_changeMask & ReplicaLayerChange) { + if (TextureMapperNode* layer = toTextureMapperNode(m_layer->replicaLayer())) + layer->m_effectTarget = this; + } + + if (m_changeMask & (TransformChange | SizeChange | AnchorPointChange | PositionChange)) + m_transforms.localDirty = true; + + if (m_changeMask & (ChildrenTransformChange | SizeChange)) + m_transforms.perspectiveDirty = true; + + if (m_changeMask & (ChildrenTransformChange | Preserves3DChange | TransformChange | AnchorPointChange | SizeChange | ContentsRectChange | BackfaceVisibilityChange | PositionChange | MaskLayerChange | DrawsContentChange | ContentChange | ReplicaLayerChange)) { + // Due to the differences between the way WebCore handles transforms and the way Qt handles transforms, + // all these elements affect the transforms of all the descendants. + needsToInvalidateTransform = true; + } + + if (m_changeMask & DisplayChange) + m_state.dirty = true; + + m_state.maskLayer = toTextureMapperNode(m_layer->maskLayer()); + m_state.replicaLayer = toTextureMapperNode(m_layer->replicaLayer()); + m_state.pos = m_layer->position(); + m_state.anchorPoint = m_layer->anchorPoint(); + m_state.size = m_layer->size(); + m_state.transform = m_layer->transform(); + m_state.contentsRect = m_layer->contentsRect(); + m_state.opacity = m_layer->opacity(); + m_state.contentsRect = m_layer->contentsRect(); + m_state.preserves3D = m_layer->preserves3D(); + m_state.masksToBounds = m_layer->masksToBounds(); + m_state.drawsContent = m_layer->drawsContent(); + m_state.contentsOpaque = m_layer->contentsOpaque(); + m_state.backfaceVisibility = m_layer->backfaceVisibility(); + m_state.childrenTransform = m_layer->childrenTransform(); + m_currentContent.contentType = m_pendingContent.contentType; + m_currentContent.image = m_pendingContent.image; + m_currentContent.media = m_pendingContent.media; + m_currentContent.backgroundColor = m_pendingContent.backgroundColor; + m_currentContent.needsDisplay = m_currentContent.needsDisplay || m_pendingContent.needsDisplay; + m_currentContent.needsDisplayRect.unite(m_pendingContent.needsDisplayRect); + m_pendingContent.needsDisplay = false; + m_pendingContent.needsDisplayRect = IntRect(); + m_changeMask = NoChanges; + afterCurrentLayerSync: + if (needsToInvalidateTransform) + invalidateTransform(); + + if (m_state.maskLayer) { + m_state.maskLayer->syncCompositingState(false); + if (m_state.maskLayer->m_size.isEmpty()) + m_state.maskLayer->m_size = m_size; + } + + if (m_state.replicaLayer) + m_state.replicaLayer->syncCompositingState(false); + +#if 0 + if (m_state.dirty && m_texture && m_texture->allowOfflineTextureUpload()) + uploadTextureFromContent(0); +#endif + + if (!recurse) + return; + + const int childrenSize = m_children.size(); + for (int i = childrenSize-1; i >= 0; --i) + m_children[i]->syncCompositingState(true); +} + +GraphicsLayerTextureMapper::GraphicsLayerTextureMapper(GraphicsLayerClient* client) + : GraphicsLayer(client) + , m_node(new TextureMapperNode(this)) +{ +} + +void GraphicsLayerTextureMapper::setName(const String& name) +{ + m_node->m_name = name; +} + +GraphicsLayerTextureMapper::~GraphicsLayerTextureMapper() +{ +} + +/* \reimp (GraphicsLayer.h): The current size might change, thus we need to update the whole display. +*/ +void GraphicsLayerTextureMapper::setNeedsDisplay() +{ + m_node->m_pendingContent.needsDisplay = true; + m_node->notifyChange(TextureMapperNode::DisplayChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setNeedsDisplayInRect(const FloatRect& rect) +{ + if (m_node->m_pendingContent.needsDisplay) + return; + m_node->m_pendingContent.needsDisplayRect.unite(IntRect(rect)); + m_node->notifyChange(TextureMapperNode::DisplayChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setParent(GraphicsLayer* layer) +{ + m_node->notifyChange(TextureMapperNode::ParentChange); + GraphicsLayer::setParent(layer); +} + +/* \reimp (GraphicsLayer.h) +*/ +bool GraphicsLayerTextureMapper::setChildren(const Vector<GraphicsLayer*>& children) +{ + m_node->notifyChange(TextureMapperNode::ChildrenChange); + return GraphicsLayer::setChildren(children); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::addChild(GraphicsLayer* layer) +{ + m_node->notifyChange(TextureMapperNode::ChildrenChange); + GraphicsLayer::addChild(layer); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::addChildAtIndex(GraphicsLayer* layer, int index) +{ + GraphicsLayer::addChildAtIndex(layer, index); + m_node->notifyChange(TextureMapperNode::ChildrenChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + GraphicsLayer::addChildAbove(layer, sibling); + m_node->notifyChange(TextureMapperNode::ChildrenChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling) +{ + + GraphicsLayer::addChildBelow(layer, sibling); + m_node->notifyChange(TextureMapperNode::ChildrenChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +bool GraphicsLayerTextureMapper::replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild) +{ + if (GraphicsLayer::replaceChild(oldChild, newChild)) { + m_node->notifyChange(TextureMapperNode::ChildrenChange); + return true; + } + + return false; +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::removeFromParent() +{ + if (!parent()) + return; + m_node->notifyChange(TextureMapperNode::ParentChange); + GraphicsLayer::removeFromParent(); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setMaskLayer(GraphicsLayer* value) +{ + if (value == maskLayer()) + return; + GraphicsLayer::setMaskLayer(value); + m_node->notifyChange(TextureMapperNode::MaskLayerChange); +} + + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setReplicatedByLayer(GraphicsLayer* value) +{ + if (value == replicaLayer()) + return; + GraphicsLayer::setReplicatedByLayer(value); + m_node->notifyChange(TextureMapperNode::ReplicaLayerChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setPosition(const FloatPoint& value) +{ + if (value == position()) + return; + GraphicsLayer::setPosition(value); + m_node->notifyChange(TextureMapperNode::PositionChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setAnchorPoint(const FloatPoint3D& value) +{ + if (value == anchorPoint()) + return; + GraphicsLayer::setAnchorPoint(value); + m_node->notifyChange(TextureMapperNode::AnchorPointChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setSize(const FloatSize& value) +{ + if (value == size()) + return; + + GraphicsLayer::setSize(value); + m_node->notifyChange(TextureMapperNode::SizeChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setTransform(const TransformationMatrix& value) +{ + if (value == transform()) + return; + + GraphicsLayer::setTransform(value); + m_node->notifyChange(TextureMapperNode::TransformChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setChildrenTransform(const TransformationMatrix& value) +{ + if (value == childrenTransform()) + return; + GraphicsLayer::setChildrenTransform(value); + m_node->notifyChange(TextureMapperNode::ChildrenTransformChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setPreserves3D(bool value) +{ + if (value == preserves3D()) + return; + GraphicsLayer::setPreserves3D(value); + m_node->notifyChange(TextureMapperNode::Preserves3DChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setMasksToBounds(bool value) +{ + if (value == masksToBounds()) + return; + GraphicsLayer::setMasksToBounds(value); + m_node->notifyChange(TextureMapperNode::MasksToBoundsChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setDrawsContent(bool value) +{ + if (value == drawsContent()) + return; + m_node->notifyChange(TextureMapperNode::DrawsContentChange); + GraphicsLayer::setDrawsContent(value); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setBackgroundColor(const Color& value) +{ + if (value == m_node->m_pendingContent.backgroundColor) + return; + m_node->m_pendingContent.backgroundColor = value; + GraphicsLayer::setBackgroundColor(value); + m_node->notifyChange(TextureMapperNode::BackgroundColorChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::clearBackgroundColor() +{ + if (!m_node->m_pendingContent.backgroundColor.isValid()) + return; + m_node->m_pendingContent.backgroundColor = Color(); + GraphicsLayer::clearBackgroundColor(); + m_node->notifyChange(TextureMapperNode::BackgroundColorChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setContentsOpaque(bool value) +{ + if (value == contentsOpaque()) + return; + m_node->notifyChange(TextureMapperNode::ContentsOpaqueChange); + GraphicsLayer::setContentsOpaque(value); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setBackfaceVisibility(bool value) +{ + if (value == backfaceVisibility()) + return; + GraphicsLayer::setBackfaceVisibility(value); + m_node->notifyChange(TextureMapperNode::BackfaceVisibilityChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setOpacity(float value) +{ + if (value == opacity()) + return; + GraphicsLayer::setOpacity(value); + m_node->notifyChange(TextureMapperNode::OpacityChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setContentsRect(const IntRect& value) +{ + if (value == contentsRect()) + return; + GraphicsLayer::setContentsRect(value); + m_node->notifyChange(TextureMapperNode::ContentsRectChange); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setContentsToImage(Image* image) +{ + m_node->notifyChange(TextureMapperNode::ContentChange); + m_node->m_pendingContent.contentType = image ? TextureMapperNode::DirectImageContentType : TextureMapperNode::HTMLContentType; + m_node->m_pendingContent.image = image; + GraphicsLayer::setContentsToImage(image); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setContentsBackgroundColor(const Color& color) +{ + m_node->notifyChange(TextureMapperNode::ContentChange); + m_node->m_pendingContent.contentType = TextureMapperNode::ColorContentType; + m_node->m_pendingContent.backgroundColor = color; + GraphicsLayer::setContentsBackgroundColor(color); +} + + +void GraphicsLayerTextureMapper::setContentsToMedia(PlatformLayer* media) +{ + GraphicsLayer::setContentsToMedia(media); + m_node->notifyChange(TextureMapperNode::ContentChange); + m_node->m_pendingContent.contentType = media ? TextureMapperNode::MediaContentType : TextureMapperNode::HTMLContentType; + if (media) + m_node->m_pendingContent.media = static_cast<TextureMapperVideoLayer*>(media); + else + m_node->m_pendingContent.media = 0; +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::setContentsOrientation(CompositingCoordinatesOrientation orientation) +{ + if (contentsOrientation() == orientation) + return; + m_node->notifyChange(TextureMapperNode::ContentsOrientationChange); + GraphicsLayer::setContentsOrientation(orientation); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::syncCompositingStateForThisLayerOnly() +{ + m_node->syncCompositingState(false); + m_node->performPostSyncOperations(); +} + +/* \reimp (GraphicsLayer.h) +*/ +void GraphicsLayerTextureMapper::syncCompositingState() +{ + GraphicsLayer::syncCompositingState(); + m_node->syncCompositingState(true); + m_node->performPostSyncOperations(); +} + +/* \reimp (GraphicsLayer.h) + */ +NativeLayer GraphicsLayerTextureMapper::nativeLayer() const +{ + return m_node.get(); +} + +/* \reimp (GraphicsLayer.h) +*/ +PlatformLayer* GraphicsLayerTextureMapper::platformLayer() const +{ + return m_node.get(); +} + +PassOwnPtr<GraphicsLayer> GraphicsLayer::create(GraphicsLayerClient* client) +{ + return new GraphicsLayerTextureMapper(client); +} + +} diff --git a/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h b/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h new file mode 100644 index 0000000..36ebd74 --- /dev/null +++ b/WebCore/platform/graphics/texmap/GraphicsLayerTextureMapper.h @@ -0,0 +1,96 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 GraphicsLayerTextureMapper_h +#define GraphicsLayerTextureMapper_h + +#include "GraphicsContext.h" +#include "GraphicsLayer.h" +#include "GraphicsLayerClient.h" +#include "Image.h" + +#if ENABLE(3D_CANVAS) +#include "GraphicsContext3D.h" +#endif + +#define ENABLE_TEXMAP_ANIMATION 0 + +namespace WebCore { + +class TextureMapperNode; +class BitmapTexture; +class TextureMapper; + +class GraphicsLayerTextureMapper : public GraphicsLayer { + friend class TextureMapperNode; + +public: + GraphicsLayerTextureMapper(GraphicsLayerClient*); + virtual ~GraphicsLayerTextureMapper(); + + // reimps from GraphicsLayer.h + virtual void setNeedsDisplay(); + virtual void setNeedsDisplayInRect(const FloatRect&); + virtual void setParent(GraphicsLayer* layer); + virtual bool setChildren(const Vector<GraphicsLayer*>&); + virtual void addChild(GraphicsLayer*); + virtual void addChildAtIndex(GraphicsLayer*, int index); + virtual void addChildAbove(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual void addChildBelow(GraphicsLayer* layer, GraphicsLayer* sibling); + virtual bool replaceChild(GraphicsLayer* oldChild, GraphicsLayer* newChild); + virtual void removeFromParent(); + virtual void setMaskLayer(GraphicsLayer* layer); + virtual void setPosition(const FloatPoint& p); + virtual void setAnchorPoint(const FloatPoint3D& p); + virtual void setSize(const FloatSize& size); + virtual void setTransform(const TransformationMatrix& t); + virtual void setChildrenTransform(const TransformationMatrix& t); + virtual void setPreserves3D(bool b); + virtual void setMasksToBounds(bool b); + virtual void setDrawsContent(bool b); + virtual void setBackgroundColor(const Color&); + virtual void clearBackgroundColor(); + virtual void setContentsOpaque(bool b); + virtual void setBackfaceVisibility(bool b); + virtual void setOpacity(float opacity); + virtual void setContentsRect(const IntRect& r); + virtual void setReplicatedByLayer(GraphicsLayer*); + virtual void setContentsToImage(Image*); + virtual void setContentsToMedia(PlatformLayer*); + virtual void setContentsBackgroundColor(const Color&); +#if ENABLE(3D_CANVAS) + virtual void setContentsToGraphicsContext3D(const GraphicsContext3D*); + virtual void setGraphicsContext3DNeedsDisplay(); +#endif + virtual void setContentsOrientation(CompositingCoordinatesOrientation orientation); + virtual void syncCompositingState(); + virtual void syncCompositingStateForThisLayerOnly(); + virtual void setName(const String& name); + virtual NativeLayer nativeLayer() const; + virtual PlatformLayer* platformLayer() const; + + virtual bool addAnimation(const KeyframeValueList&, const IntSize& /*boxSize*/, const Animation*, + const String& /*keyframesName*/, double /*timeOffset*/) { return false; } + +private: + OwnPtr<TextureMapperNode> m_node; +}; + +} +#endif // GraphicsLayerTextureMapper_h diff --git a/WebCore/platform/graphics/texmap/TextureMapper.h b/WebCore/platform/graphics/texmap/TextureMapper.h new file mode 100644 index 0000000..03c1c6d --- /dev/null +++ b/WebCore/platform/graphics/texmap/TextureMapper.h @@ -0,0 +1,120 @@ +/* + Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 TextureMapper_h +#define TextureMapper_h + +#if USE(ACCELERATED_COMPOSITING) +#if (defined(QT_OPENGL_LIB)) + #if defined(QT_OPENGL_ES_2) && !defined(TEXMAP_OPENGL_ES_2) + #define TEXMAP_OPENGL_ES_2 + #endif +#endif + +#include "GraphicsContext.h" +#include "IntRect.h" +#include "IntSize.h" +#include "TransformationMatrix.h" + +/* + TextureMapper is a mechanism that enables hardware acceleration of CSS animations (accelerated compositing) without + a need for a platform specific scene-graph library like CoreAnimations or QGraphicsView. +*/ + +namespace WebCore { + +class TextureMapper; + +// A 2D texture that can be the target of software or GL rendering. +class BitmapTexture : public RefCounted<BitmapTexture> { +public: + BitmapTexture() : m_lockCount(0) {} + virtual ~BitmapTexture() { } + + virtual bool allowOfflineTextureUpload() const { return false; } + virtual void destroy() = 0; + virtual IntSize size() const = 0; + virtual bool isValid() const = 0; + virtual void reset(const IntSize& size, bool opaque = false) + { + m_isOpaque = opaque; + m_contentSize = size; + } + + virtual PlatformGraphicsContext* beginPaint(const IntRect& dirtyRect) = 0; + virtual void endPaint() = 0; + virtual PlatformGraphicsContext* beginPaintMedia() + { + return beginPaint(IntRect(0, 0, size().width(), size().height())); + } + virtual void setContentsToImage(Image*) = 0; + virtual bool save(const String& filename) { return false; } + + inline void lock() { ++m_lockCount; } + inline void unlock() { --m_lockCount; } + inline bool isLocked() { return m_lockCount; } + inline IntSize contentSize() const { return m_contentSize; } + inline void setOffset(const IntPoint& o) { m_offset = o; } + inline IntPoint offset() const { return m_offset; } +private: + int m_lockCount; + IntSize m_contentSize; + bool m_isOpaque; + IntPoint m_offset; +}; + +// A "context" class used to encapsulate accelerated texture mapping functions: i.e. drawing a texture +// onto the screen or into another texture with a specified transform, opacity and mask. +class TextureMapper : public RefCounted<TextureMapper> { + friend class BitmapTexture; + +public: + static PassRefPtr<TextureMapper> create(GraphicsContext*); + virtual ~TextureMapper() { } + + virtual void drawTexture(const BitmapTexture& texture, const IntRect& target, const TransformationMatrix& matrix = TransformationMatrix(), float opacity = 1.0f, const BitmapTexture* maskTexture = 0) = 0; + + // makes a surface the target for the following drawTexture calls. + virtual void bindSurface(BitmapTexture* surface) = 0; + virtual void paintToTarget(const BitmapTexture& texture, const IntSize&, const TransformationMatrix& matrix, float opacity, const IntRect& visibleRect) + { + drawTexture(texture, IntRect(0, 0, texture.contentSize().width(), texture.contentSize().height()), matrix, opacity, 0); + } + + virtual void setClip(const IntRect&) = 0; + virtual bool allowSurfaceForRoot() const = 0; + virtual PassRefPtr<BitmapTexture> createTexture() = 0; + virtual const char* type() const = 0; + virtual void cleanup() {} + + GraphicsContext* graphicsContext() const + { + return m_gc; + } + +protected: + TextureMapper(GraphicsContext* gc) : m_gc(gc) {} + GraphicsContext* m_gc; +}; + +}; + +#endif + +#endif diff --git a/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h b/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h new file mode 100644 index 0000000..23e9fc9 --- /dev/null +++ b/WebCore/platform/graphics/texmap/TextureMapperPlatformLayer.h @@ -0,0 +1,70 @@ +/* + Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + 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 TextureMapperPlatformLayer_h +#define TextureMapperPlatformLayer_h + +namespace WebCore { + +class GraphicsContext; +class IntRect; +class IntSize; +class TransformationMatrix; + + +// Glue layer to connect the texmap layer to the platform specific container. +class TextureMapperLayerClient { +public: + virtual ~TextureMapperLayerClient() {} + virtual void setNeedsDisplay() = 0; + virtual void setNeedsDisplayInRect(const IntRect& rect) = 0; + virtual void setSizeChanged(const IntSize&) = 0; +}; + +class TextureMapperPlatformLayer { +public: + enum Type { + ContentLayer, + VideoLayer + }; + + virtual Type layerType() const = 0; + virtual ~TextureMapperPlatformLayer() {} +}; + +class TextureMapperContentLayer : public TextureMapperPlatformLayer { +public: + virtual void setPlatformLayerClient(TextureMapperLayerClient*) = 0; + virtual void paint(GraphicsContext*, const IntSize&, const IntRect& targetRect, const IntRect& exposedRect, const TransformationMatrix& transform, float opacity) {} + virtual IntSize size() const = 0; + virtual void cleanupTextureMapper() {} + virtual Type layerType() const { return ContentLayer; } +}; + +#if ENABLE(VIDEO) +class TextureMapperVideoLayer : public TextureMapperPlatformLayer { +public: + virtual void paint(GraphicsContext*) = 0; + virtual Type layerType() const { return VideoLayer; } +}; +#endif + +} + +#endif // TextureMapperPlatformLayer_h diff --git a/WebCore/platform/graphics/transforms/AffineTransform.cpp b/WebCore/platform/graphics/transforms/AffineTransform.cpp index be18e07..f275526 100644 --- a/WebCore/platform/graphics/transforms/AffineTransform.cpp +++ b/WebCore/platform/graphics/transforms/AffineTransform.cpp @@ -41,8 +41,8 @@ static void affineTransformDecompose(const AffineTransform& matrix, double sr[9] AffineTransform m(matrix); // Compute scaling factors - double sx = sqrt(m.a() * m.a() + m.b() * m.b()); - double sy = sqrt(m.c() * m.c() + m.d() * m.d()); + double sx = matrix.xScale(); + double sy = matrix.yScale(); // Compute cross product of transformed unit vectors. If negative, // one axis was flipped. @@ -119,6 +119,16 @@ bool AffineTransform::isIdentity() const && m_transform[4] == 0 && m_transform[5] == 0); } +double AffineTransform::xScale() const +{ + return sqrt(m_transform[0] * m_transform[0] + m_transform[1] * m_transform[1]); +} + +double AffineTransform::yScale() const +{ + return sqrt(m_transform[2] * m_transform[2] + m_transform[3] * m_transform[3]); +} + double AffineTransform::det() const { return m_transform[0] * m_transform[3] - m_transform[1] * m_transform[2]; diff --git a/WebCore/platform/graphics/transforms/AffineTransform.h b/WebCore/platform/graphics/transforms/AffineTransform.h index 289ec54..baee102 100644 --- a/WebCore/platform/graphics/transforms/AffineTransform.h +++ b/WebCore/platform/graphics/transforms/AffineTransform.h @@ -110,7 +110,10 @@ public: AffineTransform& skew(double angleX, double angleY); AffineTransform& skewX(double angle); AffineTransform& skewY(double angle); - + + double xScale() const; + double yScale() const; + double det() const; bool isInvertible() const; AffineTransform inverse() const; diff --git a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h index 7430dbc..0a0aaf0 100644 --- a/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h +++ b/WebCore/platform/graphics/transforms/Matrix3DTransformOperation.h @@ -37,6 +37,8 @@ public: return adoptRef(new Matrix3DTransformOperation(matrix)); } + TransformationMatrix matrix() const {return m_matrix; } + private: virtual bool isIdentity() const { return m_matrix.isIdentity(); } diff --git a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h index ee47a11..fd9b27e 100644 --- a/WebCore/platform/graphics/transforms/MatrixTransformOperation.h +++ b/WebCore/platform/graphics/transforms/MatrixTransformOperation.h @@ -42,6 +42,8 @@ public: return adoptRef(new MatrixTransformOperation(t)); } + TransformationMatrix matrix() const { return TransformationMatrix(m_a, m_b, m_c, m_d, m_e, m_f); } + private: virtual bool isIdentity() const { return m_a == 1 && m_b == 0 && m_c == 0 && m_d == 1 && m_e == 0 && m_f == 0; } diff --git a/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h index a665f3e..834cc83 100644 --- a/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h +++ b/WebCore/platform/graphics/transforms/PerspectiveTransformOperation.h @@ -36,6 +36,8 @@ public: { return adoptRef(new PerspectiveTransformOperation(p)); } + + double perspective() const { return m_p; } private: virtual bool isIdentity() const { return m_p == 0; } diff --git a/WebCore/platform/graphics/transforms/RotateTransformOperation.h b/WebCore/platform/graphics/transforms/RotateTransformOperation.h index 699ea43..2acb002 100644 --- a/WebCore/platform/graphics/transforms/RotateTransformOperation.h +++ b/WebCore/platform/graphics/transforms/RotateTransformOperation.h @@ -41,6 +41,9 @@ public: return adoptRef(new RotateTransformOperation(x, y, z, angle, type)); } + double x() const { return m_x; } + double y() const { return m_y; } + double z() const { return m_z; } double angle() const { return m_angle; } private: diff --git a/WebCore/platform/graphics/transforms/SkewTransformOperation.h b/WebCore/platform/graphics/transforms/SkewTransformOperation.h index 6343710..afe9a7b 100644 --- a/WebCore/platform/graphics/transforms/SkewTransformOperation.h +++ b/WebCore/platform/graphics/transforms/SkewTransformOperation.h @@ -36,6 +36,9 @@ public: return adoptRef(new SkewTransformOperation(angleX, angleY, type)); } + double angleX() const { return m_angleX; } + double angleY() const { return m_angleY; } + private: virtual bool isIdentity() const { return m_angleX == 0 && m_angleY == 0; } virtual OperationType getOperationType() const { return m_type; } diff --git a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h index a66cc3d..ea48d49 100644 --- a/WebCore/platform/graphics/transforms/TranslateTransformOperation.h +++ b/WebCore/platform/graphics/transforms/TranslateTransformOperation.h @@ -46,6 +46,10 @@ public: double y(const IntSize& borderBoxSize) const { return m_y.calcFloatValue(borderBoxSize.height()); } double z(const IntSize&) const { return m_z.calcFloatValue(1); } + Length x() const { return m_x; } + Length y() const { return m_y; } + Length z() const { return m_z; } + private: virtual bool isIdentity() const { return m_x.calcFloatValue(1) == 0 && m_y.calcFloatValue(1) == 0 && m_z.calcFloatValue(1) == 0; } diff --git a/WebCore/platform/graphics/win/FontCGWin.cpp b/WebCore/platform/graphics/win/FontCGWin.cpp index c6437f2..2f1fb41 100644 --- a/WebCore/platform/graphics/win/FontCGWin.cpp +++ b/WebCore/platform/graphics/win/FontCGWin.cpp @@ -360,14 +360,14 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo graphicsContext->clearShadow(); Color fillColor = graphicsContext->fillColor(); Color shadowFillColor(shadowColor.red(), shadowColor.green(), shadowColor.blue(), shadowColor.alpha() * fillColor.alpha() / 255); - graphicsContext->setFillColor(shadowFillColor, DeviceColorSpace); + graphicsContext->setFillColor(shadowFillColor, ColorSpaceDeviceRGB); CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); if (font->syntheticBoldOffset()) { CGContextSetTextPosition(cgContext, point.x() + translation.width() + shadowOffset.width() + font->syntheticBoldOffset(), point.y() + translation.height() + shadowOffset.height()); CGContextShowGlyphsWithAdvances(cgContext, glyphBuffer.glyphs(from), glyphBuffer.advances(from), numGlyphs); } - graphicsContext->setFillColor(fillColor, DeviceColorSpace); + graphicsContext->setFillColor(fillColor, ColorSpaceDeviceRGB); } CGContextSetTextPosition(cgContext, point.x() + translation.width(), point.y() + translation.height()); @@ -378,7 +378,7 @@ void Font::drawGlyphs(GraphicsContext* graphicsContext, const SimpleFontData* fo } if (hasSimpleShadow) - graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, DeviceColorSpace); + graphicsContext->setShadow(shadowOffset, shadowBlur, shadowColor, ColorSpaceDeviceRGB); wkRestoreFontSmoothingStyle(cgContext, oldFontSmoothingStyle); } diff --git a/WebCore/platform/graphics/win/GDIExtras.cpp b/WebCore/platform/graphics/win/GDIExtras.cpp new file mode 100644 index 0000000..4bd95da --- /dev/null +++ b/WebCore/platform/graphics/win/GDIExtras.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 "GDIExtras.h" + +#include "SoftLinking.h" + +namespace WebCore { + +#if OS(WINCE) +SOFT_LINK_LIBRARY(coredll) +SOFT_LINK_OPTIONAL(coredll, AlphaBlend, BOOL, APIENTRY, (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction)) + +AlphaBlendPointerType AlphaBlendPointer() +{ + return AlphaBlendPtr(); +} +#endif + +} // namespace WebCore diff --git a/WebCore/platform/graphics/win/GDIExtras.h b/WebCore/platform/graphics/win/GDIExtras.h new file mode 100644 index 0000000..0166124 --- /dev/null +++ b/WebCore/platform/graphics/win/GDIExtras.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 GDIExtras_h +#define GDIExtras_h + +#include <windows.h> + +namespace WebCore { + +typedef BOOL (APIENTRY *AlphaBlendPointerType) (HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction); + +#if OS(WINCE) +AlphaBlendPointerType AlphaBlendPointer(); +#endif + +inline bool hasAlphaBlendSupport() +{ +#if OS(WINCE) + return AlphaBlendPointer(); +#else + return true; +#endif +} + +inline bool alphaBlendIfSupported(HDC hdcDest, int nXOriginDest, int nYOriginDest, int nWidthDest, int nHeightDest, HDC hdcSrc, + int nXOriginSrc, int nYOriginSrc, int nWidthSrc, int nHeightSrc, BLENDFUNCTION blendFunction) +{ +#if OS(WINCE) + AlphaBlendPointerType alphaBlendPointer = AlphaBlendPointer(); + if (!alphaBlendPointer) + return false; + + alphaBlendPointer(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction); +#else + AlphaBlend(hdcDest, nXOriginDest, nYOriginDest, nWidthDest, nHeightDest, hdcSrc, nXOriginSrc, nYOriginSrc, nWidthSrc, nHeightSrc, blendFunction); +#endif + return true; +} + +} // namespace WebCore + +#endif // GDIExtras_h diff --git a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp index 0203d42..c9288e5 100644 --- a/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp +++ b/WebCore/platform/graphics/win/GraphicsContextCGWin.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2010 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -70,8 +70,8 @@ GraphicsContext::GraphicsContext(HDC hdc, bool hasAlpha) setPaintingDisabled(!m_data->m_cgContext); if (m_data->m_cgContext) { // Make sure the context starts in sync with our state. - setPlatformFillColor(fillColor(), DeviceColorSpace); - setPlatformStrokeColor(strokeColor(), DeviceColorSpace); + setPlatformFillColor(fillColor(), ColorSpaceDeviceRGB); + setPlatformStrokeColor(strokeColor(), ColorSpaceDeviceRGB); } } @@ -129,6 +129,8 @@ void GraphicsContext::drawFocusRing(const Vector<Path>& paths, int width, int of // FIXME: implement } +// FIXME: This is nearly identical to the GraphicsContext::drawFocusRing function in GraphicsContextMac.mm. +// The code could move to GraphicsContextCG.cpp and be shared. void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int offset, const Color& color) { if (paintingDisabled()) @@ -136,7 +138,7 @@ void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int float radius = (width - 1) / 2.0f; offset += radius; - CGColorRef colorRef = color.isValid() ? createCGColor(color) : 0; + CGColorRef colorRef = color.isValid() ? cachedCGColor(color, ColorSpaceDeviceRGB) : 0; CGMutablePathRef focusRingPath = CGPathCreateMutable(); unsigned rectCount = rects.size(); @@ -151,8 +153,6 @@ void GraphicsContext::drawFocusRing(const Vector<IntRect>& rects, int width, int wkDrawFocusRing(context, colorRef, radius); - CGColorRelease(colorRef); - CGPathRelease(focusRingPath); CGContextRestoreGState(context); diff --git a/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp b/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp index dad5da1..f7674db 100644 --- a/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp +++ b/WebCore/platform/graphics/win/GraphicsLayerCACF.cpp @@ -99,9 +99,7 @@ TransformationMatrix CAToTransform3D(const CATransform3D& fromT3D) static void setLayerBorderColor(WKCACFLayer* layer, const Color& color) { - CGColorRef borderColor = createCGColor(color); - layer->setBorderColor(borderColor); - CGColorRelease(borderColor); + layer->setBorderColor(cachedCGColor(color, ColorSpaceDeviceRGB)); } static void clearBorderColor(WKCACFLayer* layer) @@ -111,9 +109,7 @@ static void clearBorderColor(WKCACFLayer* layer) static void setLayerBackgroundColor(WKCACFLayer* layer, const Color& color) { - CGColorRef bgColor = createCGColor(color); - layer->setBackgroundColor(bgColor); - CGColorRelease(bgColor); + layer->setBackgroundColor(cachedCGColor(color, ColorSpaceDeviceRGB)); } static void clearLayerBackgroundColor(WKCACFLayer* layer) diff --git a/WebCore/platform/graphics/win/ImageCGWin.cpp b/WebCore/platform/graphics/win/ImageCGWin.cpp index a0fbba7..e65b859 100644 --- a/WebCore/platform/graphics/win/ImageCGWin.cpp +++ b/WebCore/platform/graphics/win/ImageCGWin.cpp @@ -78,9 +78,9 @@ bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) IntSize imageSize = BitmapImage::size(); if (size) - drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), DeviceColorSpace, CompositeCopy); + drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), ColorSpaceDeviceRGB, CompositeCopy); else - draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), DeviceColorSpace, CompositeCopy); + draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), ColorSpaceDeviceRGB, CompositeCopy); // Do cleanup CGContextRelease(cgContext); diff --git a/WebCore/platform/graphics/win/ImageCairoWin.cpp b/WebCore/platform/graphics/win/ImageCairoWin.cpp index e3c5ea0..70b132e 100644 --- a/WebCore/platform/graphics/win/ImageCairoWin.cpp +++ b/WebCore/platform/graphics/win/ImageCairoWin.cpp @@ -82,9 +82,9 @@ bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) IntSize imageSize = BitmapImage::size(); if (size) - drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), DeviceColorSpace, CompositeCopy); + drawFrameMatchingSourceSize(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), ColorSpaceDeviceRGB, CompositeCopy); else - draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), DeviceColorSpace, CompositeCopy); + draw(&gc, FloatRect(0.0f, 0.0f, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), ColorSpaceDeviceRGB, CompositeCopy); // Do cleanup cairo_destroy(targetRef); @@ -100,7 +100,7 @@ void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const Float if (cairo_image_surface_get_height(image) == static_cast<size_t>(srcSize.height()) && cairo_image_surface_get_width(image) == static_cast<size_t>(srcSize.width())) { size_t currentFrame = m_currentFrame; m_currentFrame = i; - draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, srcSize.width(), srcSize.height()), DeviceColorSpace, compositeOp); + draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, srcSize.width(), srcSize.height()), ColorSpaceDeviceRGB, compositeOp); m_currentFrame = currentFrame; return; } @@ -108,7 +108,7 @@ void BitmapImage::drawFrameMatchingSourceSize(GraphicsContext* ctxt, const Float // No image of the correct size was found, fallback to drawing the current frame IntSize imageSize = BitmapImage::size(); - draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), DeviceColorSpace, compositeOp); + draw(ctxt, dstRect, FloatRect(0.0f, 0.0f, imageSize.width(), imageSize.height()), ColorSpaceDeviceRGB, compositeOp); } } // namespace WebCore diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp index 4a7e45e..4a7e45e 100755..100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateFullscreenWindow.cpp diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp index 354e0bf..1b4f1d9 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.cpp @@ -42,7 +42,6 @@ #include "ScrollView.h" #include "Settings.h" #include "SoftLinking.h" -#include "StringBuilder.h" #include "TimeRanges.h" #include "Timer.h" #include <AssertMacros.h> @@ -53,6 +52,7 @@ #include <wtf/MainThread.h> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> #include <wtf/text/StringHash.h> #if USE(ACCELERATED_COMPOSITING) @@ -174,6 +174,8 @@ MediaPlayerPrivateQuickTimeVisualContext::MediaPlayerPrivateQuickTimeVisualConte , m_movieTransform(CGAffineTransformIdentity) #endif , m_visualContextClient(new MediaPlayerPrivateQuickTimeVisualContext::VisualContextClient(this)) + , m_delayingLoad(false) + , m_preload(MediaPlayer::Auto) { } @@ -241,7 +243,7 @@ static void addCookieParam(StringBuilder& cookieBuilder, const String& name, con // Add parameter name, and value if there is one. cookieBuilder.append(name); if (!value.isEmpty()) { - cookieBuilder.append("="); + cookieBuilder.append('='); cookieBuilder.append(value); } } @@ -275,7 +277,7 @@ void MediaPlayerPrivateQuickTimeVisualContext::setUpCookiesForQuickTime(const St addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires)); if (cookie.httpOnly) addCookieParam(cookieBuilder, "httpOnly", String()); - cookieBuilder.append(";"); + cookieBuilder.append(';'); String cookieURL; if (!cookie.domain.isEmpty()) { @@ -316,8 +318,28 @@ static void disableComponentsOnce() QTMovie::disableComponent(componentsToDisable[i]); } +void MediaPlayerPrivateQuickTimeVisualContext::resumeLoad() +{ + m_delayingLoad = false; + + if (!m_movieURL.isEmpty()) + loadInternal(m_movieURL); +} + void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url) { + m_movieURL = url; + + if (m_preload == MediaPlayer::None) { + m_delayingLoad = true; + return; + } + + loadInternal(url); +} + +void MediaPlayerPrivateQuickTimeVisualContext::loadInternal(const String& url) +{ if (!QTMovie::initializeQuickTime()) { // FIXME: is this the right error to return? m_networkState = MediaPlayer::DecodeError; @@ -347,6 +369,12 @@ void MediaPlayerPrivateQuickTimeVisualContext::load(const String& url) m_movie->setVolume(m_player->volume()); } +void MediaPlayerPrivateQuickTimeVisualContext::prepareToPlay() +{ + if (!m_movie || m_delayingLoad) + resumeLoad(); +} + void MediaPlayerPrivateQuickTimeVisualContext::play() { if (!m_movie) @@ -1005,6 +1033,13 @@ bool MediaPlayerPrivateQuickTimeVisualContext::hasSingleSecurityOrigin() const return true; } +void MediaPlayerPrivateQuickTimeVisualContext::setPreload(MediaPlayer::Preload preload) +{ + m_preload = preload; + if (m_delayingLoad && m_preload != MediaPlayer::None) + resumeLoad(); +} + MediaPlayerPrivateQuickTimeVisualContext::MediaRenderingMode MediaPlayerPrivateQuickTimeVisualContext::currentRenderingMode() const { if (!m_movie) diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h index 272b90f..4c62558 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeVisualContext.h @@ -74,9 +74,12 @@ private: void load(const String& url); void cancelLoad(); + void loadInternal(const String& url); + void resumeLoad(); void play(); void pause(); + void prepareToPlay(); bool paused() const; bool seeking() const; @@ -111,6 +114,8 @@ private: bool hasClosedCaptions() const; void setClosedCaptionsVisible(bool); + void setPreload(MediaPlayer::Preload); + void updateStates(); void doSeek(); void cancelSeek(); @@ -189,6 +194,9 @@ private: bool m_isStreaming; bool m_visible; bool m_newFrameAvailable; + bool m_delayingLoad; + String m_movieURL; + MediaPlayer::Preload m_preload; #if DRAW_FRAME_RATE double m_frameCountWhilePlaying; double m_timeStartedPlaying; diff --git a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp index c37f5d5..39e8a11 100644 --- a/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp +++ b/WebCore/platform/graphics/win/MediaPlayerPrivateQuickTimeWin.cpp @@ -38,7 +38,6 @@ #include "QTMovieTask.h" #include "ScrollView.h" #include "SoftLinking.h" -#include "StringBuilder.h" #include "TimeRanges.h" #include "Timer.h" #include <Wininet.h> @@ -46,6 +45,7 @@ #include <wtf/HashSet.h> #include <wtf/MathExtras.h> #include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> #include <wtf/text/StringHash.h> #if USE(ACCELERATED_COMPOSITING) @@ -159,7 +159,7 @@ static void addCookieParam(StringBuilder& cookieBuilder, const String& name, con // Add parameter name, and value if there is one. cookieBuilder.append(name); if (!value.isEmpty()) { - cookieBuilder.append("="); + cookieBuilder.append('='); cookieBuilder.append(value); } } @@ -194,7 +194,7 @@ void MediaPlayerPrivate::setUpCookiesForQuickTime(const String& url) addCookieParam(cookieBuilder, "expires", rfc2616DateStringFromTime(cookie.expires)); if (cookie.httpOnly) addCookieParam(cookieBuilder, "httpOnly", String()); - cookieBuilder.append(";"); + cookieBuilder.append(';'); String cookieURL; if (!cookie.domain.isEmpty()) { diff --git a/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp b/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp index 1685a30..d75c854 100644 --- a/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp +++ b/WebCore/platform/graphics/win/WKCACFContextFlusher.cpp @@ -29,8 +29,8 @@ #include "WKCACFContextFlusher.h" +#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/StdLibExtras.h> -#include <QuartzCore/CACFContext.h> namespace WebCore { @@ -48,24 +48,18 @@ WKCACFContextFlusher::~WKCACFContextFlusher() { } -void WKCACFContextFlusher::addContext(CACFContextRef context) +void WKCACFContextFlusher::addContext(WKCACFContext* context) { ASSERT(context); - if (m_contexts.add(context).second) - CFRetain(context); + m_contexts.add(context); } -void WKCACFContextFlusher::removeContext(CACFContextRef context) +void WKCACFContextFlusher::removeContext(WKCACFContext* context) { ASSERT(context); - ContextSet::iterator found = m_contexts.find(context); - if (found == m_contexts.end()) - return; - - CFRelease(*found); - m_contexts.remove(found); + m_contexts.remove(context); } void WKCACFContextFlusher::flushAllContexts() @@ -76,11 +70,8 @@ void WKCACFContextFlusher::flushAllContexts() contextsToFlush.swap(m_contexts); ContextSet::const_iterator end = contextsToFlush.end(); - for (ContextSet::const_iterator it = contextsToFlush.begin(); it != end; ++it) { - CACFContextRef context = *it; - CACFContextFlush(context); - CFRelease(context); - } + for (ContextSet::const_iterator it = contextsToFlush.begin(); it != end; ++it) + wkCACFContextFlush(*it); } } diff --git a/WebCore/platform/graphics/win/WKCACFContextFlusher.h b/WebCore/platform/graphics/win/WKCACFContextFlusher.h index 9ce76aa..17ec41d 100644 --- a/WebCore/platform/graphics/win/WKCACFContextFlusher.h +++ b/WebCore/platform/graphics/win/WKCACFContextFlusher.h @@ -32,7 +32,7 @@ #include <wtf/HashSet.h> -typedef struct _CACFContext* CACFContextRef; +struct WKCACFContext; namespace WebCore { @@ -40,8 +40,8 @@ class WKCACFContextFlusher : public Noncopyable { public: static WKCACFContextFlusher& shared(); - void addContext(CACFContextRef); - void removeContext(CACFContextRef); + void addContext(WKCACFContext*); + void removeContext(WKCACFContext*); void flushAllContexts(); @@ -49,7 +49,7 @@ private: WKCACFContextFlusher(); ~WKCACFContextFlusher(); - typedef HashSet<CACFContextRef> ContextSet; + typedef HashSet<WKCACFContext*> ContextSet; ContextSet m_contexts; }; diff --git a/WebCore/platform/graphics/win/WKCACFLayer.cpp b/WebCore/platform/graphics/win/WKCACFLayer.cpp index bf47925..a8714e3 100644 --- a/WebCore/platform/graphics/win/WKCACFLayer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayer.cpp @@ -30,15 +30,10 @@ #include "WKCACFLayer.h" #include "WKCACFLayerRenderer.h" -#include <wtf/text/CString.h> - +#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <stdio.h> -#include <QuartzCore/CACFContext.h> -#include <QuartzCore/CARender.h> - -#ifndef NDEBUG #include <wtf/CurrentTime.h> -#endif +#include <wtf/text/CString.h> namespace WebCore { @@ -190,9 +185,9 @@ WKCACFLayer::~WKCACFLayer() CACFLayerSetDisplayCallback(layer(), 0); } -void WKCACFLayer::becomeRootLayerForContext(CACFContextRef context) +void WKCACFLayer::becomeRootLayerForContext(WKCACFContext* context) { - CACFContextSetLayer(context, layer()); + wkCACFContextSetLayer(context, layer()); setNeedsCommit(); } diff --git a/WebCore/platform/graphics/win/WKCACFLayer.h b/WebCore/platform/graphics/win/WKCACFLayer.h index 7243508..4c6639a 100644 --- a/WebCore/platform/graphics/win/WKCACFLayer.h +++ b/WebCore/platform/graphics/win/WKCACFLayer.h @@ -41,6 +41,8 @@ #include "PlatformString.h" #include "TransformationMatrix.h" +struct WKCACFContext; + namespace WebCore { class WKCACFLayer; @@ -83,7 +85,7 @@ public: } // Makes this layer the root when the passed context is rendered - void becomeRootLayerForContext(CACFContextRef); + void becomeRootLayerForContext(WKCACFContext*); static RetainPtr<CFTypeRef> cfValue(float value) { return RetainPtr<CFTypeRef>(AdoptCF, CFNumberCreate(0, kCFNumberFloat32Type, &value)); } static RetainPtr<CFTypeRef> cfValue(const TransformationMatrix& value) diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp index 4f39b13..73cb794 100755..100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.cpp @@ -36,9 +36,7 @@ #include "WKCACFContextFlusher.h" #include "WKCACFLayer.h" #include "WebCoreInstanceHandle.h" -#include <CoreGraphics/CGSRegion.h> -#include <QuartzCore/CACFContext.h> -#include <QuartzCore/CARenderOGL.h> +#include <WebKitSystemInterface/WebKitSystemInterface.h> #include <wtf/HashMap.h> #include <wtf/OwnArrayPtr.h> #include <wtf/OwnPtr.h> @@ -108,7 +106,7 @@ private: WKCACFLayerRenderer* m_renderer; }; -typedef HashMap<CACFContextRef, WKCACFLayerRenderer*> ContextToWindowMap; +typedef HashMap<WKCACFContext*, WKCACFLayerRenderer*> ContextToWindowMap; static ContextToWindowMap& windowsForContexts() { @@ -206,7 +204,7 @@ bool WKCACFLayerRenderer::acceleratedCompositingAvailable() return available; } -void WKCACFLayerRenderer::didFlushContext(CACFContextRef context) +void WKCACFLayerRenderer::didFlushContext(WKCACFContext* context) { WKCACFLayerRenderer* window = windowsForContexts().get(context); if (!window) @@ -226,15 +224,13 @@ WKCACFLayerRenderer::WKCACFLayerRenderer(WKCACFLayerRendererClient* client) : m_client(client) , m_mightBeAbleToCreateDeviceLater(true) , m_rootLayer(WKCACFRootLayer::create(this)) - , m_context(AdoptCF, CACFContextCreate(0)) - , m_renderContext(static_cast<CARenderContext*>(CACFContextGetRenderContext(m_context.get()))) - , m_renderer(0) + , m_context(wkCACFContextCreate()) , m_hostWindow(0) , m_renderTimer(this, &WKCACFLayerRenderer::renderTimerFired) , m_backingStoreDirty(false) , m_mustResetLostDeviceBeforeRendering(false) { - windowsForContexts().set(m_context.get(), this); + windowsForContexts().set(m_context, this); // 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. @@ -250,13 +246,13 @@ WKCACFLayerRenderer::WKCACFLayerRenderer(WKCACFLayerRendererClient* client) m_rootLayer->setGeometryFlipped(true); #ifndef NDEBUG - CGColorRef debugColor = createCGColor(Color(255, 0, 0, 204)); + CGColorRef debugColor = CGColorCreateGenericRGB(1, 0, 0, 0.8); m_rootLayer->setBackgroundColor(debugColor); CGColorRelease(debugColor); #endif if (m_context) - m_rootLayer->becomeRootLayerForContext(m_context.get()); + m_rootLayer->becomeRootLayerForContext(m_context); #ifndef NDEBUG char* printTreeFlag = getenv("CA_PRINT_TREE"); @@ -267,6 +263,7 @@ WKCACFLayerRenderer::WKCACFLayerRenderer(WKCACFLayerRendererClient* client) WKCACFLayerRenderer::~WKCACFLayerRenderer() { destroyRenderer(); + wkCACFContextDestroy(m_context); } WKCACFLayer* WKCACFLayerRenderer::rootLayer() const @@ -298,7 +295,7 @@ void WKCACFLayerRenderer::setRootChildLayer(WKCACFLayer* layer) void WKCACFLayerRenderer::layerTreeDidChange() { - WKCACFContextFlusher::shared().addContext(m_context.get()); + WKCACFContextFlusher::shared().addContext(m_context); renderSoon(); } @@ -373,7 +370,7 @@ bool WKCACFLayerRenderer::createRenderer() initD3DGeometry(); - m_renderer = CARenderOGLNew(&kCARenderDX9Callbacks, m_d3dDevice.get(), 0); + wkCACFContextInitializeD3DDevice(m_context, m_d3dDevice.get()); if (IsWindow(m_hostWindow)) m_rootLayer->setBounds(bounds()); @@ -384,14 +381,10 @@ bool WKCACFLayerRenderer::createRenderer() void WKCACFLayerRenderer::destroyRenderer() { if (m_context) { - CACFContextSetLayer(m_context.get(), 0); - windowsForContexts().remove(m_context.get()); - WKCACFContextFlusher::shared().removeContext(m_context.get()); + windowsForContexts().remove(m_context); + WKCACFContextFlusher::shared().removeContext(m_context); } - if (m_renderer) - CARenderOGLDestroy(m_renderer); - m_renderer = 0; m_d3dDevice = 0; if (s_d3d) s_d3d->Release(); @@ -476,7 +469,7 @@ void WKCACFLayerRenderer::paint() render(dirtyRects); } -void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects) +void WKCACFLayerRenderer::render(const Vector<CGRect>& windowDirtyRects) { ASSERT(m_d3dDevice); @@ -499,31 +492,21 @@ void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects) CFTimeInterval t = CACurrentMediaTime(); // Give the renderer some space to use. This needs to be valid until the - // CARenderUpdateFinish() call below. + // wkCACFContextFinishUpdate() call below. char space[4096]; - CARenderUpdate* u = CARenderUpdateBegin(space, sizeof(space), t, 0, 0, &bounds); - if (!u) + if (!wkCACFContextBeginUpdate(m_context, space, sizeof(space), t, bounds, windowDirtyRects.data(), windowDirtyRects.size())) return; - CARenderContextLock(m_renderContext); - CARenderUpdateAddContext(u, m_renderContext); - CARenderContextUnlock(m_renderContext); - - for (size_t i = 0; i < dirtyRects.size(); ++i) - CARenderUpdateAddRect(u, &dirtyRects[i]); - HRESULT err = S_OK; do { - CGSRegionObj rgn = CARenderUpdateCopyRegion(u); + // FIXME: don't need to clear dirty region if layer tree is opaque. - if (!rgn) + WKCACFUpdateRectEnumerator* e = wkCACFContextCopyUpdateRectEnumerator(m_context); + if (!e) break; - // FIXME: don't need to clear dirty region if layer tree is opaque. - Vector<D3DRECT, 64> rects; - CGSRegionEnumeratorObj e = CGSRegionEnumerator(rgn); - for (const CGRect* r = CGSNextRect(e); r; r = CGSNextRect(e)) { + for (const CGRect* r = wkCACFUpdateRectEnumeratorNextRect(e); r; r = wkCACFUpdateRectEnumeratorNextRect(e)) { D3DRECT rect; rect.x1 = r->origin.x; rect.x2 = rect.x1 + r->size.width; @@ -532,8 +515,7 @@ void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects) rects.append(rect); } - CGSReleaseRegionEnumerator(e); - CGSReleaseRegion(rgn); + wkCACFUpdateRectEnumeratorRelease(e); if (rects.isEmpty()) break; @@ -541,13 +523,13 @@ void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects) m_d3dDevice->Clear(rects.size(), rects.data(), D3DCLEAR_TARGET, 0, 1.0f, 0); m_d3dDevice->BeginScene(); - CARenderOGLRender(m_renderer, u); + wkCACFContextRenderUpdate(m_context); m_d3dDevice->EndScene(); err = m_d3dDevice->Present(0, 0, 0, 0); if (err == D3DERR_DEVICELOST) { - CARenderUpdateAddRect(u, &bounds); + wkCACFContextAddUpdateRect(m_context, bounds); if (!resetDevice(LostDevice)) { // We can't reset the device right now. Try again soon. renderSoon(); @@ -556,7 +538,7 @@ void WKCACFLayerRenderer::render(const Vector<CGRect>& dirtyRects) } } while (err == D3DERR_DEVICELOST); - CARenderUpdateFinish(u); + wkCACFContextFinishUpdate(m_context); #ifndef NDEBUG if (m_printTree) @@ -598,7 +580,7 @@ void WKCACFLayerRenderer::initD3DGeometry() bool WKCACFLayerRenderer::resetDevice(ResetReason reason) { ASSERT(m_d3dDevice); - ASSERT(m_renderContext); + ASSERT(m_context); HRESULT hr = m_d3dDevice->TestCooperativeLevel(); @@ -617,10 +599,10 @@ bool WKCACFLayerRenderer::resetDevice(ResetReason reason) // We can reset the device. - // We have to purge the CARenderOGLContext whenever we reset the IDirect3DDevice9 in order to + // 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>. - CARenderOGLPurge(m_renderer); + wkCACFContextReleaseD3DResources(m_context); D3DPRESENT_PARAMETERS parameters = initialPresentationParameters(); hr = m_d3dDevice->Reset(¶meters); diff --git a/WebCore/platform/graphics/win/WKCACFLayerRenderer.h b/WebCore/platform/graphics/win/WKCACFLayerRenderer.h index 1d73b99..763fffa 100755..100644 --- a/WebCore/platform/graphics/win/WKCACFLayerRenderer.h +++ b/WebCore/platform/graphics/win/WKCACFLayerRenderer.h @@ -41,9 +41,7 @@ #include <CoreGraphics/CGGeometry.h> interface IDirect3DDevice9; -typedef struct _CACFContext* CACFContextRef; -typedef struct _CARenderContext CARenderContext; -typedef struct _CARenderOGLContext CARenderOGLContext; +struct WKCACFContext; namespace WebCore { @@ -64,7 +62,7 @@ public: ~WKCACFLayerRenderer(); static bool acceleratedCompositingAvailable(); - static void didFlushContext(CACFContextRef); + static void didFlushContext(WKCACFContext*); void setRootContents(CGImageRef); void setRootContentsAndDisplay(CGImageRef); @@ -104,9 +102,7 @@ private: COMPtr<IDirect3DDevice9> m_d3dDevice; RefPtr<WKCACFRootLayer> m_rootLayer; RefPtr<WKCACFLayer> m_rootChildLayer; - RetainPtr<CACFContextRef> m_context; - CARenderContext* m_renderContext; - CARenderOGLContext* m_renderer; + WKCACFContext* m_context; HWND m_hostWindow; Timer<WKCACFLayerRenderer> m_renderTimer; bool m_backingStoreDirty; diff --git a/WebCore/platform/graphics/win/WebLayer.cpp b/WebCore/platform/graphics/win/WebLayer.cpp index 70a522d..ecda294 100644 --- a/WebCore/platform/graphics/win/WebLayer.cpp +++ b/WebCore/platform/graphics/win/WebLayer.cpp @@ -94,7 +94,7 @@ void WebLayer::drawInContext(PlatformGraphicsContext* context) #endif if (m_owner->showRepaintCounter()) { - String text = String::format("%d", m_owner->incrementRepaintCount());; + String text = String::number(m_owner->incrementRepaintCount()); CGContextSaveGState(context); @@ -129,7 +129,7 @@ void WebLayer::drawInContext(PlatformGraphicsContext* context) font.update(0); GraphicsContext cg(context); - cg.setFillColor(Color::black, DeviceColorSpace); + cg.setFillColor(Color::black, ColorSpaceDeviceRGB); cg.drawText(font, TextRun(text), IntPoint(aBounds.origin.x + 5, aBounds.origin.y + 17)); CGContextRestoreGState(context); diff --git a/WebCore/platform/graphics/win/WebTiledLayer.cpp b/WebCore/platform/graphics/win/WebTiledLayer.cpp index 01dd6ae..4705033 100644 --- a/WebCore/platform/graphics/win/WebTiledLayer.cpp +++ b/WebCore/platform/graphics/win/WebTiledLayer.cpp @@ -201,7 +201,7 @@ void WebTiledLayer::addTile() CACFLayerInsertSublayer(m_tileParent.get(), newLayer.get(), sublayers ? CFArrayGetCount(sublayers) : 0); if (m_owner->showDebugBorders()) { - CGColorRef borderColor = createCGColor(Color(128, 0, 128, 180)); + CGColorRef borderColor = CGColorCreateGenericRGB(0.5, 0, 0.5, 0.7); CACFLayerSetBorderColor(newLayer.get(), borderColor); CGColorRelease(borderColor); CACFLayerSetBorderWidth(newLayer.get(), 2); diff --git a/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp b/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp index a91b988..a0c10fc 100644 --- a/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp +++ b/WebCore/platform/graphics/wince/GraphicsContextWinCE.cpp @@ -1,5 +1,6 @@ /* * Copyright (C) 2007-2009 Torch Mobile Inc. + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,6 +25,7 @@ #include "AffineTransform.h" #include "CharacterNames.h" #include "Font.h" +#include "GDIExtras.h" #include "GlyphBuffer.h" #include "Gradient.h" #include "GraphicsContextPrivate.h" @@ -322,8 +324,7 @@ public: if (hdc == m_dc) return; -#if !defined(NO_ALPHABLEND) - if (alphaPaint == AlphaPaintOther) { + if (alphaPaint == AlphaPaintOther && hasAlphaBlendSupport()) { ASSERT(bmp && bmp->bytes() && bmp->is32bit()); unsigned* pixels = (unsigned*)bmp->bytes(); const unsigned* const pixelsEnd = pixels + bmp->bitmapInfo().numPixels(); @@ -332,13 +333,13 @@ public: ++pixels; } } - if (m_opacity < 1. || alphaPaint == AlphaPaintOther) { + if ((m_opacity < 1. || alphaPaint == AlphaPaintOther) && hasAlphaBlendSupport()) { const BLENDFUNCTION blend = { AC_SRC_OVER, 0 , m_opacity >= 1. ? 255 : (BYTE)(m_opacity * 255) , alphaPaint == AlphaPaintNone ? 0 : AC_SRC_ALPHA }; - AlphaBlend(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, blend); + bool success = alphaBlendIfSupported(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, blend); + ASSERT_UNUSED(success, success); } else -#endif StretchBlt(m_dc, origRect.x(), origRect.y(), origRect.width(), origRect.height(), hdc, 0, 0, bmpRect.right, bmpRect.bottom, SRCCOPY); } @@ -347,7 +348,7 @@ public: Vector<GraphicsContextPlatformPrivateData> m_backupData; }; -static HPEN createPen(const Color& col, double fWidth, StrokeStyle style) +static PassOwnPtr<HPEN> createPen(const Color& col, double fWidth, StrokeStyle style) { int width = stableRound(fWidth); if (width < 1) @@ -367,12 +368,12 @@ static HPEN createPen(const Color& col, double fWidth, StrokeStyle style) break; } - return CreatePen(penStyle, width, RGB(col.red(), col.green(), col.blue())); + return adoptPtr(CreatePen(penStyle, width, RGB(col.red(), col.green(), col.blue()))); } -static inline HGDIOBJ createBrush(const Color& col) +static inline PassOwnPtr<HBRUSH> createBrush(const Color& col) { - return CreateSolidBrush(RGB(col.red(), col.green(), col.blue())); + return adoptPtr(CreateSolidBrush(RGB(col.red(), col.green(), col.blue()))); } template <typename PixelType, bool Is16bit> static void _rotateBitmap(SharedBitmap* destBmp, const SharedBitmap* sourceBmp, const RotationTransform& transform) @@ -644,41 +645,33 @@ void GraphicsContext::drawRect(const IntRect& rect) return; trRect.move(transparentDC.toShift()); - HGDIOBJ brush = 0; + OwnPtr<HBRUSH> brush; HGDIOBJ oldBrush; if (fillColor().alpha()) { brush = createBrush(fillColor()); - oldBrush = SelectObject(dc, brush); + oldBrush = SelectObject(dc, brush.get()); } else - SelectObject(dc, GetStockObject(NULL_BRUSH)); + oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); - HGDIOBJ pen = 0; + OwnPtr<HPEN> pen; HGDIOBJ oldPen; if (strokeStyle() != NoStroke) { pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - oldPen = SelectObject(dc, pen); + oldPen = SelectObject(dc, pen.get()); } else - SelectObject(dc, GetStockObject(NULL_PEN)); + oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); - if (!brush && !pen) - return; - - if (trRect.width() <= 0) - trRect.setWidth(1); - if (trRect.height() <= 0) - trRect.setHeight(1); - - Rectangle(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + if (brush || pen) { + if (trRect.width() <= 0) + trRect.setWidth(1); + if (trRect.height() <= 0) + trRect.setHeight(1); - if (pen) { - SelectObject(dc, oldPen); - DeleteObject(pen); + Rectangle(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); } - if (brush) { - SelectObject(dc, oldBrush); - DeleteObject(brush); - } + SelectObject(dc, oldPen); + SelectObject(dc, oldBrush); } void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) @@ -702,14 +695,13 @@ void GraphicsContext::drawLine(const IntPoint& point1, const IntPoint& point2) trPoint1 += transparentDC.toShift(); trPoint2 += transparentDC.toShift(); - HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - HGDIOBJ oldPen = SelectObject(dc, pen); + OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(dc, pen.get()); MoveToEx(dc, trPoint1.x(), trPoint1.y(), 0); LineTo(dc, trPoint2.x(), trPoint2.y()); SelectObject(dc, oldPen); - DeleteObject(pen); } void GraphicsContext::drawEllipse(const IntRect& rect) @@ -728,32 +720,27 @@ void GraphicsContext::drawEllipse(const IntRect& rect) return; trRect.move(transparentDC.toShift()); - HGDIOBJ brush = 0; + OwnPtr<HBRUSH> brush; HGDIOBJ oldBrush; if (fillColor().alpha()) { brush = createBrush(fillColor()); - oldBrush = SelectObject(dc, brush); + oldBrush = SelectObject(dc, brush.get()); } else - SelectObject(dc, GetStockObject(NULL_BRUSH)); - HGDIOBJ pen = 0; - HGDIOBJ oldPen; + oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); + + OwnPtr<HPEN> pen; + HGDIOBJ oldPen = 0; if (strokeStyle() != NoStroke) { pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - oldPen = SelectObject(dc, pen); + oldPen = SelectObject(dc, pen.get()); } else - SelectObject(dc, GetStockObject(NULL_PEN)); - - Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); + oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); - if (pen) { - SelectObject(dc, oldPen); - DeleteObject(pen); - } + if (brush || pen) + Ellipse(dc, trRect.x(), trRect.y(), trRect.right(), trRect.bottom()); - if (brush) { - SelectObject(dc, oldBrush); - DeleteObject(brush); - } + SelectObject(dc, oldPen); + SelectObject(dc, oldBrush); } static inline bool equalAngle(double a, double b) @@ -815,8 +802,8 @@ void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSp return; trRect.move(transparentDC.toShift()); - HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - HGDIOBJ oldPen = SelectObject(dc, pen); + OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(dc, pen.get()); double a = trRect.width() * 0.5; double b = trRect.height() * 0.5; @@ -872,7 +859,6 @@ void GraphicsContext::strokeArc(const IntRect& rect, int startAngle, int angleSp SelectClipRgn(dc, clipRgn.get()); SelectObject(dc, oldPen); - DeleteObject(pen); } void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points, bool shouldAntialias) @@ -916,36 +902,27 @@ void GraphicsContext::drawConvexPolygon(size_t npoints, const FloatPoint* points winPoints[i].y += transparentDC.toShift().height(); } - HGDIOBJ brush = 0; + OwnPtr<HBRUSH> brush; HGDIOBJ oldBrush; if (fillColor().alpha()) { brush = createBrush(fillColor()); - oldBrush = SelectObject(dc, brush); + oldBrush = SelectObject(dc, brush.get()); } else - SelectObject(dc, GetStockObject(NULL_BRUSH)); + oldBrush = SelectObject(dc, GetStockObject(NULL_BRUSH)); - HGDIOBJ pen = 0; + OwnPtr<HPEN> pen; HGDIOBJ oldPen; if (strokeStyle() != NoStroke) { pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - oldPen = SelectObject(dc, pen); + oldPen = SelectObject(dc, pen.get()); } else - SelectObject(dc, GetStockObject(NULL_PEN)); - - if (!brush && !pen) - return; - - Polygon(dc, winPoints.data(), npoints); + oldPen = SelectObject(dc, GetStockObject(NULL_PEN)); - if (pen) { - SelectObject(dc, oldPen); - DeleteObject(pen); - } + if (brush || pen) + Polygon(dc, winPoints.data(), npoints); - if (brush) { - SelectObject(dc, oldBrush); - DeleteObject(brush); - } + SelectObject(dc, oldPen); + SelectObject(dc, oldBrush); } void GraphicsContext::clipConvexPolygon(size_t numPoints, const FloatPoint* points, bool antialiased) @@ -1104,7 +1081,7 @@ void GraphicsContext::clearRect(const FloatRect& rect) return; } - fillRect(rect, Color(Color::white), DeviceColorSpace); + fillRect(rect, Color(Color::white), ColorSpaceDeviceRGB); } void GraphicsContext::strokeRect(const FloatRect& rect, float width) @@ -1124,8 +1101,8 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) return; trRect.move(transparentDC.toShift()); - HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - HGDIOBJ oldPen = SelectObject(dc, pen); + OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + HGDIOBJ oldPen = SelectObject(dc, pen.get()); int right = trRect.right() - 1; int bottom = trRect.bottom() - 1; @@ -1141,7 +1118,6 @@ void GraphicsContext::strokeRect(const FloatRect& rect, float width) Polyline(dc, intPoints, 5); SelectObject(dc, oldPen); - DeleteObject(pen); } void GraphicsContext::beginTransparencyLayer(float opacity) @@ -1286,9 +1262,9 @@ void GraphicsContext::fillRoundedRect(const IntRect& fillRect, const IntSize& to RECT rectWin = dstRect; - HGDIOBJ brush = createBrush(shadowColor); - HGDIOBJ oldBrush = SelectObject(dc, brush); - + OwnPtr<HBRUSH> brush = createBrush(shadowColor); + HGDIOBJ oldBrush = SelectObject(dc, brush.get()); + SelectObject(dc, GetStockObject(NULL_PEN)); IntPoint centerPoint = rectCenterPoint(rectWin); @@ -1324,7 +1300,6 @@ void GraphicsContext::fillRoundedRect(const IntRect& fillRect, const IntSize& to drawRoundCorner(needsNewClip, clipRect, rectWin, dc, stableRound(newBottomRight.width() * 2), stableRound(newBottomRight.height() * 2)); SelectObject(dc, oldBrush); - DeleteObject(brush); } @@ -1382,8 +1357,9 @@ void GraphicsContext::fillPath() if (!m_data->m_dc) return; + OwnPtr<HBRUSH> brush = createBrush(c); + if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) { - HGDIOBJ brush = createBrush(c); for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) { IntRect trRect = enclosingIntRect(m_data->mapRect(i->boundingRect())); trRect.inflate(1); @@ -1396,19 +1372,16 @@ void GraphicsContext::fillPath() tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height()); SelectObject(dc, GetStockObject(NULL_PEN)); - HGDIOBJ oldBrush = SelectObject(dc, brush); + HGDIOBJ oldBrush = SelectObject(dc, brush.get()); i->platformPath()->fillPath(dc, &tr); SelectObject(dc, oldBrush); } - DeleteObject(brush); } else { SelectObject(m_data->m_dc, GetStockObject(NULL_PEN)); - HGDIOBJ brush = createBrush(c); - HGDIOBJ oldBrush = SelectObject(m_data->m_dc, brush); + HGDIOBJ oldBrush = SelectObject(m_data->m_dc, brush.get()); for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) i->platformPath()->fillPath(m_data->m_dc, &m_data->m_transform); SelectObject(m_data->m_dc, oldBrush); - DeleteObject(brush); } } @@ -1422,8 +1395,9 @@ void GraphicsContext::strokePath() if (!m_data->m_dc) return; + OwnPtr<HPEN> pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); + if (m_data->m_opacity < 1.0f || m_data->hasAlpha()) { - HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) { IntRect trRect = enclosingIntRect(m_data->mapRect(i->boundingRect())); trRect.inflate(1); @@ -1436,19 +1410,16 @@ void GraphicsContext::strokePath() tr.translate(transparentDC.toShift().width(), transparentDC.toShift().height()); SelectObject(dc, GetStockObject(NULL_BRUSH)); - HGDIOBJ oldPen = SelectObject(dc, pen); + HGDIOBJ oldPen = SelectObject(dc, pen.get()); i->platformPath()->strokePath(dc, &tr); SelectObject(dc, oldPen); } - DeleteObject(pen); } else { SelectObject(m_data->m_dc, GetStockObject(NULL_BRUSH)); - HGDIOBJ pen = createPen(strokeColor(), strokeThickness(), strokeStyle()); - HGDIOBJ oldPen = SelectObject(m_data->m_dc, pen); + HGDIOBJ oldPen = SelectObject(m_data->m_dc, pen.get()); for (Vector<Path>::const_iterator i = m_data->m_paths.begin(); i != m_data->m_paths.end(); ++i) i->platformPath()->strokePath(m_data->m_dc, &m_data->m_transform); SelectObject(m_data->m_dc, oldPen); - DeleteObject(pen); } } @@ -1465,7 +1436,7 @@ void GraphicsContext::fillRect(const FloatRect& r, const Gradient* gradient) if (numStops == 1) { const Gradient::ColorStop& stop = stops.first(); Color color(stop.red, stop.green, stop.blue, stop.alpha); - fillRect(r, color, DeviceColorSpace); + fillRect(r, color, ColorSpaceDeviceRGB); return; } @@ -1555,7 +1526,7 @@ void GraphicsContext::fillRect(const FloatRect& rect) if (m_common->state.fillGradient) fillRect(rect, m_common->state.fillGradient.get()); else - fillRect(rect, fillColor(), DeviceColorSpace); + fillRect(rect, fillColor(), ColorSpaceDeviceRGB); restorePlatformState(); } diff --git a/WebCore/platform/graphics/wince/ImageWinCE.cpp b/WebCore/platform/graphics/wince/ImageWinCE.cpp index 61ec954..53b9b68 100644 --- a/WebCore/platform/graphics/wince/ImageWinCE.cpp +++ b/WebCore/platform/graphics/wince/ImageWinCE.cpp @@ -82,9 +82,9 @@ bool BitmapImage::getHBITMAPOfSize(HBITMAP bmp, LPSIZE size) IntSize imageSize = BitmapImage::size(); if (size) - drawFrameMatchingSourceSize(&gc, FloatRect(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), DeviceColorSpace, CompositeCopy); + drawFrameMatchingSourceSize(&gc, FloatRect(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight), IntSize(*size), ColorSpaceDeviceRGB, CompositeCopy); else - draw(&gc, FloatRect(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0, 0, imageSize.width(), imageSize.height()), DeviceColorSpace, CompositeCopy); + draw(&gc, FloatRect(0, 0, bmpInfo.bmWidth, bmpInfo.bmHeight), FloatRect(0, 0, imageSize.width(), imageSize.height()), ColorSpaceDeviceRGB, CompositeCopy); } SelectObject(hdc.get(), hOldBmp); diff --git a/WebCore/platform/graphics/wince/PathWinCE.cpp b/WebCore/platform/graphics/wince/PathWinCE.cpp index 4f0195c..fa4c8fb 100644 --- a/WebCore/platform/graphics/wince/PathWinCE.cpp +++ b/WebCore/platform/graphics/wince/PathWinCE.cpp @@ -123,11 +123,6 @@ bool Path::isEmpty() const return m_path->isEmpty(); } -String Path::debugString() const -{ - return m_path->debugString(); -} - void Path::apply(void* info, PathApplierFunction function) const { m_path->apply(info, function); diff --git a/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp b/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp index 80e01a9..8534f89 100644 --- a/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp +++ b/WebCore/platform/graphics/wince/PlatformPathWinCE.cpp @@ -754,43 +754,6 @@ void PlatformPath::addEllipse(const FloatRect& r) addEllipse(r.location() + radius, radius.width(), radius.height(), 0, 0, true); } -String PlatformPath::debugString() const -{ - String ret; - for (PlatformPathElements::const_iterator i(m_elements.begin()); i != m_elements.end(); ++i) { - switch (i->platformType()) { - case PlatformPathElement::PathMoveTo: - case PlatformPathElement::PathLineTo: - ret += String::format("M %f %f\n", i->pointAt(0).m_x, i->pointAt(0).m_y); - break; - case PlatformPathElement::PathArcTo: - ret += String::format("A %f %f %f %f %f %f %c\n" - , i->arcTo().m_end.m_x, i->arcTo().m_end.m_y - , i->arcTo().m_center.m_x, i->arcTo().m_center.m_y - , i->arcTo().m_radius.m_x, i->arcTo().m_radius.m_y - , i->arcTo().m_clockwise? 'Y' : 'N'); - break; - case PlatformPathElement::PathQuadCurveTo: - ret += String::format("Q %f %f %f %f\n" - , i->pointAt(0).m_x, i->pointAt(0).m_y - , i->pointAt(1).m_x, i->pointAt(1).m_y); - break; - case PlatformPathElement::PathBezierCurveTo: - ret += String::format("B %f %f %f %f %f %f\n" - , i->pointAt(0).m_x, i->pointAt(0).m_y - , i->pointAt(1).m_x, i->pointAt(1).m_y - , i->pointAt(2).m_x, i->pointAt(2).m_y); - break; - default: - ASSERT(i->platformType() == PlatformPathElement::PathCloseSubpath); - ret += "S\n"; - break; - } - } - - return ret; -} - void PlatformPath::apply(void* info, PathApplierFunction function) const { PathElement pelement; diff --git a/WebCore/platform/graphics/wince/PlatformPathWinCE.h b/WebCore/platform/graphics/wince/PlatformPathWinCE.h index 3414b04..4c86fc3 100644 --- a/WebCore/platform/graphics/wince/PlatformPathWinCE.h +++ b/WebCore/platform/graphics/wince/PlatformPathWinCE.h @@ -164,7 +164,6 @@ namespace WebCore { void addEllipse(const FloatPoint& p, float a, float b, float sar, float ear, bool anticlockwise); void addRect(const FloatRect& r); void addEllipse(const FloatRect& r); - String debugString() const; void apply(void* info, PathApplierFunction function) const; private: diff --git a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp index a39404a..30daa67 100644 --- a/WebCore/platform/graphics/wx/GraphicsContextWx.cpp +++ b/WebCore/platform/graphics/wx/GraphicsContextWx.cpp @@ -120,8 +120,8 @@ GraphicsContext::GraphicsContext(PlatformGraphicsContext* context) setPaintingDisabled(!context); if (context) { // Make sure the context starts in sync with our state. - setPlatformFillColor(fillColor(), DeviceColorSpace); - setPlatformStrokeColor(strokeColor(), DeviceColorSpace); + setPlatformFillColor(fillColor(), ColorSpaceDeviceRGB); + setPlatformStrokeColor(strokeColor(), ColorSpaceDeviceRGB); } #if USE(WXGC) m_data->context = (wxGCDC*)context; diff --git a/WebCore/platform/graphics/wx/PathWx.cpp b/WebCore/platform/graphics/wx/PathWx.cpp index 3006e27..f5355f2 100644 --- a/WebCore/platform/graphics/wx/PathWx.cpp +++ b/WebCore/platform/graphics/wx/PathWx.cpp @@ -117,12 +117,6 @@ bool Path::strokeContains(StrokeStyleApplier*, const FloatPoint&) const return false; } -String Path::debugString() const -{ - notImplemented(); - return String(); -} - Path& Path::operator=(const Path& path) { *m_path = *path.platformPath(); diff --git a/WebCore/platform/gtk/ClipboardGtk.cpp b/WebCore/platform/gtk/ClipboardGtk.cpp index e7ee432..49bcb7d 100644 --- a/WebCore/platform/gtk/ClipboardGtk.cpp +++ b/WebCore/platform/gtk/ClipboardGtk.cpp @@ -139,23 +139,13 @@ void ClipboardGtk::clearAllData() m_helper->writeClipboardContents(m_clipboard); } -static String joinURIList(Vector<KURL> uriList) -{ - if (uriList.isEmpty()) - return String(); - - String joined(uriList[0].string()); - for (size_t i = 1; i < uriList.size(); i++) { - joined.append("\r\n"); - joined.append(uriList[i].string()); - } - - return joined; -} - String ClipboardGtk::getData(const String& typeString, bool& success) const { - success = false; // Pessimism. + success = true; // According to http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html + // "The getData(format) method must return the data that is associated with the type format converted + // to ASCII lowercase, if any, and must return the empty string otherwise." Since success == false + // results in an 'undefined' return value, we always want to return success == true. This parameter + // should eventually be removed. if (policy() != ClipboardReadable || !m_dataObject) return String(); @@ -163,33 +153,14 @@ String ClipboardGtk::getData(const String& typeString, bool& success) const m_helper->getClipboardContents(m_clipboard); ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString); - if (type == ClipboardDataTypeURIList) { - if (!m_dataObject->hasURIList()) - return String(); - success = true; - return joinURIList(m_dataObject->uriList()); - } - - if (type == ClipboardDataTypeURL) { - if (!m_dataObject->hasURL()) - return String(); - success = true; + if (type == ClipboardDataTypeURIList) + return m_dataObject->uriList(); + if (type == ClipboardDataTypeURL) return m_dataObject->url(); - } - - if (type == ClipboardDataTypeMarkup) { - if (!m_dataObject->hasMarkup()) - return String(); - success = true; + if (type == ClipboardDataTypeMarkup) return m_dataObject->markup(); - } - - if (type == ClipboardDataTypeText) { - if (!m_dataObject->hasText()) - return String(); - success = true; + if (type == ClipboardDataTypeText) return m_dataObject->text(); - } return String(); } @@ -202,18 +173,8 @@ bool ClipboardGtk::setData(const String& typeString, const String& data) bool success = false; ClipboardDataType type = dataObjectTypeFromHTMLClipboardType(typeString); if (type == ClipboardDataTypeURIList || type == ClipboardDataTypeURL) { - Vector<KURL> uriList; - gchar** uris = g_uri_list_extract_uris(data.utf8().data()); - if (uris) { - gchar** currentURI = uris; - while (*currentURI) { - uriList.append(KURL(KURL(), *currentURI)); - currentURI++; - } - g_strfreev(uris); - m_dataObject->setURIList(uriList); - success = true; - } + m_dataObject->setURIList(data); + success = true; } else if (type == ClipboardDataTypeMarkup) { m_dataObject->setMarkup(data); success = true; @@ -248,9 +209,11 @@ HashSet<String> ClipboardGtk::types() const if (m_dataObject->hasURIList()) { types.add("text/uri-list"); types.add("URL"); - types.add("Files"); } + if (m_dataObject->hasFilenames()) + types.add("Files"); + return types; } @@ -263,11 +226,9 @@ PassRefPtr<FileList> ClipboardGtk::files() const m_helper->getClipboardContents(m_clipboard); RefPtr<FileList> fileList = FileList::create(); - Vector<String> fileVector(m_dataObject->files()); - - for (size_t i = 0; i < fileVector.size(); i++) - fileList->append(File::create(fileVector[i])); - + const Vector<String>& filenames = m_dataObject->filenames(); + for (size_t i = 0; i < filenames.size(); i++) + fileList->append(File::create(filenames[i])); return fileList.release(); } diff --git a/WebCore/platform/gtk/CursorGtk.cpp b/WebCore/platform/gtk/CursorGtk.cpp index d1f1293..9971bfb 100644 --- a/WebCore/platform/gtk/CursorGtk.cpp +++ b/WebCore/platform/gtk/CursorGtk.cpp @@ -28,28 +28,17 @@ #include "config.h" #include "CursorGtk.h" +#include "GtkVersioning.h" #include "Image.h" #include "IntPoint.h" +#include "PlatformRefPtrCairo.h" #include <gdk/gdk.h> #include <gtk/gtk.h> #include <wtf/Assertions.h> namespace WebCore { -static GdkPixmap* createPixmapFromBits(const unsigned char* bits, const IntSize& size) -{ - cairo_surface_t* dataSurface = cairo_image_surface_create_for_data(const_cast<unsigned char*>(bits), CAIRO_FORMAT_A1, size.width(), size.height(), size.width() / 8); - GdkPixmap* pixmap = gdk_pixmap_new(0, size.width(), size.height(), 1); - cairo_t* cr = gdk_cairo_create(pixmap); - cairo_set_source_surface(cr, dataSurface, 0, 0); - cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); - cairo_paint(cr); - cairo_destroy(cr); - cairo_surface_destroy(dataSurface); - return pixmap; -} - static PlatformRefPtr<GdkCursor> createNamedCursor(CustomCursorType cursorType) { CustomCursor cursor = CustomCursors[cursorType]; @@ -57,12 +46,17 @@ static PlatformRefPtr<GdkCursor> createNamedCursor(CustomCursorType cursorType) if (c) return c; - const GdkColor fg = { 0, 0, 0, 0 }; - const GdkColor bg = { 65535, 65535, 65535, 65535 }; IntSize cursorSize = IntSize(32, 32); - PlatformRefPtr<GdkPixmap> source = adoptPlatformRef(createPixmapFromBits(cursor.bits, cursorSize)); - PlatformRefPtr<GdkPixmap> mask = adoptPlatformRef(createPixmapFromBits(cursor.mask_bits, cursorSize)); - return adoptPlatformRef(gdk_cursor_new_from_pixmap(source.get(), mask.get(), &fg, &bg, cursor.hot_x, cursor.hot_y)); + PlatformRefPtr<cairo_surface_t> source = adoptPlatformRef(cairo_image_surface_create_for_data(const_cast<unsigned char*>(cursor.bits), CAIRO_FORMAT_A1, 32, 32, 4)); + PlatformRefPtr<cairo_surface_t> mask = adoptPlatformRef(cairo_image_surface_create_for_data(const_cast<unsigned char*>(cursor.mask_bits), CAIRO_FORMAT_A1, 32, 32, 4)); + PlatformRefPtr<cairo_surface_t> surface = adoptPlatformRef(cairo_image_surface_create(CAIRO_FORMAT_A1, 32, 32)); + PlatformRefPtr<cairo_t> cr = adoptPlatformRef(cairo_create(surface.get())); + + cairo_set_source_surface(cr.get(), source.get(), cursor.hot_x, cursor.hot_y); + cairo_mask_surface(cr.get(), mask.get(), cursor.hot_x, cursor.hot_y); + + PlatformRefPtr<GdkPixbuf> pixbuf = adoptPlatformRef(gdk_pixbuf_get_from_surface(surface.get(), 0, 0, 32, 32)); + return adoptPlatformRef(gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf.get(), 0, 0)); } static PlatformRefPtr<GdkCursor> createCustomCursor(Image* image, const IntPoint& hotSpot) diff --git a/WebCore/platform/gtk/DataObjectGtk.cpp b/WebCore/platform/gtk/DataObjectGtk.cpp index db13345..05a2da9 100644 --- a/WebCore/platform/gtk/DataObjectGtk.cpp +++ b/WebCore/platform/gtk/DataObjectGtk.cpp @@ -59,8 +59,51 @@ void DataObjectGtk::setMarkup(const String& newMarkup) m_markup = newMarkup; } +void DataObjectGtk::setURIList(const String& uriListString) +{ + m_uriList = uriListString; + + // This code is originally from: platform/chromium/ChromiumDataObject.cpp. + // FIXME: We should make this code cross-platform eventually. + + // Line separator is \r\n per RFC 2483 - however, for compatibility + // reasons we also allow just \n here. + Vector<String> uriList; + uriListString.split('\n', uriList); + + // Process the input and copy the first valid URL into the url member. + // In case no URLs can be found, subsequent calls to getData("URL") + // will get an empty string. This is in line with the HTML5 spec (see + // "The DragEvent and DataTransfer interfaces"). Also extract all filenames + // from the URI list. + bool setURL = false; + for (size_t i = 0; i < uriList.size(); ++i) { + String& line = uriList[i]; + line = line.stripWhiteSpace(); + if (line.isEmpty()) + continue; + if (line[0] == '#') + continue; + + KURL url = KURL(KURL(), line); + if (url.isValid()) { + if (!setURL) { + m_url = url; + setURL = true; + } + + GOwnPtr<GError> error; + GOwnPtr<gchar> filename(g_filename_from_uri(line.utf8().data(), 0, &error.outPtr())); + if (!error && filename) + m_filenames.append(String::fromUTF8(filename.get())); + } + } +} + void DataObjectGtk::setURL(const KURL& url, const String& label) { + m_url = url; + m_uriList = url; setText(url.string()); String actualLabel(label); @@ -75,10 +118,6 @@ void DataObjectGtk::setURL(const KURL& url, const String& label) append(markup, String::fromUTF8(escaped.get())); append(markup, "</a>"); setMarkup(String::adopt(markup)); - - Vector<KURL> uriList; - uriList.append(url); - setURIList(uriList); } void DataObjectGtk::clearText() @@ -93,34 +132,6 @@ void DataObjectGtk::clearMarkup() m_markup = ""; } -Vector<String> DataObjectGtk::files() -{ - Vector<KURL> uris(uriList()); - Vector<String> files; - - for (size_t i = 0; i < uris.size(); i++) { - KURL& uri = uris[0]; - if (!uri.isValid() || !uri.isLocalFile()) - continue; - - files.append(uri.string()); - } - - return files; -} - -String DataObjectGtk::url() -{ - Vector<KURL> uris(uriList()); - for (size_t i = 0; i < uris.size(); i++) { - KURL& uri = uris[0]; - if (uri.isValid()) - return uri; - } - - return String(); -} - String DataObjectGtk::urlLabel() { if (hasText()) @@ -132,18 +143,19 @@ String DataObjectGtk::urlLabel() return String(); } -bool DataObjectGtk::hasURL() -{ - return !url().isEmpty(); -} - void DataObjectGtk::clear() { m_text = ""; m_markup = ""; - m_uriList.clear(); + m_uriList = ""; + m_url = KURL(); m_image = 0; m_range = 0; + + // We do not clear filenames. According to the spec: "The clearData() method + // does not affect whether any files were included in the drag, so the types + // attribute's list might still not be empty after calling clearData() (it would + // still contain the "Files" string if any files were included in the drag)." } DataObjectGtk* DataObjectGtk::forClipboard(GtkClipboard* clipboard) diff --git a/WebCore/platform/gtk/DataObjectGtk.h b/WebCore/platform/gtk/DataObjectGtk.h index e6095da..f6b13fd 100644 --- a/WebCore/platform/gtk/DataObjectGtk.h +++ b/WebCore/platform/gtk/DataObjectGtk.h @@ -36,28 +36,30 @@ public: return adoptRef(new DataObjectGtk()); } - Vector<KURL> uriList() { return m_uriList; } + const KURL& url() { return m_url; } + const String& uriList() { return m_uriList; } + const Vector<String>& filenames() { return m_filenames; } GdkPixbuf* image() { return m_image.get(); } void setRange(PassRefPtr<Range> newRange) { m_range = newRange; } - void setURIList(const Vector<KURL>& newURIList) { m_uriList = newURIList; } void setImage(GdkPixbuf* newImage) { m_image = newImage; } void setDragContext(GdkDragContext* newDragContext) { m_dragContext = newDragContext; } void setURL(const KURL&, const String&); bool hasText() { return m_range || !m_text.isEmpty(); } bool hasMarkup() { return m_range || !m_markup.isEmpty(); } bool hasURIList() { return !m_uriList.isEmpty(); } + bool hasURL() { return !m_url.isEmpty() && m_url.isValid(); } + bool hasFilenames() { return !m_filenames.isEmpty(); } bool hasImage() { return m_image; } - void clearURIList() { m_uriList.clear(); } + void clearURIList() { m_uriList = ""; } + void clearURL() { m_url = KURL(); } void clearImage() { m_image = 0; } GdkDragContext* dragContext() { return m_dragContext.get(); } String text(); String markup(); - Vector<String> files(); - void setText(const String& newText); - void setMarkup(const String& newMarkup); - bool hasURL(); - String url(); + void setText(const String&); + void setMarkup(const String&); + void setURIList(const String&); String urlLabel(); void clear(); void clearText(); @@ -68,7 +70,9 @@ public: private: String m_text; String m_markup; - Vector<KURL> m_uriList; + KURL m_url; + String m_uriList; + Vector<String> m_filenames; PlatformRefPtr<GdkPixbuf> m_image; PlatformRefPtr<GdkDragContext> m_dragContext; RefPtr<Range> m_range; diff --git a/WebCore/platform/gtk/DragDataGtk.cpp b/WebCore/platform/gtk/DragDataGtk.cpp index 69966aa..42ddb16 100644 --- a/WebCore/platform/gtk/DragDataGtk.cpp +++ b/WebCore/platform/gtk/DragDataGtk.cpp @@ -37,14 +37,12 @@ bool DragData::containsColor() const bool DragData::containsFiles() const { - return !m_platformDragData->files().isEmpty(); + return m_platformDragData->hasFilenames(); } void DragData::asFilenames(Vector<String>& result) const { - Vector<String> files(m_platformDragData->files()); - for (size_t i = 0; i < files.size(); i++) - result.append(files[i]); + result = m_platformDragData->filenames(); } bool DragData::containsPlainText() const diff --git a/WebCore/platform/gtk/FileChooserGtk.cpp b/WebCore/platform/gtk/FileChooserGtk.cpp index 4600215..54763d4 100644 --- a/WebCore/platform/gtk/FileChooserGtk.cpp +++ b/WebCore/platform/gtk/FileChooserGtk.cpp @@ -34,7 +34,6 @@ #include <wtf/text/CString.h> #include <glib.h> -#include <gtk/gtk.h> namespace WebCore { diff --git a/WebCore/platform/gtk/GtkVersioning.c b/WebCore/platform/gtk/GtkVersioning.c index f5466be..071c5e5 100644 --- a/WebCore/platform/gtk/GtkVersioning.c +++ b/WebCore/platform/gtk/GtkVersioning.c @@ -52,7 +52,7 @@ void gtk_adjustment_configure(GtkAdjustment* adjustment, gdouble value, gdouble GdkDevice *getDefaultGDKPointerDevice(GdkWindow* window) { #ifndef GTK_API_VERSION_2 - GdkDeviceManager *manager = gdk_display_get_device_manager(gdk_drawable_get_display(window)); + GdkDeviceManager *manager = gdk_display_get_device_manager(gdk_window_get_display(window)); return gdk_device_manager_get_client_pointer(manager); #else return gdk_device_get_core_pointer(); @@ -98,3 +98,166 @@ const gchar* gtk_menu_item_get_label(GtkMenuItem* menuItem) return 0; } #endif // GTK_CHECK_VERSION(2, 16, 0) + +#ifdef GTK_API_VERSION_2 +static cairo_format_t +gdk_cairo_format_for_content(cairo_content_t content) +{ + switch (content) { + case CAIRO_CONTENT_COLOR: + return CAIRO_FORMAT_RGB24; + case CAIRO_CONTENT_ALPHA: + return CAIRO_FORMAT_A8; + case CAIRO_CONTENT_COLOR_ALPHA: + default: + return CAIRO_FORMAT_ARGB32; + } +} + +static cairo_surface_t* +gdk_cairo_surface_coerce_to_image(cairo_surface_t* surface, + cairo_content_t content, + int width, + int height) +{ + cairo_surface_t * copy; + cairo_t * cr; + + if (cairo_surface_get_type(surface) == CAIRO_SURFACE_TYPE_IMAGE + && cairo_surface_get_content(surface) == content + && cairo_image_surface_get_width(surface) >= width + && cairo_image_surface_get_height(surface) >= height) + return cairo_surface_reference(surface); + + copy = cairo_image_surface_create(gdk_cairo_format_for_content(content), + width, + height); + + cr = cairo_create(copy); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(cr, surface, 0, 0); + cairo_paint(cr); + cairo_destroy(cr); + + return copy; +} + +static void +convert_alpha(guchar * destData, int destStride, + guchar * srcData, int srcStride, + int srcX, int srcY, int width, int height) +{ + int x, y; + + srcData += srcStride * srcY + srcY * 4; + + for (y = 0; y < height; y++) { + guint32 * src = (guint32 *) srcData; + + for (x = 0; x < width; x++) { + guint alpha = src[x] >> 24; + + if (!alpha) { + destData[x * 4 + 0] = 0; + destData[x * 4 + 1] = 0; + destData[x * 4 + 2] = 0; + } else { + destData[x * 4 + 0] = (((src[x] & 0xff0000) >> 16) * 255 + alpha / 2) / alpha; + destData[x * 4 + 1] = (((src[x] & 0x00ff00) >> 8) * 255 + alpha / 2) / alpha; + destData[x * 4 + 2] = (((src[x] & 0x0000ff) >> 0) * 255 + alpha / 2) / alpha; + } + destData[x * 4 + 3] = alpha; + } + + srcData += srcStride; + destData += destStride; + } +} + +static void +convert_no_alpha(guchar * destData, int destStride, guchar * srcData, + int srcStride, int srcX, int srcY, + int width, int height) +{ + int x, y; + + srcData += srcStride * srcY + srcX * 4; + + for (y = 0; y < height; y++) { + guint32 * src = (guint32 *) srcData; + + for (x = 0; x < width; x++) { + destData[x * 3 + 0] = src[x] >> 16; + destData[x * 3 + 1] = src[x] >> 8; + destData[x * 3 + 2] = src[x]; + } + + srcData += srcStride; + destData += destStride; + } +} + +/** + * gdk_pixbuf_get_from_surface: + * @surface: surface to copy from + * @src_x: Source X coordinate within @surface + * @src_y: Source Y coordinate within @surface + * @width: Width in pixels of region to get + * @height: Height in pixels of region to get + * + * Transfers image data from a #cairo_surface_t and converts it to an RGB(A) + * representation inside a #GdkPixbuf. This allows you to efficiently read + * individual pixels from cairo surfaces. For #GdkWindows, use + * gdk_pixbuf_get_from_window() instead. + * + * This function will create an RGB pixbuf with 8 bits per channel. The pixbuf + * will contain an alpha channel if the @surface contains one. + * + * Return value: (transfer full): A newly-created pixbuf with a reference count + * of 1, or %NULL on error + **/ +GdkPixbuf* +gdk_pixbuf_get_from_surface(cairo_surface_t * surface, + int srcX, int srcY, + int width, int height) +{ + cairo_content_t content; + GdkPixbuf * dest; + + /* General sanity checks */ + g_return_val_if_fail(!surface, NULL); + g_return_val_if_fail(srcX >= 0 && srcY >= 0, NULL); + g_return_val_if_fail(width > 0 && height > 0, NULL); + + content = cairo_surface_get_content(surface) | CAIRO_CONTENT_COLOR; + dest = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + !!(content & CAIRO_CONTENT_ALPHA), + 8, + width, height); + + surface = gdk_cairo_surface_coerce_to_image(surface, content, srcX + width, srcY + height); + cairo_surface_flush(surface); + if (cairo_surface_status(surface) || !dest) { + cairo_surface_destroy(surface); + return NULL; + } + + if (gdk_pixbuf_get_has_alpha(dest)) + convert_alpha(gdk_pixbuf_get_pixels(dest), + gdk_pixbuf_get_rowstride(dest), + cairo_image_surface_get_data(surface), + cairo_image_surface_get_stride(surface), + srcX, srcY, + width, height); + else + convert_no_alpha(gdk_pixbuf_get_pixels(dest), + gdk_pixbuf_get_rowstride(dest), + cairo_image_surface_get_data(surface), + cairo_image_surface_get_stride(surface), + srcX, srcY, + width, height); + + cairo_surface_destroy(surface); + return dest; +} +#endif // GTK_API_VERSION_2 diff --git a/WebCore/platform/gtk/GtkVersioning.h b/WebCore/platform/gtk/GtkVersioning.h index a874e9e..b40e497 100644 --- a/WebCore/platform/gtk/GtkVersioning.h +++ b/WebCore/platform/gtk/GtkVersioning.h @@ -32,8 +32,18 @@ G_BEGIN_DECLS // Macros to avoid deprecation checking churn #ifndef GTK_API_VERSION_2 #define GDK_DISPLAY() (GDK_DISPLAY_XDISPLAY(gdk_display_get_default())) +#else +GdkPixbuf* gdk_pixbuf_get_from_surface(cairo_surface_t* surface, int srcX, int srcY, + int width, int height); #endif +#if !GTK_CHECK_VERSION(2, 24, 0) +#define gdk_window_get_display(window) gdk_drawable_get_display(window) +#ifdef GDK_DISABLE_DEPRECATED +#define gdk_window_get_visual gdk_drawable_get_visual +#endif +#endif // GTK_CHECK_VERSION(2, 24, 0) + #if !GTK_CHECK_VERSION(2, 21, 2) #define gdk_visual_get_depth(visual) (visual)->depth #define gdk_visual_get_bits_per_rgb(visual) (visual)->bits_per_rgb diff --git a/WebCore/platform/gtk/Language.cpp b/WebCore/platform/gtk/LanguageGtk.cpp index f1d5750..7d7a66d 100644 --- a/WebCore/platform/gtk/Language.cpp +++ b/WebCore/platform/gtk/LanguageGtk.cpp @@ -24,16 +24,15 @@ #include "PlatformString.h" #include <wtf/text/CString.h> -#include <gtk/gtk.h> +#include <glib.h> #include <locale.h> -#include <pango/pango.h> namespace WebCore { // Using pango_language_get_default() here is not an option, because // it doesn't support changing the locale in runtime, so it returns // always the same value. -String defaultLanguage() +String platformDefaultLanguage() { char* localeDefault = setlocale(LC_CTYPE, NULL); diff --git a/WebCore/platform/gtk/PasteboardHelper.cpp b/WebCore/platform/gtk/PasteboardHelper.cpp index 95df25f..9f75728 100644 --- a/WebCore/platform/gtk/PasteboardHelper.cpp +++ b/WebCore/platform/gtk/PasteboardHelper.cpp @@ -92,17 +92,6 @@ GtkTargetList* PasteboardHelper::targetList() const return m_targetList; } -static Vector<KURL> urisToKURLVector(gchar** uris) -{ - ASSERT(uris); - - Vector<KURL> uriList; - for (int i = 0; *(uris + i); i++) - uriList.append(KURL(KURL(), *(uris + i))); - - return uriList; -} - static String selectionDataToUTF8String(GtkSelectionData* data) { // g_strndup guards against selection data that is not null-terminated. @@ -130,11 +119,7 @@ void PasteboardHelper::getClipboardContents(GtkClipboard* clipboard) if (gtk_clipboard_wait_is_target_available(clipboard, uriListAtom)) { if (GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard, uriListAtom)) { - gchar** uris = gtk_selection_data_get_uris(data); - if (uris) { - dataObject->setURIList(urisToKURLVector(uris)); - g_strfreev(uris); - } + dataObject->setURIList(selectionDataToUTF8String(data)); gtk_selection_data_free(data); } } @@ -151,13 +136,9 @@ void PasteboardHelper::fillSelectionData(GtkSelectionData* selectionData, guint reinterpret_cast<const guchar*>(markup.get()), strlen(markup.get()) + 1); } else if (info == getIdForTargetType(TargetTypeURIList)) { - Vector<KURL> uriList(dataObject->uriList()); - gchar** uris = g_new0(gchar*, uriList.size() + 1); - for (size_t i = 0; i < uriList.size(); i++) - uris[i] = g_strdup(uriList[i].string().utf8().data()); - - gtk_selection_data_set_uris(selectionData, uris); - g_strfreev(uris); + CString uriList = dataObject->uriList().utf8(); + gtk_selection_data_set(selectionData, uriListAtom, 8, + reinterpret_cast<const guchar*>(uriList.data()), uriList.length() + 1); } else if (info == getIdForTargetType(TargetTypeNetscapeURL) && dataObject->hasURL()) { String url(dataObject->url()); @@ -209,27 +190,16 @@ void PasteboardHelper::fillDataObjectFromDropData(GtkSelectionData* data, guint else if (target == markupAtom) dataObject->setMarkup(selectionDataToUTF8String(data)); else if (target == uriListAtom) { - gchar** uris = gtk_selection_data_get_uris(data); - if (!uris) - return; - - Vector<KURL> uriList(urisToKURLVector(uris)); - dataObject->setURIList(uriList); - g_strfreev(uris); + dataObject->setURIList(selectionDataToUTF8String(data)); } else if (target == netscapeURLAtom) { String urlWithLabel(selectionDataToUTF8String(data)); - Vector<String> pieces; urlWithLabel.split("\n", pieces); // Give preference to text/uri-list here, as it can hold more // than one URI but still take the label if there is one. - if (!dataObject->hasURL()) { - Vector<KURL> uriList; - uriList.append(KURL(KURL(), pieces[0])); - dataObject->setURIList(uriList); - } - + if (!dataObject->hasURIList()) + dataObject->setURIList(pieces[0]); if (pieces.size() > 1) dataObject->setText(pieces[1]); } diff --git a/WebCore/platform/gtk/PlatformScreenGtk.cpp b/WebCore/platform/gtk/PlatformScreenGtk.cpp index 6ace728..9c70d0e 100644 --- a/WebCore/platform/gtk/PlatformScreenGtk.cpp +++ b/WebCore/platform/gtk/PlatformScreenGtk.cpp @@ -63,8 +63,7 @@ static GdkVisual* getVisual(Widget* widget) return 0; } - - return gdk_drawable_get_visual(GDK_DRAWABLE(gtk_widget_get_window(container))); + return gdk_window_get_visual(gtk_widget_get_window(container)); } int screenDepth(Widget* widget) @@ -123,7 +122,7 @@ FloatRect screenAvailableRect(Widget* widget) return screenRect(widget); GdkDrawable* rootWindow = GDK_DRAWABLE(gtk_widget_get_root_window(container)); - GdkDisplay* display = gdk_drawable_get_display(rootWindow); + GdkDisplay* display = gdk_window_get_display(rootWindow); Atom xproperty = gdk_x11_get_xatom_by_name_for_display(display, "_NET_WORKAREA"); Atom retType; diff --git a/WebCore/platform/gtk/PopupMenuGtk.cpp b/WebCore/platform/gtk/PopupMenuGtk.cpp index d3e933d..a679734 100644 --- a/WebCore/platform/gtk/PopupMenuGtk.cpp +++ b/WebCore/platform/gtk/PopupMenuGtk.cpp @@ -90,7 +90,7 @@ void PopupMenuGtk::show(const IntRect& rect, FrameView* view, int index) #ifdef GTK_API_VERSION_2 gtk_widget_size_request(GTK_WIDGET(m_popup.get()), &requisition); #else - gtk_size_request_get_size(GTK_SIZE_REQUEST(m_popup.get()), &requisition, NULL); + gtk_widget_get_preferred_size(GTK_WIDGET(m_popup.get()), &requisition, 0); #endif gtk_widget_set_size_request(GTK_WIDGET(m_popup.get()), std::max(rect.width(), requisition.width), -1); @@ -107,7 +107,7 @@ void PopupMenuGtk::show(const IntRect& rect, FrameView* view, int index) #ifdef GTK_API_VERSION_2 gtk_widget_get_child_requisition(item, &itemRequisition); #else - gtk_size_request_get_size(GTK_SIZE_REQUEST(item), &itemRequisition, NULL); + gtk_widget_get_preferred_size(item, &itemRequisition, 0); #endif m_menuPosition.setY(m_menuPosition.y() - itemRequisition.height); diff --git a/WebCore/platform/gtk/RenderThemeGtk.cpp b/WebCore/platform/gtk/RenderThemeGtk.cpp index 9329179..b2e3fd2 100644 --- a/WebCore/platform/gtk/RenderThemeGtk.cpp +++ b/WebCore/platform/gtk/RenderThemeGtk.cpp @@ -192,6 +192,7 @@ RenderThemeGtk::~RenderThemeGtk() GtkThemeParts* RenderThemeGtk::partsForDrawable(GdkDrawable* drawable) const { +#ifdef GTK_API_VERSION_2 // A null drawable represents the default screen colormap. GdkColormap* colormap = 0; if (!drawable) @@ -205,6 +206,10 @@ GtkThemeParts* RenderThemeGtk::partsForDrawable(GdkDrawable* drawable) const parts->colormap = colormap; g_hash_table_insert(m_partsTable.get(), colormap, parts); } +#else + // For GTK+ 3.0 we no longer have to worry about maintaining a set of widgets per-colormap. + static GtkThemeParts* parts = g_slice_new0(GtkThemeParts); +#endif // GTK_API_VERSION_2 return parts; } @@ -284,6 +289,7 @@ static void adjustMozillaStyle(const RenderThemeGtk* theme, RenderStyle* style, style->setPaddingBottom(Length(ypadding + bottom, Fixed)); } +#ifdef GTK_API_VERSION_2 bool RenderThemeGtk::paintMozillaGtkWidget(GtkThemeWidgetType type, GraphicsContext* context, const IntRect& rect, GtkWidgetState* widgetState, int flags, GtkTextDirection textDirection) { // Painting is disabled so just claim to have succeeded @@ -333,6 +339,18 @@ bool RenderThemeGtk::paintMozillaGtkWidget(GtkThemeWidgetType type, GraphicsCont return !success; } +#else +bool RenderThemeGtk::paintMozillaGtkWidget(GtkThemeWidgetType type, GraphicsContext* context, const IntRect& rect, GtkWidgetState* widgetState, int flags, GtkTextDirection textDirection) +{ + // Painting is disabled so just claim to have succeeded + if (context->paintingDisabled()) + return false; + + // false == success, because of awesome. + GdkRectangle paintRect = rect; + return moz_gtk_widget_paint(type, context->platformContext(), &paintRect, widgetState, flags, textDirection) != MOZ_GTK_SUCCESS; +} +#endif bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* renderObject, GraphicsContext* context, const IntRect& rect, int flags) { @@ -343,7 +361,13 @@ bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* re GtkWidgetState widgetState; widgetState.active = isPressed(renderObject); widgetState.focused = isFocused(renderObject); - widgetState.inHover = isHovered(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); @@ -362,16 +386,6 @@ bool RenderThemeGtk::paintRenderObject(GtkThemeWidgetType type, RenderObject* re return paintMozillaGtkWidget(type, context, rect, &widgetState, flags, textDirection); } -static void setButtonPadding(RenderStyle* style) -{ - // FIXME: This looks incorrect. - const int padding = 8; - style->setPaddingLeft(Length(padding, Fixed)); - style->setPaddingRight(Length(padding, Fixed)); - style->setPaddingTop(Length(padding / 2, Fixed)); - style->setPaddingBottom(Length(padding / 2, Fixed)); -} - static void setToggleSize(const RenderThemeGtk* theme, RenderStyle* style, ControlPart appearance) { // The width and height are both specified, so we shouldn't change them. @@ -426,18 +440,9 @@ bool RenderThemeGtk::paintRadio(RenderObject* o, const PaintInfo& i, const IntRe void RenderThemeGtk::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, WebCore::Element* e) const { - // FIXME: Is this condition necessary? - if (style->appearance() == PushButtonPart) { - style->resetBorder(); - style->setHeight(Length(Auto)); - style->setWhiteSpace(PRE); - setButtonPadding(style); - } else { - // FIXME: This should not be hard-coded. - style->setMinHeight(Length(14, Fixed)); - style->resetBorderTop(); - style->resetBorderBottom(); - } + // Some layout tests check explicitly that buttons ignore line-height. + if (style->appearance() == PushButtonPart) + style->setLineHeight(RenderStyle::initialLineHeight()); } bool RenderThemeGtk::paintButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) @@ -499,13 +504,26 @@ void RenderThemeGtk::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* s style->setHeight(Length(size.height(), Fixed)); } -bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* o, const PaintInfo& i, const IntRect& rect) +static IntRect centerRectVerticallyInParentInputElement(RenderObject* object, const IntRect& rect) { - GraphicsContext* context = i.context; + IntRect centeredRect(rect); + Node* input = object->node()->shadowAncestorNode(); // Get the renderer of <input> element. + if (!input->renderer()->isBox()) + return centeredRect; - static Image* searchImage = Image::loadPlatformThemeIcon(GTK_STOCK_FIND, rect.width()).releaseRef(); - context->drawImage(searchImage, DeviceColorSpace, rect); + // 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(); + centeredRect.setY(inputContentBox.y() + (inputContentBox.height() - centeredRect.height() + 1) / 2); + return centeredRect; +} +bool RenderThemeGtk::paintSearchFieldResultsDecoration(RenderObject* object, const PaintInfo& i, const IntRect& rect) +{ + static Image* searchImage = Image::loadPlatformThemeIcon(GTK_STOCK_FIND, rect.width()).releaseRef(); + IntRect centeredRect(centerRectVerticallyInParentInputElement(object, rect)); + i.context->drawImage(searchImage, ColorSpaceDeviceRGB, centeredRect); return false; } @@ -520,14 +538,12 @@ void RenderThemeGtk::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* select style->setHeight(Length(size.height(), Fixed)); } -bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* o, const PaintInfo& i, const IntRect& rect) +bool RenderThemeGtk::paintSearchFieldCancelButton(RenderObject* object, const PaintInfo& i, const IntRect& rect) { - GraphicsContext* context = i.context; - // TODO: Brightening up the image on hover is desirable here, I believe. static Image* cancelImage = Image::loadPlatformThemeIcon(GTK_STOCK_CLEAR, rect.width()).releaseRef(); - context->drawImage(cancelImage, DeviceColorSpace, rect); - + IntRect centeredRect(centerRectVerticallyInParentInputElement(object, rect)); + i.context->drawImage(cancelImage, ColorSpaceDeviceRGB, centeredRect); return false; } @@ -550,7 +566,7 @@ bool RenderThemeGtk::paintSliderTrack(RenderObject* object, const PaintInfo& inf if (part == SliderVerticalPart) gtkPart = MOZ_GTK_SCALE_VERTICAL; - return paintRenderObject(gtkPart, object, info.context, rect); + return paintRenderObject(gtkPart, object, info.context, toRenderBox(object)->absoluteContentBox()); } void RenderThemeGtk::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle* style, Element*) const @@ -655,10 +671,46 @@ double RenderThemeGtk::caretBlinkInterval() const return time / 2000.; } -void RenderThemeGtk::systemFont(int, FontDescription&) const +static double getScreenDPI() +{ + // FIXME: Really this should be the widget's screen. + GdkScreen* screen = gdk_screen_get_default(); + if (!screen) + return 96; // Default to 96 DPI. + + float dpi = gdk_screen_get_resolution(screen); + if (dpi <= 0) + return 96; + return dpi; +} + +void RenderThemeGtk::systemFont(int, FontDescription& fontDescription) const { - // If you remove this notImplemented(), replace it with an comment that explains why. - notImplemented(); + GtkSettings* settings = gtk_settings_get_default(); + if (!settings) + return; + + // This will be a font selection string like "Sans 10" so we cannot use it as the family name. + GOwnPtr<gchar> fontName; + g_object_get(settings, "gtk-font-name", &fontName.outPtr(), NULL); + + PangoFontDescription* pangoDescription = pango_font_description_from_string(fontName.get()); + if (!pangoDescription) + return; + + fontDescription.firstFamily().setFamily(pango_font_description_get_family(pangoDescription)); + + int size = pango_font_description_get_size(pangoDescription) / PANGO_SCALE; + // If the size of the font is in points, we need to convert it to pixels. + if (!pango_font_description_get_size_is_absolute(pangoDescription)) + size = size * (getScreenDPI() / 72.0); + + fontDescription.setSpecifiedSize(size); + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::NoFamily); + fontDescription.setWeight(FontWeightNormal); + fontDescription.setItalic(false); + pango_font_description_free(pangoDescription); } Color RenderThemeGtk::systemColor(int cssValueId) const @@ -753,8 +805,8 @@ String RenderThemeGtk::extraMediaControlsStyleSheet() static inline bool paintMediaButton(GraphicsContext* context, const IntRect& r, Image* image, Color panelColor, int mediaIconSize) { - context->fillRect(FloatRect(r), panelColor, DeviceColorSpace); - context->drawImage(image, DeviceColorSpace, + context->fillRect(FloatRect(r), panelColor, ColorSpaceDeviceRGB); + context->drawImage(image, ColorSpaceDeviceRGB, IntRect(r.x() + (r.width() - mediaIconSize) / 2, r.y() + (r.height() - mediaIconSize) / 2, mediaIconSize, mediaIconSize)); @@ -800,9 +852,9 @@ bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& pai { GraphicsContext* context = paintInfo.context; - context->fillRect(FloatRect(r), m_panelColor, DeviceColorSpace); + context->fillRect(FloatRect(r), m_panelColor, ColorSpaceDeviceRGB); context->fillRect(FloatRect(IntRect(r.x(), r.y() + (r.height() - m_mediaSliderHeight) / 2, - r.width(), m_mediaSliderHeight)), m_sliderColor, DeviceColorSpace); + r.width(), m_mediaSliderHeight)), m_sliderColor, ColorSpaceDeviceRGB); RenderStyle* style = o->style(); HTMLMediaElement* mediaElement = toParentMediaElement(o); @@ -859,7 +911,7 @@ bool RenderThemeGtk::paintMediaSliderTrack(RenderObject* o, const PaintInfo& pai bool RenderThemeGtk::paintMediaSliderThumb(RenderObject* o, const PaintInfo& paintInfo, const IntRect& r) { // Make the thumb nicer with rounded corners. - paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, DeviceColorSpace); + paintInfo.context->fillRoundedRect(r, IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), IntSize(3, 3), m_sliderThumbColor, ColorSpaceDeviceRGB); return false; } #endif diff --git a/WebCore/platform/gtk/ScrollbarThemeGtk.cpp b/WebCore/platform/gtk/ScrollbarThemeGtk.cpp index 2e942fe..19b897c 100644 --- a/WebCore/platform/gtk/ScrollbarThemeGtk.cpp +++ b/WebCore/platform/gtk/ScrollbarThemeGtk.cpp @@ -221,14 +221,8 @@ void ScrollbarThemeGtk::paintTrackBackground(GraphicsContext* context, Scrollbar void ScrollbarThemeGtk::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) { + // This is unused by the moz_gtk_scrollecd_window_paint. GtkWidgetState state; - state.focused = FALSE; - state.isDefault = FALSE; - state.canDefault = FALSE; - state.disabled = FALSE; - state.active = TRUE; - state.inHover = FALSE; - IntRect fullScrollbarRect = IntRect(scrollbar->x(), scrollbar->y(), scrollbar->width(), scrollbar->height()); static_cast<RenderThemeGtk*>(RenderTheme::defaultTheme().get())->paintMozillaGtkWidget(MOZ_GTK_SCROLLED_WINDOW, context, fullScrollbarRect, &state, 0); } @@ -303,7 +297,6 @@ bool ScrollbarThemeGtk::paint(Scrollbar* scrollbar, GraphicsContext* graphicsCon scrollMask |= ThumbPart; } - // Paint the scrollbar background (only used by custom CSS scrollbars). paintScrollbarBackground(graphicsContext, scrollbar); if (scrollMask & TrackBGPart) diff --git a/WebCore/platform/gtk/gtk2drawing.c b/WebCore/platform/gtk/gtk2drawing.c index fd770d2..2f2edb6 100644 --- a/WebCore/platform/gtk/gtk2drawing.c +++ b/WebCore/platform/gtk/gtk2drawing.c @@ -44,6 +44,11 @@ * 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" @@ -52,7 +57,6 @@ #define XTHICKNESS(style) (style->xthickness) #define YTHICKNESS(style) (style->ythickness) -#define WINDOW_IS_MAPPED(window) ((window) && GDK_IS_WINDOW(window) && gdk_window_is_visible(window)) static GtkThemeParts *gParts = NULL; static style_prop_t style_prop_func; @@ -122,26 +126,6 @@ ensure_button_widget() } static gint -ensure_hpaned_widget() -{ - if (!gParts->hpanedWidget) { - gParts->hpanedWidget = gtk_hpaned_new(); - setup_widget_prototype(gParts->hpanedWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_vpaned_widget() -{ - if (!gParts->vpanedWidget) { - gParts->vpanedWidget = gtk_vpaned_new(); - setup_widget_prototype(gParts->vpanedWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint ensure_toggle_button_widget() { if (!gParts->toggleButtonWidget) { @@ -201,16 +185,6 @@ ensure_scrollbar_widget() } static gint -ensure_spin_widget() -{ - if (!gParts->spinWidget) { - gParts->spinWidget = gtk_spin_button_new(NULL, 1, 0); - setup_widget_prototype(gParts->spinWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint ensure_scale_widget() { if (!gParts->hScaleWidget) { @@ -337,171 +311,6 @@ ensure_combo_box_widgets() return MOZ_GTK_SUCCESS; } -/* We need to have pointers to the inner widgets (entry, button, arrow) of - * the ComboBoxEntry to get the correct rendering from theme engines which - * special cases their look. Since the inner layout can change, we ask GTK - * to NULL our pointers when they are about to become invalid because the - * corresponding widgets don't exist anymore. It's the role of - * g_object_add_weak_pointer(). - * Note that if we don't find the inner widgets (which shouldn't happen), we - * fallback to use generic "non-inner" widgets, and they don't need that kind - * of weak pointer since they are explicit children of gParts->protoWindow and as - * such GTK holds a strong reference to them. */ -static void -moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, - gpointer client_data) -{ - if (GTK_IS_TOGGLE_BUTTON(widget)) { - gParts->comboBoxEntryButtonWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer) &gParts->comboBoxEntryButtonWidget); - } else if (GTK_IS_ENTRY(widget)) { - gParts->comboBoxEntryTextareaWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer) &gParts->comboBoxEntryTextareaWidget); - } else - return; - gtk_widget_realize(widget); - g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); -} - -static void -moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) -{ - if (GTK_IS_ARROW(widget)) { - gParts->comboBoxEntryArrowWidget = widget; - g_object_add_weak_pointer(G_OBJECT(widget), - (gpointer) &gParts->comboBoxEntryArrowWidget); - gtk_widget_realize(widget); - g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } -} - -static gint -ensure_combo_box_entry_widgets() -{ - GtkWidget* buttonChild; - - if (gParts->comboBoxEntryTextareaWidget && - gParts->comboBoxEntryButtonWidget && - gParts->comboBoxEntryArrowWidget) - return MOZ_GTK_SUCCESS; - - /* Create a ComboBoxEntry if needed */ - if (!gParts->comboBoxEntryWidget) { - gParts->comboBoxEntryWidget = gtk_combo_box_entry_new(); - setup_widget_prototype(gParts->comboBoxEntryWidget); - } - - /* Get its inner Entry and Button */ - gtk_container_forall(GTK_CONTAINER(gParts->comboBoxEntryWidget), - moz_gtk_get_combo_box_entry_inner_widgets, - NULL); - - if (!gParts->comboBoxEntryTextareaWidget) { - ensure_entry_widget(); - gParts->comboBoxEntryTextareaWidget = gParts->entryWidget; - } - - if (gParts->comboBoxEntryButtonWidget) { - /* Get the Arrow inside the Button */ - buttonChild = gtk_bin_get_child(GTK_BIN(gParts->comboBoxEntryButtonWidget)); - if (GTK_IS_HBOX(buttonChild)) { - /* appears-as-list = FALSE, cell-view = TRUE; the button - * contains an hbox. This hbox is there because ComboBoxEntry - * inherits from ComboBox which needs to place a cell renderer, - * a separator, and an arrow in the button when appears-as-list - * is FALSE. Here the hbox should only contain an arrow, since - * a ComboBoxEntry doesn't need all those widgets in the - * button. */ - gtk_container_forall(GTK_CONTAINER(buttonChild), - moz_gtk_get_combo_box_entry_arrow, - NULL); - } else if(GTK_IS_ARROW(buttonChild)) { - /* appears-as-list = TRUE, or cell-view = FALSE; - * the button only contains an arrow */ - gParts->comboBoxEntryArrowWidget = buttonChild; - g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) - &gParts->comboBoxEntryArrowWidget); - gtk_widget_realize(gParts->comboBoxEntryArrowWidget); - g_object_set_data(G_OBJECT(gParts->comboBoxEntryArrowWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - } else { - /* Shouldn't be reached with current internal gtk implementation; - * we use a generic toggle button as last resort fallback to avoid - * crashing. */ - ensure_toggle_button_widget(); - gParts->comboBoxEntryButtonWidget = gParts->toggleButtonWidget; - } - - if (!gParts->comboBoxEntryArrowWidget) { - /* Shouldn't be reached with current internal gtk implementation; - * we gParts->buttonArrowWidget as last resort fallback to avoid - * crashing. */ - ensure_button_arrow_widget(); - gParts->comboBoxEntryArrowWidget = gParts->buttonArrowWidget; - } - - return MOZ_GTK_SUCCESS; -} - - -static gint -ensure_handlebox_widget() -{ - if (!gParts->handleBoxWidget) { - gParts->handleBoxWidget = gtk_handle_box_new(); - setup_widget_prototype(gParts->handleBoxWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_toolbar_widget() -{ - if (!gParts->toolbarWidget) { - ensure_handlebox_widget(); - gParts->toolbarWidget = gtk_toolbar_new(); - gtk_container_add(GTK_CONTAINER(gParts->handleBoxWidget), gParts->toolbarWidget); - gtk_widget_realize(gParts->toolbarWidget); - g_object_set_data(G_OBJECT(gParts->toolbarWidget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_toolbar_separator_widget() -{ - if (!gParts->toolbarSeparatorWidget) { - ensure_toolbar_widget(); - gParts->toolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new()); - setup_widget_prototype(gParts->toolbarSeparatorWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_tooltip_widget() -{ - if (!gParts->tooltipWidget) { - gParts->tooltipWidget = gtk_window_new(GTK_WINDOW_POPUP); - gtk_widget_realize(gParts->tooltipWidget); - moz_gtk_set_widget_name(gParts->tooltipWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_tab_widget() -{ - if (!gParts->tabWidget) { - gParts->tabWidget = gtk_notebook_new(); - setup_widget_prototype(gParts->tabWidget); - } - return MOZ_GTK_SUCCESS; -} - static gint ensure_progress_widget() { @@ -513,200 +322,6 @@ ensure_progress_widget() } static gint -ensure_statusbar_widget() -{ - if (!gParts->statusbarWidget) { - gParts->statusbarWidget = gtk_statusbar_new(); - setup_widget_prototype(gParts->statusbarWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_frame_widget() -{ - if (!gParts->frameWidget) { - ensure_statusbar_widget(); - gParts->frameWidget = gtk_frame_new(NULL); - gtk_container_add(GTK_CONTAINER(gParts->statusbarWidget), gParts->frameWidget); - gtk_widget_realize(gParts->frameWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_menu_bar_widget() -{ - if (!gParts->menuBarWidget) { - gParts->menuBarWidget = gtk_menu_bar_new(); - setup_widget_prototype(gParts->menuBarWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_menu_bar_item_widget() -{ - if (!gParts->menuBarItemWidget) { - ensure_menu_bar_widget(); - gParts->menuBarItemWidget = gtk_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(gParts->menuBarWidget), - gParts->menuBarItemWidget); - gtk_widget_realize(gParts->menuBarItemWidget); - g_object_set_data(G_OBJECT(gParts->menuBarItemWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_menu_popup_widget() -{ - if (!gParts->menuPopupWidget) { - ensure_menu_bar_item_widget(); - gParts->menuPopupWidget = gtk_menu_new(); - gtk_menu_item_set_submenu(GTK_MENU_ITEM(gParts->menuBarItemWidget), - gParts->menuPopupWidget); - gtk_widget_realize(gParts->menuPopupWidget); - g_object_set_data(G_OBJECT(gParts->menuPopupWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_menu_item_widget() -{ - if (!gParts->menuItemWidget) { - ensure_menu_popup_widget(); - gParts->menuItemWidget = gtk_menu_item_new_with_label("M"); - gtk_menu_shell_append(GTK_MENU_SHELL(gParts->menuPopupWidget), - gParts->menuItemWidget); - gtk_widget_realize(gParts->menuItemWidget); - g_object_set_data(G_OBJECT(gParts->menuItemWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_image_menu_item_widget() -{ - if (!gParts->imageMenuItemWidget) { - ensure_menu_popup_widget(); - gParts->imageMenuItemWidget = gtk_image_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(gParts->menuPopupWidget), - gParts->imageMenuItemWidget); - gtk_widget_realize(gParts->imageMenuItemWidget); - g_object_set_data(G_OBJECT(gParts->imageMenuItemWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_menu_separator_widget() -{ - if (!gParts->menuSeparatorWidget) { - ensure_menu_popup_widget(); - gParts->menuSeparatorWidget = gtk_separator_menu_item_new(); - gtk_menu_shell_append(GTK_MENU_SHELL(gParts->menuPopupWidget), - gParts->menuSeparatorWidget); - gtk_widget_realize(gParts->menuSeparatorWidget); - g_object_set_data(G_OBJECT(gParts->menuSeparatorWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_check_menu_item_widget() -{ - if (!gParts->checkMenuItemWidget) { - ensure_menu_popup_widget(); - gParts->checkMenuItemWidget = gtk_check_menu_item_new_with_label("M"); - gtk_menu_shell_append(GTK_MENU_SHELL(gParts->menuPopupWidget), - gParts->checkMenuItemWidget); - gtk_widget_realize(gParts->checkMenuItemWidget); - g_object_set_data(G_OBJECT(gParts->checkMenuItemWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_tree_view_widget() -{ - if (!gParts->treeViewWidget) { - gParts->treeViewWidget = gtk_tree_view_new(); - setup_widget_prototype(gParts->treeViewWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_tree_header_cell_widget() -{ - if(!gParts->treeHeaderCellWidget) { - /* - * Some GTK engines paint the first and last cell - * of a TreeView header with a highlight. - * Since we do not know where our widget will be relative - * to the other buttons in the TreeView header, we must - * paint it as a button that is between two others, - * thus ensuring it is neither the first or last button - * in the header. - * GTK doesn't give us a way to do this explicitly, - * so we must paint with a button that is between two - * others. - */ - - GtkTreeViewColumn* firstTreeViewColumn; - GtkTreeViewColumn* lastTreeViewColumn; - - ensure_tree_view_widget(); - - /* Create and append our three columns */ - firstTreeViewColumn = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); - gtk_tree_view_append_column(GTK_TREE_VIEW(gParts->treeViewWidget), firstTreeViewColumn); - - gParts->middleTreeViewColumn = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(gParts->middleTreeViewColumn, "M"); - gtk_tree_view_append_column(GTK_TREE_VIEW(gParts->treeViewWidget), - gParts->middleTreeViewColumn); - - lastTreeViewColumn = gtk_tree_view_column_new(); - gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); - gtk_tree_view_append_column(GTK_TREE_VIEW(gParts->treeViewWidget), lastTreeViewColumn); - -#ifdef GTK_API_VERSION_2 - /* Use the middle column's header for our button */ - gParts->treeHeaderCellWidget = gParts->middleTreeViewColumn->button; - gParts->treeHeaderSortArrowWidget = gParts->middleTreeViewColumn->arrow; -#else - gParts->treeHeaderCellWidget = gtk_button_new(); - gParts->treeHeaderSortArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_NONE); -#endif - g_object_set_data(G_OBJECT(gParts->treeHeaderCellWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - g_object_set_data(G_OBJECT(gParts->treeHeaderSortArrowWidget), - "transparent-bg-hint", GINT_TO_POINTER(TRUE)); - } - return MOZ_GTK_SUCCESS; -} - -static gint -ensure_expander_widget() -{ - if (!gParts->expanderWidget) { - gParts->expanderWidget = gtk_expander_new("M"); - setup_widget_prototype(gParts->expanderWidget); - } - return MOZ_GTK_SUCCESS; -} - -static gint ensure_scrolled_window_widget() { if (!gParts->scrolledWindowWidget) { @@ -729,7 +344,6 @@ ConvertGtkState(GtkWidgetState* state) return GTK_STATE_NORMAL; } -#ifdef GTK_API_VERSION_2 static gint TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin) { @@ -739,12 +353,10 @@ TSOffsetStyleGCArray(GdkGC** gcs, gint xorigin, gint yorigin) gdk_gc_set_ts_origin(gcs[i], xorigin, yorigin); return MOZ_GTK_SUCCESS; } -#endif static gint TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint yorigin) { -#ifdef GTK_API_VERSION_2 TSOffsetStyleGCArray(style->fg_gc, xorigin, yorigin); TSOffsetStyleGCArray(style->bg_gc, xorigin, yorigin); TSOffsetStyleGCArray(style->light_gc, xorigin, yorigin); @@ -754,7 +366,6 @@ TSOffsetStyleGCs(GtkStyle* style, gint xorigin, gint 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); -#endif return MOZ_GTK_SUCCESS; } @@ -774,28 +385,18 @@ moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect, moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); - if (WINDOW_IS_MAPPED(drawable)) { - gdk_window_set_back_pixmap(drawable, NULL, TRUE); - gdk_window_clear_area(drawable, cliprect->x, cliprect->y, - cliprect->width, cliprect->height); - } - gtk_widget_set_state(widget, button_state); gtk_widget_set_direction(widget, direction); -#ifdef GTK_API_VERSION_2 if (state->isDefault) GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_DEFAULT); -#endif gtk_button_set_relief(GTK_BUTTON(widget), relief); /* Some theme engines love to cause us pain in that gtk_paint_focus is a no-op on buttons and button-like widgets. They only listen to this flag. */ -#ifdef GTK_API_VERSION_2 if (state->focused && !state->disabled) GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); -#endif if (!interior_focus && state->focused) { x += focus_width + focus_pad; @@ -842,10 +443,8 @@ moz_gtk_button_paint(GdkDrawable* drawable, GdkRectangle* rect, widget, "button", x, y, width, height); } -#ifdef GTK_API_VERSION_2 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_DEFAULT); GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); -#endif return MOZ_GTK_SUCCESS; } @@ -912,19 +511,6 @@ moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, } gint -moz_gtk_splitter_get_metrics(gint orientation, gint* size) -{ - if (orientation == GTK_ORIENTATION_HORIZONTAL) { - ensure_hpaned_widget(); - gtk_widget_style_get(gParts->hpanedWidget, "handle_size", size, NULL); - } else { - ensure_vpaned_widget(); - gtk_widget_style_get(gParts->vpanedWidget, "handle_size", size, NULL); - } - return MOZ_GTK_SUCCESS; -} - -gint moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border) { static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; @@ -1098,8 +684,6 @@ static gint moz_gtk_scrolled_window_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state) { - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = (state->active) ? GTK_SHADOW_IN : GTK_SHADOW_OUT; GtkStyle* style; GtkAllocation allocation; GtkWidget* widget; @@ -1116,9 +700,9 @@ moz_gtk_scrolled_window_paint(GdkDrawable* drawable, GdkRectangle* rect, style = gtk_widget_get_style(widget); TSOffsetStyleGCs(style, rect->x - 1, rect->y - 1); - gtk_paint_box(style, drawable, state_type, shadow_type, cliprect, - widget, "scrolled_window", rect->x - 1, rect->y - 1, - rect->width + 2, rect->height + 2); + 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; } @@ -1265,9 +849,7 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, GtkStyle* style; GtkScrollbar *scrollbar; GtkAdjustment *adj; -#ifdef GTK_API_VERSION_2 gboolean activate_slider; -#endif ensure_scrollbar_widget(); @@ -1319,7 +901,6 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); -#ifdef GTK_API_VERSION_2 gtk_widget_style_get(GTK_WIDGET(scrollbar), "activate-slider", &activate_slider, NULL); @@ -1327,7 +908,6 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, shadow_type = GTK_SHADOW_IN; state_type = GTK_STATE_ACTIVE; } -#endif TSOffsetStyleGCs(style, rect->x, rect->y); @@ -1341,59 +921,6 @@ moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, } static gint -moz_gtk_spin_paint(GdkDrawable* drawable, GdkRectangle* rect, - GtkTextDirection direction) -{ - GtkStyle* style; - - ensure_spin_widget(); - gtk_widget_set_direction(gParts->spinWidget, direction); - style = gtk_widget_get_style(gParts->spinWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_IN, NULL, - gParts->spinWidget, "spinbutton", - rect->x, rect->y, rect->width, rect->height); - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_spin_updown_paint(GdkDrawable* drawable, GdkRectangle* rect, - gboolean isDown, GtkWidgetState* state, - GtkTextDirection direction) -{ - GdkRectangle arrow_rect; - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = state_type == GTK_STATE_ACTIVE ? - GTK_SHADOW_IN : GTK_SHADOW_OUT; - GtkStyle* style; - - ensure_spin_widget(); - style = gtk_widget_get_style(gParts->spinWidget); - gtk_widget_set_direction(gParts->spinWidget, direction); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_box(style, drawable, state_type, shadow_type, NULL, gParts->spinWidget, - isDown ? "spinbutton_down" : "spinbutton_up", - rect->x, rect->y, rect->width, rect->height); - - /* hard code these values */ - arrow_rect.width = 6; - arrow_rect.height = 6; - arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; - arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; - arrow_rect.y += isDown ? -1 : 1; - - gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL, - gParts->spinWidget, "spinbutton", - isDown ? GTK_ARROW_DOWN : GTK_ARROW_UP, TRUE, - arrow_rect.x, arrow_rect.y, - arrow_rect.width, arrow_rect.height); - - return MOZ_GTK_SUCCESS; -} - -static gint moz_gtk_scale_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state, GtkOrientation flags, GtkTextDirection direction) @@ -1471,76 +998,6 @@ moz_gtk_scale_thumb_paint(GdkDrawable* drawable, GdkRectangle* rect, } static gint -moz_gtk_gripper_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) -{ - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type; - GtkStyle* style; - - ensure_handlebox_widget(); - gtk_widget_set_direction(gParts->handleBoxWidget, direction); - - style = gtk_widget_get_style(gParts->handleBoxWidget); - shadow_type = gtk_handle_box_get_shadow_type(GTK_HANDLE_BOX(gParts->handleBoxWidget)); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_box(style, drawable, state_type, shadow_type, cliprect, - gParts->handleBoxWidget, "handlebox_bin", rect->x, rect->y, - rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_hpaned_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state) -{ - GtkStateType hpaned_state = ConvertGtkState(state); - - ensure_hpaned_widget(); - gtk_paint_handle(gtk_widget_get_style(gParts->hpanedWidget), drawable, hpaned_state, - GTK_SHADOW_NONE, cliprect, gParts->hpanedWidget, "paned", - rect->x, rect->y, rect->width, rect->height, - GTK_ORIENTATION_VERTICAL); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_vpaned_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state) -{ - GtkStateType vpaned_state = ConvertGtkState(state); - - ensure_vpaned_widget(); - gtk_paint_handle(gtk_widget_get_style(gParts->vpanedWidget), drawable, vpaned_state, - GTK_SHADOW_NONE, cliprect, gParts->vpanedWidget, "paned", - rect->x, rect->y, rect->width, rect->height, - GTK_ORIENTATION_HORIZONTAL); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_caret_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GdkRectangle location = *rect; - if (direction == GTK_TEXT_DIR_RTL) { - /* gtk_draw_insertion_cursor ignores location.width */ - location.x = rect->x + rect->width; - } - - ensure_entry_widget(); - gtk_draw_insertion_cursor(gParts->entryWidget, drawable, cliprect, - &location, TRUE, direction, FALSE); - - return MOZ_GTK_SUCCESS; -} - -static gint moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state, GtkWidget* widget, GtkTextDirection direction) @@ -1583,17 +1040,8 @@ moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect, if (theme_honors_transparency) { g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); } else { -#ifndef GTK_API_VERSION_2 - cairo_t* cr = gdk_cairo_create(drawable); - gdk_cairo_set_source_color(cr, (const GdkColor*)&style->base[bg_state]); - cairo_pattern_set_extend (cairo_get_source(cr), CAIRO_EXTEND_REPEAT); - gdk_cairo_rectangle(cr, cliprect); - cairo_fill(cr); - cairo_destroy(cr); -#else gdk_draw_rectangle(drawable, style->base_gc[bg_state], TRUE, cliprect->x, cliprect->y, cliprect->width, cliprect->height); -#endif g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(FALSE)); } @@ -1620,9 +1068,7 @@ moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect, if (state->focused && !state->disabled) { /* This will get us the lit borders that focused textboxes enjoy on * some themes. */ -#ifdef GTK_API_VERSION_2 GTK_WIDGET_SET_FLAGS(widget, GTK_HAS_FOCUS); -#endif if (!interior_focus) { /* Indent the border a little bit if we have exterior focus @@ -1646,152 +1092,12 @@ moz_gtk_entry_paint(GdkDrawable* drawable, GdkRectangle* rect, /* Now unset the focus flag. We don't want other entries to look * like they're focused too! */ -#ifdef GTK_API_VERSION_2 GTK_WIDGET_UNSET_FLAGS(widget, GTK_HAS_FOCUS); -#endif } return MOZ_GTK_SUCCESS; } -static gint -moz_gtk_treeview_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) -{ - gint xthickness, ythickness; - - GtkStyle *style, *treeview_style; - GtkStateType state_type; - - ensure_tree_view_widget(); - ensure_scrolled_window_widget(); - - gtk_widget_set_direction(gParts->treeViewWidget, direction); - gtk_widget_set_direction(gParts->scrolledWindowWidget, direction); - - /* only handle disabled and normal states, otherwise the whole background - * area will be painted differently with other states */ - state_type = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; - - /* In GTK the treeview sets the background of the window - * which contains the cells to the treeview base color. - * If we don't set it here the background color will not be correct.*/ - gtk_widget_modify_bg(gParts->treeViewWidget, state_type, - >k_widget_get_style(gParts->treeViewWidget)->base[state_type]); - - style = gtk_widget_get_style(gParts->scrolledWindowWidget); - xthickness = XTHICKNESS(style); - ythickness = YTHICKNESS(style); - - treeview_style = gtk_widget_get_style(gParts->treeViewWidget); - TSOffsetStyleGCs(treeview_style, rect->x, rect->y); - TSOffsetStyleGCs(style, rect->x, rect->y); - - gtk_paint_flat_box(treeview_style, drawable, state_type, - GTK_SHADOW_NONE, cliprect, gParts->treeViewWidget, "treeview", - rect->x + xthickness, rect->y + ythickness, - rect->width - 2 * xthickness, - rect->height - 2 * ythickness); - - 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_tree_header_cell_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - gboolean isSorted, GtkTextDirection direction) -{ - gtk_tree_view_column_set_sort_indicator(gParts->middleTreeViewColumn, - isSorted); - - moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL, - gParts->treeHeaderCellWidget, direction); - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_tree_header_sort_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, - GtkWidgetState* state, GtkArrowType flags, - GtkTextDirection direction) -{ - GdkRectangle arrow_rect; - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = GTK_SHADOW_IN; - GtkArrowType arrow_type = flags; - GtkStyle* style; - - ensure_tree_header_cell_widget(); - gtk_widget_set_direction(gParts->treeHeaderSortArrowWidget, direction); - - /* hard code these values */ - arrow_rect.width = 11; - arrow_rect.height = 11; - arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; - arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; - - style = gtk_widget_get_style(gParts->treeHeaderSortArrowWidget); - TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y); - - gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, - gParts->treeHeaderSortArrowWidget, "arrow", arrow_type, TRUE, - arrow_rect.x, arrow_rect.y, - arrow_rect.width, arrow_rect.height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_treeview_expander_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkExpanderStyle expander_state, - GtkTextDirection direction) -{ - GtkStyle *style; - GtkStateType state_type; - - ensure_tree_view_widget(); - gtk_widget_set_direction(gParts->treeViewWidget, direction); - - style = gtk_widget_get_style(gParts->treeViewWidget); - - /* Because the frame we get is of the entire treeview, we can't get the precise - * event state of one expander, thus rendering hover and active feedback useless. */ - state_type = state->disabled ? GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_expander(style, drawable, state_type, cliprect, gParts->treeViewWidget, "treeview", - rect->x + rect->width / 2, rect->y + rect->height / 2, expander_state); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_expander_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkExpanderStyle expander_state, - GtkTextDirection direction) -{ - GtkStyle *style; - GtkStateType state_type = ConvertGtkState(state); - - ensure_expander_widget(); - gtk_widget_set_direction(gParts->expanderWidget, direction); - - style = gtk_widget_get_style(gParts->expanderWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_expander(style, drawable, state_type, cliprect, gParts->expanderWidget, "expander", - rect->x + rect->width / 2, rect->y + rect->height / 2, expander_state); - - return MOZ_GTK_SUCCESS; -} - static gint moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state, @@ -1816,11 +1122,7 @@ moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect, rect, &arrow_rect, direction, ishtml); /* Now arrow_rect contains the inner rect ; we want to correct the width * to what the arrow needs (see gtk_combo_box_size_allocate) */ -#ifdef GTK_API_VERSION_2 gtk_widget_size_request(gParts->comboBoxArrowWidget, &arrow_req); -#else - gtk_size_request_get_size(GTK_SIZE_REQUEST(gParts->comboBoxArrowWidget), &arrow_req, NULL); -#endif if (direction == GTK_TEXT_DIR_LTR) arrow_rect.x += arrow_rect.width - arrow_req.width; arrow_rect.width = arrow_req.width; @@ -1878,323 +1180,6 @@ moz_gtk_combo_box_paint(GdkDrawable* drawable, GdkRectangle* rect, } static gint -moz_gtk_downarrow_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state) -{ - GtkStyle* style; - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - GdkRectangle arrow_rect; - - ensure_button_arrow_widget(); - style = gtk_widget_get_style(gParts->buttonArrowWidget); - - calculate_arrow_rect(gParts->buttonArrowWidget, rect, &arrow_rect, - GTK_TEXT_DIR_LTR); - - TSOffsetStyleGCs(style, arrow_rect.x, arrow_rect.y); - gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, - gParts->buttonArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, - arrow_rect.x, arrow_rect.y, arrow_rect.width, arrow_rect.height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_combo_box_entry_button_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, - GtkWidgetState* state, - gboolean input_focus, - GtkTextDirection direction) -{ - gint x_displacement, y_displacement; - GdkRectangle arrow_rect, real_arrow_rect; - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - GtkStyle* style; - - ensure_combo_box_entry_widgets(); - - if (input_focus) { - /* Some themes draw a complementary focus ring for the dropdown button - * when the dropdown entry has focus */ -#ifdef GTK_API_VERSION_2 - GTK_WIDGET_SET_FLAGS(gParts->comboBoxEntryTextareaWidget, GTK_HAS_FOCUS); -#endif - } - - moz_gtk_button_paint(drawable, rect, cliprect, state, GTK_RELIEF_NORMAL, - gParts->comboBoxEntryButtonWidget, direction); - -#ifdef GTK_API_VERSION_2 - if (input_focus) - GTK_WIDGET_UNSET_FLAGS(gParts->comboBoxEntryTextareaWidget, GTK_HAS_FOCUS); -#endif - - calculate_button_inner_rect(gParts->comboBoxEntryButtonWidget, - rect, &arrow_rect, direction, FALSE); - if (state_type == GTK_STATE_ACTIVE) { - gtk_widget_style_get(gParts->comboBoxEntryButtonWidget, - "child-displacement-x", &x_displacement, - "child-displacement-y", &y_displacement, - NULL); - arrow_rect.x += x_displacement; - arrow_rect.y += y_displacement; - } - - calculate_arrow_rect(gParts->comboBoxEntryArrowWidget, - &arrow_rect, &real_arrow_rect, direction); - - style = gtk_widget_get_style(gParts->comboBoxEntryArrowWidget); - TSOffsetStyleGCs(style, real_arrow_rect.x, real_arrow_rect.y); - - gtk_paint_arrow(style, drawable, state_type, shadow_type, cliprect, - gParts->comboBoxEntryArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, - real_arrow_rect.x, real_arrow_rect.y, - real_arrow_rect.width, real_arrow_rect.height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_container_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - gboolean isradio, GtkTextDirection direction) -{ - GtkStateType state_type = ConvertGtkState(state); - GtkStyle* style; - GtkWidget *widget; - gboolean interior_focus; - gint focus_width, focus_pad; - - if (isradio) { - ensure_radiobutton_widget(); - widget = gParts->radiobuttonWidget; - } else { - ensure_checkbox_widget(); - widget = gParts->checkboxWidget; - } - gtk_widget_set_direction(widget, direction); - - style = gtk_widget_get_style(widget); - moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, - &focus_pad); - - TSOffsetStyleGCs(style, rect->x, rect->y); - - /* The detail argument for the gtk_paint_* calls below are "checkbutton" - even for radio buttons, to match what gtk does. */ - - /* this is for drawing a prelight box */ - if (state_type == GTK_STATE_PRELIGHT || state_type == GTK_STATE_ACTIVE) { - gtk_paint_flat_box(style, drawable, GTK_STATE_PRELIGHT, - GTK_SHADOW_ETCHED_OUT, cliprect, widget, - "checkbutton", - rect->x, rect->y, rect->width, rect->height); - } - - if (state_type != GTK_STATE_NORMAL && state_type != GTK_STATE_PRELIGHT) - state_type = GTK_STATE_NORMAL; - - if (state->focused && !interior_focus) { - gtk_paint_focus(style, drawable, state_type, cliprect, widget, - "checkbutton", - rect->x, rect->y, rect->width, rect->height); - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_toggle_label_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - gboolean isradio, GtkTextDirection direction) -{ - GtkStateType state_type; - GtkStyle *style; - GtkWidget *widget; - gboolean interior_focus; - - if (!state->focused) - return MOZ_GTK_SUCCESS; - - if (isradio) { - ensure_radiobutton_widget(); - widget = gParts->radiobuttonWidget; - } else { - ensure_checkbox_widget(); - widget = gParts->checkboxWidget; - } - gtk_widget_set_direction(widget, direction); - - gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL); - if (!interior_focus) - return MOZ_GTK_SUCCESS; - - state_type = ConvertGtkState(state); - - style = gtk_widget_get_style(widget); - TSOffsetStyleGCs(style, rect->x, rect->y); - - /* Always "checkbutton" to match gtkcheckbutton.c */ - gtk_paint_focus(style, drawable, state_type, cliprect, widget, - "checkbutton", - rect->x, rect->y, rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_toolbar_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - GtkShadowType shadow_type; - - ensure_toolbar_widget(); - gtk_widget_set_direction(gParts->toolbarWidget, direction); - - style = gtk_widget_get_style(gParts->toolbarWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - - gtk_style_apply_default_background(style, drawable, TRUE, - GTK_STATE_NORMAL, - cliprect, rect->x, rect->y, - rect->width, rect->height); - - gtk_widget_style_get(gParts->toolbarWidget, "shadow-type", &shadow_type, NULL); - - gtk_paint_box (style, drawable, GTK_STATE_NORMAL, shadow_type, - cliprect, gParts->toolbarWidget, "toolbar", - rect->x, rect->y, rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_toolbar_separator_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, - GtkTextDirection direction) -{ - GtkStyle* style; - gint separator_width; - gint paint_width; - gboolean wide_separators; - - /* Defined as constants in GTK+ 2.10.14 */ - const double start_fraction = 0.2; - const double end_fraction = 0.8; - - ensure_toolbar_separator_widget(); - gtk_widget_set_direction(gParts->toolbarSeparatorWidget, direction); - - style = gtk_widget_get_style(gParts->toolbarSeparatorWidget); - - gtk_widget_style_get(gParts->toolbarWidget, - "wide-separators", &wide_separators, - "separator-width", &separator_width, - NULL); - - TSOffsetStyleGCs(style, rect->x, rect->y); - - if (wide_separators) { - if (separator_width > rect->width) - separator_width = rect->width; - - gtk_paint_box(style, drawable, - GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, - cliprect, gParts->toolbarWidget, "vseparator", - rect->x + (rect->width - separator_width) / 2, - rect->y + rect->height * start_fraction, - separator_width, - rect->height * (end_fraction - start_fraction)); - - } else { - paint_width = style->xthickness; - - if (paint_width > rect->width) - paint_width = rect->width; - - gtk_paint_vline(style, drawable, - GTK_STATE_NORMAL, cliprect, gParts->toolbarSeparatorWidget, - "toolbar", - rect->y + rect->height * start_fraction, - rect->y + rect->height * end_fraction, - rect->x + (rect->width - paint_width) / 2); - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_tooltip_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - - ensure_tooltip_widget(); - gtk_widget_set_direction(gParts->tooltipWidget, direction); - - style = gtk_rc_get_style_by_paths(gtk_settings_get_default(), - "gtk-tooltips", "GtkWindow", - GTK_TYPE_WINDOW); - - style = gtk_style_attach(style, gtk_widget_get_window(gParts->tooltipWidget)); - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_flat_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - cliprect, gParts->tooltipWidget, "tooltip", - rect->x, rect->y, rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_resizer_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) -{ - GtkStyle* style; - GtkStateType state_type = ConvertGtkState(state); - - ensure_window_widget(); - gtk_widget_set_direction(gParts->protoWindow, direction); - - style = gtk_widget_get_style(gParts->protoWindow); - - TSOffsetStyleGCs(style, rect->x, rect->y); - - gtk_paint_resize_grip(style, drawable, state_type, cliprect, gParts->protoWindow, - NULL, (direction == GTK_TEXT_DIR_LTR) ? - GDK_WINDOW_EDGE_SOUTH_EAST : - GDK_WINDOW_EDGE_SOUTH_WEST, - rect->x, rect->y, rect->width, rect->height); - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_frame_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - GtkShadowType shadow_type; - - ensure_frame_widget(); - gtk_widget_set_direction(gParts->frameWidget, direction); - - style = gtk_widget_get_style(gParts->frameWidget); - - gtk_widget_style_get(gParts->statusbarWidget, "shadow-type", &shadow_type, NULL); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_shadow(style, drawable, GTK_STATE_NORMAL, shadow_type, - cliprect, gParts->frameWidget, "frame", rect->x, rect->y, - rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint moz_gtk_progressbar_paint(GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkTextDirection direction) { @@ -2233,438 +1218,6 @@ moz_gtk_progress_chunk_paint(GdkDrawable* drawable, GdkRectangle* rect, } gint -moz_gtk_get_tab_thickness(void) -{ - GtkStyle* style; - - ensure_tab_widget(); - style = gtk_widget_get_style(gParts->tabWidget); - if (YTHICKNESS(style) < 2) - return 2; /* some themes don't set ythickness correctly */ - - return YTHICKNESS(style); -} - -static gint -moz_gtk_tab_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTabFlags flags, - GtkTextDirection direction) -{ - /* When the tab isn't selected, we just draw a notebook extension. - * When it is selected, we overwrite the adjacent border of the tabpanel - * touching the tab with a pierced border (called "the gap") to make the - * tab appear physically attached to the tabpanel; see details below. */ - - GtkStyle* style; - - ensure_tab_widget(); - gtk_widget_set_direction(gParts->tabWidget, direction); - - style = gtk_widget_get_style(gParts->tabWidget); - TSOffsetStyleGCs(style, rect->x, rect->y); - - if ((flags & MOZ_GTK_TAB_SELECTED) == 0) { - /* Only draw the tab */ - gtk_paint_extension(style, drawable, GTK_STATE_ACTIVE, GTK_SHADOW_OUT, - cliprect, gParts->tabWidget, "tab", - rect->x, rect->y, rect->width, rect->height, - (flags & MOZ_GTK_TAB_BOTTOM) ? - GTK_POS_TOP : GTK_POS_BOTTOM ); - } else { - /* Draw the tab and the gap - * We want the gap to be positionned exactly on the tabpanel top - * border; since tabbox.css may set a negative margin so that the tab - * frame rect already overlaps the tabpanel frame rect, we need to take - * that into account when drawing. To that effect, nsNativeThemeGTK - * passes us this negative margin (bmargin in the graphic below) in the - * lowest bits of |flags|. We use it to set gap_voffset, the distance - * between the top of the gap and the bottom of the tab (resp. the - * bottom of the gap and the top of the tab when we draw a bottom tab), - * while ensuring that the gap always touches the border of the tab, - * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results - * with big negative or positive margins. - * Here is a graphical explanation in the case of top tabs: - * ___________________________ - * / \ - * | T A B | - * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel - * : ^ bmargin : ^ - * : | (-negative margin, : | - * bottom : v passed in flags) : | gap_height - * of -> :.............................: | (the size of the - * the tab . part of the gap . | tabpanel top border) - * . outside of the tab . v - * ---------------------------------------------- - * - * To draw the gap, we use gtk_paint_box_gap(), see comment in - * moz_gtk_tabpanels_paint(). This box_gap is made 3 * gap_height tall, - * which should suffice to ensure that the only visible border is the - * pierced one. If the tab is in the middle, we make the box_gap begin - * a bit to the left of the tab and end a bit to the right, adjusting - * the gap position so it still is under the tab, because we want the - * rendering of a gap in the middle of a tabpanel. This is the role of - * the gints gap_{l,r}_offset. On the contrary, if the tab is the - * first, we align the start border of the box_gap with the start - * border of the tab (left if LTR, right if RTL), by setting the - * appropriate offset to 0.*/ - gint gap_loffset, gap_roffset, gap_voffset, gap_height; - - /* Get height needed by the gap */ - gap_height = moz_gtk_get_tab_thickness(); - - /* Extract gap_voffset from the first bits of flags */ - gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK; - if (gap_voffset > gap_height) - gap_voffset = gap_height; - - /* Set gap_{l,r}_offset to appropriate values */ - gap_loffset = gap_roffset = 20; /* should be enough */ - if (flags & MOZ_GTK_TAB_FIRST) { - if (direction == GTK_TEXT_DIR_RTL) - gap_roffset = 0; - else - gap_loffset = 0; - } - - if (flags & MOZ_GTK_TAB_BOTTOM) { - /* Enlarge the cliprect to have room for the full gap height */ - cliprect->height += gap_height - gap_voffset; - cliprect->y -= gap_height - gap_voffset; - - /* Draw the tab */ - gtk_paint_extension(style, drawable, GTK_STATE_NORMAL, - GTK_SHADOW_OUT, cliprect, gParts->tabWidget, "tab", - rect->x, rect->y + gap_voffset, rect->width, - rect->height - gap_voffset, GTK_POS_TOP); - - /* Draw the gap; erase with background color before painting in - * case theme does not */ - gtk_style_apply_default_background(style, drawable, TRUE, - GTK_STATE_NORMAL, cliprect, - rect->x, - rect->y + gap_voffset - - gap_height, - rect->width, gap_height); - gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - cliprect, gParts->tabWidget, "notebook", - rect->x - gap_loffset, - rect->y + gap_voffset - 3 * gap_height, - rect->width + gap_loffset + gap_roffset, - 3 * gap_height, GTK_POS_BOTTOM, - gap_loffset, rect->width); - } else { - /* Enlarge the cliprect to have room for the full gap height */ - cliprect->height += gap_height - gap_voffset; - - /* Draw the tab */ - gtk_paint_extension(style, drawable, GTK_STATE_NORMAL, - GTK_SHADOW_OUT, cliprect, gParts->tabWidget, "tab", - rect->x, rect->y, rect->width, - rect->height - gap_voffset, GTK_POS_BOTTOM); - - /* Draw the gap; erase with background color before painting in - * case theme does not */ - gtk_style_apply_default_background(style, drawable, TRUE, - GTK_STATE_NORMAL, cliprect, - rect->x, - rect->y + rect->height - - gap_voffset, - rect->width, gap_height); - gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - cliprect, gParts->tabWidget, "notebook", - rect->x - gap_loffset, - rect->y + rect->height - gap_voffset, - rect->width + gap_loffset + gap_roffset, - 3 * gap_height, GTK_POS_TOP, - gap_loffset, rect->width); - } - - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_tabpanels_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - /* We use gtk_paint_box_gap() to draw the tabpanels widget. gtk_paint_box() - * draws an all-purpose box, which a lot of themes render differently. - * A zero-width gap is still visible in most themes, so we hide it to the - * left (10px should be enough) */ - GtkStyle* style; - - ensure_tab_widget(); - gtk_widget_set_direction(gParts->tabWidget, direction); - - style = gtk_widget_get_style(gParts->tabWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_box_gap(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - cliprect, gParts->tabWidget, "notebook", rect->x, rect->y, - rect->width, rect->height, - GTK_POS_TOP, -10, 0); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_tab_scroll_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkArrowType arrow_type, - GtkTextDirection direction) -{ - GtkStateType state_type = ConvertGtkState(state); - GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; - GtkStyle* style; - gint arrow_size = MIN(rect->width, rect->height); - gint x = rect->x + (rect->width - arrow_size) / 2; - gint y = rect->y + (rect->height - arrow_size) / 2; - - ensure_tab_widget(); - - style = gtk_widget_get_style(gParts->tabWidget); - TSOffsetStyleGCs(style, rect->x, rect->y); - - if (direction == GTK_TEXT_DIR_RTL) { - arrow_type = (arrow_type == GTK_ARROW_LEFT) ? - GTK_ARROW_RIGHT : GTK_ARROW_LEFT; - } - - gtk_paint_arrow(style, drawable, state_type, shadow_type, NULL, - gParts->tabWidget, "notebook", arrow_type, TRUE, - x, y, arrow_size, arrow_size); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_menu_bar_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - GtkShadowType shadow_type; - ensure_menu_bar_widget(); - gtk_widget_set_direction(gParts->menuBarWidget, direction); - - gtk_widget_style_get(gParts->menuBarWidget, "shadow-type", &shadow_type, NULL); - - style = gtk_widget_get_style(gParts->menuBarWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL, - cliprect, rect->x, rect->y, - rect->width, rect->height); - - gtk_paint_box(style, drawable, GTK_STATE_NORMAL, shadow_type, - cliprect, gParts->menuBarWidget, "menubar", rect->x, rect->y, - rect->width, rect->height); - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_menu_popup_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - ensure_menu_popup_widget(); - gtk_widget_set_direction(gParts->menuPopupWidget, direction); - - style = gtk_widget_get_style(gParts->menuPopupWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_style_apply_default_background(style, drawable, TRUE, GTK_STATE_NORMAL, - cliprect, rect->x, rect->y, - rect->width, rect->height); - gtk_paint_box(style, drawable, GTK_STATE_NORMAL, GTK_SHADOW_OUT, - cliprect, gParts->menuPopupWidget, "menu", - rect->x, rect->y, rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_menu_separator_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - gboolean wide_separators; - gint separator_height; - guint horizontal_padding; - gint paint_height; - - ensure_menu_separator_widget(); - gtk_widget_set_direction(gParts->menuSeparatorWidget, direction); - - style = gtk_widget_get_style(gParts->menuSeparatorWidget); - - gtk_widget_style_get(gParts->menuSeparatorWidget, - "wide-separators", &wide_separators, - "separator-height", &separator_height, - "horizontal-padding", &horizontal_padding, - NULL); - - TSOffsetStyleGCs(style, rect->x, rect->y); - - if (wide_separators) { - if (separator_height > rect->height) - separator_height = rect->height; - - gtk_paint_box(style, drawable, - GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, - cliprect, gParts->menuSeparatorWidget, "hseparator", - rect->x + horizontal_padding + style->xthickness, - rect->y + (rect->height - separator_height - style->ythickness) / 2, - rect->width - 2 * (horizontal_padding + style->xthickness), - separator_height); - } else { - paint_height = style->ythickness; - if (paint_height > rect->height) - paint_height = rect->height; - - gtk_paint_hline(style, drawable, - GTK_STATE_NORMAL, cliprect, gParts->menuSeparatorWidget, - "menuitem", - rect->x + horizontal_padding + style->xthickness, - rect->x + rect->width - horizontal_padding - style->xthickness - 1, - rect->y + (rect->height - style->ythickness) / 2); - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - gint flags, GtkTextDirection direction) -{ - GtkStyle* style; - GtkShadowType shadow_type; - GtkWidget* item_widget; - - if (state->inHover && !state->disabled) { - if (flags & MOZ_TOPLEVEL_MENU_ITEM) { - ensure_menu_bar_item_widget(); - item_widget = gParts->menuBarItemWidget; - } else { - ensure_menu_item_widget(); - item_widget = gParts->menuItemWidget; - } - gtk_widget_set_direction(item_widget, direction); - - style = gtk_widget_get_style(item_widget); - TSOffsetStyleGCs(style, rect->x, rect->y); - - gtk_widget_style_get(item_widget, "selected-shadow-type", - &shadow_type, NULL); - - gtk_paint_box(style, drawable, GTK_STATE_PRELIGHT, shadow_type, - cliprect, item_widget, "menuitem", rect->x, rect->y, - rect->width, rect->height); - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_menu_arrow_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - GtkTextDirection direction) -{ - GtkStyle* style; - GtkStateType state_type = ConvertGtkState(state); - - ensure_menu_item_widget(); - gtk_widget_set_direction(gParts->menuItemWidget, direction); - - style = gtk_widget_get_style(gParts->menuItemWidget); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_paint_arrow(style, drawable, state_type, - (state_type == GTK_STATE_PRELIGHT) ? GTK_SHADOW_IN : GTK_SHADOW_OUT, - cliprect, gParts->menuItemWidget, "menuitem", - (direction == GTK_TEXT_DIR_LTR) ? GTK_ARROW_RIGHT : GTK_ARROW_LEFT, - TRUE, rect->x, rect->y, rect->width, rect->height); - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_check_menu_item_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkWidgetState* state, - gboolean checked, gboolean isradio, - GtkTextDirection direction) -{ - GtkStateType state_type = ConvertGtkState(state); - GtkStyle* style; - GtkShadowType shadow_type = (checked)?GTK_SHADOW_IN:GTK_SHADOW_OUT; - gint offset; - gint indicator_size; - gint x, y; - - moz_gtk_menu_item_paint(drawable, rect, cliprect, state, FALSE, direction); - - ensure_check_menu_item_widget(); - gtk_widget_set_direction(gParts->checkMenuItemWidget, direction); - - gtk_widget_style_get (gParts->checkMenuItemWidget, - "indicator-size", &indicator_size, - NULL); - -#ifdef GTK_API_VERSION_2 - if (checked || GTK_CHECK_MENU_ITEM(gParts->checkMenuItemWidget)->always_show_toggle) { -#else - if (checked || FALSE) { -#endif - style = gtk_widget_get_style(gParts->checkMenuItemWidget); - - offset = gtk_container_get_border_width(GTK_CONTAINER(gParts->checkMenuItemWidget)) + style->xthickness + 2; - - /* while normally this "3" would be the horizontal-padding style value, passing it to Gecko - as the value of menuitem padding causes problems with dropdowns (bug 406129), so in the menu.css - file this is hardcoded as 3px. Yes it sucks, but we don't really have a choice. */ - x = (direction == GTK_TEXT_DIR_RTL) ? - rect->width - indicator_size - offset - 3: rect->x + offset + 3; - y = rect->y + (rect->height - indicator_size) / 2; - - TSOffsetStyleGCs(style, x, y); - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(gParts->checkMenuItemWidget), - checked); - - if (isradio) { - gtk_paint_option(style, drawable, state_type, shadow_type, cliprect, - gParts->checkMenuItemWidget, "option", - x, y, indicator_size, indicator_size); - } else { - gtk_paint_check(style, drawable, state_type, shadow_type, cliprect, - gParts->checkMenuItemWidget, "check", - x, y, indicator_size, indicator_size); - } - } - - return MOZ_GTK_SUCCESS; -} - -static gint -moz_gtk_window_paint(GdkDrawable* drawable, GdkRectangle* rect, - GdkRectangle* cliprect, GtkTextDirection direction) -{ - GtkStyle* style; - - ensure_window_widget(); - gtk_widget_set_direction(gParts->protoWindow, direction); - - style = gtk_widget_get_style(gParts->protoWindow); - - TSOffsetStyleGCs(style, rect->x, rect->y); - gtk_style_apply_default_background(style, drawable, TRUE, - GTK_STATE_NORMAL, - cliprect, rect->x, rect->y, - rect->width, rect->height); - return MOZ_GTK_SUCCESS; -} - -gint moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, gint* right, gint* bottom, GtkTextDirection direction, gboolean inhtml) @@ -2705,52 +1258,6 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, ensure_entry_widget(); w = gParts->entryWidget; break; - case MOZ_GTK_TREEVIEW: - ensure_tree_view_widget(); - w = gParts->treeViewWidget; - break; - case MOZ_GTK_TREE_HEADER_CELL: - { - /* A Tree Header in GTK is just a different styled button - * It must be placed in a TreeView for getting the correct style - * assigned. - * That is why the following code is the same as for MOZ_GTK_BUTTON. - * */ - - GtkBorder inner_border; - gboolean interior_focus; - gint focus_width, focus_pad; - GtkStyle* style; - - ensure_tree_header_cell_widget(); - *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gParts->treeHeaderCellWidget)); - - moz_gtk_widget_get_focus(gParts->treeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad); - moz_gtk_button_get_inner_border(gParts->treeHeaderCellWidget, &inner_border); - *left += focus_width + focus_pad + inner_border.left; - *right += focus_width + focus_pad + inner_border.right; - *top += focus_width + focus_pad + inner_border.top; - *bottom += focus_width + focus_pad + inner_border.bottom; - - style = gtk_widget_get_style(gParts->treeHeaderCellWidget); - *left += style->xthickness; - *right += style->xthickness; - *top += style->ythickness; - *bottom += style->ythickness; - return MOZ_GTK_SUCCESS; - } - case MOZ_GTK_TREE_HEADER_SORTARROW: - ensure_tree_header_cell_widget(); - w = gParts->treeHeaderSortArrowWidget; - break; - case MOZ_GTK_DROPDOWN_ENTRY: - ensure_combo_box_entry_widgets(); - w = gParts->comboBoxEntryTextareaWidget; - break; - case MOZ_GTK_DROPDOWN_ARROW: - ensure_combo_box_entry_widgets(); - w = gParts->comboBoxEntryButtonWidget; - break; case MOZ_GTK_DROPDOWN: { /* We need to account for the arrow on the dropdown, so text @@ -2791,11 +1298,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, XTHICKNESS(style); } -#ifdef GTK_API_VERSION_2 gtk_widget_size_request(gParts->comboBoxArrowWidget, &arrow_req); -#else - gtk_size_request_get_size(GTK_SIZE_REQUEST(gParts->comboBoxArrowWidget), &arrow_req, NULL); -#endif if (direction == GTK_TEXT_DIR_RTL) *left += separator_width + arrow_req.width; else @@ -2803,20 +1306,10 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, return MOZ_GTK_SUCCESS; } - case MOZ_GTK_TABPANELS: - ensure_tab_widget(); - w = gParts->tabWidget; - break; case MOZ_GTK_PROGRESSBAR: ensure_progress_widget(); w = gParts->progresWidget; break; - case MOZ_GTK_SPINBUTTON_ENTRY: - case MOZ_GTK_SPINBUTTON_UP: - case MOZ_GTK_SPINBUTTON_DOWN: - ensure_spin_widget(); - w = gParts->spinWidget; - break; case MOZ_GTK_SCALE_HORIZONTAL: ensure_scale_widget(); w = gParts->hScaleWidget; @@ -2825,89 +1318,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, ensure_scale_widget(); w = gParts->vScaleWidget; break; - case MOZ_GTK_FRAME: - ensure_frame_widget(); - w = gParts->frameWidget; - break; - case MOZ_GTK_CHECKBUTTON_LABEL: - case MOZ_GTK_RADIOBUTTON_LABEL: - { - gboolean interior_focus; - gint focus_width, focus_pad; - - /* If the focus is interior, then the label has a border of - (focus_width + focus_pad). */ - if (widget == MOZ_GTK_CHECKBUTTON_LABEL) { - ensure_checkbox_widget(); - moz_gtk_widget_get_focus(gParts->checkboxWidget, &interior_focus, - &focus_width, &focus_pad); - } - else { - ensure_radiobutton_widget(); - moz_gtk_widget_get_focus(gParts->radiobuttonWidget, &interior_focus, - &focus_width, &focus_pad); - } - - if (interior_focus) - *left = *top = *right = *bottom = (focus_width + focus_pad); - else - *left = *top = *right = *bottom = 0; - - return MOZ_GTK_SUCCESS; - } - - case MOZ_GTK_CHECKBUTTON_CONTAINER: - case MOZ_GTK_RADIOBUTTON_CONTAINER: - { - gboolean interior_focus; - gint focus_width, focus_pad; - - /* If the focus is _not_ interior, then the container has a border - of (focus_width + focus_pad). */ - if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) { - ensure_checkbox_widget(); - moz_gtk_widget_get_focus(gParts->checkboxWidget, &interior_focus, - &focus_width, &focus_pad); - w = gParts->checkboxWidget; - } else { - ensure_radiobutton_widget(); - moz_gtk_widget_get_focus(gParts->radiobuttonWidget, &interior_focus, - &focus_width, &focus_pad); - w = gParts->radiobuttonWidget; - } - - *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w)); - - if (!interior_focus) { - *left += (focus_width + focus_pad); - *right += (focus_width + focus_pad); - *top += (focus_width + focus_pad); - *bottom += (focus_width + focus_pad); - } - - return MOZ_GTK_SUCCESS; - } - case MOZ_GTK_MENUPOPUP: - ensure_menu_popup_widget(); - w = gParts->menuPopupWidget; - break; - case MOZ_GTK_MENUITEM: - ensure_menu_item_widget(); - ensure_menu_bar_item_widget(); - w = gParts->menuItemWidget; - break; - case MOZ_GTK_CHECKMENUITEM: - case MOZ_GTK_RADIOMENUITEM: - ensure_check_menu_item_widget(); - w = gParts->checkMenuItemWidget; - break; - case MOZ_GTK_TAB: - ensure_tab_widget(); - w = gParts->tabWidget; - break; /* These widgets have no borders, since they are not containers. */ - case MOZ_GTK_SPLITTER_HORIZONTAL: - case MOZ_GTK_SPLITTER_VERTICAL: case MOZ_GTK_CHECKBUTTON: case MOZ_GTK_RADIOBUTTON: case MOZ_GTK_SCROLLBAR_BUTTON: @@ -2917,23 +1328,7 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: case MOZ_GTK_SCALE_THUMB_HORIZONTAL: case MOZ_GTK_SCALE_THUMB_VERTICAL: - case MOZ_GTK_GRIPPER: case MOZ_GTK_PROGRESS_CHUNK: - case MOZ_GTK_EXPANDER: - case MOZ_GTK_TREEVIEW_EXPANDER: - case MOZ_GTK_TOOLBAR_SEPARATOR: - case MOZ_GTK_MENUSEPARATOR: - /* These widgets have no borders.*/ - case MOZ_GTK_SPINBUTTON: - case MOZ_GTK_TOOLTIP: - case MOZ_GTK_WINDOW: - case MOZ_GTK_RESIZER: - case MOZ_GTK_MENUARROW: - case MOZ_GTK_TOOLBARBUTTON_ARROW: - case MOZ_GTK_TOOLBAR: - case MOZ_GTK_MENUBAR: - case MOZ_GTK_TAB_SCROLLARROW: - case MOZ_GTK_ENTRY_CARET: *left = *top = *right = *bottom = 0; return MOZ_GTK_SUCCESS; default: @@ -2949,131 +1344,6 @@ moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, } gint -moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height) -{ - /* - * We get the requisition of the drop down button, which includes - * all padding, border and focus line widths the button uses, - * as well as the minimum arrow size and its padding - * */ - GtkRequisition requisition; - ensure_combo_box_entry_widgets(); - -#ifdef GTK_API_VERSION_2 - gtk_widget_size_request(gParts->comboBoxEntryButtonWidget, &requisition); -#else - gtk_size_request_get_size(GTK_SIZE_REQUEST(gParts->comboBoxEntryButtonWidget), &requisition, NULL); -#endif - - *width = requisition.width; - *height = requisition.height; - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height) -{ - gint arrow_size; - - ensure_tab_widget(); - gtk_widget_style_get(gParts->tabWidget, - "scroll-arrow-hlength", &arrow_size, - NULL); - - *height = *width = arrow_size; - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_downarrow_size(gint* width, gint* height) -{ - GtkRequisition requisition; - ensure_button_arrow_widget(); - -#ifdef GTK_API_VERSION_2 - gtk_widget_size_request(gParts->buttonArrowWidget, &requisition); -#else - gtk_size_request_get_size(GTK_SIZE_REQUEST(gParts->buttonArrowWidget), &requisition, NULL); -#endif - - *width = requisition.width; - *height = requisition.height; - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_toolbar_separator_width(gint* size) -{ - gboolean wide_separators; - gint separator_width; - GtkStyle* style; - - ensure_toolbar_widget(); - - style = gtk_widget_get_style(gParts->toolbarWidget); - - gtk_widget_style_get(gParts->toolbarWidget, - "space-size", size, - "wide-separators", &wide_separators, - "separator-width", &separator_width, - NULL); - - /* Just in case... */ - *size = MAX(*size, (wide_separators ? separator_width : style->xthickness)); - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_expander_size(gint* size) -{ - ensure_expander_widget(); - gtk_widget_style_get(gParts->expanderWidget, - "expander-size", size, - NULL); - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_treeview_expander_size(gint* size) -{ - ensure_tree_view_widget(); - gtk_widget_style_get(gParts->treeViewWidget, - "expander-size", size, - NULL); - - return MOZ_GTK_SUCCESS; -} - -gint -moz_gtk_get_menu_separator_height(gint *size) -{ - gboolean wide_separators; - gint separator_height; - GtkStyle *style; - - ensure_menu_separator_widget(); - - gtk_widget_style_get(gParts->menuSeparatorWidget, - "wide-separators", &wide_separators, - "separator-height", &separator_height, - NULL); - - style = gtk_widget_get_style(gParts->menuSeparatorWidget); - - if (wide_separators) - *size = separator_height + style->ythickness; - else - *size = style->ythickness * 2; - - return MOZ_GTK_SUCCESS; -} - -gint moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height) { GtkWidget* widget; @@ -3109,19 +1379,6 @@ moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) return MOZ_GTK_SUCCESS; } -gboolean -moz_gtk_images_in_menus() -{ - gboolean result; - GtkSettings* settings; - - ensure_image_menu_item_widget(); - settings = gtk_widget_get_settings(gParts->imageMenuItemWidget); - - g_object_get(settings, "gtk-menu-images", &result, NULL); - return result; -} - gint moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, @@ -3177,96 +1434,15 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, return moz_gtk_scale_thumb_paint(drawable, rect, cliprect, state, (GtkOrientation) flags, direction); break; - case MOZ_GTK_SPINBUTTON: - return moz_gtk_spin_paint(drawable, rect, direction); - break; - case MOZ_GTK_SPINBUTTON_UP: - case MOZ_GTK_SPINBUTTON_DOWN: - return moz_gtk_spin_updown_paint(drawable, rect, - (widget == MOZ_GTK_SPINBUTTON_DOWN), - state, direction); - break; - case MOZ_GTK_SPINBUTTON_ENTRY: - ensure_spin_widget(); - return moz_gtk_entry_paint(drawable, rect, cliprect, state, - gParts->spinWidget, direction); - break; - case MOZ_GTK_GRIPPER: - return moz_gtk_gripper_paint(drawable, rect, cliprect, state, - direction); - break; - case MOZ_GTK_TREEVIEW: - return moz_gtk_treeview_paint(drawable, rect, cliprect, state, - direction); - break; - case MOZ_GTK_TREE_HEADER_CELL: - return moz_gtk_tree_header_cell_paint(drawable, rect, cliprect, state, - flags, direction); - break; - case MOZ_GTK_TREE_HEADER_SORTARROW: - return moz_gtk_tree_header_sort_arrow_paint(drawable, rect, cliprect, - state, - (GtkArrowType) flags, - direction); - break; - case MOZ_GTK_TREEVIEW_EXPANDER: - return moz_gtk_treeview_expander_paint(drawable, rect, cliprect, state, - (GtkExpanderStyle) flags, direction); - break; - case MOZ_GTK_EXPANDER: - return moz_gtk_expander_paint(drawable, rect, cliprect, state, - (GtkExpanderStyle) flags, direction); - break; case MOZ_GTK_ENTRY: ensure_entry_widget(); return moz_gtk_entry_paint(drawable, rect, cliprect, state, gParts->entryWidget, direction); break; - case MOZ_GTK_ENTRY_CARET: - return moz_gtk_caret_paint(drawable, rect, cliprect, direction); - break; case MOZ_GTK_DROPDOWN: return moz_gtk_combo_box_paint(drawable, rect, cliprect, state, (gboolean) flags, direction); break; - case MOZ_GTK_DROPDOWN_ARROW: - return moz_gtk_combo_box_entry_button_paint(drawable, rect, cliprect, - state, flags, direction); - break; - case MOZ_GTK_DROPDOWN_ENTRY: - ensure_combo_box_entry_widgets(); - return moz_gtk_entry_paint(drawable, rect, cliprect, state, - gParts->comboBoxEntryTextareaWidget, direction); - break; - case MOZ_GTK_CHECKBUTTON_CONTAINER: - case MOZ_GTK_RADIOBUTTON_CONTAINER: - return moz_gtk_container_paint(drawable, rect, cliprect, state, - (widget == MOZ_GTK_RADIOBUTTON_CONTAINER), - direction); - break; - case MOZ_GTK_CHECKBUTTON_LABEL: - case MOZ_GTK_RADIOBUTTON_LABEL: - return moz_gtk_toggle_label_paint(drawable, rect, cliprect, state, - (widget == MOZ_GTK_RADIOBUTTON_LABEL), - direction); - break; - case MOZ_GTK_TOOLBAR: - return moz_gtk_toolbar_paint(drawable, rect, cliprect, direction); - break; - case MOZ_GTK_TOOLBAR_SEPARATOR: - return moz_gtk_toolbar_separator_paint(drawable, rect, cliprect, - direction); - break; - case MOZ_GTK_TOOLTIP: - return moz_gtk_tooltip_paint(drawable, rect, cliprect, direction); - break; - case MOZ_GTK_FRAME: - return moz_gtk_frame_paint(drawable, rect, cliprect, direction); - break; - case MOZ_GTK_RESIZER: - return moz_gtk_resizer_paint(drawable, rect, cliprect, state, - direction); - break; case MOZ_GTK_PROGRESSBAR: return moz_gtk_progressbar_paint(drawable, rect, cliprect, direction); break; @@ -3274,54 +1450,6 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, return moz_gtk_progress_chunk_paint(drawable, rect, cliprect, direction); break; - case MOZ_GTK_TAB: - return moz_gtk_tab_paint(drawable, rect, cliprect, - (GtkTabFlags) flags, direction); - break; - case MOZ_GTK_TABPANELS: - return moz_gtk_tabpanels_paint(drawable, rect, cliprect, direction); - break; - case MOZ_GTK_TAB_SCROLLARROW: - return moz_gtk_tab_scroll_arrow_paint(drawable, rect, cliprect, state, - (GtkArrowType) flags, direction); - break; - case MOZ_GTK_MENUBAR: - return moz_gtk_menu_bar_paint(drawable, rect, cliprect, direction); - break; - case MOZ_GTK_MENUPOPUP: - return moz_gtk_menu_popup_paint(drawable, rect, cliprect, direction); - break; - case MOZ_GTK_MENUSEPARATOR: - return moz_gtk_menu_separator_paint(drawable, rect, cliprect, - direction); - break; - case MOZ_GTK_MENUITEM: - return moz_gtk_menu_item_paint(drawable, rect, cliprect, state, flags, - direction); - break; - case MOZ_GTK_MENUARROW: - return moz_gtk_menu_arrow_paint(drawable, rect, cliprect, state, - direction); - break; - case MOZ_GTK_TOOLBARBUTTON_ARROW: - return moz_gtk_downarrow_paint(drawable, rect, cliprect, state); - break; - case MOZ_GTK_CHECKMENUITEM: - case MOZ_GTK_RADIOMENUITEM: - return moz_gtk_check_menu_item_paint(drawable, rect, cliprect, state, - (gboolean) flags, - (widget == MOZ_GTK_RADIOMENUITEM), - direction); - break; - case MOZ_GTK_SPLITTER_HORIZONTAL: - return moz_gtk_vpaned_paint(drawable, rect, cliprect, state); - break; - case MOZ_GTK_SPLITTER_VERTICAL: - return moz_gtk_hpaned_paint(drawable, rect, cliprect, state); - break; - case MOZ_GTK_WINDOW: - return moz_gtk_window_paint(drawable, rect, cliprect, direction); - break; default: g_warning("Unknown widget type: %d", widget); } @@ -3354,11 +1482,6 @@ void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts) if (!parts) return; - if (parts->tooltipWidget) { - gtk_widget_destroy(parts->tooltipWidget); - parts->tooltipWidget = NULL; - } - if (parts->protoWindow) { gtk_widget_destroy(parts->protoWindow); parts->protoWindow = NULL; @@ -3372,3 +1495,5 @@ GtkWidget* moz_gtk_get_progress_widget() ensure_progress_widget(); return gParts->progresWidget; } + +#endif // GTK_API_VERSION_2 diff --git a/WebCore/platform/gtk/gtk3drawing.c b/WebCore/platform/gtk/gtk3drawing.c new file mode 100644 index 0000000..dda110d --- /dev/null +++ b/WebCore/platform/gtk/gtk3drawing.c @@ -0,0 +1,1416 @@ +/* -*- 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. + */ + +#ifndef GTK_API_VERSION_2 + +#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 have_arrow_scaling; +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); + 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_button_widget() +{ + if (!gParts->buttonWidget) { + gParts->buttonWidget = gtk_button_new_with_label("M"); + setup_widget_prototype(gParts->buttonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_toggle_button_widget() +{ + if (!gParts->toggleButtonWidget) { + gParts->toggleButtonWidget = gtk_toggle_button_new(); + setup_widget_prototype(gParts->toggleButtonWidget); + /* toggle button must be set active to get the right style on hover. */ + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gParts->toggleButtonWidget), TRUE); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_arrow_widget() +{ + if (!gParts->buttonArrowWidget) { + ensure_toggle_button_widget(); + + gParts->buttonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(gParts->toggleButtonWidget), gParts->buttonArrowWidget); + gtk_widget_realize(gParts->buttonArrowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_checkbox_widget() +{ + if (!gParts->checkboxWidget) { + gParts->checkboxWidget = gtk_check_button_new_with_label("M"); + setup_widget_prototype(gParts->checkboxWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_radiobutton_widget() +{ + if (!gParts->radiobuttonWidget) { + gParts->radiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M"); + setup_widget_prototype(gParts->radiobuttonWidget); + } + 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_scale_widget() +{ + if (!gParts->hScaleWidget) { + gParts->hScaleWidget = gtk_hscale_new(NULL); + setup_widget_prototype(gParts->hScaleWidget); + } + if (!gParts->vScaleWidget) { + gParts->vScaleWidget = gtk_vscale_new(NULL); + setup_widget_prototype(gParts->vScaleWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_entry_widget() +{ + if (!gParts->entryWidget) { + gParts->entryWidget = gtk_entry_new(); + setup_widget_prototype(gParts->entryWidget); + } + return MOZ_GTK_SUCCESS; +} + +/* We need to have pointers to the inner widgets (button, separator, arrow) + * of the ComboBox to get the correct rendering from theme engines which + * special cases their look. Since the inner layout can change, we ask GTK + * to NULL our pointers when they are about to become invalid because the + * corresponding widgets don't exist anymore. It's the role of + * g_object_add_weak_pointer(). + * Note that if we don't find the inner widgets (which shouldn't happen), we + * fallback to use generic "non-inner" widgets, and they don't need that kind + * of weak pointer since they are explicit children of gParts->protoWindow and as + * such GTK holds a strong reference to them. */ +static void +moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) { + gParts->comboBoxButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxButtonWidget); + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } +} + +static void +moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_SEPARATOR(widget)) { + gParts->comboBoxSeparatorWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxSeparatorWidget); + } else if (GTK_IS_ARROW(widget)) { + gParts->comboBoxArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gParts->comboBoxArrowWidget); + } else + return; + gtk_widget_realize(widget); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); +} + +static gint +ensure_combo_box_widgets() +{ + GtkWidget* buttonChild; + + if (gParts->comboBoxButtonWidget && gParts->comboBoxArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBox if needed */ + if (!gParts->comboBoxWidget) { + gParts->comboBoxWidget = gtk_combo_box_new(); + setup_widget_prototype(gParts->comboBoxWidget); + } + + /* Get its inner Button */ + gtk_container_forall(GTK_CONTAINER(gParts->comboBoxWidget), + moz_gtk_get_combo_box_inner_button, + NULL); + + if (gParts->comboBoxButtonWidget) { + /* Get the widgets inside the Button */ + buttonChild = gtk_bin_get_child(GTK_BIN(gParts->comboBoxButtonWidget)); + if (GTK_IS_HBOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + gtk_container_forall(GTK_CONTAINER(buttonChild), + moz_gtk_get_combo_box_button_inner_widgets, + NULL); + } else if(GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + gParts->comboBoxArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gParts->comboBoxArrowWidget); + gtk_widget_realize(gParts->comboBoxArrowWidget); + g_object_set_data(G_OBJECT(gParts->comboBoxArrowWidget), + "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } + } else { + /* Shouldn't be reached with current internal gtk implementation; we + * use a generic toggle button as last resort fallback to avoid + * crashing. */ + ensure_toggle_button_widget(); + gParts->comboBoxButtonWidget = gParts->toggleButtonWidget; + } + + if (!gParts->comboBoxArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gParts->buttonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gParts->comboBoxArrowWidget = gParts->buttonArrowWidget; + } + + /* We don't test the validity of gParts->comboBoxSeparatorWidget since there + * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it + * is invalid we just won't paint it. */ + + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_progress_widget() +{ + if (!gParts->progresWidget) { + gParts->progresWidget = gtk_progress_bar_new(); + setup_widget_prototype(gParts->progresWidget); + } + 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 +moz_gtk_button_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, GtkReliefStyle relief, + GtkWidget* widget, GtkTextDirection direction) +{ + GtkShadowType shadow_type; + GtkStyle* style = gtk_widget_get_style(widget); + GtkStateType button_state = ConvertGtkState(state); + gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; + GdkWindow* window = gtk_widget_get_window(widget); + + gboolean interior_focus; + gint focus_width, focus_pad; + + moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); + + if (window && gdk_window_is_visible(window)) { + gdk_window_set_background_pattern(window, NULL); + } + + gtk_widget_set_state(widget, button_state); + gtk_widget_set_direction(widget, direction); + gtk_button_set_relief(GTK_BUTTON(widget), relief); + + if (!interior_focus && state->focused) { + x += focus_width + focus_pad; + y += focus_width + focus_pad; + width -= 2 * (focus_width + focus_pad); + height -= 2 * (focus_width + focus_pad); + } + + shadow_type = button_state == GTK_STATE_ACTIVE || + state->depressed ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + + if (state->isDefault && relief == GTK_RELIEF_NORMAL) { + gtk_paint_box(style, cr, button_state, shadow_type, + widget, "buttondefault", x, y, width, height); + } + + if (relief != GTK_RELIEF_NONE || state->depressed || + (button_state != GTK_STATE_NORMAL && + button_state != GTK_STATE_INSENSITIVE)) { + /* the following line can trigger an assertion (Crux theme) + file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area): + assertion `GDK_IS_WINDOW (window)' failed */ + gtk_paint_box(style, cr, button_state, shadow_type, + widget, "button", x, y, width, height); + } + + if (state->focused) { + if (interior_focus) { + GtkStyle* style = gtk_widget_get_style(widget); + x += style->xthickness + focus_pad; + y += style->ythickness + focus_pad; + width -= 2 * (style->xthickness + focus_pad); + height -= 2 * (style->ythickness + focus_pad); + } else { + x -= focus_width + focus_pad; + y -= focus_width + focus_pad; + width += 2 * (focus_width + focus_pad); + height += 2 * (focus_width + focus_pad); + } + + gtk_paint_focus(style, cr, button_state, + widget, "button", x, y, width, height); + } + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_init() +{ + GtkWidgetClass *entry_class; + + is_initialized = TRUE; + have_arrow_scaling = (gtk_major_version > 2 || + (gtk_major_version == 2 && gtk_minor_version >= 12)); + + /* 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; +} + +gint +moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_checkbox_widget(); + + gtk_widget_style_get (gParts->checkboxWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_radiobutton_widget(); + + gtk_widget_style_get (gParts->radiobuttonWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, + gint* focus_width, gint* focus_pad) +{ + gtk_widget_style_get (widget, + "interior-focus", interior_focus, + "focus-line-width", focus_width, + "focus-padding", focus_pad, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border) +{ + static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; + GtkBorder *tmp_border; + + gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL); + + if (tmp_border) { + *inner_border = *tmp_border; + gtk_border_free(tmp_border); + } + else + *inner_border = default_inner_border; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toggle_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, gboolean selected, + gboolean inconsistent, gboolean isradio, + GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = (selected)?GTK_SHADOW_IN:GTK_SHADOW_OUT; + gint indicator_size, indicator_spacing; + gint x, y, width, height; + gint focus_x, focus_y, focus_width, focus_height; + GtkWidget *w; + GtkStyle *style; + + if (isradio) { + moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); + w = gParts->radiobuttonWidget; + } else { + moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); + w = gParts->checkboxWidget; + } + + // "GetMinimumWidgetSize was ignored" + // FIXME: This assert causes a build failure in WebKitGTK+ debug + // builds, because it uses 'false' in its definition. We may want + // to force this file to be built with g++, by renaming it. + // ASSERT(rect->width == indicator_size); + + /* + * vertically center in the box, since XUL sometimes ignores our + * GetMinimumWidgetSize in the vertical dimension + */ + x = rect->x; + y = rect->y + (rect->height - indicator_size) / 2; + width = indicator_size; + height = indicator_size; + + focus_x = x - indicator_spacing; + focus_y = y - indicator_spacing; + focus_width = width + 2 * indicator_spacing; + focus_height = height + 2 * indicator_spacing; + + style = gtk_widget_get_style(w); + + gtk_widget_set_sensitive(w, !state->disabled); + gtk_widget_set_direction(w, direction); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w), selected); + + if (isradio) { + gtk_paint_option(style, cr, state_type, shadow_type, + gParts->radiobuttonWidget, "radiobutton", x, y, + width, height); + if (state->focused) { + gtk_paint_focus(style, cr, GTK_STATE_ACTIVE, + gParts->radiobuttonWidget, "radiobutton", focus_x, focus_y, + focus_width, focus_height); + } + } + else { + /* + * 'indeterminate' type on checkboxes. In GTK, the shadow type + * must also be changed for the state to be drawn. + */ + if (inconsistent) { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gParts->checkboxWidget), TRUE); + shadow_type = GTK_SHADOW_ETCHED_IN; + } else { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gParts->checkboxWidget), FALSE); + } + + gtk_paint_check(style, cr, state_type, shadow_type, + gParts->checkboxWidget, "checkbutton", x, y, width, height); + if (state->focused) { + gtk_paint_focus(style, cr, GTK_STATE_ACTIVE, + gParts->checkboxWidget, "checkbutton", focus_x, focus_y, + focus_width, focus_height); + } + } + + return MOZ_GTK_SUCCESS; +} + +static gint +calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, + GdkRectangle* inner_rect, + GtkTextDirection direction, + gboolean ignore_focus) +{ + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyle* style; + + style = gtk_widget_get_style(button); + + /* This mirrors gtkbutton's child positioning */ + moz_gtk_button_get_inner_border(button, &inner_border); + moz_gtk_widget_get_focus(button, &interior_focus, + &focus_width, &focus_pad); + + if (ignore_focus) + focus_width = focus_pad = 0; + + inner_rect->x = rect->x + XTHICKNESS(style) + focus_width + focus_pad; + inner_rect->x += direction == GTK_TEXT_DIR_LTR ? + inner_border.left : inner_border.right; + inner_rect->y = rect->y + inner_border.top + YTHICKNESS(style) + + focus_width + focus_pad; + inner_rect->width = MAX(1, rect->width - inner_border.left - + inner_border.right - (XTHICKNESS(style) + focus_pad + focus_width) * 2); + inner_rect->height = MAX(1, rect->height - inner_border.top - + inner_border.bottom - (YTHICKNESS(style) + focus_pad + focus_width) * 2); + + return MOZ_GTK_SUCCESS; +} + + +static gint +calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect, + GdkRectangle* arrow_rect, GtkTextDirection direction) +{ + /* defined in gtkarrow.c */ + gfloat arrow_scaling = 0.7; + gfloat xalign, xpad; + gint extent; + GtkMisc* misc = GTK_MISC(arrow); + gfloat misc_xalign, misc_yalign; + gint misc_xpad, misc_ypad; + + if (have_arrow_scaling) + gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL); + + gtk_misc_get_padding(misc, &misc_xpad, &misc_ypad); + gtk_misc_get_alignment(misc, &misc_xalign, &misc_yalign); + + extent = MIN((rect->width - misc_xpad * 2), + (rect->height - misc_ypad * 2)) * arrow_scaling; + + xalign = direction == GTK_TEXT_DIR_LTR ? misc_xalign : 1.0 - misc_xalign; + xpad = misc_xpad + (rect->width - extent) * xalign; + + arrow_rect->x = direction == GTK_TEXT_DIR_LTR ? + floor(rect->x + xpad) : ceil(rect->x + xpad); + arrow_rect->y = floor(rect->y + misc_ypad + + ((rect->height - extent) * misc_yalign)); + + arrow_rect->width = arrow_rect->height = extent; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrolled_window_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = (state->active) ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + 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); + gtk_paint_box(style, cr, state_type, shadow_type, + widget, "scrolled_window", rect->x - 1, rect->y - 1, + rect->width + 2, rect->height + 2); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_button_paint(cairo_t* cr, GdkRectangle* rect, + 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); + + gtk_paint_box(style, cr, state_type, shadow_type, + 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, cr, state_type, shadow_type, + 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, + cairo_t* cr, GdkRectangle* rect, + 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)); + + gtk_paint_box(style, cr, GTK_STATE_ACTIVE, GTK_SHADOW_IN, + GTK_WIDGET(scrollbar), "trough", rect->x, rect->y, + rect->width, rect->height); + + if (state->focused) { + gtk_paint_focus(style, cr, GTK_STATE_ACTIVE, + 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, + cairo_t* cr, GdkRectangle* rect, + 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; + + 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. */ + adj = gtk_range_get_adjustment(GTK_RANGE(scrollbar)); + + if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) { + gtk_adjustment_set_page_size(adj, rect->width); + } + else { + gtk_adjustment_set_page_size(adj, rect->height); + } + + 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)); + + style = gtk_widget_get_style(GTK_WIDGET(scrollbar)); + + gtk_paint_slider(style, cr, state_type, shadow_type, + 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; +} + +static gint +moz_gtk_scale_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, GtkOrientation flags, + GtkTextDirection direction) +{ + gint x = 0, y = 0; + GtkStateType state_type = ConvertGtkState(state); + GtkStyle* style; + GtkWidget* widget; + + ensure_scale_widget(); + widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gParts->hScaleWidget : gParts->vScaleWidget); + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style(widget); + + if (flags == GTK_ORIENTATION_HORIZONTAL) { + x = XTHICKNESS(style); + y++; + } + else { + x++; + y = YTHICKNESS(style); + } + + gtk_style_apply_default_background(style, cr, gtk_widget_get_window (widget), + GTK_STATE_NORMAL, + rect->x, rect->y, + rect->width, rect->height); + + gtk_paint_box(style, cr, GTK_STATE_ACTIVE, GTK_SHADOW_IN, + widget, "trough", rect->x + x, rect->y + y, + rect->width - 2*x, rect->height - 2*y); + + if (state->focused) + gtk_paint_focus(style, cr, state_type, widget, "trough", + rect->x, rect->y, rect->width, rect->height); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scale_thumb_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, GtkOrientation flags, + GtkTextDirection direction) +{ + GtkStateType state_type = ConvertGtkState(state); + GtkStyle* style; + GtkWidget* widget; + gint thumb_width, thumb_height, x, y; + + ensure_scale_widget(); + widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gParts->hScaleWidget : gParts->vScaleWidget); + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style(widget); + + /* determine the thumb size, and position the thumb in the center in the opposite axis */ + if (flags == GTK_ORIENTATION_HORIZONTAL) { + moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height); + x = rect->x; + y = rect->y + (rect->height - thumb_height) / 2; + } + else { + moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width); + x = rect->x + (rect->width - thumb_width) / 2; + y = rect->y; + } + + gtk_paint_slider(style, cr, state_type, GTK_SHADOW_OUT, + widget, (flags == GTK_ORIENTATION_HORIZONTAL) ? "hscale" : "vscale", + x, y, thumb_width, thumb_height, flags); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_entry_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, GtkWidget* widget, + GtkTextDirection direction) +{ + GtkStateType bg_state = state->disabled ? + GTK_STATE_INSENSITIVE : GTK_STATE_NORMAL; + gint x, y, width = rect->width, height = rect->height; + GtkStyle* style; + gboolean interior_focus; + gboolean theme_honors_transparency = FALSE; + gint focus_width; + + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style(widget); + + gtk_widget_style_get(widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, + "honors-transparent-bg-hint", &theme_honors_transparency, + NULL); + + /* gtkentry.c uses two windows, one for the entire widget and one for the + * text area inside it. The background of both windows is set to the "base" + * color of the new state in gtk_entry_state_changed, but only the inner + * textarea window uses gtk_paint_flat_box when exposed */ + + /* This gets us a lovely greyish disabledish look */ + gtk_widget_set_sensitive(widget, !state->disabled); + + /* GTK fills the outer widget window with the base color before drawing the widget. + * Some older themes rely on this behavior, but many themes nowadays use rounded + * corners on their widgets. While most GTK apps are blissfully unaware of this + * problem due to their use of the default window background, we render widgets on + * many kinds of backgrounds on the web. + * If the theme is able to cope with transparency, then we can skip pre-filling + * and notify the theme it will paint directly on the canvas. */ + if (theme_honors_transparency) { + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(TRUE)); + } else { + cairo_save(cr); + gdk_cairo_set_source_color(cr, (const GdkColor*)&style->base[bg_state]); + cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_REPEAT); + cairo_fill(cr); + cairo_restore(cr); + g_object_set_data(G_OBJECT(widget), "transparent-bg-hint", GINT_TO_POINTER(FALSE)); + } + + /* Get the position of the inner window, see _gtk_entry_get_borders */ + x = XTHICKNESS(style); + y = YTHICKNESS(style); + + if (!interior_focus) { + x += focus_width; + y += focus_width; + } + + /* Simulate an expose of the inner window */ + gtk_paint_flat_box(style, cr, bg_state, GTK_SHADOW_NONE, + widget, "entry_bg", rect->x + x, + rect->y + y, rect->width - 2*x, rect->height - 2*y); + + /* Now paint the shadow and focus border. + * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad + * smaller when focused if the focus is not interior, then the focus. */ + x = rect->x; + y = rect->y; + + if (state->focused && !state->disabled) { + /* This will get us the lit borders that focused textboxes enjoy on + * some themes. */ + if (!interior_focus) { + /* Indent the border a little bit if we have exterior focus + (this is what GTK does to draw native entries) */ + x += focus_width; + y += focus_width; + width -= 2 * focus_width; + height -= 2 * focus_width; + } + } + + gtk_paint_shadow(style, cr, GTK_STATE_NORMAL, GTK_SHADOW_IN, + widget, "entry", x, y, width, height); + + if (state->focused && !state->disabled) { + if (!interior_focus) { + gtk_paint_focus(style, cr, GTK_STATE_NORMAL, + widget, "entry", + rect->x, rect->y, rect->width, rect->height); + } + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_combo_box_paint(cairo_t* cr, GdkRectangle* rect, + GtkWidgetState* state, gboolean ishtml, + GtkTextDirection direction) +{ + GdkRectangle arrow_rect, real_arrow_rect; + gint /* arrow_size, */ separator_width; + gboolean wide_separators; + GtkStateType state_type = ConvertGtkState(state); + GtkShadowType shadow_type = state->active ? GTK_SHADOW_IN : GTK_SHADOW_OUT; + GtkStyle* style; + GtkRequisition arrow_req; + + ensure_combo_box_widgets(); + + /* Also sets the direction on gParts->comboBoxButtonWidget, which is then + * inherited by the separator and arrow */ + moz_gtk_button_paint(cr, rect, state, GTK_RELIEF_NORMAL, + gParts->comboBoxButtonWidget, direction); + + calculate_button_inner_rect(gParts->comboBoxButtonWidget, + rect, &arrow_rect, direction, ishtml); + /* Now arrow_rect contains the inner rect ; we want to correct the width + * to what the arrow needs (see gtk_combo_box_size_allocate) */ + gtk_widget_get_preferred_size(gParts->comboBoxArrowWidget, &arrow_req, NULL); + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x += arrow_rect.width - arrow_req.width; + arrow_rect.width = arrow_req.width; + + calculate_arrow_rect(gParts->comboBoxArrowWidget, + &arrow_rect, &real_arrow_rect, direction); + + style = gtk_widget_get_style(gParts->comboBoxArrowWidget); + + gtk_widget_size_allocate(gParts->comboBoxWidget, rect); + + gtk_paint_arrow(style, cr, state_type, shadow_type, + gParts->comboBoxArrowWidget, "arrow", GTK_ARROW_DOWN, TRUE, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width, real_arrow_rect.height); + + + /* If there is no separator in the theme, there's nothing left to do. */ + if (!gParts->comboBoxSeparatorWidget) + return MOZ_GTK_SUCCESS; + + style = gtk_widget_get_style(gParts->comboBoxSeparatorWidget); + + gtk_widget_style_get(gParts->comboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (wide_separators) { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= separator_width; + else + arrow_rect.x += arrow_rect.width; + + gtk_paint_box(style, cr, + GTK_STATE_NORMAL, GTK_SHADOW_ETCHED_OUT, + gParts->comboBoxSeparatorWidget, "vseparator", + arrow_rect.x, arrow_rect.y, + separator_width, arrow_rect.height); + } else { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= XTHICKNESS(style); + else + arrow_rect.x += arrow_rect.width; + + gtk_paint_vline(style, cr, GTK_STATE_NORMAL, + gParts->comboBoxSeparatorWidget, "vseparator", + arrow_rect.y, arrow_rect.y + arrow_rect.height, + arrow_rect.x); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progressbar_paint(cairo_t* cr, GdkRectangle* rect, + GtkTextDirection direction) +{ + GtkStyle* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gParts->progresWidget, direction); + + style = gtk_widget_get_style(gParts->progresWidget); + + gtk_paint_box(style, cr, GTK_STATE_NORMAL, GTK_SHADOW_IN, + gParts->progresWidget, "trough", rect->x, rect->y, + rect->width, rect->height); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progress_chunk_paint(cairo_t* cr, GdkRectangle* rect, + GtkTextDirection direction) +{ + GtkStyle* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gParts->progresWidget, direction); + + style = gtk_widget_get_style(gParts->progresWidget); + + gtk_paint_box(style, cr, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT, + gParts->progresWidget, "bar", rect->x, rect->y, + rect->width, rect->height); + + 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) { + case MOZ_GTK_BUTTON: + { + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyle *style; + + ensure_button_widget(); + *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gParts->buttonWidget)); + + /* Don't add this padding in HTML, otherwise the buttons will + become too big and stuff the layout. */ + if (!inhtml) { + moz_gtk_widget_get_focus(gParts->buttonWidget, &interior_focus, &focus_width, &focus_pad); + moz_gtk_button_get_inner_border(gParts->buttonWidget, &inner_border); + *left += focus_width + focus_pad + inner_border.left; + *right += focus_width + focus_pad + inner_border.right; + *top += focus_width + focus_pad + inner_border.top; + *bottom += focus_width + focus_pad + inner_border.bottom; + } + + style = gtk_widget_get_style(gParts->buttonWidget); + *left += style->xthickness; + *right += style->xthickness; + *top += style->ythickness; + *bottom += style->ythickness; + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + w = gParts->entryWidget; + break; + case MOZ_GTK_DROPDOWN: + { + /* We need to account for the arrow on the dropdown, so text + * doesn't come too close to the arrow, or in some cases spill + * into the arrow. */ + gboolean ignored_interior_focus, wide_separators; + gint focus_width, focus_pad, separator_width; + GtkRequisition arrow_req; + GtkStyle* style; + + ensure_combo_box_widgets(); + + *left = gtk_container_get_border_width(GTK_CONTAINER(gParts->comboBoxButtonWidget)); + + if (!inhtml) { + moz_gtk_widget_get_focus(gParts->comboBoxButtonWidget, + &ignored_interior_focus, + &focus_width, &focus_pad); + *left += focus_width + focus_pad; + } + + style = gtk_widget_get_style(gParts->comboBoxButtonWidget); + *top = *left + style->ythickness; + *left += style->xthickness; + + *right = *left; *bottom = *top; + + /* If there is no separator, don't try to count its width. */ + separator_width = 0; + if (gParts->comboBoxSeparatorWidget) { + gtk_widget_style_get(gParts->comboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (!wide_separators) + separator_width = + XTHICKNESS(style); + } + + gtk_widget_get_preferred_size(gParts->comboBoxArrowWidget, &arrow_req, NULL); + if (direction == GTK_TEXT_DIR_RTL) + *left += separator_width + arrow_req.width; + else + *right += separator_width + arrow_req.width; + + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_PROGRESSBAR: + ensure_progress_widget(); + w = gParts->progresWidget; + break; + case MOZ_GTK_SCALE_HORIZONTAL: + ensure_scale_widget(); + w = gParts->hScaleWidget; + break; + case MOZ_GTK_SCALE_VERTICAL: + ensure_scale_widget(); + w = gParts->vScaleWidget; + break; + /* These widgets have no borders, since they are not containers. */ + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + 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: + case MOZ_GTK_SCALE_THUMB_HORIZONTAL: + case MOZ_GTK_SCALE_THUMB_VERTICAL: + case MOZ_GTK_PROGRESS_CHUNK: + *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_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height) +{ + GtkWidget* widget; + + ensure_scale_widget(); + widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gParts->hScaleWidget : gParts->vScaleWidget); + + gtk_widget_style_get (widget, + "slider_length", thumb_length, + "slider_width", thumb_height, + NULL); + + 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, cairo_t* cr, + GdkRectangle* rect, GtkWidgetState* state, + gint flags, GtkTextDirection direction) +{ + switch (widget) { + case MOZ_GTK_BUTTON: + if (state->depressed) { + ensure_toggle_button_widget(); + return moz_gtk_button_paint(cr, rect, state, + (GtkReliefStyle) flags, + gParts->toggleButtonWidget, direction); + } + ensure_button_widget(); + return moz_gtk_button_paint(cr, rect, state, + (GtkReliefStyle) flags, gParts->buttonWidget, + direction); + break; + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + return moz_gtk_toggle_paint(cr, rect, state, + !!(flags & MOZ_GTK_WIDGET_CHECKED), + !!(flags & MOZ_GTK_WIDGET_INCONSISTENT), + (widget == MOZ_GTK_RADIOBUTTON), + direction); + break; + case MOZ_GTK_SCROLLBAR_BUTTON: + return moz_gtk_scrollbar_button_paint(cr, rect, state, + (GtkScrollbarButtonFlags) flags, + direction); + break; + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + return moz_gtk_scrollbar_trough_paint(widget, cr, rect, + state, direction); + break; + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + return moz_gtk_scrollbar_thumb_paint(widget, cr, rect, + state, direction); + break; + case MOZ_GTK_SCROLLED_WINDOW: + return moz_gtk_scrolled_window_paint(cr, rect, state); + break; + case MOZ_GTK_SCALE_HORIZONTAL: + case MOZ_GTK_SCALE_VERTICAL: + return moz_gtk_scale_paint(cr, rect, state, + (GtkOrientation) flags, direction); + break; + case MOZ_GTK_SCALE_THUMB_HORIZONTAL: + case MOZ_GTK_SCALE_THUMB_VERTICAL: + return moz_gtk_scale_thumb_paint(cr, rect, state, + (GtkOrientation) flags, direction); + break; + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + return moz_gtk_entry_paint(cr, rect, state, + gParts->entryWidget, direction); + break; + case MOZ_GTK_DROPDOWN: + return moz_gtk_combo_box_paint(cr, rect, state, + (gboolean) flags, direction); + break; + case MOZ_GTK_PROGRESSBAR: + return moz_gtk_progressbar_paint(cr, rect, direction); + break; + case MOZ_GTK_PROGRESS_CHUNK: + return moz_gtk_progress_chunk_paint(cr, rect, direction); + 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; + } +} + +GtkWidget* moz_gtk_get_progress_widget() +{ + if (!is_initialized) + return NULL; + ensure_progress_widget(); + return gParts->progresWidget; +} + +#endif // GTK_API_VERSION_2 diff --git a/WebCore/platform/gtk/gtkdrawing.h b/WebCore/platform/gtk/gtkdrawing.h index c00da97..a981543 100644 --- a/WebCore/platform/gtk/gtkdrawing.h +++ b/WebCore/platform/gtk/gtkdrawing.h @@ -81,7 +81,9 @@ typedef struct { } MozGtkScrollbarMetrics; typedef struct _GtkThemeParts { +#ifdef GTK_API_VERSION_2 GdkColormap* colormap; +#endif // GTK_API_VERSION_2 GtkWidget* protoWindow; GtkWidget* protoLayout; GtkWidget* buttonWidget; @@ -91,7 +93,6 @@ typedef struct _GtkThemeParts { GtkWidget* radiobuttonWidget; GtkWidget* horizScrollbarWidget; GtkWidget* vertScrollbarWidget; - GtkWidget* spinWidget; GtkWidget* hScaleWidget; GtkWidget* vScaleWidget; GtkWidget* entryWidget; @@ -103,28 +104,7 @@ typedef struct _GtkThemeParts { GtkWidget* comboBoxEntryTextareaWidget; GtkWidget* comboBoxEntryButtonWidget; GtkWidget* comboBoxEntryArrowWidget; - GtkWidget* handleBoxWidget; - GtkWidget* toolbarWidget; - GtkWidget* frameWidget; - GtkWidget* statusbarWidget; GtkWidget* progresWidget; - GtkWidget* tabWidget; - GtkWidget* tooltipWidget; - GtkWidget* menuBarWidget; - GtkWidget* menuBarItemWidget; - GtkWidget* menuPopupWidget; - GtkWidget* menuItemWidget; - GtkWidget* imageMenuItemWidget; - GtkWidget* checkMenuItemWidget; - GtkWidget* treeViewWidget; - GtkTreeViewColumn* middleTreeViewColumn; - GtkWidget* treeHeaderCellWidget; - GtkWidget* treeHeaderSortArrowWidget; - GtkWidget* expanderWidget; - GtkWidget* toolbarSeparatorWidget; - GtkWidget* menuSeparatorWidget; - GtkWidget* hpanedWidget; - GtkWidget* vpanedWidget; GtkWidget* scrolledWindowWidget; } GtkThemeParts; @@ -134,24 +114,6 @@ typedef enum { MOZ_GTK_STEPPER_VERTICAL = 1 << 2 } GtkScrollbarButtonFlags; -/** flags for tab state **/ -typedef enum { - /* first eight bits are used to pass a margin */ - MOZ_GTK_TAB_MARGIN_MASK = 0xFF, - /* bottom tabs */ - MOZ_GTK_TAB_BOTTOM = 1 << 8, - /* the first tab in the group */ - MOZ_GTK_TAB_FIRST = 1 << 9, - /* the selected tab */ - MOZ_GTK_TAB_SELECTED = 1 << 10 -} GtkTabFlags; - -/** flags for menuitems **/ -typedef enum { - /* menuitem is part of the menubar */ - MOZ_TOPLEVEL_MENU_ITEM = 1 << 0 -} GtkMenuItemFlags; - /* function type for moz_gtk_enable_style_props */ typedef gint (*style_prop_t)(GtkStyle*, const gchar*, gint); @@ -191,80 +153,13 @@ typedef enum { /* Paints a GtkScale thumb. */ MOZ_GTK_SCALE_THUMB_HORIZONTAL, MOZ_GTK_SCALE_THUMB_VERTICAL, - /* Paints a GtkSpinButton */ - MOZ_GTK_SPINBUTTON, - MOZ_GTK_SPINBUTTON_UP, - MOZ_GTK_SPINBUTTON_DOWN, - MOZ_GTK_SPINBUTTON_ENTRY, - /* Paints the gripper of a GtkHandleBox. */ - MOZ_GTK_GRIPPER, - /* Paints a GtkEntry. */ MOZ_GTK_ENTRY, - /* Paints the native caret (or in GTK-speak: insertion cursor) */ - MOZ_GTK_ENTRY_CARET, /* Paints a GtkOptionMenu. */ MOZ_GTK_DROPDOWN, - /* Paints a dropdown arrow (a GtkButton containing a down GtkArrow). */ - MOZ_GTK_DROPDOWN_ARROW, - /* Paints an entry in an editable option menu */ - MOZ_GTK_DROPDOWN_ENTRY, - /* Paints the container part of a GtkCheckButton. */ - MOZ_GTK_CHECKBUTTON_CONTAINER, - /* Paints the container part of a GtkRadioButton. */ - MOZ_GTK_RADIOBUTTON_CONTAINER, - /* Paints the label of a GtkCheckButton (focus outline) */ - MOZ_GTK_CHECKBUTTON_LABEL, - /* Paints the label of a GtkRadioButton (focus outline) */ - MOZ_GTK_RADIOBUTTON_LABEL, - /* Paints the background of a GtkHandleBox. */ - MOZ_GTK_TOOLBAR, - /* Paints a toolbar separator */ - MOZ_GTK_TOOLBAR_SEPARATOR, - /* Paints a GtkToolTip */ - MOZ_GTK_TOOLTIP, - /* Paints a GtkFrame (e.g. a status bar panel). */ - MOZ_GTK_FRAME, - /* Paints a resize grip for a GtkWindow */ - MOZ_GTK_RESIZER, /* Paints a GtkProgressBar. */ MOZ_GTK_PROGRESSBAR, /* Paints a progress chunk of a GtkProgressBar. */ - MOZ_GTK_PROGRESS_CHUNK, - /* Paints a tab of a GtkNotebook. flags is a GtkTabFlags, defined above. */ - MOZ_GTK_TAB, - /* Paints the background and border of a GtkNotebook. */ - MOZ_GTK_TABPANELS, - /* Paints a GtkArrow for a GtkNotebook. flags is a GtkArrowType. */ - MOZ_GTK_TAB_SCROLLARROW, - /* Paints the background and border of a GtkTreeView */ - MOZ_GTK_TREEVIEW, - /* Paints treeheader cells */ - MOZ_GTK_TREE_HEADER_CELL, - /* Paints sort arrows in treeheader cells */ - MOZ_GTK_TREE_HEADER_SORTARROW, - /* Paints an expander for a GtkTreeView */ - MOZ_GTK_TREEVIEW_EXPANDER, - /* Paints a GtkExpander */ - MOZ_GTK_EXPANDER, - /* Paints the background of the menu bar. */ - MOZ_GTK_MENUBAR, - /* Paints the background of menus, context menus. */ - MOZ_GTK_MENUPOPUP, - /* Paints the arrow of menuitems that contain submenus */ - MOZ_GTK_MENUARROW, - /* Paints an arrow that points down */ - MOZ_GTK_TOOLBARBUTTON_ARROW, - /* Paints items of menubar and popups. */ - MOZ_GTK_MENUITEM, - MOZ_GTK_CHECKMENUITEM, - MOZ_GTK_RADIOMENUITEM, - MOZ_GTK_MENUSEPARATOR, - /* Paints a GtkVPaned separator */ - MOZ_GTK_SPLITTER_HORIZONTAL, - /* Paints a GtkHPaned separator */ - MOZ_GTK_SPLITTER_VERTICAL, - /* Paints the background of a window, dialog or page. */ - MOZ_GTK_WINDOW + MOZ_GTK_PROGRESS_CHUNK } GtkThemeWidgetType; /*** General library functions ***/ @@ -318,12 +213,18 @@ void moz_gtk_destroy_theme_parts_widgets(GtkThemeParts* parts); * flags: widget-dependant flags; see the GtkThemeWidgetType definition. * direction: the text direction, to draw the widget correctly LTR and RTL. */ +#ifdef GTK_API_VERSION_2 gint moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state, gint flags, GtkTextDirection direction); - +#else +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t* cr, + GdkRectangle* rect, GtkWidgetState* state, + gint flags, GtkTextDirection direction); +#endif /*** Widget metrics ***/ /** @@ -362,16 +263,6 @@ moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing); gint moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing); -/** - * Get the inner-border value for a GtkButton widget (button or tree header) - * widget: [IN] the widget to get the border value for - * inner_border: [OUT] the inner border - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint -moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border); - /** Get the focus metrics for a treeheadercell, button, checkbox, or radio button. * widget: [IN] the widget to get the focus metrics for * interior_focus: [OUT] whether the focus is drawn around the @@ -406,91 +297,12 @@ gint moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics* metrics); /** - * Get the desired size of a dropdown arrow button - * width: [OUT] the desired width - * height: [OUT] the desired height - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height); - -/** - * Get the desired size of a scroll arrow widget - * width: [OUT] the desired width - * height: [OUT] the desired height - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height); - -/** - * Get the desired size of a toolbar button dropdown arrow - * width: [OUT] the desired width - * height: [OUT] the desired height - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_downarrow_size(gint* width, gint* height); - -/** - * Get the desired size of a toolbar separator - * size: [OUT] the desired width - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_toolbar_separator_width(gint* size); - -/** - * Get the size of a regular GTK expander that shows/hides content - * size: [OUT] the size of the GTK expander, size = width = height. - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_expander_size(gint* size); - -/** - * Get the size of a treeview's expander (we call them twisties) - * size: [OUT] the size of the GTK expander, size = width = height. - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_treeview_expander_size(gint* size); - -/** - * Get the desired height of a menu separator - * size: [OUT] the desired height - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_get_menu_separator_height(gint* size); - -/** - * Get the desired size of a splitter - * orientation: [IN] GTK_ORIENTATION_HORIZONTAL or GTK_ORIENTATION_VERTICAL - * size: [OUT] width or height of the splitter handle - * - * returns: MOZ_GTK_SUCCESS if there was no error, an error code otherwise - */ -gint moz_gtk_splitter_get_metrics(gint orientation, gint* size); - -/** * Retrieve an actual GTK scrollbar widget for style analysis. It will not * be modified. */ GtkWidget* moz_gtk_get_scrollbar_widget(void); /** - * Get the YTHICKNESS of a tab (notebook extension). - */ -gint moz_gtk_get_tab_thickness(void); - -/** - * Get a boolean which indicates whether or not to use images in menus. - * If TRUE, use images in menus. - */ -gboolean moz_gtk_images_in_menus(void); - -/** * Retrieve an actual GTK progress bar widget for style analysis. It will not * be modified. */ diff --git a/WebCore/platform/image-decoders/ImageDecoder.cpp b/WebCore/platform/image-decoders/ImageDecoder.cpp index 1c3dcf8..10c0b3b 100644 --- a/WebCore/platform/image-decoders/ImageDecoder.cpp +++ b/WebCore/platform/image-decoders/ImageDecoder.cpp @@ -31,6 +31,7 @@ #include "ICOImageDecoder.h" #include "JPEGImageDecoder.h" #include "PNGImageDecoder.h" +#include "WEBPImageDecoder.h" #include "SharedBuffer.h" using namespace std; @@ -78,6 +79,19 @@ ImageDecoder* ImageDecoder::create(const SharedBuffer& data, bool premultiplyAlp if (!memcmp(contents, "\xFF\xD8\xFF", 3)) return new JPEGImageDecoder(premultiplyAlpha); +#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(premultiplyAlpha); + } + } +#endif + // BMP if (strncmp(contents, "BM", 2) == 0) return new BMPImageDecoder(premultiplyAlpha); diff --git a/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp b/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp new file mode 100644 index 0000000..5794fd6 --- /dev/null +++ b/WebCore/platform/image-decoders/webp/WEBPImageDecoder.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WEBPImageDecoder.h" + +#if USE(WEBP) + +#include "webp/decode.h" + +namespace WebCore { + +WEBPImageDecoder::WEBPImageDecoder(bool premultiplyAlpha) + : ImageDecoder(premultiplyAlpha) +{ +} + +WEBPImageDecoder::~WEBPImageDecoder() +{ +} + +bool WEBPImageDecoder::isSizeAvailable() +{ + if (!ImageDecoder::isSizeAvailable()) + decode(true); + + return ImageDecoder::isSizeAvailable(); +} + +RGBA32Buffer* WEBPImageDecoder::frameBufferAtIndex(size_t index) +{ + if (index) + return 0; + + if (m_frameBufferCache.isEmpty()) { + m_frameBufferCache.resize(1); + m_frameBufferCache[0].setPremultiplyAlpha(m_premultiplyAlpha); + } + + RGBA32Buffer& frame = m_frameBufferCache[0]; + if (frame.status() != RGBA32Buffer::FrameComplete) + decode(false); + return &frame; +} + + +bool WEBPImageDecoder::decode(bool onlySize) +{ + // Minimum number of bytes needed to ensure one can parse size information. + static const size_t sizeOfHeader = 30; + // Number of bytes per pixel. + static const int bytesPerPixel = 3; + + if (failed()) + return false; + const size_t dataSize = m_data->buffer().size(); + const uint8_t* dataBytes = + reinterpret_cast<const uint8_t*>(m_data->buffer().data()); + int width, height; + if (dataSize < sizeOfHeader) + return true; + if (!WebPGetInfo(dataBytes, dataSize, &width, &height)) + return setFailed(); + if (onlySize) + return setSize(width, height) || setFailed(); + + // FIXME: Add support for progressive decoding. + if (!isAllDataReceived()) + return true; + if (m_frameBufferCache.isEmpty()) + return true; + RGBA32Buffer& buffer = m_frameBufferCache[0]; + if (buffer.status() == RGBA32Buffer::FrameEmpty) { + ASSERT(width == size().width()); + ASSERT(height == size().height()); + if (!buffer.setSize(width, height)) + return setFailed(); + } + const int stride = width * bytesPerPixel; + Vector<uint8_t> rgb; + rgb.reserveCapacity(height * stride); + if (!WebPDecodeBGRInto(dataBytes, dataSize, &rgb[0], height * stride, stride)) + return setFailed(); + // FIXME: remove this data copy. + for (int y = 0; y < height; ++y) { + const uint8_t* const src = &rgb[y * stride]; + for (int x = 0; x < width; ++x) + buffer.setRGBA(x, y, src[bytesPerPixel * x + 2], src[bytesPerPixel * x + 1], src[bytesPerPixel * x + 0], 0xff); + } + buffer.setStatus(RGBA32Buffer::FrameComplete); + buffer.setHasAlpha(false); + buffer.setRect(IntRect(IntPoint(), size())); + return true; +} + +} + +#endif diff --git a/WebCore/platform/text/StringBuilder.h b/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h index 72adfa7..266c0ff 100644 --- a/WebCore/platform/text/StringBuilder.h +++ b/WebCore/platform/image-decoders/webp/WEBPImageDecoder.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 Apple Inc. All rights reserved. + * Copyright (C) 2010 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,44 +26,31 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef StringBuilder_h -#define StringBuilder_h +#ifndef WEBPImageDecoder_h +#define WEBPImageDecoder_h -#include "PlatformString.h" +#include "ImageDecoder.h" -namespace WebCore { +#if USE(WEBP) -enum ConcatMode { - ConcatUnaltered, - ConcatAddingSpacesBetweenIndividualStrings -}; +namespace WebCore { -class StringBuilder { +class WEBPImageDecoder : public ImageDecoder { public: - StringBuilder() : m_totalLength(UINT_MAX) {} - - void setNonNull() - { - if (m_totalLength == UINT_MAX) - m_totalLength = 0; - } - - void append(const String&); - void append(UChar); - void append(char); - - void clear(); - unsigned length() const; - - String toString(ConcatMode mode = ConcatUnaltered) const; + WEBPImageDecoder(bool premultiplyAlpha); + virtual ~WEBPImageDecoder(); + virtual String filenameExtension() const { return "vp8"; } + virtual bool isSizeAvailable(); + virtual RGBA32Buffer* frameBufferAtIndex(size_t index); + virtual bool supportsAlpha() const { return false; } private: - bool isNull() const { return m_totalLength == UINT_MAX; } - - unsigned m_totalLength; - Vector<String, 16> m_strings; + // Returns false in case of decoding failure. + bool decode(bool onlySize); }; -} +} // namespace WebCore + +#endif #endif diff --git a/WebCore/platform/mac/Language.mm b/WebCore/platform/mac/Language.mm index 96caaa6..bb51cb5 100644 --- a/WebCore/platform/mac/Language.mm +++ b/WebCore/platform/mac/Language.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003, 2006 Apple Computer, Inc. All rights reserved. + * Copyright (C) 2003, 2005, 2006, 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 @@ -10,32 +10,106 @@ * 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. + * 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. */ #import "config.h" #import "Language.h" #import "BlockExceptions.h" -#import "PlatformString.h" -#import "WebCoreViewFactory.h" +#import "WebCoreSystemInterface.h" +#import <wtf/Assertions.h> +#import <wtf/MainThread.h> +#import <wtf/text/WTFString.h> + +using namespace WebCore; + +static NSString *preferredLanguageCode; + +@interface LanguageChangeObserver : NSObject { +} +@end + +@implementation LanguageChangeObserver + ++ (void)_webkit_languagePreferencesDidChange +{ + ASSERT(isMainThread()); + + [preferredLanguageCode release]; + preferredLanguageCode = nil; + + languageDidChange(); +} + +@end namespace WebCore { -String defaultLanguage() +static NSString *createHTTPStyleLanguageCode(NSString *languageCode) { + ASSERT(isMainThread()); + + // Look up the language code using CFBundle. + CFStringRef preferredLanguageCode = wkCopyCFLocalizationPreferredName((CFStringRef)languageCode); + + if (preferredLanguageCode) + languageCode = (NSString *)preferredLanguageCode; + + // Make the string lowercase. + NSString *lowercaseLanguageCode = [languageCode lowercaseString]; + + // Turn a '_' into a '-' if it appears after a 2-letter language code. + if ([lowercaseLanguageCode length] < 3 || [lowercaseLanguageCode characterAtIndex:2] != '_') + return lowercaseLanguageCode; + + NSMutableString *result = [lowercaseLanguageCode mutableCopy]; + [result replaceCharactersInRange:NSMakeRange(2, 1) withString:@"-"]; + + if (preferredLanguageCode) + CFRelease(preferredLanguageCode); + + return result; +} + +String platformDefaultLanguage() +{ + ASSERT(isMainThread()); + BEGIN_BLOCK_OBJC_EXCEPTIONS; - return [[WebCoreViewFactory sharedFactory] defaultLanguageCode]; + + if (!preferredLanguageCode) { + [[NSUserDefaults standardUserDefaults] synchronize]; + NSArray *languages = [[NSUserDefaults standardUserDefaults] stringArrayForKey:@"AppleLanguages"]; + if (![languages count]) + preferredLanguageCode = @"en"; + else + preferredLanguageCode = createHTTPStyleLanguageCode([languages objectAtIndex:0]); + } + + NSString *code = [[preferredLanguageCode retain] autorelease]; + + static bool languageChangeObserverAdded; + if (!languageChangeObserverAdded) { + [[NSDistributedNotificationCenter defaultCenter] addObserver:[LanguageChangeObserver self] + selector:@selector(_webkit_languagePreferencesDidChange) + name:@"AppleLanguagePreferencesChangedNotification" + object:nil]; + languageChangeObserverAdded = true; + } + + return code; + END_BLOCK_OBJC_EXCEPTIONS; return String(); } diff --git a/WebCore/platform/mac/LoggingMac.mm b/WebCore/platform/mac/LoggingMac.mm index 3e83579..ee2f39e 100644 --- a/WebCore/platform/mac/LoggingMac.mm +++ b/WebCore/platform/mac/LoggingMac.mm @@ -62,6 +62,7 @@ void InitializeLoggingChannelsIfNecessary() initializeWithUserDefault(LogHistory); initializeWithUserDefault(LogPageCache); initializeWithUserDefault(LogPlatformLeaks); + initializeWithUserDefault(LogResourceLoading); initializeWithUserDefault(LogNetwork); initializeWithUserDefault(LogFTP); initializeWithUserDefault(LogThreading); diff --git a/WebCore/platform/mac/ScrollbarThemeMac.mm b/WebCore/platform/mac/ScrollbarThemeMac.mm index bfa584a..ce3be1a 100644 --- a/WebCore/platform/mac/ScrollbarThemeMac.mm +++ b/WebCore/platform/mac/ScrollbarThemeMac.mm @@ -378,10 +378,9 @@ bool ScrollbarThemeMac::paint(Scrollbar* scrollbar, GraphicsContext* context, co trackInfo.enableState = kThemeTrackNothingToScroll; trackInfo.trackInfo.scrollbar.pressState = scrollbarPartToHIPressedState(scrollbar->pressedPart()); - CGAffineTransform currentCTM = CGContextGetCTM(context->platformContext()); - // The Aqua scrollbar is buggy when rotated and scaled. We will just draw into a bitmap if we detect a scale or rotation. - bool canDrawDirectly = currentCTM.a == 1.0f && currentCTM.b == 0.0f && currentCTM.c == 0.0f && (currentCTM.d == 1.0f || currentCTM.d == -1.0f); + const AffineTransform& currentCTM = context->getCTM(); + bool canDrawDirectly = currentCTM.isIdentityOrTranslationOrFlipped(); if (canDrawDirectly) HIThemeDrawTrack(&trackInfo, 0, context->platformContext(), kHIThemeOrientationNormal); else { @@ -396,7 +395,7 @@ bool ScrollbarThemeMac::paint(Scrollbar* scrollbar, GraphicsContext* context, co return true; HIThemeDrawTrack(&trackInfo, 0, imageBuffer->context()->platformContext(), kHIThemeOrientationNormal); - context->drawImageBuffer(imageBuffer.get(), DeviceColorSpace, scrollbar->frameRect().location()); + context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, scrollbar->frameRect().location()); } return true; diff --git a/WebCore/platform/mac/SharedTimerMac.mm b/WebCore/platform/mac/SharedTimerMac.mm index b9eaaef..cc9ff17 100644 --- a/WebCore/platform/mac/SharedTimerMac.mm +++ b/WebCore/platform/mac/SharedTimerMac.mm @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006 Apple Inc. All rights reserved. + * Copyright (C) 2006, 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 @@ -26,51 +26,131 @@ #import "config.h" #import "SharedTimer.h" +#import <IOKit/IOMessage.h> +#import <IOKit/pwr_mgt/IOPMLib.h> #import <wtf/Assertions.h> +#import <wtf/Noncopyable.h> +#import <wtf/PassOwnPtr.h> #import <wtf/UnusedParam.h> -@class WebCorePowerNotifier; +#include <stdio.h> + +// On Snow Leopard and newer we'll ask IOKit to deliver notifications on a queue. +#if defined(BUILDING_ON_TIGER) || defined(BUILDING_ON_LEOPARD) +#define IOKIT_WITHOUT_LIBDISPATCH 1 +#endif namespace WebCore { -static WebCorePowerNotifier *powerNotifier; static CFRunLoopTimerRef sharedTimer; static void (*sharedTimerFiredFunction)(); static void timerFired(CFRunLoopTimerRef, void*); +#if !defined(IOKIT_WITHOUT_LIBDISPATCH) && defined(BUILDING_ON_SNOW_LEOPARD) +extern "C" void IONotificationPortSetDispatchQueue(IONotificationPortRef notify, dispatch_queue_t queue); +#endif + +class PowerObserver { + WTF_MAKE_NONCOPYABLE(PowerObserver); + +public: + static PassOwnPtr<PowerObserver> create() + { + return adoptPtr(new PowerObserver); + } + ~PowerObserver(); + +private: + PowerObserver(); + + static void didReceiveSystemPowerNotification(void* context, io_service_t, uint32_t messageType, void* messageArgument); + void didReceiveSystemPowerNotification(io_service_t, uint32_t messageType, void* messageArgument); + + void restartSharedTimer(); + + io_connect_t m_powerConnection; + IONotificationPortRef m_notificationPort; + io_object_t m_notifierReference; +#ifdef IOKIT_WITHOUT_LIBDISPATCH + CFRunLoopSourceRef m_runLoopSource; +#else + dispatch_queue_t m_dispatchQueue; +#endif +}; + +PowerObserver::PowerObserver() + : m_powerConnection(0) + , m_notificationPort(0) + , m_notifierReference(0) +#ifdef IOKIT_WITHOUT_LIBDISPATCH + , m_runLoopSource(0) +#else + , m_dispatchQueue(dispatch_queue_create("com.apple.WebKit.PowerObserver", 0)) +#endif +{ + m_powerConnection = IORegisterForSystemPower(this, &m_notificationPort, didReceiveSystemPowerNotification, &m_notifierReference); + if (!m_powerConnection) + return; + +#ifdef IOKIT_WITHOUT_LIBDISPATCH + m_runLoopSource = IONotificationPortGetRunLoopSource(m_notificationPort); + CFRunLoopAddSource(CFRunLoopGetMain(), m_runLoopSource, kCFRunLoopCommonModes); +#else + IONotificationPortSetDispatchQueue(m_notificationPort, m_dispatchQueue); +#endif } -@interface WebCorePowerNotifier : NSObject -@end +PowerObserver::~PowerObserver() +{ + if (!m_powerConnection) + return; + +#ifdef IOKIT_WITHOUT_LIBDISPATCH + CFRunLoopRemoveSource(CFRunLoopGetMain(), m_runLoopSource, kCFRunLoopCommonModes); +#else + dispatch_release(m_dispatchQueue); +#endif + + IODeregisterForSystemPower(&m_notifierReference); + IOServiceClose(m_powerConnection); + IONotificationPortDestroy(m_notificationPort); +} -@implementation WebCorePowerNotifier +void PowerObserver::didReceiveSystemPowerNotification(void* context, io_service_t service, uint32_t messageType, void* messageArgument) +{ + static_cast<PowerObserver*>(context)->didReceiveSystemPowerNotification(service, messageType, messageArgument); +} -- (id)init +void PowerObserver::didReceiveSystemPowerNotification(io_service_t, uint32_t messageType, void* messageArgument) { - self = [super init]; - - if (self) - [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self - selector:@selector(didWake:) - name:NSWorkspaceDidWakeNotification - object:nil]; - - return self; + IOAllowPowerChange(m_powerConnection, reinterpret_cast<long>(messageArgument)); + + // We only care about the "wake from sleep" message. + if (messageType != kIOMessageSystemWillPowerOn) + return; + +#ifdef IOKIT_WITHOUT_LIBDISPATCH + restartSharedTimer(); +#else + // We need to restart the timer on the main thread. + CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^() { + restartSharedTimer(); + }); +#endif } -- (void)didWake:(NSNotification *)unusedNotification +void PowerObserver::restartSharedTimer() { - UNUSED_PARAM(unusedNotification); + ASSERT(CFRunLoopGetCurrent() == CFRunLoopGetMain()); - if (WebCore::sharedTimer) { - WebCore::stopSharedTimer(); - WebCore::timerFired(0, 0); - } -} + if (!sharedTimer) + return; -@end + stopSharedTimer(); + timerFired(0, 0); +} -namespace WebCore { +static PowerObserver* PowerObserver; void setSharedTimerFiredFunction(void (*f)()) { @@ -100,11 +180,8 @@ void setSharedTimerFireTime(double fireTime) sharedTimer = CFRunLoopTimerCreate(0, fireDate, 0, 0, 0, timerFired, 0); CFRunLoopAddTimer(CFRunLoopGetCurrent(), sharedTimer, kCFRunLoopCommonModes); - if (!powerNotifier) { - powerNotifier = [[WebCorePowerNotifier alloc] init]; - CFRetain(powerNotifier); - [powerNotifier release]; - } + if (!PowerObserver) + PowerObserver = PowerObserver::create().leakPtr(); } void stopSharedTimer() @@ -116,4 +193,4 @@ void stopSharedTimer() } } -} +} // namespace WebCore diff --git a/WebCore/platform/mac/WebCoreSystemInterface.h b/WebCore/platform/mac/WebCoreSystemInterface.h index 0cc7fd5..7cac443 100644 --- a/WebCore/platform/mac/WebCoreSystemInterface.h +++ b/WebCore/platform/mac/WebCoreSystemInterface.h @@ -94,6 +94,7 @@ extern CFReadStreamRef (*wkCreateCustomCFReadStream)(void *(*formCreate)(CFReadS void (*formSchedule)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void *), void (*formUnschedule)(CFReadStreamRef, CFRunLoopRef, CFStringRef, void *), void *context); +extern CFStringRef (*wkCopyCFLocalizationPreferredName)(CFStringRef); extern NSString* (*wkCopyNSURLResponseStatusLine)(NSURLResponse*); extern id (*wkCreateNSURLConnectionDelegateProxy)(void); extern void (*wkDrawBezeledTextFieldCell)(NSRect, BOOL enabled); diff --git a/WebCore/platform/mac/WebCoreSystemInterface.mm b/WebCore/platform/mac/WebCoreSystemInterface.mm index 309a8fb..52e0064 100644 --- a/WebCore/platform/mac/WebCoreSystemInterface.mm +++ b/WebCore/platform/mac/WebCoreSystemInterface.mm @@ -29,6 +29,7 @@ void (*wkAdvanceDefaultButtonPulseAnimation)(NSButtonCell *); BOOL (*wkCGContextGetShouldSmoothFonts)(CGContextRef); +CFStringRef (*wkCopyCFLocalizationPreferredName)(CFStringRef); NSString* (*wkCopyNSURLResponseStatusLine)(NSURLResponse*); NSString* (*wkCreateURLPasteboardFlavorTypeName)(void); NSString* (*wkCreateURLNPasteboardFlavorTypeName)(void); diff --git a/WebCore/platform/network/BlobData.cpp b/WebCore/platform/network/BlobData.cpp index 21e8917..ff39ecc 100644 --- a/WebCore/platform/network/BlobData.cpp +++ b/WebCore/platform/network/BlobData.cpp @@ -31,40 +31,46 @@ #include "config.h" #include "BlobData.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassOwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + namespace WebCore { const long long BlobDataItem::toEndOfFile = -1; const double BlobDataItem::doNotCheckFileChange = 0; -void BlobDataItem::copy(const BlobDataItem& item) +RawData::RawData() { - type = item.type; - data = item.data; // This is OK because the underlying storage is Vector<char>. - path = item.path.crossThreadString(); - url = item.url.copy(); - offset = item.offset; - length = item.length; - expectedModificationTime = item.expectedModificationTime; } -PassOwnPtr<BlobData> BlobData::copy() const +void RawData::detachFromCurrentThread() { - OwnPtr<BlobData> blobData = adoptPtr(new BlobData()); - blobData->m_contentType = m_contentType.crossThreadString(); - blobData->m_contentDisposition = m_contentDisposition.crossThreadString(); - blobData->m_items.resize(m_items.size()); - for (size_t i = 0; i < m_items.size(); ++i) - blobData->m_items.at(i).copy(m_items.at(i)); +} + +void BlobDataItem::detachFromCurrentThread() +{ + data->detachFromCurrentThread(); + path = path.crossThreadString(); + url = url.copy(); +} - return blobData.release(); +PassOwnPtr<BlobData> BlobData::create() +{ + return adoptPtr(new BlobData()); } -void BlobData::appendData(const CString& data) +void BlobData::detachFromCurrentThread() { - m_items.append(BlobDataItem(data)); + m_contentType = m_contentType.crossThreadString(); + m_contentDisposition = m_contentDisposition.crossThreadString(); + for (size_t i = 0; i < m_items.size(); ++i) + m_items.at(i).detachFromCurrentThread(); } -void BlobData::appendData(const CString& data, long long offset, long long length) +void BlobData::appendData(PassRefPtr<RawData> data, long long offset, long long length) { m_items.append(BlobDataItem(data, offset, length)); } diff --git a/WebCore/platform/network/BlobData.h b/WebCore/platform/network/BlobData.h index 13e3b9c..1ff6344 100644 --- a/WebCore/platform/network/BlobData.h +++ b/WebCore/platform/network/BlobData.h @@ -33,12 +33,30 @@ #include "KURL.h" #include "PlatformString.h" -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> -#include <wtf/text/CString.h> +#include <wtf/Forward.h> +#include <wtf/ThreadSafeShared.h> namespace WebCore { +class RawData : public ThreadSafeShared<RawData> { +public: + static PassRefPtr<RawData> create() + { + return adoptRef(new RawData()); + } + + void detachFromCurrentThread(); + + const char* data() const { return m_data.data(); } + size_t length() const { return m_data.size(); } + Vector<char>* mutableData() { return &m_data; } + +private: + RawData(); + + Vector<char> m_data; +}; + struct BlobDataItem { static const long long toEndOfFile; static const double doNotCheckFileChange; @@ -53,7 +71,7 @@ struct BlobDataItem { } // Constructor for String type (complete string). - explicit BlobDataItem(const CString& data) + explicit BlobDataItem(PassRefPtr<RawData> data) : type(Data) , data(data) , offset(0) @@ -62,16 +80,6 @@ struct BlobDataItem { { } - // Constructor for String type (partial string). - BlobDataItem(const CString& data, long long offset, long long length) - : type(Data) - , data(data) - , offset(offset) - , length(length) - , expectedModificationTime(doNotCheckFileChange) - { - } - // Constructor for File type (complete file). explicit BlobDataItem(const String& path) : type(File) @@ -102,13 +110,13 @@ struct BlobDataItem { { } - // Gets a copy of the data suitable for passing to another thread. - void copy(const BlobDataItem&); + // Detaches from current thread so that it can be passed to another thread. + void detachFromCurrentThread(); enum { Data, File, Blob } type; // For Data type. - CString data; + RefPtr<RawData> data; // For File type. String path; @@ -119,19 +127,29 @@ struct BlobDataItem { long long offset; long long length; double expectedModificationTime; + +private: + friend class BlobData; + + // Constructor for String type (partial string). + BlobDataItem(PassRefPtr<RawData> data, long long offset, long long length) + : type(Data) + , data(data) + , offset(offset) + , length(length) + , expectedModificationTime(doNotCheckFileChange) + { + } }; typedef Vector<BlobDataItem> BlobDataItemList; class BlobData { public: - static PassOwnPtr<BlobData> create() - { - return adoptPtr(new BlobData()); - } + static PassOwnPtr<BlobData> create(); - // Gets a copy of the data suitable for passing to another thread. - PassOwnPtr<BlobData> copy() const; + // Detaches from current thread so that it can be passed to another thread. + void detachFromCurrentThread(); const String& contentType() const { return m_contentType; } void setContentType(const String& contentType) { m_contentType = contentType; } @@ -141,8 +159,8 @@ public: const BlobDataItemList& items() const { return m_items; } void swapItems(BlobDataItemList&); - - void appendData(const CString&); + + void appendData(PassRefPtr<RawData>, long long offset, long long length); void appendFile(const String& path); void appendFile(const String& path, long long offset, long long length, double expectedModificationTime); void appendBlob(const KURL&, long long offset, long long length); @@ -154,7 +172,7 @@ private: BlobData() { } // This is only exposed to BlobStorageData. - void appendData(const CString&, long long offset, long long length); + void appendData(const RawData&, long long offset, long long length); String m_contentType; String m_contentDisposition; diff --git a/WebCore/platform/network/BlobRegistryImpl.cpp b/WebCore/platform/network/BlobRegistryImpl.cpp index c5beb64..2c4e8fa 100644 --- a/WebCore/platform/network/BlobRegistryImpl.cpp +++ b/WebCore/platform/network/BlobRegistryImpl.cpp @@ -134,7 +134,7 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, PassOwnPtr<BlobData> blo for (BlobDataItemList::const_iterator iter = blobData->items().begin(); iter != blobData->items().end(); ++iter) { switch (iter->type) { case BlobDataItem::Data: - blobStorageData->m_data.appendData(iter->data, 0, iter->data.length()); + blobStorageData->m_data.appendData(iter->data, 0, iter->data->length()); break; case BlobDataItem::File: blobStorageData->m_data.appendFile(iter->path, iter->offset, iter->length, iter->expectedModificationTime); @@ -158,10 +158,7 @@ void BlobRegistryImpl::registerBlobURL(const KURL& url, const KURL& srcURL) if (!src) return; - RefPtr<BlobStorageData> blobStorageData = BlobStorageData::create(src->contentType(), src->contentDisposition()); - appendStorageItems(blobStorageData.get(), src->items()); - - m_blobs.set(url.string(), blobStorageData); + m_blobs.set(url.string(), src); } void BlobRegistryImpl::unregisterBlobURL(const KURL& url) diff --git a/WebCore/platform/network/BlobResourceHandle.cpp b/WebCore/platform/network/BlobResourceHandle.cpp index 48ac2c0..753052a 100644 --- a/WebCore/platform/network/BlobResourceHandle.cpp +++ b/WebCore/platform/network/BlobResourceHandle.cpp @@ -359,7 +359,7 @@ int BlobResourceHandle::readDataSync(const BlobDataItem& item, char* buf, int le int bytesToRead = (length > remaining) ? static_cast<int>(remaining) : length; if (bytesToRead > m_totalRemainingSize) bytesToRead = static_cast<int>(m_totalRemainingSize); - memcpy(buf, item.data.data() + item.offset + m_currentItemReadSize, bytesToRead); + memcpy(buf, item.data->data() + item.offset + m_currentItemReadSize, bytesToRead); m_totalRemainingSize -= bytesToRead; m_currentItemReadSize += bytesToRead; @@ -434,7 +434,7 @@ void BlobResourceHandle::readDataAsync(const BlobDataItem& item) long long bytesToRead = item.length - m_currentItemReadSize; if (bytesToRead > m_totalRemainingSize) bytesToRead = m_totalRemainingSize; - consumeData(item.data.data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead)); + consumeData(item.data->data() + item.offset + m_currentItemReadSize, static_cast<int>(bytesToRead)); m_currentItemReadSize = 0; } diff --git a/WebCore/platform/network/CredentialStorage.cpp b/WebCore/platform/network/CredentialStorage.cpp index 38f71a4..428181d 100644 --- a/WebCore/platform/network/CredentialStorage.cpp +++ b/WebCore/platform/network/CredentialStorage.cpp @@ -29,7 +29,7 @@ #include "Credential.h" #include "KURL.h" #include "ProtectionSpaceHash.h" -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/text/StringHash.h> #include <wtf/HashMap.h> #include <wtf/HashSet.h> @@ -60,9 +60,9 @@ static PathToDefaultProtectionSpaceMap& pathToDefaultProtectionSpaceMap() static String originStringFromURL(const KURL& url) { if (url.port()) - return url.protocol() + "://" + url.host() + String::format(":%i/", url.port()); - - return url.protocol() + "://" + url.host() + "/"; + return makeString(url.protocol(), "://", url.host(), ':', String::number(url.port()), '/'); + + return makeString(url.protocol(), "://", url.host(), '/'); } static String protectionSpaceMapKeyFromURL(const KURL& url) diff --git a/WebCore/platform/network/DataURL.cpp b/WebCore/platform/network/DataURL.cpp new file mode 100644 index 0000000..5de1b34 --- /dev/null +++ b/WebCore/platform/network/DataURL.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2007 Alp Toker <alp@atoker.com> + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "DataURL.h" + +#include "Base64.h" +#include "HTTPParsers.h" +#include "ResourceHandle.h" +#include "ResourceHandleClient.h" +#include "ResourceRequest.h" +#include "ResourceResponse.h" +#include "TextEncoding.h" +#include <wtf/text/CString.h> + +namespace WebCore { + +void handleDataURL(ResourceHandle* handle) +{ + ASSERT(handle->firstRequest().url().protocolIs("data")); + String url = handle->firstRequest().url().string(); + + int index = url.find(','); + if (index == -1) { + handle->client()->cannotShowURL(handle); + return; + } + + String mediaType = url.substring(5, index - 5); + String data = url.substring(index + 1); + + bool base64 = mediaType.endsWith(";base64", false); + if (base64) + mediaType = mediaType.left(mediaType.length() - 7); + + if (mediaType.isEmpty()) + mediaType = "text/plain;charset=US-ASCII"; + + String mimeType = extractMIMETypeFromMediaType(mediaType); + String charset = extractCharsetFromMediaType(mediaType); + + ResourceResponse response; + response.setMimeType(mimeType); + response.setTextEncodingName(charset); + response.setURL(handle->firstRequest().url()); + + if (base64) { + data = decodeURLEscapeSequences(data); + handle->client()->didReceiveResponse(handle, response); + + Vector<char> out; + if (base64Decode(data, out, IgnoreWhitespace) && out.size() > 0) { + response.setExpectedContentLength(out.size()); + handle->client()->didReceiveData(handle, out.data(), out.size(), 0); + } + } else { + data = decodeURLEscapeSequences(data, TextEncoding(charset)); + handle->client()->didReceiveResponse(handle, response); + + CString encodedData = TextEncoding().encode(data.characters(), data.length(), URLEncodedEntitiesForUnencodables); + response.setExpectedContentLength(encodedData.length()); + if (encodedData.length()) + handle->client()->didReceiveData(handle, encodedData.data(), encodedData.length(), 0); + } + + handle->client()->didFinishLoading(handle, 0); +} + +} // namespace WebCore diff --git a/WebCore/platform/network/DataURL.h b/WebCore/platform/network/DataURL.h new file mode 100644 index 0000000..33076a0 --- /dev/null +++ b/WebCore/platform/network/DataURL.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 DataURL_h +#define DataURL_h + +namespace WebCore { + +class ResourceHandle; + +void handleDataURL(ResourceHandle*); + +} + +#endif // DataURL_h diff --git a/WebCore/platform/network/HTTPHeaderMap.cpp b/WebCore/platform/network/HTTPHeaderMap.cpp index e304ffa..92079a0 100644 --- a/WebCore/platform/network/HTTPHeaderMap.cpp +++ b/WebCore/platform/network/HTTPHeaderMap.cpp @@ -37,6 +37,14 @@ using namespace std; namespace WebCore { +HTTPHeaderMap::HTTPHeaderMap() +{ +} + +HTTPHeaderMap::~HTTPHeaderMap() +{ +} + PassOwnPtr<CrossThreadHTTPHeaderMapData> HTTPHeaderMap::copyData() const { OwnPtr<CrossThreadHTTPHeaderMapData> data(new CrossThreadHTTPHeaderMapData()); @@ -58,7 +66,17 @@ void HTTPHeaderMap::adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData> data) set(header.first, header.second); } } - + +String HTTPHeaderMap::get(const AtomicString& name) const +{ + return HashMap<AtomicString, String, CaseFoldingHash>::get(name); +} + +pair<HTTPHeaderMap::iterator, bool> HTTPHeaderMap::add(const AtomicString& name, const String& value) +{ + return HashMap<AtomicString, String, CaseFoldingHash>::add(name, value); +} + // Adapter that allows the HashMap to take C strings as keys. struct CaseFoldingCStringTranslator { static unsigned hash(const char* cString) diff --git a/WebCore/platform/network/HTTPHeaderMap.h b/WebCore/platform/network/HTTPHeaderMap.h index c6145bd..02071e3 100644 --- a/WebCore/platform/network/HTTPHeaderMap.h +++ b/WebCore/platform/network/HTTPHeaderMap.h @@ -41,20 +41,17 @@ namespace WebCore { class HTTPHeaderMap : public HashMap<AtomicString, String, CaseFoldingHash> { public: + HTTPHeaderMap(); + ~HTTPHeaderMap(); + // Gets a copy of the data suitable for passing to another thread. PassOwnPtr<CrossThreadHTTPHeaderMapData> copyData() const; void adopt(PassOwnPtr<CrossThreadHTTPHeaderMapData>); - String get(const AtomicString& name) const - { - return HashMap<AtomicString, String, CaseFoldingHash>::get(name); - } + String get(const AtomicString& name) const; - pair<iterator, bool> add(const AtomicString& name, const String& value) - { - return HashMap<AtomicString, String, CaseFoldingHash>::add(name, value); - } + pair<iterator, bool> add(const AtomicString& name, const String& value); // Alternate accessors that are faster than converting the char* to AtomicString first. bool contains(const char*) const; diff --git a/WebCore/platform/network/ProxyServer.cpp b/WebCore/platform/network/ProxyServer.cpp new file mode 100644 index 0000000..7ef283b --- /dev/null +++ b/WebCore/platform/network/ProxyServer.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (C) 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 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. + */ + +#include "config.h" +#include "ProxyServer.h" + +#include <wtf/text/StringBuilder.h> + +namespace WebCore { + +static void appendProxyServerString(StringBuilder& builder, const ProxyServer& proxyServer) +{ + switch (proxyServer.type()) { + case ProxyServer::Direct: + builder.append("DIRECT"); + return; + case ProxyServer::HTTP: + case ProxyServer::HTTPS: + builder.append("PROXY"); + break; + case ProxyServer::SOCKS: + builder.append("SOCKS"); + break; + } + + builder.append(' '); + + ASSERT(!proxyServer.hostName().isNull()); + builder.append(proxyServer.hostName()); + + builder.append(':'); + ASSERT(proxyServer.port() != -1); + builder.append(String::number(proxyServer.port())); +} + +String toString(const Vector<ProxyServer>& proxyServers) +{ + if (proxyServers.isEmpty()) + return "DIRECT"; + + StringBuilder stringBuilder; + for (size_t i = 0; i < proxyServers.size(); ++i) { + if (i) + stringBuilder.append("; "); + + appendProxyServerString(stringBuilder, proxyServers[i]); + } + + return stringBuilder.toString(); +} + + +} // namespace WebCore diff --git a/WebCore/platform/network/ProxyServer.h b/WebCore/platform/network/ProxyServer.h new file mode 100644 index 0000000..8f7612e --- /dev/null +++ b/WebCore/platform/network/ProxyServer.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 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 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 ProxyServer_h +#define ProxyServer_h + +#include <wtf/Vector.h> +#include <wtf/text/WTFString.h> + +namespace WebCore { + +class KURL; +class NetworkingContext; + +// Represents a single proxy server. +class ProxyServer { +public: + enum Type { + Direct, + HTTP, + HTTPS, + SOCKS, + }; + + ProxyServer() + : m_type(Direct) + , m_port(-1) + { + } + + ProxyServer(Type type, const String& hostName, int port) + : m_type(type) + , m_hostName(hostName) + , m_port(port) + { + } + + Type type() const { return m_type; } + const String& hostName() const { return m_hostName; } + int port() const { return m_port; } + +private: + Type m_type; + String m_hostName; + int m_port; +}; + +// Return a vector of proxy servers for the given URL. +Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*); + +// Converts the given vector of proxy servers to a PAC string, as described in +// http://web.archive.org/web/20060424005037/wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html +String toString(const Vector<ProxyServer>&); + +} // namespace WebCore + +#endif // ProxyServer_h diff --git a/WebCore/platform/network/ResourceHandle.h b/WebCore/platform/network/ResourceHandle.h index 2ea42b1..bb94b59 100644 --- a/WebCore/platform/network/ResourceHandle.h +++ b/WebCore/platform/network/ResourceHandle.h @@ -94,16 +94,6 @@ class ResourceHandle : public RefCounted<ResourceHandle> , public AuthenticationClient #endif { -protected: - ResourceHandle(const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); - -private: - enum FailureType { - NoFailure, - BlockedFailure, - InvalidURLFailure - }; - public: static PassRefPtr<ResourceHandle> create(NetworkingContext*, const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); static void loadResourceSynchronously(NetworkingContext*, const ResourceRequest&, StoredCredentials, ResourceError&, ResourceResponse&, Vector<char>& data); @@ -203,7 +193,16 @@ public: using RefCounted<ResourceHandle>::ref; using RefCounted<ResourceHandle>::deref; +protected: + ResourceHandle(const ResourceRequest&, ResourceHandleClient*, bool defersLoading, bool shouldContentSniff); + private: + enum FailureType { + NoFailure, + BlockedFailure, + InvalidURLFailure + }; + void platformSetDefersLoading(bool); void scheduleFailure(FailureType); diff --git a/WebCore/platform/network/ResourceHandleInternal.h b/WebCore/platform/network/ResourceHandleInternal.h index 96fbf00..9cccecd 100644 --- a/WebCore/platform/network/ResourceHandleInternal.h +++ b/WebCore/platform/network/ResourceHandleInternal.h @@ -46,6 +46,8 @@ #endif #if USE(SOUP) +#include "soup-requester.h" +#include <GRefPtr.h> #include <libsoup/soup.h> class Frame; #endif @@ -108,15 +110,11 @@ namespace WebCore { , m_formDataStream(loader) #endif #if USE(SOUP) - , m_msg(0) , m_cancelled(false) - , m_gfile(0) - , m_inputStream(0) - , m_cancellable(0) , m_buffer(0) - , m_bufferSize(0) , m_total(0) , m_idleHandler(0) + , m_gotChunkHandler(0) #endif #if PLATFORM(QT) , m_job(0) @@ -133,6 +131,9 @@ namespace WebCore { m_user = url.user(); m_pass = url.pass(); m_firstRequest.removeCredentials(); +#if USE(SOUP) + m_requester = adoptPlatformRef(webkit_soup_requester_new()); +#endif } ~ResourceHandleInternal(); @@ -185,16 +186,18 @@ namespace WebCore { Vector<char> m_postBytes; #endif #if USE(SOUP) - SoupMessage* m_msg; + PlatformRefPtr<SoupMessage> m_soupMessage; ResourceResponse m_response; bool m_cancelled; - GFile* m_gfile; - GInputStream* m_inputStream; - GCancellable* m_cancellable; + PlatformRefPtr<WebKitSoupRequest> m_soupRequest; + PlatformRefPtr<WebKitSoupRequester> m_requester; + PlatformRefPtr<GInputStream> m_inputStream; + PlatformRefPtr<GCancellable> m_cancellable; char* m_buffer; - gsize m_bufferSize, m_total; + gsize m_total; guint m_idleHandler; RefPtr<NetworkingContext> m_context; + gulong m_gotChunkHandler; #endif #if PLATFORM(QT) QNetworkReplyHandler* m_job; diff --git a/WebCore/platform/network/ResourceRawHeaders.h b/WebCore/platform/network/ResourceRawHeaders.h new file mode 100644 index 0000000..e5f8d99 --- /dev/null +++ b/WebCore/platform/network/ResourceRawHeaders.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 Google, Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef ResourceRawHeaders_h +#define ResourceRawHeaders_h + +#include "HTTPHeaderMap.h" + +namespace WebCore { + +struct ResourceRawHeaders : RefCounted<ResourceRawHeaders> { + HTTPHeaderMap requestHeaders; + HTTPHeaderMap responseHeaders; +}; + +} + +#endif diff --git a/WebCore/platform/network/ResourceRequestBase.cpp b/WebCore/platform/network/ResourceRequestBase.cpp index fd8832f..5312007 100644 --- a/WebCore/platform/network/ResourceRequestBase.cpp +++ b/WebCore/platform/network/ResourceRequestBase.cpp @@ -65,6 +65,7 @@ PassOwnPtr<ResourceRequest> ResourceRequestBase::adopt(PassOwnPtr<CrossThreadRes } request->setHTTPBody(data->m_httpBody); request->setAllowCookies(data->m_allowCookies); + request->doPlatformAdopt(data); return request.release(); } @@ -87,7 +88,7 @@ PassOwnPtr<CrossThreadResourceRequestData> ResourceRequestBase::copyData() const if (m_httpBody) data->m_httpBody = m_httpBody->deepCopy(); data->m_allowCookies = m_allowCookies; - return data.release(); + return asResourceRequest().doPlatformCopyData(data.release()); } bool ResourceRequestBase::isEmpty() const @@ -355,7 +356,7 @@ bool equalIgnoringHeaderFields(const ResourceRequestBase& a, const ResourceReque return true; } -bool operator==(const ResourceRequestBase& a, const ResourceRequestBase& b) +bool ResourceRequestBase::compare(const ResourceRequest& a, const ResourceRequest& b) { if (!equalIgnoringHeaderFields(a, b)) return false; @@ -363,7 +364,7 @@ bool operator==(const ResourceRequestBase& a, const ResourceRequestBase& b) if (a.httpHeaderFields() != b.httpHeaderFields()) return false; - return true; + return ResourceRequest::platformCompare(a, b); } bool ResourceRequestBase::isConditional() const diff --git a/WebCore/platform/network/ResourceRequestBase.h b/WebCore/platform/network/ResourceRequestBase.h index 1622cdd..33a184e 100644 --- a/WebCore/platform/network/ResourceRequestBase.h +++ b/WebCore/platform/network/ResourceRequestBase.h @@ -136,10 +136,16 @@ namespace WebCore { bool reportLoadTiming() const { return m_reportLoadTiming; } void setReportLoadTiming(bool reportLoadTiming) { m_reportLoadTiming = reportLoadTiming; } + // Whether actual headers being sent/received should be collected and reported for the request. + bool reportRawHeaders() const { return m_reportRawHeaders; } + void setReportRawHeaders(bool reportRawHeaders) { m_reportRawHeaders = reportRawHeaders; } + // What this request is for. TargetType targetType() const { return m_targetType; } void setTargetType(TargetType type) { m_targetType = type; } + static bool compare(const ResourceRequest&, const ResourceRequest&); + protected: // Used when ResourceRequest is initialized from a platform representation of the request ResourceRequestBase() @@ -147,6 +153,7 @@ namespace WebCore { , m_platformRequestUpdated(true) , m_reportUploadProgress(false) , m_reportLoadTiming(false) + , m_reportRawHeaders(false) , m_targetType(TargetIsSubresource) { } @@ -161,6 +168,7 @@ namespace WebCore { , m_platformRequestUpdated(false) , m_reportUploadProgress(false) , m_reportLoadTiming(false) + , m_reportRawHeaders(false) , m_targetType(TargetIsSubresource) { } @@ -168,6 +176,9 @@ namespace WebCore { void updatePlatformRequest() const; void updateResourceRequest() const; + // The ResourceRequest subclass may "shadow" this method to compare platform specific fields + static bool platformCompare(const ResourceRequest&, const ResourceRequest&) { return true; } + KURL m_url; ResourceRequestCachePolicy m_cachePolicy; @@ -182,6 +193,7 @@ namespace WebCore { mutable bool m_platformRequestUpdated; bool m_reportUploadProgress; bool m_reportLoadTiming; + bool m_reportRawHeaders; TargetType m_targetType; private: @@ -190,10 +202,10 @@ namespace WebCore { bool equalIgnoringHeaderFields(const ResourceRequestBase&, const ResourceRequestBase&); - bool operator==(const ResourceRequestBase&, const ResourceRequestBase&); - inline bool operator!=(ResourceRequestBase& a, const ResourceRequestBase& b) { return !(a == b); } + inline bool operator==(const ResourceRequest& a, const ResourceRequest& b) { return ResourceRequestBase::compare(a, b); } + inline bool operator!=(ResourceRequest& a, const ResourceRequest& b) { return !(a == b); } - struct CrossThreadResourceRequestData : Noncopyable { + struct CrossThreadResourceRequestDataBase : Noncopyable { KURL m_url; ResourceRequestCachePolicy m_cachePolicy; diff --git a/WebCore/platform/network/ResourceResponseBase.cpp b/WebCore/platform/network/ResourceResponseBase.cpp index e231652..a254168 100644 --- a/WebCore/platform/network/ResourceResponseBase.cpp +++ b/WebCore/platform/network/ResourceResponseBase.cpp @@ -39,6 +39,11 @@ namespace WebCore { static void parseCacheHeader(const String& header, Vector<pair<String, String> >& result); +inline const ResourceResponse& ResourceResponseBase::asResourceResponse() const +{ + return *static_cast<const ResourceResponse*>(this); +} + ResourceResponseBase::ResourceResponseBase() : m_expectedContentLength(0) , m_httpStatusCode(0) @@ -107,7 +112,7 @@ PassOwnPtr<ResourceResponse> ResourceResponseBase::adopt(PassOwnPtr<CrossThreadR response->m_httpHeaderFields.adopt(data->m_httpHeaders.release()); response->setLastModifiedDate(data->m_lastModifiedDate); response->setResourceLoadTiming(data->m_resourceLoadTiming.release()); - + response->doPlatformAdopt(data); return response.release(); } @@ -125,7 +130,7 @@ PassOwnPtr<CrossThreadResourceResponseData> ResourceResponseBase::copyData() con data->m_lastModifiedDate = lastModifiedDate(); if (m_resourceLoadTiming) data->m_resourceLoadTiming = m_resourceLoadTiming->deepCopy(); - return data.release(); + return asResourceResponse().doPlatformCopyData(data.release()); } bool ResourceResponseBase::isHTTP() const @@ -515,6 +520,20 @@ void ResourceResponseBase::setResourceLoadTiming(PassRefPtr<ResourceLoadTiming> m_resourceLoadTiming = resourceLoadTiming; } +PassRefPtr<ResourceRawHeaders> ResourceResponseBase::resourceRawHeaders() const +{ + lazyInit(); + + return m_resourceRawHeaders.get(); +} + +void ResourceResponseBase::setResourceRawHeaders(PassRefPtr<ResourceRawHeaders> headers) +{ + lazyInit(); + + m_resourceRawHeaders = headers; +} + void ResourceResponseBase::lazyInit() const { const_cast<ResourceResponse*>(static_cast<const ResourceResponse*>(this))->platformLazyInit(); diff --git a/WebCore/platform/network/ResourceResponseBase.h b/WebCore/platform/network/ResourceResponseBase.h index 65e24ad..4da13bb 100644 --- a/WebCore/platform/network/ResourceResponseBase.h +++ b/WebCore/platform/network/ResourceResponseBase.h @@ -30,6 +30,7 @@ #include "HTTPHeaderMap.h" #include "KURL.h" #include "ResourceLoadTiming.h" +#include "ResourceRawHeaders.h" #include <wtf/PassOwnPtr.h> #include <wtf/RefPtr.h> @@ -109,6 +110,9 @@ public: ResourceLoadTiming* resourceLoadTiming() const; void setResourceLoadTiming(PassRefPtr<ResourceLoadTiming>); + PassRefPtr<ResourceRawHeaders> resourceRawHeaders() const; + void setResourceRawHeaders(PassRefPtr<ResourceRawHeaders>); + // The ResourceResponse subclass may "shadow" this method to provide platform-specific memory usage information unsigned memoryUsage() const { @@ -116,7 +120,7 @@ public: return 1280; } - static bool compare(const ResourceResponse& a, const ResourceResponse& b); + static bool compare(const ResourceResponse&, const ResourceResponse&); protected: ResourceResponseBase(); @@ -143,10 +147,12 @@ protected: unsigned m_connectionID; bool m_connectionReused : 1; RefPtr<ResourceLoadTiming> m_resourceLoadTiming; + RefPtr<ResourceRawHeaders> m_resourceRawHeaders; bool m_isNull : 1; private: + const ResourceResponse& asResourceResponse() const; void parseCacheControlDirectives() const; mutable bool m_haveParsedCacheControlHeader : 1; @@ -169,7 +175,7 @@ private: inline bool operator==(const ResourceResponse& a, const ResourceResponse& b) { return ResourceResponseBase::compare(a, b); } inline bool operator!=(const ResourceResponse& a, const ResourceResponse& b) { return !(a == b); } -struct CrossThreadResourceResponseData : Noncopyable { +struct CrossThreadResourceResponseDataBase : Noncopyable { KURL m_url; String m_mimeType; long long m_expectedContentLength; diff --git a/WebCore/platform/network/android/ResourceRequest.h b/WebCore/platform/network/android/ResourceRequest.h index 1edd4bf..745c2ef 100644 --- a/WebCore/platform/network/android/ResourceRequest.h +++ b/WebCore/platform/network/android/ResourceRequest.h @@ -53,6 +53,12 @@ public: private: friend class ResourceRequestBase; + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } +}; + +struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/android/ResourceResponse.h b/WebCore/platform/network/android/ResourceResponse.h index e6ae2cc..78307b9 100644 --- a/WebCore/platform/network/android/ResourceResponse.h +++ b/WebCore/platform/network/android/ResourceResponse.h @@ -46,6 +46,12 @@ private: { notImplemented(); } + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } +}; + +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/cf/ProxyServerCFNet.cpp b/WebCore/platform/network/cf/ProxyServerCFNet.cpp new file mode 100644 index 0000000..3bef808 --- /dev/null +++ b/WebCore/platform/network/cf/ProxyServerCFNet.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 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 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. + */ + +#include "config.h" +#include "ProxyServer.h" + +#include "KURL.h" +#include <wtf/RetainPtr.h> + +namespace WebCore { + +#if !defined(BUILDING_ON_TIGER) && !defined(BUILDING_ON_LEOPARD) +static void addProxyServersForURL(Vector<ProxyServer>& proxyServers, const KURL& url) +{ + RetainPtr<CFDictionaryRef> proxySettings(AdoptCF, CFNetworkCopySystemProxySettings()); + if (!proxySettings) + return; + + RetainPtr<CFURLRef> cfURL(AdoptCF, url.createCFURL()); + RetainPtr<CFArrayRef> proxiesForURL(AdoptCF, CFNetworkCopyProxiesForURL(cfURL.get(), proxySettings.get())); + if (!proxiesForURL) + return; + + CFIndex numProxies = CFArrayGetCount(proxiesForURL.get()); + for (CFIndex i = 0; i < numProxies; ++i) { + CFDictionaryRef proxyDictionary = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(proxiesForURL.get(), i)); + + ProxyServer::Type type = ProxyServer::Direct; + CFStringRef typeString = static_cast<CFStringRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyTypeKey)); + if (CFEqual(typeString, kCFProxyTypeAutoConfigurationURL)) { + // FIXME: Handle PAC URLs. + continue; + } + + if (CFEqual(typeString, kCFProxyTypeNone)) { + proxyServers.append(ProxyServer(ProxyServer::Direct, String(), -1)); + continue; + } + + if (CFEqual(typeString, kCFProxyTypeHTTP)) + type = ProxyServer::HTTP; + else if (CFEqual(typeString, kCFProxyTypeHTTPS)) + type = ProxyServer::HTTPS; + else if (CFEqual(typeString, kCFProxyTypeSOCKS)) + type = ProxyServer::SOCKS; + else { + // We don't know how to handle this type. + continue; + } + + CFStringRef host = static_cast<CFStringRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyHostNameKey)); + CFNumberRef port = static_cast<CFNumberRef>(CFDictionaryGetValue(proxyDictionary, kCFProxyPortNumberKey)); + SInt32 portValue; + CFNumberGetValue(port, kCFNumberSInt32Type, &portValue); + + proxyServers.append(ProxyServer(type, host, portValue)); + } +} + +Vector<ProxyServer> proxyServersForURL(const KURL& url, const NetworkingContext*) +{ + Vector<ProxyServer> proxyServers; + + addProxyServersForURL(proxyServers, url); + return proxyServers; + +} +#else +Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*) +{ + // FIXME: Implement. + return Vector<ProxyServer>(); +} +#endif + +} // namespace WebCore diff --git a/WebCore/platform/network/cf/ResourceRequest.h b/WebCore/platform/network/cf/ResourceRequest.h index e361af5..9ed79f2 100644 --- a/WebCore/platform/network/cf/ResourceRequest.h +++ b/WebCore/platform/network/cf/ResourceRequest.h @@ -68,10 +68,16 @@ namespace WebCore { void doUpdatePlatformRequest(); void doUpdateResourceRequest(); - + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + RetainPtr<CFURLRequestRef> m_cfRequest; }; + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + }; + } // namespace WebCore #endif // ResourceRequest_h diff --git a/WebCore/platform/network/cf/ResourceResponse.h b/WebCore/platform/network/cf/ResourceResponse.h index 04cc82c..5e27670 100644 --- a/WebCore/platform/network/cf/ResourceResponse.h +++ b/WebCore/platform/network/cf/ResourceResponse.h @@ -70,12 +70,18 @@ private: friend class ResourceResponseBase; void platformLazyInit(); + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } + static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b); RetainPtr<CFURLResponseRef> m_cfResponse; bool m_isUpToDate; }; +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { +}; + } // namespace WebCore #endif // ResourceResponse_h diff --git a/WebCore/platform/network/cf/SocketStreamHandle.h b/WebCore/platform/network/cf/SocketStreamHandle.h index 63bf9a7..8643db7 100644 --- a/WebCore/platform/network/cf/SocketStreamHandle.h +++ b/WebCore/platform/network/cf/SocketStreamHandle.h @@ -36,6 +36,7 @@ #include "SocketStreamHandleBase.h" #include <wtf/RetainPtr.h> + namespace WebCore { class AuthenticationChallenge; diff --git a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp index c66de33..800301a 100644 --- a/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp +++ b/WebCore/platform/network/cf/SocketStreamHandleCFNet.cpp @@ -194,8 +194,10 @@ void SocketStreamHandle::chooseProxy() void SocketStreamHandle::chooseProxyFromArray(CFArrayRef proxyArray) { - if (!proxyArray) + if (!proxyArray) { m_connectionType = Direct; + return; + } CFIndex proxyArrayCount = CFArrayGetCount(proxyArray); @@ -565,7 +567,7 @@ void SocketStreamHandle::writeStreamCallback(CFStreamEventType type) break; } - ASSERT(m_state = Open); + ASSERT(m_state == Open); ASSERT(m_connectingSubstate == Connected); sendPendingData(); diff --git a/WebCore/platform/network/chromium/ResourceRequest.cpp b/WebCore/platform/network/chromium/ResourceRequest.cpp index 016bd34..a33895a 100644 --- a/WebCore/platform/network/chromium/ResourceRequest.cpp +++ b/WebCore/platform/network/chromium/ResourceRequest.cpp @@ -37,4 +37,19 @@ unsigned initializeMaximumHTTPConnectionCountPerHost() return 10000; } +PassOwnPtr<CrossThreadResourceRequestData> ResourceRequest::doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const +{ + data->m_requestorID = m_requestorID; + data->m_requestorProcessID = m_requestorProcessID; + data->m_appCacheHostID = m_appCacheHostID; + return data; +} + +void ResourceRequest::doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData> data) +{ + m_requestorID = data->m_requestorID; + m_requestorProcessID = data->m_requestorProcessID; + m_appCacheHostID = data->m_appCacheHostID; +} + } // namespace WebCore diff --git a/WebCore/platform/network/chromium/ResourceRequest.h b/WebCore/platform/network/chromium/ResourceRequest.h index 8ef0c5e..8571cf4 100644 --- a/WebCore/platform/network/chromium/ResourceRequest.h +++ b/WebCore/platform/network/chromium/ResourceRequest.h @@ -90,6 +90,15 @@ namespace WebCore { void doUpdatePlatformRequest() {} void doUpdateResourceRequest() {} + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData>) const; + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>); + + int m_requestorID; + int m_requestorProcessID; + int m_appCacheHostID; + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { int m_requestorID; int m_requestorProcessID; int m_appCacheHostID; diff --git a/WebCore/platform/network/chromium/ResourceResponse.cpp b/WebCore/platform/network/chromium/ResourceResponse.cpp new file mode 100644 index 0000000..acd44d3 --- /dev/null +++ b/WebCore/platform/network/chromium/ResourceResponse.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Google, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 "ResourceResponse.h" + +namespace WebCore { + +PassOwnPtr<CrossThreadResourceResponseData> ResourceResponse::doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const +{ + data->m_appCacheID = m_appCacheID; + data->m_appCacheManifestURL = m_appCacheManifestURL.copy(); + data->m_isContentFiltered = m_isContentFiltered; + data->m_isMultipartPayload = m_isMultipartPayload; + data->m_wasFetchedViaSPDY = m_wasFetchedViaSPDY; + data->m_wasNpnNegotiated = m_wasNpnNegotiated; + data->m_wasAlternateProtocolAvailable = m_wasAlternateProtocolAvailable; + data->m_wasFetchedViaProxy = m_wasFetchedViaProxy; + data->m_responseTime = m_responseTime; + return data; +} + +void ResourceResponse::doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData> data) +{ + m_appCacheID = data->m_appCacheID; + m_appCacheManifestURL = data->m_appCacheManifestURL.copy(); + m_isContentFiltered = data->m_isContentFiltered; + m_isMultipartPayload = data->m_isMultipartPayload; + m_wasFetchedViaSPDY = data->m_wasFetchedViaSPDY; + m_wasNpnNegotiated = data->m_wasNpnNegotiated; + m_wasAlternateProtocolAvailable = data->m_wasAlternateProtocolAvailable; + m_wasFetchedViaProxy = data->m_wasFetchedViaProxy; + m_responseTime = data->m_responseTime; +} + +} // namespace WebCore diff --git a/WebCore/platform/network/chromium/ResourceResponse.h b/WebCore/platform/network/chromium/ResourceResponse.h index 852b9f5..5e99994 100644 --- a/WebCore/platform/network/chromium/ResourceResponse.h +++ b/WebCore/platform/network/chromium/ResourceResponse.h @@ -109,6 +109,9 @@ namespace WebCore { notImplemented(); } + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData>) const; + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>); + // The id of the appcache this response was retrieved from, or zero if // the response was not retrieved from an appcache. long long m_appCacheID; @@ -142,6 +145,18 @@ namespace WebCore { double m_responseTime; }; + struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { + long long m_appCacheID; + KURL m_appCacheManifestURL; + bool m_isContentFiltered; + bool m_isMultipartPayload; + bool m_wasFetchedViaSPDY; + bool m_wasNpnNegotiated; + bool m_wasAlternateProtocolAvailable; + bool m_wasFetchedViaProxy; + double m_responseTime; + }; + } // namespace WebCore #endif diff --git a/WebCore/platform/network/curl/ProxyServerCurl.cpp b/WebCore/platform/network/curl/ProxyServerCurl.cpp new file mode 100644 index 0000000..212768a --- /dev/null +++ b/WebCore/platform/network/curl/ProxyServerCurl.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org>. 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. + */ + +#include "config.h" +#include "ProxyServer.h" + +#include "KURL.h" +#include <wtf/RetainPtr.h> + +namespace WebCore { + +Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*) +{ + // FIXME: Implement. + return Vector<ProxyServer>(); +} + +} // namespace WebCore diff --git a/WebCore/platform/network/curl/ResourceHandleCurl.cpp b/WebCore/platform/network/curl/ResourceHandleCurl.cpp index 052ac56..f360f86 100644 --- a/WebCore/platform/network/curl/ResourceHandleCurl.cpp +++ b/WebCore/platform/network/curl/ResourceHandleCurl.cpp @@ -83,14 +83,6 @@ void WebCoreSynchronousLoader::didFail(ResourceHandle*, const ResourceError& err m_error = error; } - -static HashSet<String>& allowsAnyHTTPSCertificateHosts() -{ - static HashSet<String> hosts; - - return hosts; -} - ResourceHandleInternal::~ResourceHandleInternal() { fastFree(m_url); @@ -133,6 +125,13 @@ bool ResourceHandle::supportsBufferedData() } #if PLATFORM(WIN) && PLATFORM(CF) +static HashSet<String>& allowsAnyHTTPSCertificateHosts() +{ + static HashSet<String> hosts; + + return hosts; +} + void ResourceHandle::setHostAllowsAnyHTTPSCertificate(const String& host) { allowsAnyHTTPSCertificateHosts().add(host.lower()); diff --git a/WebCore/platform/network/curl/ResourceHandleManager.cpp b/WebCore/platform/network/curl/ResourceHandleManager.cpp index a4d71e0..2e4259c 100644 --- a/WebCore/platform/network/curl/ResourceHandleManager.cpp +++ b/WebCore/platform/network/curl/ResourceHandleManager.cpp @@ -34,14 +34,13 @@ #include "config.h" #include "ResourceHandleManager.h" -#include "Base64.h" +#include "DataURL.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" #include "NotImplemented.h" #include "ResourceError.h" #include "ResourceHandle.h" #include "ResourceHandleInternal.h" -#include "TextEncoding.h" #include <errno.h> #include <stdio.h> @@ -121,8 +120,9 @@ static void curl_unlock_callback(CURL* handle, curl_lock_data data, void* userPt ResourceHandleManager::ResourceHandleManager() : m_downloadTimer(this, &ResourceHandleManager::downloadTimerCallback) , m_cookieJarFileName(0) - , m_runningJobs(0) , m_certificatePath (certificatePath()) + , m_runningJobs(0) + { curl_global_init(CURL_GLOBAL_ALL); m_curlMultiHandle = curl_multi_init(); @@ -164,7 +164,7 @@ static void handleLocalReceiveResponse (CURL* handle, ResourceHandle* job, Resou // TODO: See if there is a better approach for handling this. const char* hdr; CURLcode err = curl_easy_getinfo(handle, CURLINFO_EFFECTIVE_URL, &hdr); - ASSERT(CURLE_OK == err); + ASSERT_UNUSED(err, CURLE_OK == err); d->m_response.setURL(KURL(ParsedURLString, hdr)); if (d->client()) d->client()->didReceiveResponse(job, d->m_response); @@ -372,7 +372,7 @@ void ResourceHandleManager::downloadTimerCallback(Timer<ResourceHandleManager>* ASSERT(handle); ResourceHandle* job = 0; CURLcode err = curl_easy_getinfo(handle, CURLINFO_PRIVATE, &job); - ASSERT(CURLE_OK == err); + ASSERT_UNUSED(err, CURLE_OK == err); ASSERT(job); if (!job) continue; @@ -572,67 +572,12 @@ bool ResourceHandleManager::startScheduledJobs() return started; } -static void parseDataUrl(ResourceHandle* handle) -{ - ResourceHandleClient* client = handle->client(); - - ASSERT(client); - if (!client) - return; - - String url = handle->firstRequest().url().string(); - ASSERT(url.startsWith("data:", false)); - - int index = url.find(','); - if (index == -1) { - client->cannotShowURL(handle); - return; - } - - String mediaType = url.substring(5, index - 5); - String data = url.substring(index + 1); - - bool base64 = mediaType.endsWith(";base64", false); - if (base64) - mediaType = mediaType.left(mediaType.length() - 7); - - if (mediaType.isEmpty()) - mediaType = "text/plain;charset=US-ASCII"; - - String mimeType = extractMIMETypeFromMediaType(mediaType); - String charset = extractCharsetFromMediaType(mediaType); - - ResourceResponse response; - response.setMimeType(mimeType); - - if (base64) { - data = decodeURLEscapeSequences(data); - response.setTextEncodingName(charset); - client->didReceiveResponse(handle, response); - - // WebCore's decoder fails on Acid3 test 97 (whitespace). - Vector<char> out; - CString latin1 = data.latin1(); - if (base64Decode(latin1.data(), latin1.length(), out) && out.size() > 0) - client->didReceiveData(handle, out.data(), out.size(), 0); - } else { - // We have to convert to UTF-16 early due to limitations in KURL - data = decodeURLEscapeSequences(data, TextEncoding(charset)); - response.setTextEncodingName("UTF-16"); - client->didReceiveResponse(handle, response); - if (data.length() > 0) - client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0); - } - - client->didFinishLoading(handle, 0); -} - void ResourceHandleManager::dispatchSynchronousJob(ResourceHandle* job) { KURL kurl = job->firstRequest().url(); - if (kurl.protocolIs("data")) { - parseDataUrl(job); + if (kurl.protocolIsData()) { + handleDataURL(job); return; } @@ -662,8 +607,8 @@ void ResourceHandleManager::startJob(ResourceHandle* job) { KURL kurl = job->firstRequest().url(); - if (kurl.protocolIs("data")) { - parseDataUrl(job); + if (kurl.protocolIsData()) { + handleDataURL(job); return; } @@ -711,7 +656,7 @@ void ResourceHandleManager::initializeHandle(ResourceHandle* job) CURLcode error = curl_easy_pause(d->m_handle, CURLPAUSE_ALL); // If we did not pause the handle, we would ASSERT in the // header callback. So just assert here. - ASSERT(error == CURLE_OK); + ASSERT_UNUSED(error, error == CURLE_OK); } #endif #ifndef NDEBUG diff --git a/WebCore/platform/network/curl/ResourceRequest.h b/WebCore/platform/network/curl/ResourceRequest.h index 12dc214..c88501a 100644 --- a/WebCore/platform/network/curl/ResourceRequest.h +++ b/WebCore/platform/network/curl/ResourceRequest.h @@ -69,6 +69,12 @@ namespace WebCore { void doUpdatePlatformRequest() {} void doUpdateResourceRequest() {} + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/curl/ResourceResponse.h b/WebCore/platform/network/curl/ResourceResponse.h index 860e902..49dd56c 100644 --- a/WebCore/platform/network/curl/ResourceResponse.h +++ b/WebCore/platform/network/curl/ResourceResponse.h @@ -28,7 +28,7 @@ #include "ResourceResponseBase.h" -typedef const struct _CFURLResponse* CFURLResponseRef; +typedef struct _CFURLResponse* CFURLResponseRef; namespace WebCore { @@ -52,9 +52,17 @@ public: CFURLResponseRef cfURLResponse() const { return 0; } private: + friend class ResourceResponseBase; + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } + bool m_responseFired; }; +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { +}; + } // namespace WebCore #endif // ResourceResponse_h diff --git a/WebCore/platform/network/mac/FormDataStreamMac.mm b/WebCore/platform/network/mac/FormDataStreamMac.mm index ed98356..9b5884f 100644 --- a/WebCore/platform/network/mac/FormDataStreamMac.mm +++ b/WebCore/platform/network/mac/FormDataStreamMac.mm @@ -424,7 +424,7 @@ void setHTTPBody(NSMutableURLRequest *request, PassRefPtr<FormData> formData) for (size_t j = 0; j < blobData->items().size(); ++j) { const BlobDataItem& blobItem = blobData->items()[j]; if (blobItem.type == BlobDataItem::Data) { - newFormData->appendData(blobItem.data.data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length)); + newFormData->appendData(blobItem.data->data() + static_cast<int>(blobItem.offset), static_cast<int>(blobItem.length)); } else { ASSERT(blobItem.type == BlobDataItem::File); newFormData->appendFileRange(blobItem.path, blobItem.offset, blobItem.length, blobItem.expectedModificationTime); diff --git a/WebCore/platform/network/mac/ResourceRequest.h b/WebCore/platform/network/mac/ResourceRequest.h index b09e72d..ea0d13a 100644 --- a/WebCore/platform/network/mac/ResourceRequest.h +++ b/WebCore/platform/network/mac/ResourceRequest.h @@ -73,10 +73,16 @@ namespace WebCore { void doUpdatePlatformRequest(); void doUpdateResourceRequest(); - + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + RetainPtr<NSURLRequest> m_nsRequest; }; + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { + }; + } // namespace WebCore #endif // ResourceRequest_h diff --git a/WebCore/platform/network/mac/ResourceResponse.h b/WebCore/platform/network/mac/ResourceResponse.h index 16b0cbf..2867e9f 100644 --- a/WebCore/platform/network/mac/ResourceResponse.h +++ b/WebCore/platform/network/mac/ResourceResponse.h @@ -75,12 +75,19 @@ private: friend class ResourceResponseBase; void platformLazyInit(); + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } + static bool platformCompare(const ResourceResponse& a, const ResourceResponse& b); RetainPtr<NSURLResponse> m_nsResponse; bool m_isUpToDate; }; +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { +}; + } // namespace WebCore #endif // ResourceResponse_h diff --git a/WebCore/platform/network/qt/ProxyServerQt.cpp b/WebCore/platform/network/qt/ProxyServerQt.cpp new file mode 100644 index 0000000..0bf3687 --- /dev/null +++ b/WebCore/platform/network/qt/ProxyServerQt.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 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 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. + */ + +#include "config.h" +#include "ProxyServer.h" + +#include "KURL.h" +#include "NetworkingContext.h" + +#include <QNetworkAccessManager> +#include <QNetworkProxy> +#include <QNetworkProxyFactory> +#include <QNetworkProxyQuery> +#include <QUrl> + +namespace WebCore { + +Vector<ProxyServer> proxyServersForURL(const KURL& url, const NetworkingContext* context) +{ + Vector<ProxyServer> servers; + + const QNetworkAccessManager* accessManager = context ? context->networkAccessManager() : 0; + QNetworkProxyFactory* proxyFactory = accessManager ? accessManager->proxyFactory() : 0; + + if (proxyFactory) { + const QList<QNetworkProxy> proxies = proxyFactory->queryProxy(QNetworkProxyQuery(url)); + Q_FOREACH(const QNetworkProxy& proxy, proxies) { + ProxyServer::Type proxyType; + switch (proxy.type()) { + case QNetworkProxy::Socks5Proxy: + proxyType = ProxyServer::SOCKS; + break; + case QNetworkProxy::HttpProxy: + case QNetworkProxy::HttpCachingProxy: + case QNetworkProxy::FtpCachingProxy: + proxyType = ProxyServer::HTTP; + break; + case QNetworkProxy::DefaultProxy: + case QNetworkProxy::NoProxy: + default: + proxyType = ProxyServer::Direct; + break; + } + servers.append(ProxyServer(proxyType, proxy.hostName(), proxy.port())); + } + } + + return servers; +} + +} // namespace WebCore diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp index ca3af75..01e624e 100644 --- a/WebCore/platform/network/qt/QNetworkReplyHandler.cpp +++ b/WebCore/platform/network/qt/QNetworkReplyHandler.cpp @@ -30,6 +30,7 @@ #include "ResourceRequest.h" #include <QDateTime> #include <QFile> +#include <QFileInfo> #include <QNetworkReply> #include <QNetworkCookie> #include <qwebframe.h> @@ -59,11 +60,14 @@ FormDataIODevice::FormDataIODevice(FormData* data) : m_formElements(data ? data->elements() : Vector<FormDataElement>()) , m_currentFile(0) , m_currentDelta(0) + , m_fileSize(0) + , m_dataSize(0) { setOpenMode(FormDataIODevice::ReadOnly); if (!m_formElements.isEmpty() && m_formElements[0].m_type == FormDataElement::encodedFile) openFileForCurrentElement(); + computeSize(); } FormDataIODevice::~FormDataIODevice() @@ -71,6 +75,20 @@ FormDataIODevice::~FormDataIODevice() delete m_currentFile; } +qint64 FormDataIODevice::computeSize() +{ + for (int i = 0; i < m_formElements.size(); ++i) { + const FormDataElement& element = m_formElements[i]; + if (element.m_type == FormDataElement::data) + m_dataSize += element.m_data.size(); + else { + QFileInfo fi(element.m_filename); + m_fileSize += fi.size(); + } + } + return m_dataSize + m_fileSize; +} + void FormDataIODevice::moveToNextElement() { if (m_currentFile) @@ -137,6 +155,29 @@ bool FormDataIODevice::isSequential() const return true; } +String QNetworkReplyHandler::httpMethod() const +{ + switch (m_method) { + case QNetworkAccessManager::GetOperation: + return "GET"; + case QNetworkAccessManager::HeadOperation: + return "HEAD"; + case QNetworkAccessManager::PostOperation: + return "POST"; + case QNetworkAccessManager::PutOperation: + return "PUT"; + case QNetworkAccessManager::DeleteOperation: + return "DELETE"; +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + case QNetworkAccessManager::CustomOperation: + return m_resourceHandle->firstRequest().httpMethod(); +#endif + default: + ASSERT_NOT_REACHED(); + return "GET"; + } +} + QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode loadMode) : QObject(0) , m_reply(0) @@ -164,11 +205,12 @@ QNetworkReplyHandler::QNetworkReplyHandler(ResourceHandle* handle, LoadMode load else if (r.httpMethod() == "DELETE") m_method = QNetworkAccessManager::DeleteOperation; #if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - else if (r.httpMethod() == "OPTIONS") + else m_method = QNetworkAccessManager::CustomOperation; -#endif +#else else m_method = QNetworkAccessManager::UnknownOperation; +#endif QObject* originatingObject = 0; if (m_resourceHandle->getInternal()->m_context) @@ -359,13 +401,17 @@ void QNetworkReplyHandler::sendResponseIfNeeded() } m_redirected = true; - ResourceRequest newRequest = m_resourceHandle->firstRequest(); - newRequest.setURL(newUrl); - if (((statusCode >= 301 && statusCode <= 303) || statusCode == 307) && newRequest.httpMethod() == "POST") { + // Status Code 301 (Moved Permanently), 302 (Moved Temporarily), 303 (See Other): + // - If original request is POST convert to GET and redirect automatically + // Status Code 307 (Temporary Redirect) and all other redirect status codes: + // - Use the HTTP method from the previous request + if ((statusCode >= 301 && statusCode <= 303) && m_resourceHandle->firstRequest().httpMethod() == "POST") m_method = QNetworkAccessManager::GetOperation; - newRequest.setHTTPMethod("GET"); - } + + ResourceRequest newRequest = m_resourceHandle->firstRequest(); + newRequest.setHTTPMethod(httpMethod()); + newRequest.setURL(newUrl); // Should not set Referer after a redirect from a secure resource to non-secure one. if (!newRequest.url().protocolIs("https") && protocolIs(newRequest.httpReferrer(), "https")) @@ -453,6 +499,9 @@ void QNetworkReplyHandler::start() break; case QNetworkAccessManager::PostOperation: { FormDataIODevice* postDevice = new FormDataIODevice(d->m_firstRequest.httpBody()); + // We may be uploading files so prevent QNR from buffering data + m_request.setHeader(QNetworkRequest::ContentLengthHeader, postDevice->getFormDataSize()); + m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true)); m_reply = manager->post(m_request, postDevice); postDevice->setParent(m_reply); break; @@ -462,6 +511,9 @@ void QNetworkReplyHandler::start() break; case QNetworkAccessManager::PutOperation: { FormDataIODevice* putDevice = new FormDataIODevice(d->m_firstRequest.httpBody()); + // We may be uploading files so prevent QNR from buffering data + m_request.setHeader(QNetworkRequest::ContentLengthHeader, putDevice->getFormDataSize()); + m_request.setAttribute(QNetworkRequest::DoNotBufferUploadDataAttribute, QVariant(true)); m_reply = manager->put(m_request, putDevice); putDevice->setParent(m_reply); break; diff --git a/WebCore/platform/network/qt/QNetworkReplyHandler.h b/WebCore/platform/network/qt/QNetworkReplyHandler.h index f6b2358..884a1a4 100644 --- a/WebCore/platform/network/qt/QNetworkReplyHandler.h +++ b/WebCore/platform/network/qt/QNetworkReplyHandler.h @@ -67,6 +67,7 @@ private slots: private: void start(); void resetState(); + String httpMethod() const; QNetworkReply* m_reply; ResourceHandle* m_resourceHandle; @@ -97,6 +98,7 @@ public: ~FormDataIODevice(); bool isSequential() const; + qint64 getFormDataSize() const { return m_fileSize + m_dataSize; } protected: qint64 readData(char*, qint64); @@ -104,12 +106,15 @@ protected: private: void moveToNextElement(); + qint64 computeSize(); void openFileForCurrentElement(); private: Vector<FormDataElement> m_formElements; QFile* m_currentFile; qint64 m_currentDelta; + qint64 m_fileSize; + qint64 m_dataSize; }; } diff --git a/WebCore/platform/network/qt/ResourceRequest.h b/WebCore/platform/network/qt/ResourceRequest.h index fb69326..0c7b62b 100644 --- a/WebCore/platform/network/qt/ResourceRequest.h +++ b/WebCore/platform/network/qt/ResourceRequest.h @@ -66,6 +66,12 @@ namespace WebCore { void doUpdatePlatformRequest() {} void doUpdateResourceRequest() {} + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/qt/ResourceResponse.h b/WebCore/platform/network/qt/ResourceResponse.h index 345ef25..288cf84 100644 --- a/WebCore/platform/network/qt/ResourceResponse.h +++ b/WebCore/platform/network/qt/ResourceResponse.h @@ -40,6 +40,15 @@ public: : ResourceResponseBase(url, mimeType, expectedLength, textEncodingName, filename) { } + +private: + friend class ResourceResponseBase; + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } +}; + +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/qt/SocketStreamHandle.h b/WebCore/platform/network/qt/SocketStreamHandle.h index 5c55749..e725344 100644 --- a/WebCore/platform/network/qt/SocketStreamHandle.h +++ b/WebCore/platform/network/qt/SocketStreamHandle.h @@ -38,6 +38,10 @@ #include <wtf/PassRefPtr.h> #include <wtf/RefCounted.h> +#if !PLATFORM(QT) +#error This should only be built on Qt +#endif + namespace WebCore { class AuthenticationChallenge; diff --git a/WebCore/platform/network/qt/SocketStreamHandlePrivate.h b/WebCore/platform/network/qt/SocketStreamHandlePrivate.h index 235f1b1..d074f42 100644 --- a/WebCore/platform/network/qt/SocketStreamHandlePrivate.h +++ b/WebCore/platform/network/qt/SocketStreamHandlePrivate.h @@ -54,7 +54,7 @@ public slots: void socketReadyRead(); int send(const char* data, int len); void close(); - void socketSentdata(); + void socketSentData(); void socketClosed(); void socketError(QAbstractSocket::SocketError); void socketClosedCallback(); diff --git a/WebCore/platform/network/qt/SocketStreamHandleQt.cpp b/WebCore/platform/network/qt/SocketStreamHandleQt.cpp index cc508b6..10550ae 100644 --- a/WebCore/platform/network/qt/SocketStreamHandleQt.cpp +++ b/WebCore/platform/network/qt/SocketStreamHandleQt.cpp @@ -110,7 +110,7 @@ void SocketStreamHandlePrivate::close() m_socket->close(); } -void SocketStreamHandlePrivate::socketSentdata() +void SocketStreamHandlePrivate::socketSentData() { if (m_streamHandle) m_streamHandle->sendPendingData(); diff --git a/WebCore/platform/network/soup/ProxyServerSoup.cpp b/WebCore/platform/network/soup/ProxyServerSoup.cpp new file mode 100644 index 0000000..50b32de --- /dev/null +++ b/WebCore/platform/network/soup/ProxyServerSoup.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 Brent Fulgham <bfulgham@webkit.org>. 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. + */ + +#include "config.h" +#include "ProxyServer.h" + +#include "KURL.h" + +namespace WebCore { + +Vector<ProxyServer> proxyServersForURL(const KURL&, const NetworkingContext*) +{ + // FIXME: Implement. + return Vector<ProxyServer>(); +} + +} // namespace WebCore diff --git a/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/WebCore/platform/network/soup/ResourceHandleSoup.cpp index 0d84388..05c659f 100644 --- a/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -45,8 +45,8 @@ #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> @@ -58,6 +58,8 @@ namespace WebCore { +#define READ_BUFFER_SIZE 8192 + class WebCoreSynchronousLoader : public ResourceHandleClient, public Noncopyable { public: WebCoreSynchronousLoader(ResourceError&, ResourceResponse &, Vector<char>&); @@ -120,16 +122,16 @@ void WebCoreSynchronousLoader::run() g_main_loop_run(m_mainLoop); } -static void cleanupGioOperation(ResourceHandle* handle, bool isDestroying); -static bool startData(ResourceHandle* handle, String urlString); -static bool startGio(ResourceHandle* handle, KURL url); +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); ResourceHandleInternal::~ResourceHandleInternal() { - if (m_msg) { - g_object_unref(m_msg); - m_msg = 0; - } + if (m_soupRequest) + g_object_set_data(G_OBJECT(m_soupRequest.get()), "webkit-resource", 0); if (m_idleHandler) { g_source_remove(m_idleHandler); @@ -139,11 +141,7 @@ ResourceHandleInternal::~ResourceHandleInternal() ResourceHandle::~ResourceHandle() { - if (d->m_msg) - g_signal_handlers_disconnect_matched(d->m_msg, G_SIGNAL_MATCH_DATA, - 0, 0, 0, 0, this); - - cleanupGioOperation(this, true); + cleanupSoupRequestOperation(this, true); } void ResourceHandle::prepareForURL(const KURL &url) @@ -185,9 +183,8 @@ static void restartedCallback(SoupMessage* msg, gpointer data) if (d->m_cancelled) return; - char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); - String location = String(uri); - g_free(uri); + GOwnPtr<char> uri(soup_uri_to_string(soup_message_get_uri(msg), false)); + String location = String::fromUTF8(uri.get()); KURL newURL = KURL(handle->firstRequest().url(), location); ResourceRequest request = handle->firstRequest(); @@ -213,7 +210,7 @@ static void restartedCallback(SoupMessage* msg, gpointer data) String firstPartyString = request.firstPartyForCookies().string(); if (!firstPartyString.isEmpty()) { GOwnPtr<SoupURI> firstParty(soup_uri_new(firstPartyString.utf8().data())); - soup_message_set_first_party(d->m_msg, firstParty.get()); + soup_message_set_first_party(d->m_soupMessage.get(), firstParty.get()); } #endif } @@ -221,8 +218,10 @@ static void restartedCallback(SoupMessage* msg, gpointer data) static void gotHeadersCallback(SoupMessage* msg, gpointer data) { // For 401, we will accumulate the resource body, and only use it - // in case authentication with the soup feature doesn't happen - if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { + // in case authentication with the soup feature doesn't happen. + // For 302 we accumulate the body too because it could be used by + // some servers to redirect with a clunky http-equiv=REFRESH + if (statusWillBeHandledBySoup(msg->status_code)) { soup_message_body_set_accumulate(msg->response_body, TRUE); return; } @@ -295,51 +294,6 @@ static void gotChunkCallback(SoupMessage* msg, SoupBuffer* chunk, gpointer data) client->didReceiveData(handle.get(), chunk->data, chunk->length, false); } -// Called at the end of the message, with all the necessary about the last informations. -// Doesn't get called for redirects. -static void finishedCallback(SoupSession *session, SoupMessage* msg, gpointer data) -{ - RefPtr<ResourceHandle> handle = adoptRef(static_cast<ResourceHandle*>(data)); - // TODO: maybe we should run this code even if there's no client? - if (!handle) - return; - - ResourceHandleInternal* d = handle->getInternal(); - - ResourceHandleClient* client = handle->client(); - if (!client) - return; - - if (d->m_cancelled) - return; - - if (SOUP_STATUS_IS_TRANSPORT_ERROR(msg->status_code)) { - char* uri = soup_uri_to_string(soup_message_get_uri(msg), false); - ResourceError error(g_quark_to_string(SOUP_HTTP_ERROR), - msg->status_code, - uri, - String::fromUTF8(msg->reason_phrase)); - g_free(uri); - client->didFail(handle.get(), error); - return; - } - - if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { - fillResponseFromMessage(msg, &d->m_response); - client->didReceiveResponse(handle.get(), d->m_response); - - // WebCore might have cancelled the job in the while - if (d->m_cancelled) - return; - - if (msg->response_body->data) - client->didReceiveData(handle.get(), msg->response_body->data, msg->response_body->length, true); - } - - client->didFinishLoading(handle.get(), 0); -} - -// parseDataUrl() is taken from the CURL http backend. static gboolean parseDataUrl(gpointer callbackData) { ResourceHandle* handle = static_cast<ResourceHandle*>(callbackData); @@ -364,7 +318,6 @@ static gboolean parseDataUrl(gpointer callbackData) } String mediaType = url.substring(5, index - 5); - String data = url.substring(index + 1); bool isBase64 = mediaType.endsWith(";base64", false); if (isBase64) @@ -380,42 +333,43 @@ static gboolean parseDataUrl(gpointer callbackData) response.setURL(handle->firstRequest().url()); response.setMimeType(mimeType); - if (isBase64) { - data = decodeURLEscapeSequences(data); - response.setTextEncodingName(charset); - client->didReceiveResponse(handle, 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; - - // Use the GLib Base64, since WebCore's decoder isn't - // general-purpose and fails on Acid3 test 97 (whitespace). - size_t outLength = 0; - char* outData = 0; - outData = reinterpret_cast<char*>(g_base64_decode(data.utf8().data(), &outLength)); - if (outData && outLength > 0) - client->didReceiveData(handle, outData, outLength, 0); - g_free(outData); - } else { - // We have to convert to UTF-16 early due to limitations in KURL - data = decodeURLEscapeSequences(data, TextEncoding(charset)); - response.setTextEncodingName("UTF-16"); - client->didReceiveResponse(handle, response); - - if (d->m_cancelled || !handle->client()) - return false; - - if (data.length() > 0) - client->didReceiveData(handle, reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0); - } + // For non base64 encoded data we have to convert to UTF-16 early + // due to limitations in KURL + response.setTextEncodingName(isBase64 ? charset : "UTF-16"); + client->didReceiveResponse(handle, 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; - client->didFinishLoading(handle, 0); + SoupSession* session = handle->defaultSession(); + GOwnPtr<GError> error; + d->m_soupRequest = adoptPlatformRef(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 = adoptPlatformRef(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 = adoptPlatformRef(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; } @@ -469,6 +423,132 @@ static void ensureSessionIsInitialized(SoupSession* session) 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(); + + if (d->m_soupRequest) { + g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", 0); + d->m_soupRequest.clear(); + } + + if (d->m_inputStream) { + g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", 0); + d->m_inputStream.clear(); + } + + d->m_cancellable.clear(); + + if (d->m_soupMessage) { + g_signal_handlers_disconnect_matched(d->m_soupMessage.get(), G_SIGNAL_MATCH_DATA, + 0, 0, 0, 0, handle); + d->m_soupMessage.clear(); + } + + if (d->m_buffer) { + g_slice_free1(READ_BUFFER_SIZE, d->m_buffer); + d->m_buffer = 0; + } + + if (!isDestroying) + handle->deref(); +} + +static void sendRequestCallback(GObject* source, GAsyncResult* res, gpointer userData) +{ + RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); + if (!handle) + return; + + ResourceHandleInternal* d = handle->getInternal(); + ResourceHandleClient* client = handle->client(); + + if (d->m_gotChunkHandler) { + // No need to call gotChunkHandler anymore. Received data will + // be reported by readCallback + if (g_signal_handler_is_connected(d->m_soupMessage.get(), d->m_gotChunkHandler)) + g_signal_handler_disconnect(d->m_soupMessage.get(), d->m_gotChunkHandler); + } + + if (d->m_cancelled || !client) { + cleanupSoupRequestOperation(handle.get()); + return; + } + + GOwnPtr<GError> error; + GInputStream* in = webkit_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()); + GOwnPtr<char> uriStr(soup_uri_to_string(uri, false)); + gint errorCode = isTransportError ? soupMsg->status_code : error->code; + const gchar* errorMsg = isTransportError ? soupMsg->reason_phrase : error->message; + const gchar* quarkStr = isTransportError ? g_quark_to_string(SOUP_HTTP_ERROR) : g_quark_to_string(G_IO_ERROR); + ResourceError resourceError(quarkStr, errorCode, uriStr.get(), String::fromUTF8(errorMsg)); + + cleanupSoupRequestOperation(handle.get()); + client->didFail(handle.get(), resourceError); + return; + } + + if (d->m_soupMessage && statusWillBeHandledBySoup(d->m_soupMessage->status_code)) { + fillResponseFromMessage(soupMsg, &d->m_response); + client->didReceiveResponse(handle.get(), d->m_response); + + // WebCore might have cancelled the job in the while + if (!d->m_cancelled && soupMsg->response_body->data) + client->didReceiveData(handle.get(), soupMsg->response_body->data, soupMsg->response_body->length, true); + } + + // didReceiveData above might have cancelled it + if (d->m_cancelled || !client) { + cleanupSoupRequestOperation(handle.get()); + return; + } + + client->didFinishLoading(handle.get(), 0); + return; + } + + if (d->m_cancelled) { + cleanupSoupRequestOperation(handle.get()); + return; + } + + d->m_inputStream = adoptPlatformRef(in); + d->m_buffer = static_cast<char*>(g_slice_alloc0(READ_BUFFER_SIZE)); + d->m_total = 0; + + // readCallback needs it + g_object_set_data(G_OBJECT(d->m_inputStream.get()), "webkit-resource", handle.get()); + + // We need to check if it's a file: URL and if it is a regular + // file as it could be a directory. In that case Soup properly + // returns a stream whose content is a HTML with a list of files + // in the directory + if (equalIgnoringCase(handle->firstRequest().url().protocol(), "file") + && G_IS_FILE_INPUT_STREAM(in)) { + ResourceResponse response; + + response.setURL(handle->firstRequest().url()); + response.setMimeType(webkit_soup_request_get_content_type(d->m_soupRequest.get())); + response.setExpectedContentLength(webkit_soup_request_get_content_length(d->m_soupRequest.get())); + client->didReceiveResponse(handle.get(), response); + + if (d->m_cancelled) { + cleanupSoupRequestOperation(handle.get()); + return; + } + } + + g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, + G_PRIORITY_DEFAULT, d->m_cancellable.get(), readCallback, 0); +} + static bool startHttp(ResourceHandle* handle) { ASSERT(handle); @@ -483,26 +563,37 @@ static bool startHttp(ResourceHandle* handle) url.removeFragmentIdentifier(); request.setURL(url); - d->m_msg = request.toSoupMessage(); - if (!d->m_msg) + GOwnPtr<GError> error; + d->m_soupRequest = adoptPlatformRef(webkit_soup_requester_request(d->m_requester.get(), url.string().utf8().data(), session, &error.outPtr())); + if (error) { + d->m_soupRequest = 0; return false; + } + + g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", handle); + + d->m_soupMessage = adoptPlatformRef(webkit_soup_request_http_get_message(WEBKIT_SOUP_REQUEST_HTTP(d->m_soupRequest.get()))); + if (!d->m_soupMessage) + return false; + + SoupMessage* soupMessage = d->m_soupMessage.get(); + request.updateSoupMessage(soupMessage); if (!handle->shouldContentSniff()) - soup_message_disable_feature(d->m_msg, SOUP_TYPE_CONTENT_SNIFFER); + soup_message_disable_feature(soupMessage, SOUP_TYPE_CONTENT_SNIFFER); - g_signal_connect(d->m_msg, "restarted", G_CALLBACK(restartedCallback), handle); - g_signal_connect(d->m_msg, "got-headers", G_CALLBACK(gotHeadersCallback), handle); - g_signal_connect(d->m_msg, "content-sniffed", G_CALLBACK(contentSniffedCallback), handle); - g_signal_connect(d->m_msg, "got-chunk", G_CALLBACK(gotChunkCallback), handle); + g_signal_connect(soupMessage, "restarted", G_CALLBACK(restartedCallback), handle); + g_signal_connect(soupMessage, "got-headers", G_CALLBACK(gotHeadersCallback), handle); + g_signal_connect(soupMessage, "content-sniffed", G_CALLBACK(contentSniffedCallback), 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(d->m_msg, firstParty.get()); + soup_message_set_first_party(soupMessage, firstParty.get()); } #endif - g_object_set_data(G_OBJECT(d->m_msg), "resourceHandle", reinterpret_cast<void*>(handle)); FormData* httpBody = d->m_firstRequest.httpBody(); if (httpBody && !httpBody->isEmpty()) { @@ -512,7 +603,7 @@ static bool startHttp(ResourceHandle* handle) if (numElements < 2) { Vector<char> body; httpBody->flatten(body); - soup_message_set_request(d->m_msg, d->m_firstRequest.httpContentType().utf8().data(), + soup_message_set_request(soupMessage, d->m_firstRequest.httpContentType().utf8().data(), SOUP_MEMORY_COPY, body.data(), body.size()); } else { /* @@ -521,27 +612,25 @@ static bool startHttp(ResourceHandle* handle) * copying into memory; TODO: support upload of non-local * (think sftp://) files by using GIO? */ - soup_message_body_set_accumulate(d->m_msg->request_body, FALSE); + 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(d->m_msg->request_body, SOUP_MEMORY_TEMPORARY, element.m_data.data(), element.m_data.size()); + 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 */ - GError* error = 0; + GOwnPtr<GError> error; CString fileName = fileSystemRepresentation(element.m_filename); - GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error); + GMappedFile* fileMapping = g_mapped_file_new(fileName.data(), false, &error.outPtr()); if (error) { - g_error_free(error); - g_signal_handlers_disconnect_matched(d->m_msg, G_SIGNAL_MATCH_DATA, + g_signal_handlers_disconnect_matched(soupMessage, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, handle); - g_object_unref(d->m_msg); - d->m_msg = 0; + d->m_soupMessage.clear(); return false; } @@ -549,38 +638,31 @@ static bool startHttp(ResourceHandle* handle) SoupBuffer* soupBuffer = soup_buffer_new_with_owner(g_mapped_file_get_contents(fileMapping), g_mapped_file_get_length(fileMapping), fileMapping, -#if GLIB_CHECK_VERSION(2, 21, 3) reinterpret_cast<GDestroyNotify>(g_mapped_file_unref)); -#else - reinterpret_cast<GDestroyNotify>(g_mapped_file_free)); -#endif - soup_message_body_append_buffer(d->m_msg->request_body, soupBuffer); + soup_message_body_append_buffer(soupMessage->request_body, soupBuffer); soup_buffer_free(soupBuffer); } } } } - // balanced by a deref() in finishedCallback, which should always run + // balanced by a deref() in cleanupSoupRequestOperation, which should always run handle->ref(); // Make sure we have an Accept header for subresources; some sites // want this to serve some of their subresources - if (!soup_message_headers_get_one(d->m_msg->request_headers, "Accept")) - soup_message_headers_append(d->m_msg->request_headers, "Accept", "*/*"); + if (!soup_message_headers_get_one(soupMessage->request_headers, "Accept")) + soup_message_headers_append(soupMessage->request_headers, "Accept", "*/*"); - // Balanced in ResourceHandleInternal's destructor; we need to - // keep our own ref, because after queueing the message, the - // session owns the initial reference. - g_object_ref(d->m_msg); - soup_session_queue_message(session, d->m_msg, finishedCallback, handle); + d->m_cancellable = adoptPlatformRef(g_cancellable_new()); + webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); return true; } bool ResourceHandle::start(NetworkingContext* context) { - ASSERT(!d->m_msg); + ASSERT(!d->m_soupMessage); // The frame could be null if the ResourceHandle is not associated to any // Frame, e.g. if we are downloading a file. @@ -620,10 +702,10 @@ bool ResourceHandle::start(NetworkingContext* context) void ResourceHandle::cancel() { d->m_cancelled = true; - if (d->m_msg) - soup_session_cancel_message(defaultSession(), d->m_msg, SOUP_STATUS_CANCELLED); + if (d->m_soupMessage) + soup_session_cancel_message(defaultSession(), d->m_soupMessage.get(), SOUP_STATUS_CANCELLED); else if (d->m_cancellable) - g_cancellable_cancel(d->m_cancellable); + g_cancellable_cancel(d->m_cancellable.get()); } PassRefPtr<SharedBuffer> ResourceHandle::bufferedData() @@ -667,38 +749,6 @@ void ResourceHandle::loadResourceSynchronously(NetworkingContext* context, const syncLoader.run(); } -// GIO-based loader - -static void cleanupGioOperation(ResourceHandle* handle, bool isDestroying = false) -{ - ResourceHandleInternal* d = handle->getInternal(); - - if (d->m_gfile) { - g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", 0); - g_object_unref(d->m_gfile); - d->m_gfile = 0; - } - - if (d->m_cancellable) { - g_object_unref(d->m_cancellable); - d->m_cancellable = 0; - } - - if (d->m_inputStream) { - g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", 0); - g_object_unref(d->m_inputStream); - d->m_inputStream = 0; - } - - if (d->m_buffer) { - g_free(d->m_buffer); - d->m_buffer = 0; - } - - if (!isDestroying) - handle->deref(); -} - static void closeCallback(GObject* source, GAsyncResult* res, gpointer) { RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); @@ -708,8 +758,8 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer) ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); - g_input_stream_close_finish(d->m_inputStream, res, 0); - cleanupGioOperation(handle.get()); + 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 @@ -720,210 +770,92 @@ static void closeCallback(GObject* source, GAsyncResult* res, gpointer) client->didFinishLoading(handle.get(), 0); } -static void readCallback(GObject* source, GAsyncResult* res, gpointer) +static void readCallback(GObject* source, GAsyncResult* asyncResult, gpointer data) { RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); if (!handle) return; + bool convertToUTF16 = static_cast<bool>(data); ResourceHandleInternal* d = handle->getInternal(); ResourceHandleClient* client = handle->client(); if (d->m_cancelled || !client) { - cleanupGioOperation(handle.get()); + cleanupSoupRequestOperation(handle.get()); return; } - GError* error = 0; + GOwnPtr<GError> error; - gssize bytesRead = g_input_stream_read_finish(d->m_inputStream, res, &error); + gssize bytesRead = g_input_stream_read_finish(d->m_inputStream.get(), asyncResult, &error.outPtr()); if (error) { - char* uri = g_file_get_uri(d->m_gfile); - ResourceError resourceError(g_quark_to_string(G_IO_ERROR), - error->code, - uri, + SoupURI* uri = webkit_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()); - g_free(uri); - g_error_free(error); - cleanupGioOperation(handle.get()); + cleanupSoupRequestOperation(handle.get()); client->didFail(handle.get(), resourceError); return; } if (!bytesRead) { - g_input_stream_close_async(d->m_inputStream, G_PRIORITY_DEFAULT, + g_input_stream_close_async(d->m_inputStream.get(), G_PRIORITY_DEFAULT, 0, closeCallback, 0); return; } d->m_total += bytesRead; - client->didReceiveData(handle.get(), d->m_buffer, bytesRead, d->m_total); - - // didReceiveData may cancel the load, which may release the last reference. - if (d->m_cancelled) { - cleanupGioOperation(handle.get()); - return; + if (G_LIKELY(!convertToUTF16)) + client->didReceiveData(handle.get(), d->m_buffer, bytesRead, d->m_total); + else { + // We have to convert it to UTF-16 due to limitations in KURL + String data = String::fromUTF8(d->m_buffer, bytesRead); + client->didReceiveData(handle.get(), reinterpret_cast<const char*>(data.characters()), data.length() * sizeof(UChar), 0); } - g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize, - G_PRIORITY_DEFAULT, d->m_cancellable, - readCallback, 0); -} - -static void openCallback(GObject* source, GAsyncResult* res, gpointer) -{ - RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); - if (!handle) - return; - - ResourceHandleInternal* d = handle->getInternal(); - ResourceHandleClient* client = handle->client(); - + // didReceiveData may cancel the load, which may release the last reference. if (d->m_cancelled || !client) { - cleanupGioOperation(handle.get()); - return; - } - - GError* error = 0; - GFileInputStream* in = g_file_read_finish(G_FILE(source), res, &error); - if (error) { - char* uri = g_file_get_uri(d->m_gfile); - ResourceError resourceError(g_quark_to_string(G_IO_ERROR), - error->code, - uri, - error ? String::fromUTF8(error->message) : String()); - g_free(uri); - g_error_free(error); - cleanupGioOperation(handle.get()); - client->didFail(handle.get(), resourceError); + cleanupSoupRequestOperation(handle.get()); return; } - d->m_inputStream = G_INPUT_STREAM(in); - d->m_bufferSize = 8192; - d->m_buffer = static_cast<char*>(g_malloc(d->m_bufferSize)); - d->m_total = 0; - - g_object_set_data(G_OBJECT(d->m_inputStream), "webkit-resource", handle.get()); - g_input_stream_read_async(d->m_inputStream, d->m_buffer, d->m_bufferSize, - G_PRIORITY_DEFAULT, d->m_cancellable, - readCallback, 0); + g_input_stream_read_async(d->m_inputStream.get(), d->m_buffer, READ_BUFFER_SIZE, G_PRIORITY_DEFAULT, + d->m_cancellable.get(), readCallback, data); } -static void queryInfoCallback(GObject* source, GAsyncResult* res, gpointer) -{ - RefPtr<ResourceHandle> handle = static_cast<ResourceHandle*>(g_object_get_data(source, "webkit-resource")); - if (!handle) - return; - - ResourceHandleInternal* d = handle->getInternal(); - ResourceHandleClient* client = handle->client(); - - if (d->m_cancelled) { - cleanupGioOperation(handle.get()); - return; - } - - ResourceResponse response; - - char* uri = g_file_get_uri(d->m_gfile); - response.setURL(KURL(KURL(), uri)); - g_free(uri); - - GError* error = 0; - GFileInfo* info = g_file_query_info_finish(d->m_gfile, res, &error); - - if (error) { - // FIXME: to be able to handle ftp URIs properly, we must - // check if the error is G_IO_ERROR_NOT_MOUNTED, and if so, - // call g_file_mount_enclosing_volume() to mount the ftp - // server (and then keep track of the fact that we mounted it, - // and set a timeout to unmount it later after it's been idle - // for a while). - char* uri = g_file_get_uri(d->m_gfile); - ResourceError resourceError(g_quark_to_string(G_IO_ERROR), - error->code, - uri, - error ? String::fromUTF8(error->message) : String()); - g_free(uri); - g_error_free(error); - cleanupGioOperation(handle.get()); - client->didFail(handle.get(), resourceError); - return; - } - - if (g_file_info_get_file_type(info) != G_FILE_TYPE_REGULAR) { - // FIXME: what if the URI points to a directory? Should we - // generate a listing? How? What do other backends do here? - char* uri = g_file_get_uri(d->m_gfile); - ResourceError resourceError(g_quark_to_string(G_IO_ERROR), - G_IO_ERROR_FAILED, - uri, - String()); - g_free(uri); - cleanupGioOperation(handle.get()); - client->didFail(handle.get(), resourceError); - return; - } - - // According to http://library.gnome.org/devel/gio/stable/gio-GContentType.html - // GContentType on Unix is the mime type, but not on Win32. - GOwnPtr<gchar> mimeType(g_content_type_get_mime_type(g_file_info_get_content_type(info))); - response.setMimeType(mimeType.get()); - response.setExpectedContentLength(g_file_info_get_size(info)); - - GTimeVal tv; - g_file_info_get_modification_time(info, &tv); - response.setLastModifiedDate(tv.tv_sec); - - client->didReceiveResponse(handle.get(), response); - - if (d->m_cancelled) { - cleanupGioOperation(handle.get()); - return; - } - - g_file_read_async(d->m_gfile, G_PRIORITY_DEFAULT, d->m_cancellable, - openCallback, 0); -} static bool startGio(ResourceHandle* handle, KURL url) { ASSERT(handle); - ResourceHandleInternal* d = handle->getInternal(); - if (handle->firstRequest().httpMethod() != "GET" && handle->firstRequest().httpMethod() != "POST") return false; + SoupSession* session = handle->defaultSession(); + 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(); -#if !OS(WINDOWS) - // we avoid the escaping for local files, because - // g_filename_from_uri (used internally by GFile) has problems - // decoding strings with arbitrary percent signs - if (url.isLocalFile()) - d->m_gfile = g_file_new_for_path(url.prettyURL().utf8().data() + sizeof("file://") - 1); - else -#endif - d->m_gfile = g_file_new_for_uri(url.string().utf8().data()); - g_object_set_data(G_OBJECT(d->m_gfile), "webkit-resource", handle); + GOwnPtr<GError> error; + d->m_soupRequest = adoptPlatformRef(webkit_soup_requester_request(d->m_requester.get(), urlStr.data(), session, &error.outPtr())); + if (error) { + d->m_soupRequest = 0; + return false; + } + + g_object_set_data(G_OBJECT(d->m_soupRequest.get()), "webkit-resource", handle); - // balanced by a deref() in cleanupGioOperation, which should always run + // balanced by a deref() in cleanupSoupRequestOperation, which should always run handle->ref(); - d->m_cancellable = g_cancellable_new(); - g_file_query_info_async(d->m_gfile, - G_FILE_ATTRIBUTE_STANDARD_TYPE "," - G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," - G_FILE_ATTRIBUTE_STANDARD_SIZE, - G_FILE_QUERY_INFO_NONE, - G_PRIORITY_DEFAULT, d->m_cancellable, - queryInfoCallback, 0); + d->m_cancellable = adoptPlatformRef(g_cancellable_new()); + webkit_soup_request_send_async(d->m_soupRequest.get(), d->m_cancellable.get(), sendRequestCallback, 0); + return true; } diff --git a/WebCore/platform/network/soup/ResourceRequest.h b/WebCore/platform/network/soup/ResourceRequest.h index a1d916f..879a47f 100644 --- a/WebCore/platform/network/soup/ResourceRequest.h +++ b/WebCore/platform/network/soup/ResourceRequest.h @@ -67,8 +67,9 @@ namespace WebCore { updateFromSoupMessage(soupMessage); } + void updateSoupMessage(SoupMessage*) const; SoupMessage* toSoupMessage() const; - void updateFromSoupMessage(SoupMessage* soupMessage); + void updateFromSoupMessage(SoupMessage*); SoupMessageFlags soupMessageFlags() const { return m_soupFlags; } void setSoupMessageFlags(SoupMessageFlags soupFlags) { m_soupFlags = soupFlags; } @@ -80,6 +81,12 @@ namespace WebCore { void doUpdatePlatformRequest() {}; void doUpdateResourceRequest() {}; + + PassOwnPtr<CrossThreadResourceRequestData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceRequestData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceRequestData>) { } + }; + + struct CrossThreadResourceRequestData : public CrossThreadResourceRequestDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/soup/ResourceRequestSoup.cpp b/WebCore/platform/network/soup/ResourceRequestSoup.cpp index 62deb01..380fc84 100644 --- a/WebCore/platform/network/soup/ResourceRequestSoup.cpp +++ b/WebCore/platform/network/soup/ResourceRequestSoup.cpp @@ -33,6 +33,29 @@ using namespace std; namespace WebCore { +void ResourceRequest::updateSoupMessage(SoupMessage* soupMessage) const +{ + g_object_set(soupMessage, SOUP_MESSAGE_METHOD, httpMethod().utf8().data(), NULL); + + const HTTPHeaderMap& headers = httpHeaderFields(); + SoupMessageHeaders* soupHeaders = soupMessage->request_headers; + if (!headers.isEmpty()) { + HTTPHeaderMap::const_iterator end = headers.end(); + for (HTTPHeaderMap::const_iterator it = headers.begin(); it != end; ++it) + 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); +} + SoupMessage* ResourceRequest::toSoupMessage() const { SoupMessage* soupMessage = soup_message_new(httpMethod().utf8().data(), url().string().utf8().data()); diff --git a/WebCore/platform/network/soup/ResourceResponse.h b/WebCore/platform/network/soup/ResourceResponse.h index e6d872c..e7213f5 100644 --- a/WebCore/platform/network/soup/ResourceResponse.h +++ b/WebCore/platform/network/soup/ResourceResponse.h @@ -63,9 +63,13 @@ private: SoupMessageFlags m_soupFlags; - void doUpdateResourceResponse() - { - } + void doUpdateResourceResponse() { } + + PassOwnPtr<CrossThreadResourceResponseData> doPlatformCopyData(PassOwnPtr<CrossThreadResourceResponseData> data) const { return data; } + void doPlatformAdopt(PassOwnPtr<CrossThreadResourceResponseData>) { } +}; + +struct CrossThreadResourceResponseData : public CrossThreadResourceResponseDataBase { }; } // namespace WebCore diff --git a/WebCore/platform/network/soup/cache/soup-directory-input-stream.c b/WebCore/platform/network/soup/cache/soup-directory-input-stream.c new file mode 100644 index 0000000..c14863b --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-directory-input-stream.c @@ -0,0 +1,200 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-directory-input-stream.h b/WebCore/platform/network/soup/cache/soup-directory-input-stream.h new file mode 100644 index 0000000..0c5b0be --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-directory-input-stream.h @@ -0,0 +1,62 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-http-input-stream.c b/WebCore/platform/network/soup/cache/soup-http-input-stream.c new file mode 100644 index 0000000..dc95d6e --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-http-input-stream.c @@ -0,0 +1,921 @@ +/* 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 > 0) { + 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); +} + +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/WebCore/platform/network/soup/cache/soup-http-input-stream.h b/WebCore/platform/network/soup/cache/soup-http-input-stream.h new file mode 100644 index 0000000..6b98559 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-http-input-stream.h @@ -0,0 +1,77 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request-data.c b/WebCore/platform/network/soup/cache/soup-request-data.c new file mode 100644 index 0000000..ced5c4a --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request-data.c @@ -0,0 +1,170 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request-data.h b/WebCore/platform/network/soup/cache/soup-request-data.h new file mode 100644 index 0000000..c9631a4 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request-data.h @@ -0,0 +1,52 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request-file.c b/WebCore/platform/network/soup/cache/soup-request-file.c new file mode 100644 index 0000000..24ccb10 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request-file.c @@ -0,0 +1,331 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request-file.h b/WebCore/platform/network/soup/cache/soup-request-file.h new file mode 100644 index 0000000..459e82a --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request-file.h @@ -0,0 +1,54 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request-http.c b/WebCore/platform/network/soup/cache/soup-request-http.c new file mode 100644 index 0000000..f157cfc --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request-http.c @@ -0,0 +1,340 @@ +/* -*- 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); + 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; +} 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; + WebKitSoupHTTPInputStream *httpstream; + SoupSession *session; + WebKitSoupCache *cache; + SendAsyncHelper *helper = (SendAsyncHelper *)data; + + session = webkit_soup_request_get_session (WEBKIT_SOUP_REQUEST (helper->http)); + cache = (WebKitSoupCache *)soup_session_get_feature (session, WEBKIT_TYPE_SOUP_CACHE); + + httpstream = (WebKitSoupHTTPInputStream *)webkit_soup_cache_send_response (cache, SOUP_MESSAGE (helper->http->priv->msg)); + + if (httpstream) { + 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, 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); + 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_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) { + /* 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 = http; + helper->callback = callback; + helper->user_data = user_data; + 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/WebCore/platform/network/soup/cache/soup-request-http.h b/WebCore/platform/network/soup/cache/soup-request-http.h new file mode 100644 index 0000000..a06a821 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request-http.h @@ -0,0 +1,54 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request.c b/WebCore/platform/network/soup/cache/soup-request.c new file mode 100644 index 0000000..46b9f5a --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request.c @@ -0,0 +1,312 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-request.h b/WebCore/platform/network/soup/cache/soup-request.h new file mode 100644 index 0000000..837d8f4 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-request.h @@ -0,0 +1,100 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-requester.c b/WebCore/platform/network/soup/cache/soup-requester.c new file mode 100644 index 0000000..4d8a860 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-requester.c @@ -0,0 +1,188 @@ +/* -*- 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/WebCore/platform/network/soup/cache/soup-requester.h b/WebCore/platform/network/soup/cache/soup-requester.h new file mode 100644 index 0000000..71ff103 --- /dev/null +++ b/WebCore/platform/network/soup/cache/soup-requester.h @@ -0,0 +1,81 @@ +/* -*- 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/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h b/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h new file mode 100644 index 0000000..8af8de2 --- /dev/null +++ b/WebCore/platform/network/soup/cache/webkit/soup-cache-private.h @@ -0,0 +1,42 @@ +/* -*- 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/WebCore/platform/network/soup/cache/webkit/soup-cache.c b/WebCore/platform/network/soup/cache/webkit/soup-cache.c new file mode 100644 index 0000000..73b15ba --- /dev/null +++ b/WebCore/platform/network/soup/cache/webkit/soup-cache.c @@ -0,0 +1,1653 @@ +/* -*- 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; + } + } + + 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; + GHashTable *hash; + + hash = NULL; + + cache_control = soup_message_headers_get (entry->headers, "Cache-Control"); + if (cache_control) { + const char *max_age, *s_maxage; + gint64 freshness_lifetime = 0; + 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; + } + } + + if (hash != NULL) + 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 (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); + + g_return_if_fail (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_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 key, + gpointer value, + WebKitSoupCache *cache) +{ + WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key); + 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; + + priv = WEBKIT_SOUP_CACHE (object)->priv; + + g_hash_table_foreach (priv->cache, (GHFunc)remove_cache_item, object); + 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; + GHashTable *hash; + 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); + + /* 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 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) { + hash = soup_header_parse_param_list (cache_control); + + if (g_hash_table_lookup_extended (hash, "no-store", NULL, NULL)) { + g_hash_table_destroy (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); + + g_hash_table_destroy (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; + } + } + + /* 5. 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 key, + gpointer value, + WebKitSoupCache *cache) +{ + WebKitSoupCacheEntry *entry = g_hash_table_lookup (cache->priv->cache, (const gchar *)key); + 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; + + g_return_if_fail (WEBKIT_IS_SOUP_CACHE (cache)); + + hash = cache->priv->cache; + g_return_if_fail (hash); + + g_hash_table_foreach (hash, (GHFunc)clear_cache_item, cache); +} + +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); + 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, NULL, NULL); + 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/WebCore/platform/network/soup/cache/webkit/soup-cache.h b/WebCore/platform/network/soup/cache/webkit/soup-cache.h new file mode 100644 index 0000000..e447cfc --- /dev/null +++ b/WebCore/platform/network/soup/cache/webkit/soup-cache.h @@ -0,0 +1,102 @@ +/* -*- 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 + +#if BUILDING_GTK__ +#include <webkit/webkitdefines.h> +#else +#ifndef WEBKIT_API +#define WEBKIT_API +#endif +#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/WebCore/platform/network/win/CookieJarWin.cpp b/WebCore/platform/network/win/CookieJarWin.cpp index 2bdd6b3..a2afbc6 100644 --- a/WebCore/platform/network/win/CookieJarWin.cpp +++ b/WebCore/platform/network/win/CookieJarWin.cpp @@ -48,13 +48,17 @@ String cookies(const Document* /*document*/, const KURL& url) { String str = url.string(); - DWORD count = str.length() + 1; - InternetGetCookie(str.charactersWithNullTermination(), 0, 0, &count); + DWORD count = 0; + if (!InternetGetCookie(str.charactersWithNullTermination(), 0, 0, &count)) + return String(); + if (count <= 1) // Null terminator counts as 1. return String(); Vector<UChar> buffer(count); - InternetGetCookie(str.charactersWithNullTermination(), 0, buffer.data(), &count); + if (!InternetGetCookie(str.charactersWithNullTermination(), 0, buffer.data(), &count)) + return String(); + buffer.shrink(count - 1); // Ignore the null terminator. return String::adopt(buffer); } diff --git a/WebCore/platform/network/win/ResourceHandleWin.cpp b/WebCore/platform/network/win/ResourceHandleWin.cpp index 5de2e1b..28035b9 100644 --- a/WebCore/platform/network/win/ResourceHandleWin.cpp +++ b/WebCore/platform/network/win/ResourceHandleWin.cpp @@ -27,6 +27,7 @@ #include "config.h" #include "ResourceHandle.h" +#include "DataURL.h" #include "HTTPParsers.h" #include "MIMETypeRegistry.h" #include "MainThread.h" @@ -259,7 +260,7 @@ bool ResourceHandle::onRequestComplete() bool ResourceHandle::start(NetworkingContext* context) { - if (request().url().isLocalFile()) { + if (firstRequest().url().isLocalFile() || firstRequest().url().protocolIsData()) { ref(); // balanced by deref in fileLoadTimer if (d->m_loadSynchronously) fileLoadTimer(0); @@ -349,6 +350,11 @@ void ResourceHandle::fileLoadTimer(Timer<ResourceHandle>*) RefPtr<ResourceHandle> protector(this); deref(); // balances ref in start + if (firstRequest().url().protocolIsData()) { + handleDataURL(this); + return; + } + String fileName = firstRequest().url().fileSystemPath(); HANDLE fileHandle = CreateFileW(fileName.charactersWithNullTermination(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); diff --git a/WebCore/platform/qt/ClipboardQt.cpp b/WebCore/platform/qt/ClipboardQt.cpp index 90e3bfe..20cf62b 100644 --- a/WebCore/platform/qt/ClipboardQt.cpp +++ b/WebCore/platform/qt/ClipboardQt.cpp @@ -28,7 +28,6 @@ #include "config.h" #include "ClipboardQt.h" -#include "CSSHelper.h" #include "CachedImage.h" #include "Document.h" #include "DragData.h" @@ -36,6 +35,7 @@ #include "FileList.h" #include "Frame.h" #include "HTMLNames.h" +#include "HTMLParserIdioms.h" #include "Image.h" #include "IntPoint.h" #include "KURL.h" @@ -276,7 +276,7 @@ void ClipboardQt::declareAndWriteDragImage(Element* element, const KURL& url, co if (imageURL.isEmpty()) return; - KURL fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)); + KURL fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)); if (fullURL.isEmpty()) return; diff --git a/WebCore/platform/qt/CursorQt.cpp b/WebCore/platform/qt/CursorQt.cpp index 227b80c..5883600 100644 --- a/WebCore/platform/qt/CursorQt.cpp +++ b/WebCore/platform/qt/CursorQt.cpp @@ -72,15 +72,13 @@ Cursor& Cursor::operator=(const Cursor& other) return *this; } +#ifndef QT_NO_CURSOR static QCursor* createCustomCursor(Image* image, const IntPoint& hotSpot) { -#ifndef QT_NO_CURSOR IntPoint effectiveHotSpot = determineHotSpot(image, hotSpot); return new QCursor(*(image->nativeImageForCurrentFrame()), effectiveHotSpot.x(), effectiveHotSpot.y()); -#else - return 0; -#endif } +#endif void Cursor::ensurePlatformCursor() const { diff --git a/WebCore/platform/qt/Language.cpp b/WebCore/platform/qt/LanguageQt.cpp index 0d2900b..71e554f 100644 --- a/WebCore/platform/qt/Language.cpp +++ b/WebCore/platform/qt/LanguageQt.cpp @@ -32,7 +32,7 @@ namespace WebCore { -String defaultLanguage() +String platformDefaultLanguage() { QLocale locale; return locale.name().replace("_", "-"); diff --git a/WebCore/platform/qt/PlatformBridge.h b/WebCore/platform/qt/PlatformBridge.h index 918e139..e478d8f 100644 --- a/WebCore/platform/qt/PlatformBridge.h +++ b/WebCore/platform/qt/PlatformBridge.h @@ -27,7 +27,6 @@ #define PlatformBridge_h #include "KURL.h" -#include "npapi.h" #include "PlatformString.h" #include <wtf/Vector.h> @@ -77,7 +76,9 @@ static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) -class NPObject; +typedef struct NPObject NPObject; +typedef struct _NPP NPP_t; +typedef NPP_t* NPP; namespace WebCore { diff --git a/WebCore/platform/qt/PlatformKeyboardEventQt.cpp b/WebCore/platform/qt/PlatformKeyboardEventQt.cpp index 33e9552..498bb88 100644 --- a/WebCore/platform/qt/PlatformKeyboardEventQt.cpp +++ b/WebCore/platform/qt/PlatformKeyboardEventQt.cpp @@ -505,6 +505,79 @@ int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad) } } +static bool isVirtualKeyCodeRepresentingCharacter(int code) +{ + switch (code) { + case VK_SPACE: + case VK_0: + case VK_1: + case VK_2: + case VK_3: + case VK_4: + case VK_5: + case VK_6: + case VK_7: + case VK_8: + case VK_9: + case VK_A: + case VK_B: + case VK_C: + case VK_D: + case VK_E: + case VK_F: + case VK_G: + case VK_H: + case VK_I: + case VK_J: + case VK_K: + case VK_L: + case VK_M: + case VK_N: + case VK_O: + case VK_P: + case VK_Q: + case VK_R: + case VK_S: + case VK_T: + case VK_U: + case VK_V: + case VK_W: + case VK_X: + case VK_Y: + case VK_Z: + case VK_NUMPAD0: + case VK_NUMPAD1: + case VK_NUMPAD2: + case VK_NUMPAD3: + case VK_NUMPAD4: + case VK_NUMPAD5: + case VK_NUMPAD6: + case VK_NUMPAD7: + case VK_NUMPAD8: + case VK_NUMPAD9: + case VK_MULTIPLY: + case VK_ADD: + case VK_SEPARATOR: + case VK_SUBTRACT: + case VK_DECIMAL: + case VK_DIVIDE: + case VK_OEM_1: + case VK_OEM_PLUS: + case VK_OEM_COMMA: + case VK_OEM_MINUS: + case VK_OEM_PERIOD: + case VK_OEM_2: + case VK_OEM_3: + case VK_OEM_4: + case VK_OEM_5: + case VK_OEM_6: + case VK_OEM_7: + return true; + default: + return false; + } +} + PlatformKeyboardEvent::PlatformKeyboardEvent(QKeyEvent* event) { const int state = event->modifiers(); @@ -539,7 +612,7 @@ void PlatformKeyboardEvent::disambiguateKeyDownEvent(Type type, bool) we try to detect this situation and still set the text, to ensure that the general event handling sends a key press event after this disambiguation. */ - if (m_text.isEmpty() && m_windowsVirtualKeyCode && m_qtEvent->key() < Qt::Key_Escape) + if (m_text.isEmpty() && m_windowsVirtualKeyCode && isVirtualKeyCodeRepresentingCharacter(m_windowsVirtualKeyCode)) m_text.append(UChar(m_windowsVirtualKeyCode)); m_keyIdentifier = String(); @@ -562,6 +635,18 @@ void PlatformKeyboardEvent::getCurrentModifierState(bool& shiftKey, bool& ctrlKe metaKey = false; } +uint32_t PlatformKeyboardEvent::nativeModifiers() const +{ + ASSERT(m_qtEvent); + return m_qtEvent->nativeModifiers(); +} + +uint32_t PlatformKeyboardEvent::nativeScanCode() const +{ + ASSERT(m_qtEvent); + return m_qtEvent->nativeScanCode(); +} + } // vim: ts=4 sw=4 et diff --git a/WebCore/platform/qt/QWebPageClient.h b/WebCore/platform/qt/QWebPageClient.h index 9301cd5..f5d8c1b 100644 --- a/WebCore/platform/qt/QWebPageClient.h +++ b/WebCore/platform/qt/QWebPageClient.h @@ -30,6 +30,7 @@ #include <QCursor> #endif +#include <QPalette> #include <QRect> QT_BEGIN_NAMESPACE diff --git a/WebCore/platform/qt/Maemo5Webstyle.cpp b/WebCore/platform/qt/QtMobileWebStyle.cpp index 42b0b71..240faa5 100644 --- a/WebCore/platform/qt/Maemo5Webstyle.cpp +++ b/WebCore/platform/qt/QtMobileWebStyle.cpp @@ -18,7 +18,7 @@ * */ #include "config.h" -#include "Maemo5Webstyle.h" +#include "QtMobileWebStyle.h" #include "QtStyleOptionWebComboBox.h" @@ -26,7 +26,7 @@ #include <QPixmapCache> #include <QStyleOption> -Maemo5WebStyle::Maemo5WebStyle() +QtMobileWebStyle::QtMobileWebStyle() { } @@ -44,10 +44,10 @@ static inline void drawRectangularControlBackground(QPainter* painter, const QPe painter->setBrush(oldBrush); } -void Maemo5WebStyle::drawChecker(QPainter* painter, int size, QColor color) const +void QtMobileWebStyle::drawChecker(QPainter* painter, int size, QColor color) const { int border = qMin(qMax(1, int(0.2 * size)), 6); - int checkerSize = size - 2 * border; + int checkerSize = qMax(size - 2 * border, 3); int width = checkerSize / 3; int middle = qMax(3 * checkerSize / 7, 3); int x = ((size - checkerSize) >> 1); @@ -67,7 +67,7 @@ void Maemo5WebStyle::drawChecker(QPainter* painter, int size, QColor color) cons painter->drawLines(lines.constData(), lines.size()); } -QPixmap Maemo5WebStyle::findChecker(const QRect& rect, bool disabled) const +QPixmap QtMobileWebStyle::findChecker(const QRect& rect, bool disabled) const { int size = qMin(rect.width(), rect.height()); QPixmap result; @@ -77,24 +77,31 @@ QPixmap Maemo5WebStyle::findChecker(const QRect& rect, bool disabled) const result = QPixmap(size, size); result.fill(Qt::transparent); QPainter painter(&result); - drawChecker(&painter, size, disabled ? Qt::gray : Qt::black); + drawChecker(&painter, size, disabled ? Qt::lightGray : Qt::darkGray); QPixmapCache::insert(key, result); } return result; } -void Maemo5WebStyle::drawRadio(QPainter* painter, const QSize& size, bool checked, QColor color) const +void QtMobileWebStyle::drawRadio(QPainter* painter, const QSize& size, bool checked, QColor color) const { painter->setRenderHint(QPainter::Antialiasing, true); // deflate one pixel QRect rect = QRect(QPoint(1, 1), QSize(size.width() - 2, size.height() - 2)); + const QPoint centerGradient(rect.bottomRight() * 0.7); + + QRadialGradient radialGradient(centerGradient, centerGradient.x() - 1); + radialGradient.setColorAt(0.0, Qt::white); + radialGradient.setColorAt(0.6, Qt::white); + radialGradient.setColorAt(1.0, color); - QPen pen(Qt::black); - pen.setWidth(1); painter->setPen(color); - painter->setBrush(Qt::white); + painter->setBrush(color); + painter->drawEllipse(rect); + painter->setBrush(radialGradient); painter->drawEllipse(rect); + int border = 0.1 * (rect.width() + rect.height()); border = qMin(qMax(2, border), 10); rect.adjust(border, border, -border, -border); @@ -105,7 +112,7 @@ void Maemo5WebStyle::drawRadio(QPainter* painter, const QSize& size, bool checke } } -QPixmap Maemo5WebStyle::findRadio(const QSize& size, bool checked, bool disabled) const +QPixmap QtMobileWebStyle::findRadio(const QSize& size, bool checked, bool disabled) const { QPixmap result; static const QString prefix = "$qt-maemo5-" + QLatin1String(metaObject()->className()) + "-radio-"; @@ -115,19 +122,29 @@ QPixmap Maemo5WebStyle::findRadio(const QSize& size, bool checked, bool disabled result = QPixmap(size); result.fill(Qt::transparent); QPainter painter(&result); - drawRadio(&painter, size, checked, disabled ? Qt::gray : Qt::black); + drawRadio(&painter, size, checked, disabled ? Qt::lightGray : Qt::darkGray); QPixmapCache::insert(key, result); } return result; } -void Maemo5WebStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const +void QtMobileWebStyle::drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget) const { switch (element) { case CE_CheckBox: { QRect rect = option->rect; const bool disabled = !(option->state & State_Enabled); - drawRectangularControlBackground(painter, QPen(disabled ? Qt::gray : Qt::black), rect, option->palette.base()); + + QLinearGradient linearGradient(rect.topLeft(), rect.bottomLeft()); + if (disabled) { + linearGradient.setColorAt(0.0, Qt::lightGray); + linearGradient.setColorAt(0.5, Qt::white); + } else { + linearGradient.setColorAt(0.0, Qt::darkGray); + linearGradient.setColorAt(0.5, Qt::white); + } + + drawRectangularControlBackground(painter, QPen(disabled ? Qt::lightGray : Qt::darkGray), rect, linearGradient); rect.adjust(1, 1, -1, -1); if (option->state & State_Off) @@ -155,7 +172,7 @@ void Maemo5WebStyle::drawControl(ControlElement element, const QStyleOption* opt } } -void Maemo5WebStyle::drawMultipleComboButton(QPainter* painter, const QSize& size, QColor color) const +void QtMobileWebStyle::drawMultipleComboButton(QPainter* painter, const QSize& size, QColor color) const { int rectWidth = size.width() - 1; int width = qMax(2, rectWidth >> 3); @@ -170,17 +187,25 @@ void Maemo5WebStyle::drawMultipleComboButton(QPainter* painter, const QSize& siz painter->drawRect(2 * (width + distance), top, width, width); } -void Maemo5WebStyle::drawSimpleComboButton(QPainter* painter, const QSize& size, QColor color) const +void QtMobileWebStyle::drawSimpleComboButton(QPainter* painter, const QSize& size, QColor color) const { - QPolygon polygon; int width = size.width(); - polygon.setPoints(3, 0, 0, width - 1, 0, width >> 1, size.height()); + int midle = width >> 1; + QVector<QLine> lines(width + 1); + + for (int x = 0, y = 0; x < midle; x++, y++) { + lines[x] = QLine(x, y, x, y + 2); + lines[x + midle] = QLine(width - x - 1, y, width - x - 1, y + 2); + } + // Just to ensure the lines' intersection. + lines[width] = QLine(midle, midle, midle, midle + 2); + painter->setPen(color); painter->setBrush(color); - painter->drawPolygon(polygon); + painter->drawLines(lines); } -QSize Maemo5WebStyle::getButtonImageSize(const QSize& buttonSize) const +QSize QtMobileWebStyle::getButtonImageSize(const QSize& buttonSize) const { const int border = qMax(3, buttonSize.width() >> 3) << 1; @@ -195,10 +220,10 @@ QSize Maemo5WebStyle::getButtonImageSize(const QSize& buttonSize) const else width = height << 1; - return QSize(width + 1, width >> 1); + return QSize(width + 1, width); } -QPixmap Maemo5WebStyle::findComboButton(const QSize& size, bool multiple, bool disabled) const +QPixmap QtMobileWebStyle::findComboButton(const QSize& size, bool multiple, bool disabled) const { QPixmap result; QSize imageSize = getButtonImageSize(size); @@ -214,15 +239,15 @@ QPixmap Maemo5WebStyle::findComboButton(const QSize& size, bool multiple, bool d result.fill(Qt::transparent); QPainter painter(&result); if (multiple) - drawMultipleComboButton(&painter, imageSize, disabled ? Qt::gray : Qt::black); + drawMultipleComboButton(&painter, imageSize, disabled ? Qt::lightGray : Qt::darkGray); else - drawSimpleComboButton(&painter, imageSize, disabled ? Qt::gray : Qt::black); + drawSimpleComboButton(&painter, imageSize, disabled ? Qt::lightGray : Qt::darkGray); QPixmapCache::insert(key, result); } return result; } -void Maemo5WebStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const +void QtMobileWebStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget) const { switch (control) { case CC_ComboBox: { diff --git a/WebCore/platform/qt/Maemo5Webstyle.h b/WebCore/platform/qt/QtMobileWebStyle.h index ce717b6..779bd26 100644 --- a/WebCore/platform/qt/Maemo5Webstyle.h +++ b/WebCore/platform/qt/QtMobileWebStyle.h @@ -18,14 +18,14 @@ * */ -#ifndef Maemo5Webstyle_h -#define Maemo5Webstyle_h +#ifndef QtMobileWebStyle_h +#define QtMobileWebStyle_h #include <QWindowsStyle> -class Maemo5WebStyle : public QWindowsStyle { +class QtMobileWebStyle : public QWindowsStyle { public: - Maemo5WebStyle(); + QtMobileWebStyle(); void drawControl(ControlElement element, const QStyleOption* option, QPainter* painter, const QWidget* widget = 0) const; void drawComplexControl(ComplexControl cc, const QStyleOptionComplex* option, QPainter* painter, const QWidget* widget = 0) const; @@ -44,4 +44,4 @@ private: }; -#endif // Maemo5WebStyle_h +#endif // QtMobileWebStyle_h diff --git a/WebCore/platform/qt/RenderThemeQt.cpp b/WebCore/platform/qt/RenderThemeQt.cpp index 7388b76..50b5de6 100644 --- a/WebCore/platform/qt/RenderThemeQt.cpp +++ b/WebCore/platform/qt/RenderThemeQt.cpp @@ -43,7 +43,7 @@ #include "HTMLMediaElement.h" #include "HTMLNames.h" #if USE(QT_MOBILE_THEME) -#include "Maemo5Webstyle.h" +#include "QtMobileWebStyle.h" #endif #include "NotImplemented.h" #include "Page.h" @@ -152,7 +152,7 @@ RenderThemeQt::RenderThemeQt(Page* page) #endif #if USE(QT_MOBILE_THEME) - m_fallbackStyle = new Maemo5WebStyle; + m_fallbackStyle = new QtMobileWebStyle; #else m_fallbackStyle = QStyleFactory::create(QLatin1String("windows")); #endif @@ -779,6 +779,7 @@ bool RenderThemeQt::paintSliderTrack(RenderObject* o, const PaintInfo& pi, QStyleOptionSlider option; if (p.widget) option.initFrom(p.widget); + option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; ControlPart appearance = initializeCommonQStyleOptions(option, o); RenderSlider* renderSlider = toRenderSlider(o); diff --git a/WebCore/platform/sql/SQLiteDatabase.cpp b/WebCore/platform/sql/SQLiteDatabase.cpp index 80d3946..b9e7639 100644 --- a/WebCore/platform/sql/SQLiteDatabase.cpp +++ b/WebCore/platform/sql/SQLiteDatabase.cpp @@ -34,6 +34,7 @@ #include <sqlite3.h> #include <wtf/Threading.h> #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> namespace WebCore { @@ -64,8 +65,7 @@ bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase) { close(); - m_lastError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase); - if (m_lastError != SQLITE_OK) { + if (SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase) != SQLITE_OK) { LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(), sqlite3_errmsg(m_db)); sqlite3_close(m_db); @@ -100,6 +100,7 @@ void SQLiteDatabase::close() void SQLiteDatabase::interrupt() { +#if !ENABLE(SINGLE_THREADED) m_interrupted = true; while (!m_lockingMutex.tryLock()) { MutexLocker locker(m_databaseClosingMutex); @@ -110,6 +111,7 @@ void SQLiteDatabase::interrupt() } m_lockingMutex.unlock(); +#endif } bool SQLiteDatabase::isInterrupted() @@ -217,7 +219,7 @@ int64_t SQLiteDatabase::totalSize() void SQLiteDatabase::setSynchronous(SynchronousPragma sync) { - executeCommand(String::format("PRAGMA synchronous = %i", sync)); + executeCommand(makeString("PRAGMA synchronous = ", String::number(sync))); } void SQLiteDatabase::setBusyTimeout(int ms) diff --git a/WebCore/platform/sql/SQLiteDatabase.h b/WebCore/platform/sql/SQLiteDatabase.h index 8151380..c329273 100644 --- a/WebCore/platform/sql/SQLiteDatabase.h +++ b/WebCore/platform/sql/SQLiteDatabase.h @@ -139,7 +139,6 @@ private: int pageSize(); sqlite3* m_db; - int m_lastError; int m_pageSize; bool m_transactionInProgress; diff --git a/WebCore/platform/text/Base64.cpp b/WebCore/platform/text/Base64.cpp index cc22cf8..98b537a 100644 --- a/WebCore/platform/text/Base64.cpp +++ b/WebCore/platform/text/Base64.cpp @@ -2,6 +2,7 @@ Copyright (C) 2000-2001 Dawit Alemayehu <adawit@kde.org> Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org> Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) @@ -25,6 +26,7 @@ #include <limits.h> #include <wtf/StringExtras.h> +#include <wtf/text/WTFString.h> namespace WebCore { @@ -70,7 +72,7 @@ void base64Encode(const char* data, unsigned len, Vector<char>& out, bool insert return; // If the input string is pathologically large, just return nothing. - // Note: Keep this in sync with the "out_len" computation below. + // Note: Keep this in sync with the "outLength" computation below. // Rather than being perfectly precise, this is a bit conservative. const unsigned maxInputBufferSize = UINT_MAX / 77 * 76 / 4 * 3 - 2; if (len > maxInputBufferSize) @@ -79,21 +81,21 @@ void base64Encode(const char* data, unsigned len, Vector<char>& out, bool insert unsigned sidx = 0; unsigned didx = 0; - unsigned out_len = ((len + 2) / 3) * 4; + unsigned outLength = ((len + 2) / 3) * 4; // Deal with the 76 character per line limit specified in RFC 2045. - insertLFs = (insertLFs && out_len > 76); + insertLFs = (insertLFs && outLength > 76); if (insertLFs) - out_len += ((out_len - 1) / 76); + outLength += ((outLength - 1) / 76); int count = 0; - out.grow(out_len); + out.grow(outLength); // 3-byte to 4-byte conversion + 0-63 to ascii printable conversion if (len > 1) { while (sidx < len - 2) { if (insertLFs) { - if (count && (count % 76) == 0) + if (count && !(count % 76)) out[didx++] = '\n'; count += 4; } @@ -106,7 +108,7 @@ void base64Encode(const char* data, unsigned len, Vector<char>& out, bool insert } if (sidx < len) { - if (insertLFs && (count > 0) && (count % 76) == 0) + if (insertLFs && (count > 0) && !(count % 76)) out[didx++] = '\n'; out[didx++] = base64EncMap[(data[sidx] >> 2) & 077]; @@ -124,7 +126,7 @@ void base64Encode(const char* data, unsigned len, Vector<char>& out, bool insert } } -bool base64Decode(const Vector<char>& in, Vector<char>& out) +bool base64Decode(const Vector<char>& in, Vector<char>& out, Base64DecodePolicy policy) { out.clear(); @@ -132,36 +134,49 @@ bool base64Decode(const Vector<char>& in, Vector<char>& out) if (in.size() > UINT_MAX) return false; - return base64Decode(in.data(), in.size(), out); + return base64Decode(in.data(), in.size(), out, policy); } -bool base64Decode(const char* data, unsigned len, Vector<char>& out) +template<typename T> +static inline bool base64DecodeInternal(const T* data, unsigned len, Vector<char>& out, Base64DecodePolicy policy) { out.clear(); - if (len == 0) + if (!len) return true; - while (len && data[len-1] == '=') - --len; - out.grow(len); + + bool sawEqualsSign = false; + unsigned outLength = 0; for (unsigned idx = 0; idx < len; idx++) { - unsigned char ch = data[idx]; - if ((ch > 47 && ch < 58) || (ch > 64 && ch < 91) || (ch > 96 && ch < 123) || ch == '+' || ch == '/' || ch == '=') - out[idx] = base64DecMap[ch]; - else + unsigned ch = data[idx]; + if (ch == '=') + sawEqualsSign = true; + else if (('0' <= ch && ch <= '9') || ('A' <= ch && ch <= 'Z') || ('a' <= ch && ch <= 'z') || ch == '+' || ch == '/') { + if (sawEqualsSign) + return false; + out[outLength] = base64DecMap[ch]; + outLength++; + } else if (policy == FailOnInvalidCharacter || (policy == IgnoreWhitespace && !isSpaceOrNewline(ch))) return false; } + if (!outLength) + return !sawEqualsSign; + + // Valid data is (n * 4 + [0,2,3]) characters long. + if ((outLength % 4) == 1) + return false; + // 4-byte to 3-byte conversion - unsigned outLen = len - ((len + 3) / 4); - if (!outLen || ((outLen + 2) / 3) * 4 < len) + outLength -= (outLength + 3) / 4; + if (!outLength) return false; unsigned sidx = 0; unsigned didx = 0; - if (outLen > 1) { - while (didx < outLen - 2) { + if (outLength > 1) { + while (didx < outLength - 2) { out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003)); out[didx + 1] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017)); out[didx + 2] = (((out[sidx + 2] << 6) & 255) | (out[sidx + 3] & 077)); @@ -170,16 +185,26 @@ bool base64Decode(const char* data, unsigned len, Vector<char>& out) } } - if (didx < outLen) + if (didx < outLength) out[didx] = (((out[sidx] << 2) & 255) | ((out[sidx + 1] >> 4) & 003)); - if (++didx < outLen) + if (++didx < outLength) out[didx] = (((out[sidx + 1] << 4) & 255) | ((out[sidx + 2] >> 2) & 017)); - if (outLen < out.size()) - out.shrink(outLen); + if (outLength < out.size()) + out.shrink(outLength); return true; } +bool base64Decode(const char* data, unsigned len, Vector<char>& out, Base64DecodePolicy policy) +{ + return base64DecodeInternal<char>(data, len, out, policy); } + +bool base64Decode(const String& in, Vector<char>& out, Base64DecodePolicy policy) +{ + return base64DecodeInternal<UChar>(in.characters(), in.length(), out, policy); +} + +} // namespace WebCore diff --git a/WebCore/platform/text/Base64.h b/WebCore/platform/text/Base64.h index 53b29b0..211bd3c 100644 --- a/WebCore/platform/text/Base64.h +++ b/WebCore/platform/text/Base64.h @@ -1,5 +1,6 @@ /* - * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) + * Copyright (C) 2006 Alexey Proskuryakov <ap@webkit.org> + * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,16 +27,19 @@ #ifndef Base64_h #define Base64_h +#include <wtf/Forward.h> #include <wtf/Vector.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); -// this decoder is not general purpose - it returns an error if it encounters a linefeed, as needed for window.atob -bool base64Decode(const Vector<char>&, Vector<char>&); -bool base64Decode(const char*, unsigned, Vector<char>&); +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); } diff --git a/WebCore/platform/text/LineEnding.cpp b/WebCore/platform/text/LineEnding.cpp index 545f22b..00a90eb 100644 --- a/WebCore/platform/text/LineEnding.cpp +++ b/WebCore/platform/text/LineEnding.cpp @@ -35,12 +35,69 @@ #include "PlatformString.h" #include <wtf/text/CString.h> -namespace WebCore { +namespace { -// Normalize all line-endings to CRLF. -CString normalizeLineEndingsToCRLF(const CString& from) +class OutputBuffer { +public: + virtual char* allocate(size_t size) = 0; + virtual void copy(const CString&) = 0; + virtual ~OutputBuffer() { } +}; + +class CStringBuffer : public OutputBuffer { +public: + CStringBuffer(CString& buffer) + : m_buffer(buffer) + { + } + virtual ~CStringBuffer() { } + + virtual char* allocate(size_t size) + { + char* ptr; + m_buffer = CString::newUninitialized(size, ptr); + return ptr; + } + + virtual void copy(const CString& source) + { + m_buffer = source; + } + + const CString& buffer() const { return m_buffer; } + +private: + CString m_buffer; +}; + +class VectorCharAppendBuffer : public OutputBuffer { +public: + VectorCharAppendBuffer(Vector<char>& buffer) + : m_buffer(buffer) + { + } + virtual ~VectorCharAppendBuffer() { } + + virtual char* allocate(size_t size) + { + size_t oldSize = m_buffer.size(); + m_buffer.grow(oldSize + size); + return m_buffer.data() + oldSize; + } + + virtual void copy(const CString& source) + { + m_buffer.append(source.data(), source.length()); + } + +private: + Vector<char>& m_buffer; +}; + +void internalNormalizeLineEndingsToCRLF(const CString& from, OutputBuffer& buffer) { - unsigned newLen = 0; + // Compute the new length. + size_t newLen = 0; const char* p = from.data(); while (char c = *p++) { if (c == '\r') { @@ -57,13 +114,18 @@ CString normalizeLineEndingsToCRLF(const CString& from) newLen += 1; } } - if (newLen == from.length()) - return from; + if (newLen < from.length()) + return; + + if (newLen == from.length()) { + buffer.copy(from); + return; + } - // Make a copy of the string. p = from.data(); - char* q; - CString result = CString::newUninitialized(newLen, q); + char* q = buffer.allocate(newLen); + + // Make a copy of the string. while (char c = *p++) { if (c == '\r') { // Safe to look ahead because of trailing '\0'. @@ -81,13 +143,19 @@ CString normalizeLineEndingsToCRLF(const CString& from) *q++ = c; } } - return result; } +}; + +namespace WebCore { + +void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR); + // Normalize all line-endings to CR or LF. -static CString normalizeToCROrLF(const CString& from, bool toCR) +void normalizeToCROrLF(const CString& from, Vector<char>& result, bool toCR) { - unsigned newLen = 0; + // Compute the new length. + size_t newLen = 0; bool needFix = false; const char* p = from.data(); char fromEndingChar = toCR ? '\n' : '\r'; @@ -103,13 +171,20 @@ static CString normalizeToCROrLF(const CString& from, bool toCR) } newLen += 1; } - if (!needFix) - return from; - // Make a copy of the string. + // Grow the result buffer. p = from.data(); - char* q; - CString result = CString::newUninitialized(newLen, q); + size_t oldResultSize = result.size(); + result.grow(oldResultSize + newLen); + char* q = result.data() + oldResultSize; + + // If no need to fix the string, just copy the string over. + if (!needFix) { + memcpy(q, p, from.length()); + return; + } + + // Make a copy of the string. while (char c = *p++) { if (c == '\r' && *p == '\n') { // Turn CRLF or CR into CR or LF. @@ -123,27 +198,33 @@ static CString normalizeToCROrLF(const CString& from, bool toCR) *q++ = c; } } - return result; } -// Normalize all line-endings to CR. -CString normalizeLineEndingsToCR(const CString& from) +CString normalizeLineEndingsToCRLF(const CString& from) +{ + CString result; + CStringBuffer buffer(result); + internalNormalizeLineEndingsToCRLF(from, buffer); + return buffer.buffer(); +} + +void normalizeLineEndingsToCR(const CString& from, Vector<char>& result) { - return normalizeToCROrLF(from, true); + normalizeToCROrLF(from, result, true); } -// Normalize all line-endings to LF. -CString normalizeLineEndingsToLF(const CString& from) +void normalizeLineEndingsToLF(const CString& from, Vector<char>& result) { - return normalizeToCROrLF(from, false); + normalizeToCROrLF(from, result, false); } -CString normalizeLineEndingsToNative(const CString& from) +void normalizeLineEndingsToNative(const CString& from, Vector<char>& result) { #if OS(WINDOWS) - return normalizeLineEndingsToCRLF(from); + VectorCharAppendBuffer buffer(result); + internalNormalizeLineEndingsToCRLF(from, buffer); #else - return normalizeLineEndingsToLF(from); + normalizeLineEndingsToLF(from, result); #endif } diff --git a/WebCore/platform/text/LineEnding.h b/WebCore/platform/text/LineEnding.h index ab8d6ee..4306ce8 100644 --- a/WebCore/platform/text/LineEnding.h +++ b/WebCore/platform/text/LineEnding.h @@ -33,25 +33,22 @@ #define LineEnding_h #include <wtf/Forward.h> - -namespace WTF { -class CString; -} +#include <wtf/Vector.h> namespace WebCore { // Normalize all line-endings in the given string to CRLF. -WTF::CString normalizeLineEndingsToCRLF(const WTF::CString&); +CString normalizeLineEndingsToCRLF(const CString& from); -// Normalize all line-endings in the given string to CR. -WTF::CString normalizeLineEndingsToCR(const WTF::CString&); +// Normalize all line-endings in the given string to CR and append the result to the given buffer. +void normalizeLineEndingsToCR(const CString& from, Vector<char>& result); -// Normalize all line-endings in the given string to LF. -WTF::CString normalizeLineEndingsToLF(const WTF::CString&); +// Normalize all line-endings in the given string to LF and append the result to the given buffer. +void normalizeLineEndingsToLF(const CString& from, Vector<char>& result); -// Normalize all line-endings in the given string to the native line-endings. +// Normalize all line-endings in the given string to the native line-endings and append the result to the given buffer. // (Normalize to CRLF on Windows and normalize to LF on all other platforms.) -WTF::CString normalizeLineEndingsToNative(const WTF::CString&); +void normalizeLineEndingsToNative(const CString& from, Vector<char>& result); } // namespace WebCore diff --git a/WebCore/platform/text/StringBuffer.h b/WebCore/platform/text/StringBuffer.h deleted file mode 100644 index 3a753b4..0000000 --- a/WebCore/platform/text/StringBuffer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Inc. ("Apple") nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef WebCoreStringBuffer_h -#define WebCoreStringBuffer_h - -// FIXME: remove this header, use the forward from wtf directly. -#include <wtf/text/StringBuffer.h> - -#endif // StringBuffer_h diff --git a/WebCore/platform/text/StringBuilder.cpp b/WebCore/platform/text/StringBuilder.cpp deleted file mode 100644 index 1c47129..0000000 --- a/WebCore/platform/text/StringBuilder.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2008 Apple Inc. All rights reserved. - * Copyright (C) Research In Motion Limited 2010. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "StringBuilder.h" - -#include <wtf/text/StringBuffer.h> - -namespace WebCore { - -void StringBuilder::append(const String& string) -{ - if (string.isNull()) - return; - - if (m_totalLength == UINT_MAX) - m_totalLength = string.length(); - else - m_totalLength += string.length(); - - if (!string.isEmpty()) - m_strings.append(string); -} - -void StringBuilder::append(UChar c) -{ - if (m_totalLength == UINT_MAX) - m_totalLength = 1; - else - m_totalLength += 1; - - m_strings.append(String(&c, 1)); -} - -void StringBuilder::append(char c) -{ - if (m_totalLength == UINT_MAX) - m_totalLength = 1; - else - m_totalLength += 1; - - m_strings.append(String(&c, 1)); -} - -String StringBuilder::toString(ConcatMode mode) const -{ - if (isNull()) - return String(); - - unsigned count = m_strings.size(); - - if (!count) - return String(StringImpl::empty()); - if (count == 1) - return m_strings[0]; - - UChar* buffer; - unsigned totalLength = m_totalLength; - if (mode == ConcatAddingSpacesBetweenIndividualStrings) - totalLength += count - 1; - String result = String::createUninitialized(totalLength, buffer); - - UChar* p = buffer; - - // We could handle both Concat modes in a single for loop, not doing that for performance reasons. - if (mode == ConcatUnaltered) { - for (unsigned i = 0; i < count; ++i) { - StringImpl* string = m_strings[i].impl(); - unsigned length = string->length(); - memcpy(p, string->characters(), length * 2); - p += length; - } - } else { - ASSERT(mode == ConcatAddingSpacesBetweenIndividualStrings); - for (unsigned i = 0; i < count; ++i) { - StringImpl* string = m_strings[i].impl(); - unsigned length = string->length(); - memcpy(p, string->characters(), length * 2); - p += length; - - // Add space after string before the start of the next string, if we're not processing the last string. - if (i < count - 1) { - *p = ' '; - ++p; - } - } - } - ASSERT(p == totalLength + buffer); - - return result; -} - -void StringBuilder::clear() -{ - m_totalLength = UINT_MAX; - m_strings.clear(); -} - -unsigned StringBuilder::length() const -{ - if (m_totalLength == UINT_MAX) - return 0; - return m_totalLength; -} - -} diff --git a/WebCore/platform/text/TextEncoding.cpp b/WebCore/platform/text/TextEncoding.cpp index 29ae170..921ceeb 100644 --- a/WebCore/platform/text/TextEncoding.cpp +++ b/WebCore/platform/text/TextEncoding.cpp @@ -133,6 +133,10 @@ CString TextEncoding::encode(const UChar* characters, size_t length, Unencodable // normalization will be done by Windows CE API OwnPtr<TextCodec> textCodec = newTextCodec(*this); return textCodec.get() ? textCodec->encode(characters, length, handling) : CString(); +#elif USE(BREWMP_UNICODE) + // FIXME: not sure if Brew MP normalizes the input string automatically + OwnPtr<TextCodec> textCodec = newTextCodec(*this); + return textCodec.get() ? textCodec->encode(characters, length, handling) : CString(); #endif } diff --git a/WebCore/platform/text/TextEncodingRegistry.cpp b/WebCore/platform/text/TextEncodingRegistry.cpp index 40fcdc5..fbe5826 100644 --- a/WebCore/platform/text/TextEncodingRegistry.cpp +++ b/WebCore/platform/text/TextEncodingRegistry.cpp @@ -31,6 +31,7 @@ #include "TextCodecLatin1.h" #include "TextCodecUserDefined.h" #include "TextCodecUTF16.h" +#include "TextEncoding.h" #include <wtf/ASCIICType.h> #include <wtf/Assertions.h> #include <wtf/HashFunctions.h> @@ -51,6 +52,9 @@ #if USE(GLIB_UNICODE) #include "gtk/TextCodecGtk.h" #endif +#if USE(BREWMP_UNICODE) +#include "brew/TextCodecBrew.h" +#endif #if OS(WINCE) && !PLATFORM(QT) #include "TextCodecWinCE.h" #endif @@ -235,6 +239,11 @@ static void buildBaseTextCodecMaps() TextCodecGtk::registerBaseCodecs(addToTextCodecMap); #endif +#if USE(BREWMP_UNICODE) + TextCodecBrew::registerBaseEncodingNames(addToTextEncodingNameMap); + TextCodecBrew::registerBaseCodecs(addToTextCodecMap); +#endif + #if OS(WINCE) && !PLATFORM(QT) TextCodecWinCE::registerBaseEncodingNames(addToTextEncodingNameMap); TextCodecWinCE::registerBaseCodecs(addToTextCodecMap); diff --git a/WebCore/platform/text/brew/TextBoundariesBrew.cpp b/WebCore/platform/text/brew/TextBoundariesBrew.cpp new file mode 100644 index 0000000..506bdcf --- /dev/null +++ b/WebCore/platform/text/brew/TextBoundariesBrew.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2006 Zack Rusin <zack@kde.org> + * Copyright (C) 2007-2009 Torch Mobile, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextBoundaries.h" + +#include "NotImplemented.h" +#include "PlatformString.h" + +using namespace WTF::Unicode; + +namespace WebCore { + +int findNextWordFromIndex(const UChar* buffer, int len, int position, bool forward) +{ + notImplemented(); + return 0; +} + +void findWordBoundary(const UChar* buffer, int len, int position, int* start, int* end) +{ + if (position > len) { + *start = 0; + *end = 0; + return; + } + + String str(buffer, len); + + int currentPosition = position - 1; + String foundWord; + while (currentPosition >= 0 && isLetter(str[currentPosition])) { + UChar c = str[currentPosition]; + foundWord.insert(&c, 1, 0); + --currentPosition; + } + + // currentPosition == 0 means the first char is not letter + // currentPosition == -1 means we reached the beginning + int startPos = (currentPosition < 0) ? 0 : ++currentPosition; + currentPosition = position; + while (isLetter(str[currentPosition])) { + foundWord.append(str[currentPosition]); + ++currentPosition; + } + + *start = startPos; + *end = currentPosition; +} + +} diff --git a/WebCore/platform/text/brew/TextBreakIteratorBrew.cpp b/WebCore/platform/text/brew/TextBreakIteratorBrew.cpp new file mode 100644 index 0000000..7f46e4f --- /dev/null +++ b/WebCore/platform/text/brew/TextBreakIteratorBrew.cpp @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2006 Lars Knoll <lars@trolltech.com> + * Copyright (C) 2007-2009 Torch Mobile, Inc. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "TextBreakIterator.h" + +#include "PlatformString.h" +#include <wtf/StdLibExtras.h> +#include <wtf/unicode/Unicode.h> + +using namespace WTF::Unicode; + +namespace WebCore { + +// Hack, not entirely correct +static inline bool isCharStop(UChar c) +{ + CharCategory charCategory = category(c); + return charCategory != Mark_NonSpacing && (charCategory != Other_Surrogate || (c < 0xd800 || c >= 0xdc00)); +} + +static inline bool isLineStop(UChar c) +{ + return category(c) != Separator_Line; +} + +static inline bool isSentenceStop(UChar c) +{ + return isPunct(c); +} + +class TextBreakIterator { +public: + void reset(const UChar* str, int len) + { + string = str; + length = len; + currentPos = 0; + } + virtual int first() = 0; + virtual int next() = 0; + virtual int previous() = 0; + int following(int position) + { + currentPos = position; + return next(); + } + int preceding(int position) + { + currentPos = position; + return previous(); + } + + int currentPos; + const UChar* string; + int length; +}; + +struct WordBreakIterator: TextBreakIterator { + virtual int first(); + virtual int next(); + virtual int previous(); +}; + +struct CharBreakIterator: TextBreakIterator { + virtual int first(); + virtual int next(); + virtual int previous(); +}; + +struct LineBreakIterator: TextBreakIterator { + virtual int first(); + virtual int next(); + virtual int previous(); +}; + +struct SentenceBreakIterator : TextBreakIterator { + virtual int first(); + virtual int next(); + virtual int previous(); +}; + +int WordBreakIterator::first() +{ + currentPos = 0; + return currentPos; +} + +int WordBreakIterator::next() +{ + if (currentPos == length) { + currentPos = -1; + return currentPos; + } + bool haveSpace = false; + while (currentPos < length) { + if (haveSpace && !isSpace(string[currentPos])) + break; + if (isSpace(string[currentPos])) + haveSpace = true; + ++currentPos; + } + return currentPos; +} + +int WordBreakIterator::previous() +{ + if (!currentPos) { + currentPos = -1; + return currentPos; + } + bool haveSpace = false; + while (currentPos > 0) { + if (haveSpace && !isSpace(string[currentPos])) + break; + if (isSpace(string[currentPos])) + haveSpace = true; + --currentPos; + } + return currentPos; +} + +int CharBreakIterator::first() +{ + currentPos = 0; + return currentPos; +} + +int CharBreakIterator::next() +{ + if (currentPos >= length) + return -1; + ++currentPos; + while (currentPos < length && !isCharStop(string[currentPos])) + ++currentPos; + return currentPos; +} + +int CharBreakIterator::previous() +{ + if (currentPos <= 0) + return -1; + if (currentPos > length) + currentPos = length; + --currentPos; + while (currentPos > 0 && !isCharStop(string[currentPos])) + --currentPos; + return currentPos; +} + +int LineBreakIterator::first() +{ + currentPos = 0; + return currentPos; +} + +int LineBreakIterator::next() +{ + if (currentPos == length) { + currentPos = -1; + return currentPos; + } + bool haveSpace = false; + while (currentPos < length) { + if (haveSpace && !isLineStop(string[currentPos])) + break; + if (isLineStop(string[currentPos])) + haveSpace = true; + ++currentPos; + } + return currentPos; +} + +int LineBreakIterator::previous() +{ + if (!currentPos) { + currentPos = -1; + return currentPos; + } + bool haveSpace = false; + while (currentPos > 0) { + if (haveSpace && !isLineStop(string[currentPos])) + break; + if (isLineStop(string[currentPos])) + haveSpace = true; + --currentPos; + } + return currentPos; +} + +int SentenceBreakIterator::first() +{ + currentPos = 0; + return currentPos; +} + +int SentenceBreakIterator::next() +{ + if (currentPos == length) { + currentPos = -1; + return currentPos; + } + bool haveSpace = false; + while (currentPos < length) { + if (haveSpace && !isSentenceStop(string[currentPos])) + break; + if (isSentenceStop(string[currentPos])) + haveSpace = true; + ++currentPos; + } + return currentPos; +} + +int SentenceBreakIterator::previous() +{ + if (!currentPos) { + currentPos = -1; + return currentPos; + } + bool haveSpace = false; + while (currentPos > 0) { + if (haveSpace && !isSentenceStop(string[currentPos])) + break; + if (isSentenceStop(string[currentPos])) + haveSpace = true; + --currentPos; + } + return currentPos; +} + +TextBreakIterator* wordBreakIterator(const UChar* string, int length) +{ + DEFINE_STATIC_LOCAL(WordBreakIterator, iterator, ()); + iterator.reset(string, length); + return &iterator; +} + +TextBreakIterator* characterBreakIterator(const UChar* string, int length) +{ + DEFINE_STATIC_LOCAL(CharBreakIterator, iterator, ()); + iterator.reset(string, length); + return &iterator; +} + +TextBreakIterator* lineBreakIterator(const UChar* string, int length) +{ + DEFINE_STATIC_LOCAL(LineBreakIterator , iterator, ()); + iterator.reset(string, length); + return &iterator; +} + +TextBreakIterator* sentenceBreakIterator(const UChar* string, int length) +{ + DEFINE_STATIC_LOCAL(SentenceBreakIterator, iterator, ()); + iterator.reset(string, length); + return &iterator; +} + +int textBreakFirst(TextBreakIterator* breakIterator) +{ + return breakIterator->first(); +} + +int textBreakNext(TextBreakIterator* breakIterator) +{ + return breakIterator->next(); +} + +int textBreakPreceding(TextBreakIterator* breakIterator, int position) +{ + return breakIterator->preceding(position); +} + +int textBreakFollowing(TextBreakIterator* breakIterator, int position) +{ + return breakIterator->following(position); +} + +int textBreakCurrent(TextBreakIterator* breakIterator) +{ + return breakIterator->currentPos; +} + +bool isTextBreak(TextBreakIterator*, int) +{ + return true; +} + +TextBreakIterator* cursorMovementIterator(const UChar* string, int length) +{ + return characterBreakIterator(string, length); +} + +} // namespace WebCore diff --git a/WebCore/platform/text/brew/TextCodecBrew.cpp b/WebCore/platform/text/brew/TextCodecBrew.cpp new file mode 100644 index 0000000..1f32298 --- /dev/null +++ b/WebCore/platform/text/brew/TextCodecBrew.cpp @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2010 Company 100, 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 "TextCodecBrew.h" + +#include "AEEAppGen.h" +#include "AEEICharsetConv.h" +#include "NotImplemented.h" +#include "PlatformString.h" +#include <wtf/Assertions.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +// FIXME: Not sure if there are Brew MP devices which use big endian. +const char* WebCore::TextCodecBrew::m_internalEncodingName = "UTF-16LE"; + +static PassOwnPtr<TextCodec> newTextCodecBrew(const TextEncoding& encoding, const void*) +{ + return new TextCodecBrew(encoding); +} + +void TextCodecBrew::registerBaseEncodingNames(EncodingNameRegistrar registrar) +{ + registrar("UTF-8", "UTF-8"); +} + +void TextCodecBrew::registerBaseCodecs(TextCodecRegistrar registrar) +{ + registrar("UTF-8", newTextCodecBrew, 0); +} + +void TextCodecBrew::registerExtendedEncodingNames(EncodingNameRegistrar registrar) +{ + // FIXME: Not sure how to enumerate all available encodings. + notImplemented(); +} + +void TextCodecBrew::registerExtendedCodecs(TextCodecRegistrar registrar) +{ + notImplemented(); +} + +TextCodecBrew::TextCodecBrew(const TextEncoding& encoding) + : m_charsetConverter(0) + , m_encoding(encoding) + , m_numBufferedBytes(0) +{ + String format = String::format("%s>%s", encoding.name(), m_internalEncodingName); + + IShell* shell = reinterpret_cast<AEEApplet*>(GETAPPINSTANCE())->m_pIShell; + AEECLSID classID = ISHELL_GetHandler(shell, AEEIID_ICharsetConv, format.latin1().data()); + ISHELL_CreateInstance(shell, classID, reinterpret_cast<void**>(&m_charsetConverter)); + + ASSERT(m_charsetConverter); +} + +TextCodecBrew::~TextCodecBrew() +{ + if (m_charsetConverter) + ICharsetConv_Release(m_charsetConverter); +} + +String TextCodecBrew::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError) +{ + int code = ICharsetConv_Initialize(m_charsetConverter, m_encoding.name(), m_internalEncodingName, 0); + ASSERT(code == AEE_SUCCESS); + + Vector<UChar> result; + Vector<unsigned char> prefixedBytes(length); + + int srcSize; + unsigned char* srcBegin; + + if (m_numBufferedBytes) { + srcSize = length + m_numBufferedBytes; + prefixedBytes.grow(srcSize); + memcpy(prefixedBytes.data(), m_bufferedBytes, m_numBufferedBytes); + memcpy(prefixedBytes.data() + m_numBufferedBytes, bytes, length); + + srcBegin = prefixedBytes.data(); + + // all buffered bytes are consumed now + m_numBufferedBytes = 0; + } else { + srcSize = length; + srcBegin = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(bytes)); + } + + unsigned char* src = srcBegin; + unsigned char* srcEnd = srcBegin + srcSize; + + Vector<UChar> dstBuffer(srcSize); + + while (src < srcEnd) { + int numCharsConverted; + unsigned char* dstBegin = reinterpret_cast<unsigned char*>(dstBuffer.data()); + unsigned char* dst = dstBegin; + int dstSize = dstBuffer.size() * sizeof(UChar); + + code = ICharsetConv_CharsetConvert(m_charsetConverter, &src, &srcSize, &dst, &dstSize, &numCharsConverted); + ASSERT(code != AEE_ENOSUCH); + + if (code == AEE_EBUFFERTOOSMALL) { + // Increase the buffer and try it again. + dstBuffer.grow(dstBuffer.size() * 2); + continue; + } + + if (code == AEE_EBADITEM) { + sawError = true; + if (stopOnError) { + result.append(L'?'); + break; + } + + src++; + } + + if (code == AEE_EINCOMPLETEITEM) { + if (flush) { + LOG_ERROR("Partial bytes at end of input while flush requested."); + sawError = true; + return String(); + } + + m_numBufferedBytes = srcEnd - src; + memcpy(m_bufferedBytes, src, m_numBufferedBytes); + break; + } + + int numChars = (dst - dstBegin) / sizeof(UChar); + if (numChars > 0) + result.append(dstBuffer.data(), numChars); + } + + return String::adopt(result); +} + +CString TextCodecBrew::encode(const UChar* characters, size_t length, UnencodableHandling handling) +{ + if (!length) + return ""; + + unsigned int replacementCharacter = '?'; + + // FIXME: Impossible to handle EntitiesForUnencodables or URLEncodedEntitiesForUnencodables with ICharsetConv. + int code = ICharsetConv_Initialize(m_charsetConverter, m_internalEncodingName, m_encoding.name(), replacementCharacter); + ASSERT(code == AEE_SUCCESS); + + Vector<char> result; + + int srcSize = length * sizeof(UChar); + unsigned char* srcBegin = const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(characters)); + unsigned char* src = srcBegin; + unsigned char* srcEnd = srcBegin + srcSize; + + Vector<unsigned char> dstBuffer(length * sizeof(UChar)); + + while (src < srcEnd) { + int numCharsConverted; + unsigned char* dstBegin = dstBuffer.data(); + unsigned char* dst = dstBegin; + int dstSize = dstBuffer.size(); + + code = ICharsetConv_CharsetConvert(m_charsetConverter, &src, &srcSize, &dst, &dstSize, &numCharsConverted); + ASSERT(code != AEE_EINCOMPLETEITEM); + + if (code == AEE_ENOSUCH) { + LOG_ERROR("Conversion error, Code=%d", code); + return CString(); + } + + if (code == AEE_EBUFFERTOOSMALL) { + // Increase the buffer and try it again. + dstBuffer.grow(dstBuffer.size() * 2); + continue; + } + + if (code == AEE_EBADITEM) + src += sizeof(UChar); // Skip the invalid character + + int numBytes = dst - dstBegin; + if (numBytes > 0) + result.append(dstBuffer.data(), numBytes); + } + + return CString(result.data(), result.size()); +} + +} // namespace WebCore diff --git a/WebCore/platform/text/brew/TextCodecBrew.h b/WebCore/platform/text/brew/TextCodecBrew.h new file mode 100644 index 0000000..97e2c87 --- /dev/null +++ b/WebCore/platform/text/brew/TextCodecBrew.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2010 Company 100, 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 TextCodecBrew_h +#define TextCodecBrew_h + +#include "TextCodec.h" +#include "TextEncoding.h" + +typedef struct ICharsetConv ICharsetConv; + +namespace WebCore { + +class TextCodecBrew : public TextCodec { +public: + static void registerBaseEncodingNames(EncodingNameRegistrar); + static void registerBaseCodecs(TextCodecRegistrar); + + static void registerExtendedEncodingNames(EncodingNameRegistrar); + static void registerExtendedCodecs(TextCodecRegistrar); + + TextCodecBrew(const TextEncoding&); + virtual ~TextCodecBrew(); + + virtual String decode(const char*, size_t length, bool flush, bool stopOnError, bool& sawError); + virtual CString encode(const UChar*, size_t length, UnencodableHandling); + +private: + TextEncoding m_encoding; + size_t m_numBufferedBytes; + unsigned char m_bufferedBytes[16]; // bigger than any single multi-byte character + ICharsetConv* m_charsetConverter; + + static const char* m_internalEncodingName; +}; + +} // namespace WebCore + +#endif // TextCodecBrew_h diff --git a/WebCore/platform/text/qt/TextBreakIteratorQt.cpp b/WebCore/platform/text/qt/TextBreakIteratorQt.cpp index dda443f..b9f5a9e 100644 --- a/WebCore/platform/text/qt/TextBreakIteratorQt.cpp +++ b/WebCore/platform/text/qt/TextBreakIteratorQt.cpp @@ -33,6 +33,12 @@ namespace WebCore { +#if USE(QT_ICU_TEXT_BREAKING) +const char* currentTextBreakLocaleID() +{ + return QLocale::system().name().toLatin1(); +} +#else static unsigned char buffer[1024]; class TextBreakIterator : public QTextBoundaryFinder { @@ -135,5 +141,6 @@ namespace WebCore { { return true; } +#endif } diff --git a/WebCore/platform/text/wince/TextCodecWinCE.cpp b/WebCore/platform/text/wince/TextCodecWinCE.cpp index 499035f..da6d5a5 100644 --- a/WebCore/platform/text/wince/TextCodecWinCE.cpp +++ b/WebCore/platform/text/wince/TextCodecWinCE.cpp @@ -33,6 +33,7 @@ #include <wtf/HashMap.h> #include <wtf/HashSet.h> #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #include <wtf/text/StringHash.h> #include <wtf/unicode/UTF8.h> @@ -110,7 +111,7 @@ LanguageManager::LanguageManager() info.m_aliases.append(name); info.m_aliases.append(String(cpInfo.wszHeaderCharset).latin1()); info.m_aliases.append(String(cpInfo.wszBodyCharset).latin1()); - String cpName = String::format("cp%d", cpInfo.uiCodePage); + String cpName = makeString("cp", String::number(cpInfo.uiCodePage)); info.m_aliases.append(cpName.latin1()); supportedCharsets().add(i->second.data()); } diff --git a/WebCore/platform/win/ClipboardUtilitiesWin.cpp b/WebCore/platform/win/ClipboardUtilitiesWin.cpp index 0130a70..19888bf 100644 --- a/WebCore/platform/win/ClipboardUtilitiesWin.cpp +++ b/WebCore/platform/win/ClipboardUtilitiesWin.cpp @@ -33,7 +33,9 @@ #include "markup.h" #include <shlwapi.h> #include <wininet.h> // for INTERNET_MAX_URL_LENGTH +#include <wtf/StringExtras.h> #include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> #if PLATFORM(CF) #include <CoreFoundation/CoreFoundation.h> @@ -239,7 +241,11 @@ void markupToCFHTML(const String& markup, const String& srcURL, Vector<char>& re unsigned endFragmentOffset = startFragmentOffset + markupUTF8.length(); unsigned endHTMLOffset = endFragmentOffset + strlen(endMarkup); - append(result, String::format(header, startHTMLOffset, endHTMLOffset, startFragmentOffset, endFragmentOffset).utf8()); + unsigned headerBufferLength = startHTMLOffset + 1; // + 1 for '\0' terminator. + char* headerBuffer = (char*)malloc(headerBufferLength); + snprintf(headerBuffer, headerBufferLength, header, startHTMLOffset, endHTMLOffset, startFragmentOffset, endFragmentOffset); + append(result, CString(headerBuffer)); + free(headerBuffer); if (sourceURLUTF8.length()) { append(result, sourceURLPrefix); append(result, sourceURLUTF8); diff --git a/WebCore/platform/win/ClipboardWin.cpp b/WebCore/platform/win/ClipboardWin.cpp index 529963f..7a54737 100644 --- a/WebCore/platform/win/ClipboardWin.cpp +++ b/WebCore/platform/win/ClipboardWin.cpp @@ -38,6 +38,7 @@ #include "FrameLoader.h" #include "FrameView.h" #include "HTMLNames.h" +#include "HTMLParserIdioms.h" #include "Image.h" #include "MIMETypeRegistry.h" #include "NotImplemented.h" @@ -50,7 +51,6 @@ #include "ResourceResponse.h" #include "SharedBuffer.h" #include "WCDataObject.h" -#include "csshelper.h" #include "markup.h" #include <shlwapi.h> #include <wininet.h> @@ -723,7 +723,7 @@ void ClipboardWin::declareAndWriteDragImage(Element* element, const KURL& url, c if (imageURL.isEmpty()) return; - String fullURL = frame->document()->completeURL(deprecatedParseURL(imageURL)).string(); + String fullURL = frame->document()->completeURL(stripLeadingAndTrailingHTMLSpaces(imageURL)).string(); if (fullURL.isEmpty()) return; STGMEDIUM medium = {0}; diff --git a/WebCore/platform/win/Language.cpp b/WebCore/platform/win/LanguageWin.cpp index 676510b..cafda5d 100644 --- a/WebCore/platform/win/Language.cpp +++ b/WebCore/platform/win/LanguageWin.cpp @@ -26,8 +26,7 @@ #include "config.h" #include "Language.h" -#include "PlatformString.h" -#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> namespace WebCore { @@ -45,10 +44,11 @@ static String localeInfo(LCTYPE localeType, const String& fallback) if (localeName.isEmpty()) return fallback; + localeName.truncate(localeName.length() - 1); return localeName; } -String defaultLanguage() +String platformDefaultLanguage() { static String computedDefaultLanguage; if (!computedDefaultLanguage.isEmpty()) @@ -60,7 +60,7 @@ String defaultLanguage() if (countryName.isEmpty()) computedDefaultLanguage = languageName; else - computedDefaultLanguage = String::format("%s-%s", languageName.latin1().data(), countryName.latin1().data()); + computedDefaultLanguage = makeString(languageName, '-', countryName); return computedDefaultLanguage; } diff --git a/WebCore/platform/win/PlatformScreenWin.cpp b/WebCore/platform/win/PlatformScreenWin.cpp index 4af9e17..21fa10d 100644 --- a/WebCore/platform/win/PlatformScreenWin.cpp +++ b/WebCore/platform/win/PlatformScreenWin.cpp @@ -68,14 +68,18 @@ static DEVMODE deviceInfoForWidget(Widget* widget) int screenDepth(Widget* widget) { DEVMODE deviceInfo = deviceInfoForWidget(widget); + if (deviceInfo.dmBitsPerPel == 32) { + // Some video drivers return 32, but this function is supposed to ignore the alpha + // component. See <http://webkit.org/b/42972>. + return 24; + } return deviceInfo.dmBitsPerPel; } int screenDepthPerComponent(Widget* widget) { // FIXME: Assumes RGB -- not sure if this is right. - DEVMODE deviceInfo = deviceInfoForWidget(widget); - return deviceInfo.dmBitsPerPel / 3; + return screenDepth(widget) / 3; } bool screenIsMonochrome(Widget* widget) diff --git a/WebCore/platform/win/PopupMenuWin.cpp b/WebCore/platform/win/PopupMenuWin.cpp index a782b03..e86aef9 100644 --- a/WebCore/platform/win/PopupMenuWin.cpp +++ b/WebCore/platform/win/PopupMenuWin.cpp @@ -619,11 +619,11 @@ void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) // Draw the background for this menu item if (itemStyle.isVisible()) - context.fillRect(itemRect, optionBackgroundColor, DeviceColorSpace); + context.fillRect(itemRect, optionBackgroundColor, ColorSpaceDeviceRGB); if (client()->itemIsSeparator(index)) { IntRect separatorRect(itemRect.x() + separatorPadding, itemRect.y() + (itemRect.height() - separatorHeight) / 2, itemRect.width() - 2 * separatorPadding, separatorHeight); - context.fillRect(separatorRect, optionTextColor, DeviceColorSpace); + context.fillRect(separatorRect, optionTextColor, ColorSpaceDeviceRGB); continue; } @@ -633,7 +633,7 @@ void PopupMenuWin::paint(const IntRect& damageRect, HDC hdc) const UChar* string = itemText.characters(); TextRun textRun(string, length, false, 0, 0, itemText.defaultWritingDirection() == WTF::Unicode::RightToLeft); - context.setFillColor(optionTextColor, DeviceColorSpace); + context.setFillColor(optionTextColor, ColorSpaceDeviceRGB); Font itemFont = client()->menuStyle().font(); if (client()->itemIsLabel(index)) { diff --git a/WebCore/platform/win/WebCoreTextRenderer.cpp b/WebCore/platform/win/WebCoreTextRenderer.cpp index f771e00..a32fa4f 100644 --- a/WebCore/platform/win/WebCoreTextRenderer.cpp +++ b/WebCore/platform/win/WebCoreTextRenderer.cpp @@ -49,7 +49,7 @@ static void doDrawTextAtPoint(GraphicsContext& context, const String& text, cons { TextRun run(text.characters(), text.length()); - context.setFillColor(color, DeviceColorSpace); + context.setFillColor(color, ColorSpaceDeviceRGB); if (isOneLeftToRightRun(run)) font.drawText(&context, run, point); else @@ -71,7 +71,7 @@ static void doDrawTextAtPoint(GraphicsContext& context, const String& text, cons IntPoint underlinePoint(point); underlinePoint.move(beforeWidth, 1); - context.setStrokeColor(color, DeviceColorSpace); + context.setStrokeColor(color, ColorSpaceDeviceRGB); context.drawLineForText(underlinePoint, underlinedWidth, false); } } diff --git a/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp b/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp index 82259f4..56a5d07 100644 --- a/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp +++ b/WebCore/platform/wx/wxcode/mac/carbon/non-kerned-drawing.cpp @@ -48,7 +48,7 @@ void drawTextWithSpacing(GraphicsContext* graphicsContext, const SimpleFontData* wxGCDC* dc = static_cast<wxGCDC*>(graphicsContext->platformContext()); wxFont* wxfont = font->getWxFont(); - graphicsContext->setFillColor(graphicsContext->fillColor(), DeviceColorSpace); + graphicsContext->setFillColor(graphicsContext->fillColor(), ColorSpaceDeviceRGB); CGContextRef cgContext = static_cast<CGContextRef>(dc->GetGraphicsContext()->GetNativeContext()); |