diff options
Diffstat (limited to 'Source/WebCore/platform')
20 files changed, 440 insertions, 164 deletions
diff --git a/Source/WebCore/platform/LengthPoint.cpp b/Source/WebCore/platform/LengthPoint.cpp new file mode 100644 index 000000000..6e6235cfc --- /dev/null +++ b/Source/WebCore/platform/LengthPoint.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2016 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 "LengthPoint.h" + +#include "TextStream.h" + +namespace WebCore { + +TextStream& operator<<(TextStream& ts, const LengthPoint& size) +{ + return ts << size.x() << " " << size.y(); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/LengthPoint.h b/Source/WebCore/platform/LengthPoint.h new file mode 100644 index 000000000..21b1a3f47 --- /dev/null +++ b/Source/WebCore/platform/LengthPoint.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 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 LengthPoint_h +#define LengthPoint_h + +#include "Length.h" + +namespace WebCore { + +struct LengthPoint { +public: + LengthPoint() + { + } + + LengthPoint(Length x, Length y) + : m_x(WTFMove(x)) + , m_y(WTFMove(y)) + { + } + + bool operator==(const LengthPoint& o) const + { + return m_x == o.m_x && m_y == o.m_y; + } + + bool operator!=(const LengthPoint& o) const + { + return !(*this == o); + } + + void setX(Length x) { m_x = WTFMove(x); } + const Length& x() const { return m_x; } + + void setY(Length y) { m_y = WTFMove(y); } + const Length& y() const { return m_y; } + + LengthPoint blend(const LengthPoint& from, double progress) const + { + return LengthPoint(m_x.blend(from.x(), progress), m_y.blend(from.y(), progress)); + } + +private: + // FIXME: it would be nice to pack the two Lengths together better somehow (to avoid padding between them). + Length m_x; + Length m_y; +}; + +TextStream& operator<<(TextStream&, const LengthPoint&); + +} // namespace WebCore + +#endif // LengthPoint_h diff --git a/Source/WebCore/platform/SharedBuffer.cpp b/Source/WebCore/platform/SharedBuffer.cpp index 00fcf6753..c7e05bf23 100644 --- a/Source/WebCore/platform/SharedBuffer.cpp +++ b/Source/WebCore/platform/SharedBuffer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006-2016 Apple Inc. All rights reserved. * Copyright (C) Research In Motion Limited 2009-2010. All rights reserved. * Copyright (C) 2015 Canon Inc. All rights reserved. * @@ -144,6 +144,10 @@ const char* SharedBuffer::data() const PassRefPtr<ArrayBuffer> SharedBuffer::createArrayBuffer() const { RefPtr<ArrayBuffer> arrayBuffer = ArrayBuffer::createUninitialized(static_cast<unsigned>(size()), sizeof(char)); + if (!arrayBuffer) { + WTFLogAlways("SharedBuffer::createArrayBuffer Unable to create buffer. Requested size was %d x %lu\n", size(), sizeof(char)); + return nullptr; + } const char* segment = 0; unsigned position = 0; diff --git a/Source/WebCore/platform/URL.cpp b/Source/WebCore/platform/URL.cpp index b2e1fde94..857bc7e6e 100644 --- a/Source/WebCore/platform/URL.cpp +++ b/Source/WebCore/platform/URL.cpp @@ -104,15 +104,19 @@ enum URLCharacterClasses { PathSegmentEndChar = 1 << 5, // not allowed in path - BadChar = 1 << 6 + BadChar = 1 << 6, + + // "\t" | "\n" | "\r" + TabNewline = 1 << 7 }; static const unsigned char characterClassTable[256] = { /* 0 nul */ PathSegmentEndChar, /* 1 soh */ BadChar, /* 2 stx */ BadChar, /* 3 etx */ BadChar, /* 4 eot */ BadChar, /* 5 enq */ BadChar, /* 6 ack */ BadChar, /* 7 bel */ BadChar, - /* 8 bs */ BadChar, /* 9 ht */ BadChar, /* 10 nl */ BadChar, /* 11 vt */ BadChar, - /* 12 np */ BadChar, /* 13 cr */ BadChar, /* 14 so */ BadChar, /* 15 si */ BadChar, + /* 8 bs */ BadChar, /* 9 ht */ BadChar | TabNewline, /* 10 nl */ BadChar | TabNewline, + /* 11 vt */ BadChar, /* 12 np */ BadChar, /* 13 cr */ BadChar | TabNewline, + /* 14 so */ BadChar, /* 15 si */ BadChar, /* 16 dle */ BadChar, /* 17 dc1 */ BadChar, /* 18 dc2 */ BadChar, /* 19 dc3 */ BadChar, /* 20 dc4 */ BadChar, /* 21 nak */ BadChar, /* 22 syn */ BadChar, /* 23 etb */ BadChar, /* 24 can */ BadChar, /* 25 em */ BadChar, /* 26 sub */ BadChar, /* 27 esc */ BadChar, @@ -350,6 +354,7 @@ static inline bool isIPv6Char(unsigned char c) { return characterClassTable[c] & static inline bool isPathSegmentEndChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & PathSegmentEndChar; } static inline bool isPathSegmentEndChar(UChar c) { return c <= 0xff && (characterClassTable[c] & PathSegmentEndChar); } static inline bool isBadChar(unsigned char c) { return characterClassTable[c] & BadChar; } +static inline bool isTabNewline(UChar c) { return c <= 0xff && (characterClassTable[c] & TabNewline); } static inline bool isSchemeCharacterMatchIgnoringCase(char character, char schemeCharacter) { @@ -455,7 +460,7 @@ URL::URL(const URL& base, const String& relative, const TextEncoding& encoding) init(base, relative, encoding.encodingForFormSubmission()); } -static bool shouldTrimFromURL(unsigned char c) +static bool shouldTrimFromURL(UChar c) { // Browsers ignore leading/trailing whitespace and control // characters from URLs. Note that c is an *unsigned* char here @@ -473,9 +478,14 @@ void URL::init(const URL& base, const String& relative, const TextEncoding& enco return; } + // Get rid of leading and trailing whitespace and control characters. + String rel = relative.stripWhiteSpace(shouldTrimFromURL); + + // Get rid of any tabs and newlines. + rel = rel.removeCharacters(isTabNewline); + // For compatibility with Win IE, treat backslashes as if they were slashes, // as long as we're not dealing with javascript: or data: URLs. - String rel = relative; if (rel.contains('\\') && !(protocolIsJavaScript(rel) || protocolIs(rel, "data"))) rel = substituteBackslashes(rel); @@ -500,16 +510,6 @@ void URL::init(const URL& base, const String& relative, const TextEncoding& enco len = strlen(str); } - // Get rid of leading whitespace and control characters. - while (len && shouldTrimFromURL(*str)) { - str++; - --len; - } - - // Get rid of trailing whitespace and control characters. - while (len && shouldTrimFromURL(str[len - 1])) - str[--len] = '\0'; - // According to the RFC, the reference should be interpreted as an // absolute URI if possible, using the "leftmost, longest" // algorithm. If the URI reference is absolute it will have a @@ -1934,12 +1934,26 @@ bool protocolIs(const String& url, const char* protocol) { // Do the comparison without making a new string object. assertProtocolIsGood(protocol); - for (int i = 0; ; ++i) { - if (!protocol[i]) + bool isLeading = true; + for (int i = 0, j = 0; url[i]; ++i) { + // skip leading whitespace and control characters. + if (isLeading && shouldTrimFromURL(url[i])) + continue; + isLeading = false; + + // skip any tabs and newlines. + if (isTabNewline(url[i])) + continue; + + if (!protocol[j]) return url[i] == ':'; - if (!isLetterMatchIgnoringCase(url[i], protocol[i])) + if (!isLetterMatchIgnoringCase(url[i], protocol[j])) return false; + + ++j; } + + return false; } bool isValidProtocol(const String& protocol) diff --git a/Source/WebCore/platform/URL.h b/Source/WebCore/platform/URL.h index 5fcee4880..f9bfce6dc 100644 --- a/Source/WebCore/platform/URL.h +++ b/Source/WebCore/platform/URL.h @@ -120,6 +120,9 @@ public: WEBCORE_EXPORT String fragmentIdentifier() const; WEBCORE_EXPORT bool hasFragmentIdentifier() const; + bool hasUsername() const; + bool hasPassword() const; + // Unlike user() and pass(), these functions don't decode escape sequences. // This is necessary for accurate round-tripping, because encoding doesn't encode '%' characters. String encodedUser() const; @@ -354,6 +357,16 @@ inline bool URL::hasPort() const return m_hostEnd < m_portEnd; } +inline bool URL::hasUsername() const +{ + return m_userEnd > m_userStart; +} + +inline bool URL::hasPassword() const +{ + return m_passwordEnd > (m_userEnd + 1); +} + inline bool URL::protocolIsInHTTPFamily() const { return m_protocolIsInHTTPFamily; diff --git a/Source/WebCore/platform/graphics/PlatformDisplay.cpp b/Source/WebCore/platform/graphics/PlatformDisplay.cpp index 19a6b99a8..bdae5f45f 100644 --- a/Source/WebCore/platform/graphics/PlatformDisplay.cpp +++ b/Source/WebCore/platform/graphics/PlatformDisplay.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "PlatformDisplay.h" +#include <cstdlib> #include <mutex> #include <wtf/NeverDestroyed.h> @@ -112,10 +113,8 @@ PlatformDisplay::PlatformDisplay() PlatformDisplay::~PlatformDisplay() { - // WinCairo crashes when terminating EGL on exit. - // https://bugs.webkit.org/show_bug.cgi?id=145832 -#if USE(EGL) && !PLATFORM(WIN) - terminateEGLDisplay(); +#if USE(EGL) + ASSERT(m_eglDisplay == EGL_NO_DISPLAY); #endif } @@ -159,10 +158,21 @@ void PlatformDisplay::initializeEGLDisplay() terminateEGLDisplay(); return; } + + // EGL registers atexit handlers to cleanup its global display list. + // Since the global PlatformDisplay instance is created before, + // when the PlatformDisplay destructor is called, EGL has already removed the + // display from the list, causing eglTerminate() to crash. So, here we register + // our own atexit handler, after EGL has been initialized and after the global + // instance has been created to ensure we call eglTerminate() before the other + // EGL atexit handlers and the PlatformDisplay destructor. + // See https://bugs.webkit.org/show_bug.cgi?id=157973. + std::atexit([] { PlatformDisplay::sharedDisplay().terminateEGLDisplay(); }); } void PlatformDisplay::terminateEGLDisplay() { + ASSERT(m_eglDisplayInitialized); if (m_eglDisplay == EGL_NO_DISPLAY) return; eglTerminate(m_eglDisplay); diff --git a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp index 051871ac6..61a58b904 100644 --- a/Source/WebCore/platform/graphics/WOFFFileFormat.cpp +++ b/Source/WebCore/platform/graphics/WOFFFileFormat.cpp @@ -30,6 +30,11 @@ #include "SharedBuffer.h" #include <wtf/ByteOrder.h> +#if USE(WOFF2) +#include "woff2_common.h" +#include "woff2_dec.h" +#endif + namespace WebCore { static bool readUInt32(SharedBuffer* buffer, size_t& offset, uint32_t& value) @@ -75,7 +80,14 @@ bool isWOFF(SharedBuffer* buffer) size_t offset = 0; uint32_t signature; - return readUInt32(buffer, offset, signature) && signature == woffSignature; + if (!readUInt32(buffer, offset, signature)) + return false; + +#if USE(WOFF2) + return signature == woffSignature || signature == woff2::kWoff2Signature; +#else + return signature == woffSignature; +#endif } bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) @@ -86,7 +98,26 @@ bool convertWOFFToSfnt(SharedBuffer* woff, Vector<char>& sfnt) // Read the WOFF header. uint32_t signature; - if (!readUInt32(woff, offset, signature) || signature != woffSignature) { + if (!readUInt32(woff, offset, signature)) { + ASSERT_NOT_REACHED(); + return false; + } + +#if USE(WOFF2) + if (signature == woff2::kWoff2Signature) { + const uint8_t* woffData = reinterpret_cast_ptr<const uint8_t*>(woff->data()); + const size_t woffSize = woff->size(); + const size_t sfntSize = woff2::ComputeWOFF2FinalSize(woffData, woffSize); + + if (!sfnt.tryReserveCapacity(sfntSize)) + return false; + sfnt.resize(sfntSize); + + return woff2::ConvertWOFF2ToTTF(reinterpret_cast<uint8_t*>(sfnt.data()), sfnt.size(), woffData, woffSize); + } +#endif + + if (signature != woffSignature) { ASSERT_NOT_REACHED(); return false; } diff --git a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp index 1904ed91a..e50ec669d 100644 --- a/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp +++ b/Source/WebCore/platform/graphics/filters/FEGaussianBlur.cpp @@ -5,7 +5,7 @@ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) 2010 Igalia, S.L. * Copyright (C) Research In Motion Limited 2010. All rights reserved. - * Copyright (C) 2015 Apple, Inc. All rights reserved. + * Copyright (C) 2015-2016 Apple, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -540,9 +540,12 @@ void FEGaussianBlur::platformApplySoftware() IntSize paintSize = absolutePaintRect().size(); paintSize.scale(filter().filterScale()); RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitialized(paintSize.width() * paintSize.height() * 4); - Uint8ClampedArray* tmpPixelArray = tmpImageData.get(); + if (!tmpImageData) { + WTFLogAlways("FEGaussianBlur::platformApplySoftware Unable to create buffer. Requested size was %d x %d\n", paintSize.width(), paintSize.height()); + return; + } - platformApply(srcPixelArray, tmpPixelArray, kernelSize.width(), kernelSize.height(), paintSize); + platformApply(srcPixelArray, tmpImageData.get(), kernelSize.width(), kernelSize.height(), paintSize); } void FEGaussianBlur::dump() diff --git a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp index 1dcb0cb06..515d1299b 100644 --- a/Source/WebCore/platform/graphics/filters/FilterEffect.cpp +++ b/Source/WebCore/platform/graphics/filters/FilterEffect.cpp @@ -3,7 +3,7 @@ * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> * Copyright (C) Research In Motion Limited 2010. All rights reserved. * Copyright (C) 2012 University of Szeged - * Copyright (C) 2015 Apple Inc. All rights reserved. + * Copyright (C) 2015-2016 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -260,6 +260,9 @@ inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8Clamped IntSize scaledPaintSize(m_absolutePaintRect.size()); scaledPaintSize.scale(m_filter.filterScale()); + if (!source || !destination) + return; + // Initialize the destination to transparent black, if not entirely covered by the source. if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height()) memset(destination->data(), 0, destination->length()); @@ -315,6 +318,10 @@ void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const I ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize)); inputSize.scale(m_filter.filterScale()); m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4); + if (!m_unmultipliedImageResult) { + WTFLogAlways("FilterEffect::copyUnmultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height()); + return; + } unsigned char* sourceComponent = m_premultipliedImageResult->data(); unsigned char* destinationComponent = m_unmultipliedImageResult->data(); unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4); @@ -351,6 +358,10 @@ void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const ASSERT(!ImageBuffer::sizeNeedsClamping(inputSize)); inputSize.scale(m_filter.filterScale()); m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4); + if (!m_premultipliedImageResult) { + WTFLogAlways("FilterEffect::copyPremultipliedImage Unable to create buffer. Requested size was %d x %d\n", inputSize.width(), inputSize.height()); + return; + } unsigned char* sourceComponent = m_unmultipliedImageResult->data(); unsigned char* destinationComponent = m_premultipliedImageResult->data(); unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4); diff --git a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp index a1a189a2b..27517db20 100644 --- a/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp +++ b/Source/WebCore/platform/graphics/freetype/FontCustomPlatformDataFreeType.cpp @@ -91,6 +91,9 @@ bool FontCustomPlatformData::supportsFormat(const String& format) { return equalLettersIgnoringASCIICase(format, "truetype") || equalLettersIgnoringASCIICase(format, "opentype") +#if USE(WOFF2) + || equalLettersIgnoringASCIICase(format, "woff2") +#endif || equalLettersIgnoringASCIICase(format, "woff"); } diff --git a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp index 428038c08..310a02247 100644 --- a/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/MediaPlayerPrivateGStreamerBase.cpp @@ -537,11 +537,18 @@ void MediaPlayerPrivateGStreamerBase::repaint() void MediaPlayerPrivateGStreamerBase::triggerRepaint(GstSample* sample) { + bool triggerResize; { WTF::GMutexLocker<GMutex> lock(m_sampleMutex); + triggerResize = !m_sample; m_sample = sample; } + if (triggerResize) { + LOG_MEDIA_MESSAGE("First sample reached the sink, triggering video dimensions update"); + m_player->sizeChanged(); + } + #if USE(COORDINATED_GRAPHICS_THREADED) #if USE(GSTREAMER_GL) pushTextureToCompositor(); diff --git a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp index d2ae28f2c..df7c46836 100644 --- a/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp +++ b/Source/WebCore/platform/graphics/gstreamer/WebKitWebSourceGStreamer.cpp @@ -39,6 +39,8 @@ #include <gst/app/gstappsrc.h> #include <gst/gst.h> #include <gst/pbutils/missing-plugins.h> +#include <wtf/Condition.h> +#include <wtf/Lock.h> #include <wtf/MainThread.h> #include <wtf/Noncopyable.h> #include <wtf/glib/GMutexLocker.h> @@ -83,7 +85,7 @@ class CachedResourceStreamingClient final : public PlatformMediaResourceClient, class ResourceHandleStreamingClient : public ResourceHandleClient, public StreamingClient { WTF_MAKE_NONCOPYABLE(ResourceHandleStreamingClient); WTF_MAKE_FAST_ALLOCATED; public: - ResourceHandleStreamingClient(WebKitWebSrc*, const ResourceRequest&); + ResourceHandleStreamingClient(WebKitWebSrc*, ResourceRequest&&); virtual ~ResourceHandleStreamingClient(); // StreamingClient virtual methods. @@ -102,6 +104,12 @@ class ResourceHandleStreamingClient : public ResourceHandleClient, public Stream virtual void wasBlocked(ResourceHandle*); virtual void cannotShowURL(ResourceHandle*); + ThreadIdentifier m_thread { 0 }; + Lock m_initializeRunLoopConditionMutex; + Condition m_initializeRunLoopCondition; + RunLoop* m_runLoop { nullptr }; + Lock m_terminateRunLoopConditionMutex; + Condition m_terminateRunLoopCondition; RefPtr<ResourceHandle> m_resource; }; @@ -127,18 +135,19 @@ struct _WebKitWebSrcPrivate { RefPtr<PlatformMediaResourceLoader> loader; RefPtr<PlatformMediaResource> resource; - ResourceHandleStreamingClient* client; + std::unique_ptr<ResourceHandleStreamingClient> client; bool didPassAccessControlCheck; guint64 offset; guint64 size; gboolean seekable; - gboolean paused; + bool paused; bool isSeeking; guint64 requestedOffset; + bool createdInMainThread; MainThreadNotifier<MainThreadSourceNotification> notifier; GRefPtr<GstBuffer> buffer; }; @@ -172,57 +181,20 @@ static gboolean webKitWebSrcQueryWithParent(GstPad*, GstObject*, GstQuery*); static void webKitWebSrcNeedData(WebKitWebSrc*); static void webKitWebSrcEnoughData(WebKitWebSrc*); -static void webKitWebSrcSeek(WebKitWebSrc*); +static gboolean webKitWebSrcSeek(WebKitWebSrc*, guint64); static GstAppSrcCallbacks appsrcCallbacks = { // need_data [](GstAppSrc*, guint, gpointer userData) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - - { - WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - if (!priv->paused) - return; - } - - GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); - priv->notifier.notify(MainThreadSourceNotification::NeedData, [protector] { webKitWebSrcNeedData(protector.get()); }); + webKitWebSrcNeedData(WEBKIT_WEB_SRC(userData)); }, // enough_data [](GstAppSrc*, gpointer userData) { - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - - { - WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - if (priv->paused) - return; - } - - GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); - priv->notifier.notify(MainThreadSourceNotification::EnoughData, [protector] { webKitWebSrcEnoughData(protector.get()); }); + webKitWebSrcEnoughData(WEBKIT_WEB_SRC(userData)); }, // seek_data [](GstAppSrc*, guint64 offset, gpointer userData) -> gboolean { - WebKitWebSrc* src = WEBKIT_WEB_SRC(userData); - WebKitWebSrcPrivate* priv = src->priv; - - { - WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - if (offset == priv->offset && priv->requestedOffset == priv->offset) - return TRUE; - - if (!priv->seekable) - return FALSE; - - priv->isSeeking = true; - priv->requestedOffset = offset; - } - - GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); - priv->notifier.notify(MainThreadSourceNotification::Seek, [protector] { webKitWebSrcSeek(protector.get()); }); - return TRUE; + return webKitWebSrcSeek(WEBKIT_WEB_SRC(userData), offset); }, { nullptr } }; @@ -287,6 +259,8 @@ static void webkit_web_src_init(WebKitWebSrc* src) src->priv = priv; new (priv) WebKitWebSrcPrivate(); + priv->createdInMainThread = isMainThread(); + priv->appsrc = GST_APP_SRC(gst_element_factory_make("appsrc", 0)); if (!priv->appsrc) { GST_ERROR_OBJECT(src, "Failed to create appsrc"); @@ -412,28 +386,37 @@ static void webKitWebSrcStop(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); + if (priv->resource || (priv->loader && !priv->keepAlive)) { + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek); + bool keepAlive = priv->keepAlive; + priv->notifier.notify(MainThreadSourceNotification::Stop, [protector, keepAlive] { + WebKitWebSrcPrivate* priv = protector->priv; + + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get())); + if (priv->resource) { + priv->resource->stop(); + priv->resource->setClient(nullptr); + priv->resource = nullptr; + } + + if (!keepAlive) + priv->loader = nullptr; + }); + } WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); bool wasSeeking = std::exchange(priv->isSeeking, false); - priv->notifier.cancelPendingNotifications(MainThreadSourceNotification::NeedData | MainThreadSourceNotification::EnoughData | MainThreadSourceNotification::Seek); - - if (priv->client) { - delete priv->client; - priv->client = 0; - } - - if (!priv->keepAlive) - priv->loader = nullptr; + priv->client = nullptr; if (priv->buffer) { unmapGstBuffer(priv->buffer.get()); priv->buffer.clear(); } - priv->paused = FALSE; + priv->paused = false; priv->offset = 0; priv->seekable = FALSE; @@ -510,8 +493,6 @@ static void webKitWebSrcStart(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); - WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); priv->didPassAccessControlCheck = false; @@ -576,37 +557,41 @@ static void webKitWebSrcStart(WebKitWebSrc* src) // We always request Icecast/Shoutcast metadata, just in case ... request.setHTTPHeaderField(HTTPHeaderName::IcyMetadata, "1"); - bool loadFailed = true; - if (priv->player && !priv->loader) - priv->loader = priv->player->createResourceLoader(); + if (!priv->player || !priv->createdInMainThread) { + priv->client = std::make_unique<ResourceHandleStreamingClient>(src, WTFMove(request)); + if (priv->client->loadFailed()) { + GST_ERROR_OBJECT(src, "Failed to setup streaming client"); + priv->client = nullptr; + locker.unlock(); + webKitWebSrcStop(src); + } else + GST_DEBUG_OBJECT(src, "Started request"); + return; + } + + locker.unlock(); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::Start, [protector, request] { + WebKitWebSrcPrivate* priv = protector->priv; + + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(protector.get())); + if (!priv->loader) + priv->loader = priv->player->createResourceLoader(); - if (priv->loader) { PlatformMediaResourceLoader::LoadOptions loadOptions = 0; if (request.url().protocolIsBlob()) loadOptions |= PlatformMediaResourceLoader::LoadOption::BufferData; priv->resource = priv->loader->requestResource(request, loadOptions); - loadFailed = !priv->resource; - - if (priv->resource) - priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(src)); - } else { - priv->client = new ResourceHandleStreamingClient(src, request); - loadFailed = priv->client->loadFailed(); - } - - if (loadFailed) { - GST_ERROR_OBJECT(src, "Failed to setup streaming client"); - if (priv->client) { - delete priv->client; - priv->client = nullptr; + if (priv->resource) { + priv->resource->setClient(std::make_unique<CachedResourceStreamingClient>(protector.get())); + GST_DEBUG_OBJECT(protector.get(), "Started request"); + } else { + GST_ERROR_OBJECT(protector.get(), "Failed to setup streaming client"); + priv->loader = nullptr; + locker.unlock(); + webKitWebSrcStop(protector.get()); } - priv->loader = nullptr; - priv->resource = nullptr; - locker.unlock(); - webKitWebSrcStop(src); - return; - } - GST_DEBUG_OBJECT(src, "Started request"); + }); } static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStateChange transition) @@ -638,16 +623,13 @@ static GstStateChangeReturn webKitWebSrcChangeState(GstElement* element, GstStat case GST_STATE_CHANGE_READY_TO_PAUSED: { GST_DEBUG_OBJECT(src, "READY->PAUSED"); - GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); - priv->notifier.notify(MainThreadSourceNotification::Start, [protector] { webKitWebSrcStart(protector.get()); }); + webKitWebSrcStart(src); break; } case GST_STATE_CHANGE_PAUSED_TO_READY: { GST_DEBUG_OBJECT(src, "PAUSED->READY"); - priv->notifier.cancelPendingNotifications(); - GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); - priv->notifier.notify(MainThreadSourceNotification::Stop, [protector] { webKitWebSrcStop(protector.get()); }); + webKitWebSrcStop(src); break; } default: @@ -772,59 +754,91 @@ static void webKitWebSrcUriHandlerInit(gpointer gIface, gpointer) iface->set_uri = webKitWebSrcSetUri; } -// appsrc callbacks - static void webKitWebSrcNeedData(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); - GST_DEBUG_OBJECT(src, "Need more data"); { WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - priv->paused = FALSE; + if (!priv->paused) + return; + priv->paused = false; + if (priv->client) { + priv->client->setDefersLoading(false); + return; + } } - if (priv->client) - priv->client->setDefersLoading(false); - if (priv->resource) - priv->resource->setDefersLoading(false); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::NeedData, [protector] { + WebKitWebSrcPrivate* priv = protector->priv; + if (priv->resource) + priv->resource->setDefersLoading(false); + }); } static void webKitWebSrcEnoughData(WebKitWebSrc* src) { WebKitWebSrcPrivate* priv = src->priv; - ASSERT(isMainThread()); - GST_DEBUG_OBJECT(src, "Have enough data"); { WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); - priv->paused = TRUE; + if (priv->paused) + return; + priv->paused = true; + if (priv->client) { + priv->client->setDefersLoading(true); + return; + } } - if (priv->client) - priv->client->setDefersLoading(true); - if (priv->resource) - priv->resource->setDefersLoading(true); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::EnoughData, [protector] { + WebKitWebSrcPrivate* priv = protector->priv; + if (priv->resource) + priv->resource->setDefersLoading(true); + }); } -static void webKitWebSrcSeek(WebKitWebSrc* src) +static gboolean webKitWebSrcSeek(WebKitWebSrc* src, guint64 offset) { - ASSERT(isMainThread()); + WebKitWebSrcPrivate* priv = src->priv; + + { + WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); + if (offset == priv->offset && priv->requestedOffset == priv->offset) + return TRUE; + + if (!priv->seekable) + return FALSE; + + priv->isSeeking = true; + priv->requestedOffset = offset; + } GST_DEBUG_OBJECT(src, "Seeking to offset: %" G_GUINT64_FORMAT, src->priv->requestedOffset); + if (priv->client) { + webKitWebSrcStop(src); + webKitWebSrcStart(src); + return TRUE; + } - webKitWebSrcStop(src); - webKitWebSrcStart(src); + GRefPtr<WebKitWebSrc> protector = WTF::ensureGRef(src); + priv->notifier.notify(MainThreadSourceNotification::Seek, [protector] { + webKitWebSrcStop(protector.get()); + webKitWebSrcStart(protector.get()); + }); + return TRUE; } void webKitWebSrcSetMediaPlayer(WebKitWebSrc* src, WebCore::MediaPlayer* player) { ASSERT(player); + ASSERT(src->priv->createdInMainThread); WTF::GMutexLocker<GMutex> locker(*GST_OBJECT_GET_LOCK(src)); src->priv->player = player; } @@ -1051,17 +1065,47 @@ void CachedResourceStreamingClient::loadFinished(PlatformMediaResource&) handleNotifyFinished(); } -ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, const ResourceRequest& request) +ResourceHandleStreamingClient::ResourceHandleStreamingClient(WebKitWebSrc* src, ResourceRequest&& request) : StreamingClient(src) { - m_resource = ResourceHandle::create(0 /*context*/, request, this, false, false); + LockHolder locker(m_initializeRunLoopConditionMutex); + m_thread = createThread("ResourceHandleStreamingClient", [this, request] { + { + LockHolder locker(m_initializeRunLoopConditionMutex); + m_runLoop = &RunLoop::current(); + m_resource = ResourceHandle::create(nullptr /*context*/, request, this, true, false); + m_initializeRunLoopCondition.notifyOne(); + } + if (!m_resource) + return; + + m_runLoop->dispatch([this] { m_resource->setDefersLoading(false); }); + m_runLoop->run(); + { + LockHolder locker(m_terminateRunLoopConditionMutex); + m_runLoop = nullptr; + m_resource->clearClient(); + m_resource->cancel(); + m_resource = nullptr; + m_terminateRunLoopCondition.notifyOne(); + } + }); + m_initializeRunLoopCondition.wait(m_initializeRunLoopConditionMutex); } ResourceHandleStreamingClient::~ResourceHandleStreamingClient() { - if (m_resource) { - m_resource->cancel(); - m_resource = nullptr; + if (m_thread) { + detachThread(m_thread); + m_thread = 0; + } + + if (m_runLoop == &RunLoop::current()) + m_runLoop->stop(); + else { + LockHolder locker(m_terminateRunLoopConditionMutex); + m_runLoop->stop(); + m_terminateRunLoopCondition.wait(m_terminateRunLoopConditionMutex); } } @@ -1072,8 +1116,10 @@ bool ResourceHandleStreamingClient::loadFailed() const void ResourceHandleStreamingClient::setDefersLoading(bool defers) { - if (m_resource) - m_resource->setDefersLoading(defers); + m_runLoop->dispatch([this, defers] { + if (m_resource) + m_resource->setDefersLoading(defers); + }); } char* ResourceHandleStreamingClient::getOrCreateReadBuffer(size_t requestedSize, size_t& actualSize) diff --git a/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp b/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp index 39cd47ce2..78e159aa0 100644 --- a/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp +++ b/Source/WebCore/platform/graphics/qt/FontCustomPlatformDataQt.cpp @@ -24,9 +24,7 @@ #include "FontPlatformData.h" #include "SharedBuffer.h" -#if USE(ZLIB) #include "WOFFFileFormat.h" -#endif #include <QStringList> namespace WebCore { @@ -41,7 +39,6 @@ FontPlatformData FontCustomPlatformData::fontPlatformData(const FontDescription& std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffer& buffer) { -#if USE(ZLIB) SharedBuffer* fontBuffer = &buffer; RefPtr<SharedBuffer> sfntBuffer; if (isWOFF(&buffer)) { @@ -52,15 +49,9 @@ std::unique_ptr<FontCustomPlatformData> createFontCustomPlatformData(SharedBuffe sfntBuffer = SharedBuffer::adoptVector(sfnt); fontBuffer = sfntBuffer.get(); } -#endif // USE(ZLIB) const QByteArray fontData(fontBuffer->data(), fontBuffer->size()); -#if !USE(ZLIB) - if (fontData.startsWith("wOFF")) { - qWarning("WOFF support requires QtWebKit to be built with zlib support."); - return 0; - } -#endif // !USE(ZLIB) + // Pixel size doesn't matter at this point, it is set in FontCustomPlatformData::fontPlatformData. QRawFont rawFont(fontData, /*pixelSize = */0, QFont::PreferDefaultHinting); if (!rawFont.isValid()) @@ -75,8 +66,9 @@ bool FontCustomPlatformData::supportsFormat(const String& format) { return equalLettersIgnoringASCIICase(format, "truetype") || equalLettersIgnoringASCIICase(format, "opentype") -#if USE(ZLIB) || equalLettersIgnoringASCIICase(format, "woff") +#if USE(WOFF2) + || equalLettersIgnoringASCIICase(format, "woff2") #endif ; } diff --git a/Source/WebCore/platform/graphics/qt/FontPlatformData.h b/Source/WebCore/platform/graphics/qt/FontPlatformData.h index 14e36f018..c8375ebcd 100644 --- a/Source/WebCore/platform/graphics/qt/FontPlatformData.h +++ b/Source/WebCore/platform/graphics/qt/FontPlatformData.h @@ -34,6 +34,8 @@ namespace WebCore { +class SharedBuffer; + class FontPlatformDataPrivate : public RefCounted<FontPlatformDataPrivate> { WTF_MAKE_NONCOPYABLE(FontPlatformDataPrivate); WTF_MAKE_FAST_ALLOCATED; public: @@ -117,6 +119,7 @@ public: FontOrientation orientation() const { return Horizontal; } // FIXME: Implement. void setOrientation(FontOrientation) { } // FIXME: Implement. + PassRefPtr<SharedBuffer> openTypeTable(uint32_t table) const; unsigned hash() const; diff --git a/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp b/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp index a239d643d..2ed4a98c3 100644 --- a/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp +++ b/Source/WebCore/platform/graphics/qt/FontPlatformDataQt.cpp @@ -25,6 +25,7 @@ #include "FontPlatformData.h" #include "FontCascade.h" +#include "SharedBuffer.h" #include <wtf/text/WTFString.h> namespace WebCore { @@ -139,6 +140,20 @@ bool FontPlatformData::operator==(const FontPlatformData& other) const return equals; } +PassRefPtr<SharedBuffer> FontPlatformData::openTypeTable(uint32_t table) const +{ + const char tag[4] = { + char(table & 0xff), + char((table & 0xff00) >> 8), + char((table & 0xff0000) >> 16), + char(table >> 24) + }; + QByteArray tableData = m_data->rawFont.fontTable(tag); + + // TODO: Wrap SharedBuffer around QByteArray when it's possible + return SharedBuffer::create(tableData.data(), tableData.size()); +} + unsigned FontPlatformData::hash() const { if (!m_data) diff --git a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp index 618bf63ae..9e992381c 100644 --- a/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp +++ b/Source/WebCore/platform/graphics/qt/GraphicsContext3DQt.cpp @@ -362,7 +362,7 @@ void GraphicsContext3DPrivate::createGraphicsSurfaces(const IntSize& size) { #if USE(GRAPHICS_SURFACE) if (size.isEmpty()) - m_graphicsSurface.clear(); + m_graphicsSurface = nullptr; else m_graphicsSurface = GraphicsSurface::create(size, m_surfaceFlags, m_platformContext); #endif diff --git a/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp b/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp index 2ff7df102..306f99e0e 100644 --- a/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp +++ b/Source/WebCore/platform/network/qt/QNetworkReplyHandler.cpp @@ -606,7 +606,7 @@ void QNetworkReplyHandler::sendResponseIfNeeded() // Add remaining headers. foreach (const QNetworkReply::RawHeaderPair& pair, m_replyWrapper->reply()->rawHeaderPairs()) - response.setHTTPHeaderField(QString::fromLatin1(pair.first), QString::fromLatin1(pair.second)); + response.setHTTPHeaderField(String(pair.first.constData(), pair.first.size()), String(pair.second.constData(), pair.second.size())); } QUrl redirection = m_replyWrapper->reply()->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); diff --git a/Source/WebCore/platform/network/qt/ResourceRequest.h b/Source/WebCore/platform/network/qt/ResourceRequest.h index 1e4911f4b..e74d9024c 100644 --- a/Source/WebCore/platform/network/qt/ResourceRequest.h +++ b/Source/WebCore/platform/network/qt/ResourceRequest.h @@ -59,7 +59,7 @@ class NetworkingContext; { } - void updateFromDelegatePreservingOldHTTPBody(const ResourceRequest& delegateProvidedRequest) { *this = delegateProvidedRequest; } + void updateFromDelegatePreservingOldProperties(const ResourceRequest& delegateProvidedRequest) { *this = delegateProvidedRequest; } QNetworkRequest toNetworkRequest(NetworkingContext* = 0) const; diff --git a/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp b/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp index 46229027e..83c50062d 100644 --- a/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp +++ b/Source/WebCore/platform/network/qt/ResourceRequestQt.cpp @@ -89,6 +89,13 @@ static void resolveBlobUrl(const QUrl& url, QUrl& resolvedUrl) } #endif +static inline QByteArray stringToByteArray(const String& string) +{ + if (string.is8Bit()) + return QByteArray(reinterpret_cast<const char*>(string.characters8()), string.length()); + return QString(string).toLatin1(); +} + QNetworkRequest ResourceRequest::toNetworkRequest(NetworkingContext *context) const { QNetworkRequest request; @@ -105,14 +112,13 @@ QNetworkRequest ResourceRequest::toNetworkRequest(NetworkingContext *context) co const HTTPHeaderMap &headers = httpHeaderFields(); for (HTTPHeaderMap::const_iterator it = headers.begin(), end = headers.end(); it != end; ++it) { - QByteArray name = QString(it->key).toLatin1(); - QByteArray value = QString(it->value).toLatin1(); + QByteArray name = stringToByteArray(it->key); // QNetworkRequest::setRawHeader() would remove the header if the value is null // Make sure to set an empty header instead of null header. - if (!value.isNull()) - request.setRawHeader(name, value); + if (!it->value.isNull()) + request.setRawHeader(name, stringToByteArray(it->value)); else - request.setRawHeader(name, ""); + request.setRawHeader(name, QByteArrayLiteral("")); } // Make sure we always have an Accept header; some sites require this to diff --git a/Source/WebCore/platform/qt/RenderThemeQtMobile.cpp b/Source/WebCore/platform/qt/RenderThemeQtMobile.cpp index 977e42c9a..6ac59a5b8 100644 --- a/Source/WebCore/platform/qt/RenderThemeQtMobile.cpp +++ b/Source/WebCore/platform/qt/RenderThemeQtMobile.cpp @@ -275,9 +275,13 @@ QSizeF StylePainterMobile::sizeForPainterScale(const QRectF& rect) const void StylePainterMobile::drawChecker(QPainter* painter, const QRect& rect, const QColor& color) const { painter->setRenderHint(QPainter::Antialiasing, true); - QPen pen(Qt::darkGray); + int minSideSize = qMin(rect.width(), rect.height()); + QPen pen(minSideSize < 12 ? color : Qt::darkGray); pen.setCosmetic(true); - painter->setPen(pen); + if (minSideSize < 12 || minSideSize >= 16) + painter->setPen(pen); + else + painter->setPen(Qt::NoPen); painter->scale(rect.width(), rect.height()); QPainterPath path; path.moveTo(0.18, 0.47); |