diff options
Diffstat (limited to 'src')
81 files changed, 2745 insertions, 1244 deletions
diff --git a/src/core/accessibility_activation_observer.cpp b/src/core/accessibility_activation_observer.cpp new file mode 100644 index 000000000..75ad90c54 --- /dev/null +++ b/src/core/accessibility_activation_observer.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "accessibility_activation_observer.h" + +#ifndef QT_NO_ACCESSIBILITY + +#include "content/browser/accessibility/browser_accessibility_state_impl.h" + +namespace QtWebEngineCore { + +namespace { + +bool isAccessibilityEnabled() { + // On Linux accessibility is disabled by default due to performance issues, + // and can be re-enabled by setting the QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment + // variable. For details, see QTBUG-59922. +#ifdef Q_OS_LINUX + static bool accessibility_enabled + = qEnvironmentVariableIsSet("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY"); +#else + const bool accessibility_enabled = true; +#endif + return accessibility_enabled; +} + +} // namespace + +AccessibilityActivationObserver::AccessibilityActivationObserver() +{ + if (isAccessibilityEnabled()) { + QAccessible::installActivationObserver(this); + if (QAccessible::isActive()) + content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + } +} + +AccessibilityActivationObserver::~AccessibilityActivationObserver() +{ + QAccessible::removeActivationObserver(this); +} + +void AccessibilityActivationObserver::accessibilityActiveChanged(bool active) +{ + if (active) + content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); + else + content::BrowserAccessibilityStateImpl::GetInstance()->DisableAccessibility(); +} + +} // namespace QtWebEngineCore + +#endif // QT_NO_ACCESSIBILITY diff --git a/src/core/net/qrc_protocol_handler_qt.cpp b/src/core/accessibility_activation_observer.h index eb716f182..e42c83eb5 100644 --- a/src/core/net/qrc_protocol_handler_qt.cpp +++ b/src/core/accessibility_activation_observer.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,27 +37,27 @@ ** ****************************************************************************/ -#include "qrc_protocol_handler_qt.h" -#include "url_request_qrc_job_qt.h" +#ifndef ACCESSIBILITY_ACTIVATION_OBSERVER_H +#define ACCESSIBILITY_ACTIVATION_OBSERVER_H -#include "net/base/net_errors.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_error_job.h" +#ifndef QT_NO_ACCESSIBILITY +#include <QtGui/qaccessible.h> namespace QtWebEngineCore { -const char kQrcSchemeQt[] = "qrc"; +class RenderWidgetHostViewQt; -QrcProtocolHandlerQt::QrcProtocolHandlerQt() +class AccessibilityActivationObserver : public QAccessible::ActivationObserver { -} +public: + AccessibilityActivationObserver(); + ~AccessibilityActivationObserver(); -net::URLRequestJob *QrcProtocolHandlerQt::MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const -{ - if (!networkDelegate) - return new net::URLRequestErrorJob(request, Q_NULLPTR, net::ERR_ACCESS_DENIED); - - return new URLRequestQrcJobQt(request, networkDelegate); -} + void accessibilityActiveChanged(bool active) override; +}; } // namespace QtWebEngineCore + +#endif // QT_NO_ACCESSIBILITY + +#endif // ACCESSIBILITY_ACTIVATION_OBSERVER_H diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index ea9081fc1..c3e5b5445 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -115,8 +115,11 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q \fn void QWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) Reimplementing this virtual function makes it possible to intercept URL - requests. This function is executed on the IO thread, and therefore running - long tasks here will block networking. + requests. For interceptors installed on a QWebEngineProfile, the function is executed + on the I/O thread, and thus it may not be thread-safe to interact with pages. If the + interceptor was installed on a QWebEnginePage, the function is executed on the main + application thread, and can safely interact with other user classes. Both versions will + be stalling the URL request until handled. \a info contains the information about the URL request and will track internally whether its members have been altered. @@ -140,10 +143,17 @@ QWebEngineUrlRequestInfoPrivate::QWebEngineUrlRequestInfoPrivate(QWebEngineUrlRe /*! \internal */ +QWebEngineUrlRequestInfo::QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p) + : d_ptr(p.d_ptr.take()) +{ +} + +/*! + \internal +*/ QWebEngineUrlRequestInfo::~QWebEngineUrlRequestInfo() { - } /*! @@ -260,6 +270,14 @@ bool QWebEngineUrlRequestInfo::changed() const } /*! + \internal +*/ +void QWebEngineUrlRequestInfo::resetChanged() +{ + d_ptr->changed = false; +} + +/*! Redirects this request to \a url. It is only possible to redirect requests that do not have payload data, such as GET requests. */ diff --git a/src/core/api/qwebengineurlrequestinfo.h b/src/core/api/qwebengineurlrequestinfo.h index 68c46dcf4..e1f9ca6ef 100644 --- a/src/core/api/qwebengineurlrequestinfo.h +++ b/src/core/api/qwebengineurlrequestinfo.h @@ -47,6 +47,7 @@ namespace QtWebEngineCore { class NetworkDelegateQt; +class URLRequestNotification; } QT_BEGIN_NAMESPACE @@ -104,10 +105,14 @@ public: private: friend class QtWebEngineCore::NetworkDelegateQt; + friend class QtWebEngineCore::URLRequestNotification; Q_DISABLE_COPY(QWebEngineUrlRequestInfo) Q_DECLARE_PRIVATE(QWebEngineUrlRequestInfo) + void resetChanged(); + QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfoPrivate *p); + QWebEngineUrlRequestInfo(QWebEngineUrlRequestInfo &&p); ~QWebEngineUrlRequestInfo(); QScopedPointer<QWebEngineUrlRequestInfoPrivate> d_ptr; }; diff --git a/src/core/api/qwebengineurlschemehandler.cpp b/src/core/api/qwebengineurlschemehandler.cpp index 6f06b2c6e..2e93f4b73 100644 --- a/src/core/api/qwebengineurlschemehandler.cpp +++ b/src/core/api/qwebengineurlschemehandler.cpp @@ -111,7 +111,6 @@ QWebEngineUrlSchemeHandler::QWebEngineUrlSchemeHandler(QObject *parent) */ QWebEngineUrlSchemeHandler::~QWebEngineUrlSchemeHandler() { - Q_EMIT _q_destroyedUrlSchemeHandler(this); } /*! diff --git a/src/core/api/qwebengineurlschemehandler.h b/src/core/api/qwebengineurlschemehandler.h index 23fee4b95..549778561 100644 --- a/src/core/api/qwebengineurlschemehandler.h +++ b/src/core/api/qwebengineurlschemehandler.h @@ -60,11 +60,6 @@ public: virtual void requestStarted(QWebEngineUrlRequestJob*) = 0; -#ifndef Q_QDOC -Q_SIGNALS: - void _q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*); -#endif - private: Q_DISABLE_COPY(QWebEngineUrlSchemeHandler) }; diff --git a/src/core/browser_main_parts_qt.cpp b/src/core/browser_main_parts_qt.cpp index 8f39386d4..056d6fda8 100644 --- a/src/core/browser_main_parts_qt.cpp +++ b/src/core/browser_main_parts_qt.cpp @@ -59,6 +59,12 @@ #include <QEventLoop> #include <QObject> #include <QTimerEvent> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +#include "ui/gl/gl_context.h" +#include <QOpenGLContext> +#endif #if defined(OS_MACOSX) #include "ui/base/idle/idle.h" @@ -156,8 +162,55 @@ protected: } private: + // Both Qt and Chromium keep track of the current GL context by using + // thread-local variables, and naturally they are completely unaware of each + // other. As a result, when a QOpenGLContext is made current, the previous + // gl::GLContext is not released, and vice-versa. This is fine as long as + // each thread uses exclusively either Qt or Chromium GL bindings, which is + // usually the case. + // + // The only exception is when the GL driver is considered thread-unsafe + // (QOpenGLContext::supportsThreadedOpenGL() is false), in which case we + // have to run all GL operations, including Chromium's GPU service, on the + // UI thread. Now the bindings are being mixed and both Qt and Chromium get + // quite confused regarding the current state of the surface. + // + // To get this to work we have to release the current QOpenGLContext before + // executing any tasks from Chromium's GPU service and the gl::GLContext + // afterwards. Since GPU service just posts tasks to the UI thread task + // runner, we'll have to instrument the entire UI thread message pump. + class ScopedGLContextChecker + { +#if QT_CONFIG(opengl) + public: + ScopedGLContextChecker() + { + if (!m_enabled) + return; + + if (QOpenGLContext *context = QOpenGLContext::currentContext()) + context->doneCurrent(); + } + + ~ScopedGLContextChecker() + { + if (!m_enabled) + return; + + if (gl::GLContext *context = gl::GLContext::GetCurrent()) + context->ReleaseCurrent(nullptr); + } + + private: + bool m_enabled = !QOpenGLContext::supportsThreadedOpenGL(); +#endif // QT_CONFIG(opengl) + }; + + void handleScheduledWork() { + ScopedGLContextChecker glContextChecker; + bool more_work_is_plausible = m_delegate->DoWork(); base::TimeTicks delayed_work_time; diff --git a/src/core/chromium_gpu_helper.cpp b/src/core/compositor/chromium_gpu_helper.cpp index 92a8b13ed..2164f434f 100644 --- a/src/core/chromium_gpu_helper.cpp +++ b/src/core/compositor/chromium_gpu_helper.cpp @@ -46,7 +46,6 @@ // Including gpu/command_buffer headers before content/gpu headers makes sure that // guards are defined to prevent duplicate definition errors with forward declared // GL typedefs cascading through content header includes. -#include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/command_buffer/service/mailbox_manager.h" #include "gpu/command_buffer/service/texture_base.h" @@ -62,12 +61,6 @@ scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner() return content::GpuChildThread::instance()->main_thread_runner(); } -gpu::SyncPointManager *sync_point_manager() -{ - gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); - return gpuChannelManager->sync_point_manager(); -} - gpu::MailboxManager *mailbox_manager() { gpu::GpuChannelManager *gpuChannelManager = content::GpuChildThread::instance()->gpu_channel_manager(); diff --git a/src/core/chromium_gpu_helper.h b/src/core/compositor/chromium_gpu_helper.h index 21b764997..c2799204b 100644 --- a/src/core/chromium_gpu_helper.h +++ b/src/core/compositor/chromium_gpu_helper.h @@ -50,7 +50,6 @@ class SingleThreadTaskRunner; namespace gpu { struct Mailbox; -class SyncPointManager; class MailboxManager; class TextureBase; } @@ -61,7 +60,6 @@ class TextureBase; // functions should only be forward-declared and considered as opaque types. scoped_refptr<base::SingleThreadTaskRunner> gpu_task_runner(); -gpu::SyncPointManager *sync_point_manager(); gpu::MailboxManager *mailbox_manager(); gpu::TextureBase* ConsumeTexture(gpu::MailboxManager *mailboxManager, unsigned target, const gpu::Mailbox& mailbox); diff --git a/src/core/compositor.cpp b/src/core/compositor/compositor.cpp index f7a5e651c..77a973748 100644 --- a/src/core/compositor.cpp +++ b/src/core/compositor/compositor.cpp @@ -39,6 +39,7 @@ #include "compositor.h" +#include "compositor_resource_tracker.h" #include "delegated_frame_node.h" #include "render_widget_host_view_qt.h" @@ -49,7 +50,7 @@ namespace QtWebEngineCore { Compositor::Compositor(RenderWidgetHostViewQt *hostView) - : m_chromiumCompositorData(new ChromiumCompositorData) + : m_resourceTracker(new CompositorResourceTracker) , m_view(hostView) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -67,13 +68,6 @@ Compositor::~Compositor() DCHECK_CURRENTLY_ON(content::BrowserThread::UI); } -void Compositor::setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - m_viewDelegate = viewDelegate; -} - void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -85,7 +79,7 @@ void Compositor::setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frame // should not be returned. // // TODO(juvaldma): Can there be a pending frame from the old client? - m_resourcesToRelease.clear(); + m_resourceTracker->returnResources(); m_frameSinkClient = frameSinkClient; } @@ -104,21 +98,19 @@ void Compositor::setNeedsBeginFrames(bool needsBeginFrames) m_needsBeginFrames = needsBeginFrames; } -void Compositor::submitFrame(viz::CompositorFrame frame) +void Compositor::submitFrame(viz::CompositorFrame frame, base::OnceClosure callback) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - DCHECK(!m_havePendingFrame); + DCHECK(!m_submitCallback); - m_chromiumCompositorData->frameDevicePixelRatio = frame.metadata.device_scale_factor; - m_chromiumCompositorData->previousFrameData = std::move(m_chromiumCompositorData->frameData); - m_chromiumCompositorData->frameData = std::move(frame); - m_havePendingFrame = true; - - // Tell viewDelegate to call updatePaintNode() soon. - m_viewDelegate->update(); + m_pendingFrame = std::move(frame); + m_submitCallback = std::move(callback); + m_resourceTracker->submitResources( + m_pendingFrame, + base::BindOnce(&Compositor::runSubmitCallback, base::Unretained(this))); } -QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) +QSGNode *Compositor::updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate) { // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // @@ -129,30 +121,44 @@ QSGNode *Compositor::updatePaintNode(QSGNode *oldNode) if (!frameNode) frameNode = new DelegatedFrameNode; - frameNode->commit(m_chromiumCompositorData.data(), &m_resourcesToRelease, m_viewDelegate); - - if (m_havePendingFrame) { - m_havePendingFrame = false; - content::BrowserThread::PostTask( - content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); + if (!m_updatePaintNodeShouldCommit) { + frameNode->commit(m_committedFrame, viz::CompositorFrame(), m_resourceTracker.get(), viewDelegate); + return frameNode; } - if (m_chromiumCompositorData->frameData.metadata.request_presentation_feedback) + m_updatePaintNodeShouldCommit = false; + + if (m_committedFrame.metadata.request_presentation_feedback) content::BrowserThread::PostTask( content::BrowserThread::UI, FROM_HERE, - base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), m_chromiumCompositorData->frameData.metadata.frame_token)); + base::BindOnce(&Compositor::sendPresentationFeedback, m_weakPtrFactory.GetWeakPtr(), m_committedFrame.metadata.frame_token)); + + m_resourceTracker->commitResources(); + frameNode->commit(m_pendingFrame, m_committedFrame, m_resourceTracker.get(), viewDelegate); + m_committedFrame = std::move(m_pendingFrame); + m_pendingFrame = viz::CompositorFrame(); + + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&Compositor::notifyFrameCommitted, m_weakPtrFactory.GetWeakPtr())); return frameNode; } +void Compositor::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + m_updatePaintNodeShouldCommit = true; + std::move(m_submitCallback).Run(); +} + void Compositor::notifyFrameCommitted() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); m_beginFrameSource->DidFinishFrame(this); if (m_frameSinkClient) - m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourcesToRelease); - m_resourcesToRelease.clear(); + m_frameSinkClient->DidReceiveCompositorFrameAck(m_resourceTracker->returnResources()); } void Compositor::sendPresentationFeedback(uint frame_token) diff --git a/src/core/compositor.h b/src/core/compositor/compositor.h index 7d7db5d04..b025f901d 100644 --- a/src/core/compositor.h +++ b/src/core/compositor/compositor.h @@ -42,6 +42,10 @@ #include <base/memory/weak_ptr.h> #include <components/viz/common/frame_sinks/begin_frame_source.h> +#include <components/viz/common/quads/compositor_frame.h> + +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> #include <QtCore/qglobal.h> #include <QtCore/qshareddata.h> @@ -51,7 +55,6 @@ class QSGNode; QT_END_NAMESPACE namespace viz { -class CompositorFrame; struct ReturnedResource; namespace mojom { class CompositorFrameSinkClient; @@ -60,9 +63,9 @@ class CompositorFrameSinkClient; namespace QtWebEngineCore { +class CompositorResourceTracker; class RenderWidgetHostViewQt; class RenderWidgetHostViewQtDelegate; -class ChromiumCompositorData; // Receives viz::CompositorFrames from child compositors and provides QSGNodes // to the Qt Quick renderer. @@ -72,10 +75,10 @@ class ChromiumCompositorData; // Step 1. A new CompositorFrame is received from child compositors and handed // off to submitFrame(). The new frame will start off in a pending state. // -// Step 2. Once the new frame is ready to be rendered, Compositor will call -// update() on the delegate. +// Step 2. Once the new frame is ready to be rendered, Compositor will notify +// the client by running the callback given to submitFrame(). // -// Step 3. Once the delegate is ready to render, updatePaintNode() should be +// Step 3. Once the client is ready to render, updatePaintNode() should be // called to receive the scene graph for the new frame. This call will commit // the pending frame. Until the next frame is ready, all subsequent calls to // updatePaintNode() will keep using this same committed frame. @@ -88,15 +91,14 @@ public: explicit Compositor(RenderWidgetHostViewQt *hostView); ~Compositor() override; - void setViewDelegate(RenderWidgetHostViewQtDelegate *viewDelegate); void setFrameSinkClient(viz::mojom::CompositorFrameSinkClient *frameSinkClient); void setNeedsBeginFrames(bool needsBeginFrames); - void submitFrame(viz::CompositorFrame frame); - - QSGNode *updatePaintNode(QSGNode *oldNode); + void submitFrame(viz::CompositorFrame frame, base::OnceClosure callback); + QSGNode *updatePaintNode(QSGNode *oldNode, RenderWidgetHostViewQtDelegate *viewDelegate); private: + void runSubmitCallback(); void notifyFrameCommitted(); void sendPresentationFeedback(uint frame_token); @@ -104,13 +106,14 @@ private: bool OnBeginFrameDerivedImpl(const viz::BeginFrameArgs &args) override; void OnBeginFrameSourcePausedChanged(bool paused) override; - std::vector<viz::ReturnedResource> m_resourcesToRelease; - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; + viz::CompositorFrame m_committedFrame; + viz::CompositorFrame m_pendingFrame; + base::OnceClosure m_submitCallback; + std::unique_ptr<CompositorResourceTracker> m_resourceTracker; RenderWidgetHostViewQt *m_view; - RenderWidgetHostViewQtDelegate *m_viewDelegate = nullptr; std::unique_ptr<viz::SyntheticBeginFrameSource> m_beginFrameSource; viz::mojom::CompositorFrameSinkClient *m_frameSinkClient = nullptr; - bool m_havePendingFrame = false; + bool m_updatePaintNodeShouldCommit = false; bool m_needsBeginFrames = false; base::WeakPtrFactory<Compositor> m_weakPtrFactory{this}; diff --git a/src/core/compositor/compositor_resource.h b/src/core/compositor/compositor_resource.h new file mode 100644 index 000000000..c40ce3d5e --- /dev/null +++ b/src/core/compositor/compositor_resource.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_H +#define COMPOSITOR_RESOURCE_H + +#include <base/memory/ref_counted.h> +#include <components/viz/common/resources/transferable_resource.h> + +#include <QtCore/qglobal.h> +#include <QtGui/qtgui-config.h> + +#if QT_CONFIG(opengl) +# include "compositor_resource_fence.h" +#endif + +namespace viz { +class SharedBitmap; +} // namespace viz + +namespace QtWebEngineCore { + +using CompositorResourceId = quint32; + +// A resource (OpenGL texture or software shared bitmap). +// +// - Created by the CompositorResourceTracker from a newly submitted +// CompositorFrame's resource_list. +// +// - Until the frame is committed, its resources are in a 'pending' state and +// are inaccessible from outside the CompositorResourceTracker. +// +// - Once the frame is committed, its resources can be found via +// CompositorResourceTracker::findResource. +// +// - A committed resource's fields may not be updated and are safe to use from +// other threads without synchronization (unless noted otherwise). +struct CompositorResource : viz::TransferableResource +{ + CompositorResource(const viz::TransferableResource &tr) : viz::TransferableResource(tr) {} + + // Counts the number of times this resource has been encountered in + // CompositorFrames' resource lists. + // + // Corresponds to viz::ReturnedResource::count. + // + // Updated by CompositorResourceTracker on UI thread. + int import_count = 1; + + // Identifies the last frame that needed this resource. Used by + // CompositorResourceTracker to return unused resources back to child + // compositors. + // + // Updated by CompositorResourceTracker on UI thread. + quint32 last_used_for_frame = 0; + + // Bitmap (if is_software). + std::unique_ptr<viz::SharedBitmap> bitmap; + +#if QT_CONFIG(opengl) + // OpenGL texture id (if !is_software). + quint32 texture_id = 0; + + // Should be waited on before using the texture (non-null if !is_software). + scoped_refptr<CompositorResourceFence> texture_fence; +#endif // QT_CONFIG(opengl) +}; + +inline bool operator<(const CompositorResource &r1, const CompositorResource &r2) +{ + return r1.id < r2.id; +} + +inline bool operator<(const CompositorResource &r, CompositorResourceId id) +{ + return r.id < id; +} + +inline bool operator<(CompositorResourceId id, const CompositorResource &r) +{ + return id < r.id; +} + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_H diff --git a/src/core/compositor/compositor_resource_fence.cpp b/src/core/compositor/compositor_resource_fence.cpp new file mode 100644 index 000000000..4efed00d3 --- /dev/null +++ b/src/core/compositor/compositor_resource_fence.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "compositor_resource_fence.h" + +#include <ui/gl/gl_context.h> + +#include <QtGui/qopenglcontext.h> + +#ifndef GL_TIMEOUT_IGNORED +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +namespace QtWebEngineCore { + +void CompositorResourceFence::wait() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + Q_ASSERT(context); + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); + resolved = true; + } + + if (eglClientWaitSyncKHR) + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglClientWaitSyncKHR(m_sync.egl.display, m_sync.egl.sync, 0, EGL_FOREVER_KHR); + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); + static WaitSyncPtr glWaitSync_ = 0; + if (!glWaitSync_) { + glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); + Q_ASSERT(glWaitSync_); + } + glWaitSync_(m_sync.arb.sync, 0, GL_TIMEOUT_IGNORED); + break; + } + + release(); +} + +void CompositorResourceFence::release() +{ + if (!m_sync) + return; + + QOpenGLContext *context = QOpenGLContext::currentContext(); + if (!context) + return; + + // Chromium uses its own GL bindings and stores in in thread local storage. + // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium + // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. + switch (m_sync.type) { + case gl::TransferableFence::NoSync: + break; + case gl::TransferableFence::EglSync: +#ifdef EGL_KHR_reusable_sync + { + static bool resolved = false; + static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; + + if (!resolved) { + if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) + eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); + resolved = true; + } + + if (eglDestroySyncKHR) { + // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. + eglDestroySyncKHR(m_sync.egl.display, m_sync.egl.sync); + m_sync.reset(); + } + } +#endif + break; + case gl::TransferableFence::ArbSync: + typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); + static DeleteSyncPtr glDeleteSync_ = 0; + if (!glDeleteSync_) { + glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); + Q_ASSERT(glDeleteSync_); + } + glDeleteSync_(m_sync.arb.sync); + m_sync.reset(); + break; + } + // If Chromium was able to create a sync, we should have been able to handle its type here too. + Q_ASSERT(!m_sync); +} + +// static +scoped_refptr<CompositorResourceFence> CompositorResourceFence::create() +{ + if (gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { + std::unique_ptr<gl::GLFence> glFence{gl::GLFence::Create()}; + return base::MakeRefCounted<CompositorResourceFence>(glFence->Transfer()); + } + return nullptr; +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/url_request_qrc_job_qt.h b/src/core/compositor/compositor_resource_fence.h index 11c130693..1c2ea3695 100644 --- a/src/core/net/url_request_qrc_job_qt.h +++ b/src/core/compositor/compositor_resource_fence.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,40 +37,35 @@ ** ****************************************************************************/ -#ifndef URL_REQUEST_QRC_JOB_QT_H_ -#define URL_REQUEST_QRC_JOB_QT_H_ +#ifndef COMPOSITOR_RESOURCE_FENCE_H +#define COMPOSITOR_RESOURCE_FENCE_H -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_job.h" - -#include <QFile> +#include <base/memory/ref_counted.h> +#include <ui/gl/gl_fence.h> namespace QtWebEngineCore { -// A request job that handles reading qrc file URLs -class URLRequestQrcJobQt : public net::URLRequestJob { - +// Sync object created on GPU thread and consumed on render thread. +class CompositorResourceFence final : public base::RefCountedThreadSafe<CompositorResourceFence> +{ public: - URLRequestQrcJobQt(net::URLRequest *request, net::NetworkDelegate *networkDelegate); - void Start() override; - void Kill() override; - int ReadRawData(net::IOBuffer* buf, int buf_size) override;; - bool GetMimeType(std::string *mimeType) const override; + REQUIRE_ADOPTION_FOR_REFCOUNTED_TYPE(); -protected: - virtual ~URLRequestQrcJobQt(); - // Get file mime type and try open file on a background thread. - void startGetHead(); + CompositorResourceFence() {} + CompositorResourceFence(const gl::TransferableFence &sync) : m_sync(sync) {}; + ~CompositorResourceFence() { release(); } -private: - qint64 m_remainingBytes; - QFile m_file; - std::string m_mimeType; - base::WeakPtrFactory<URLRequestQrcJobQt> m_weakFactory; + // May be used only by Qt Quick render thread. + void wait(); + void release(); - DISALLOW_COPY_AND_ASSIGN(URLRequestQrcJobQt); + // May be used only by GPU thread. + static scoped_refptr<CompositorResourceFence> create(); + +private: + gl::TransferableFence m_sync; }; } // namespace QtWebEngineCore -#endif // URL_REQUEST_QRC_JOB_QT_H_ +#endif // !COMPOSITOR_RESOURCE_FENCE_H diff --git a/src/core/compositor/compositor_resource_tracker.cpp b/src/core/compositor/compositor_resource_tracker.cpp new file mode 100644 index 000000000..90f98191c --- /dev/null +++ b/src/core/compositor/compositor_resource_tracker.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "compositor_resource_tracker.h" + +#include "chromium_gpu_helper.h" +#include "render_widget_host_view_qt_delegate.h" +#include "web_engine_context.h" + +#include "base/message_loop/message_loop.h" +#include "components/viz/common/quads/compositor_frame.h" +#include "components/viz/common/resources/returned_resource.h" +#include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" +#include "content/browser/browser_main_loop.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/gpu/content_gpu_client.h" +#include "gpu/command_buffer/service/mailbox_manager.h" +#include "gpu/command_buffer/service/sync_point_manager.h" + +namespace QtWebEngineCore { + +CompositorResourceTracker::CompositorResourceTracker() +{} + +CompositorResourceTracker::~CompositorResourceTracker() +{} + +void CompositorResourceTracker::submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + DCHECK(!m_submitCallback); + DCHECK(m_pendingResources.empty()); + DCHECK(m_pendingImports.empty()); + DCHECK(m_pendingResourceUpdates == 0); + + m_submitCallback = std::move(callback); + + m_pendingResources.reserve(frame.resource_list.size()); + m_pendingImports.reserve(frame.resource_list.size()); + + for (const viz::TransferableResource &transferableResource : frame.resource_list) { + auto it = m_committedResources.find(transferableResource.id); + if (it != m_committedResources.end()) + m_pendingImports.push_back(&*it); + else + m_pendingResources.emplace_back(transferableResource); + } + + if (m_pendingResources.empty()) { + scheduleRunSubmitCallback(); + return; + } + + m_pendingResourceUpdates = m_pendingResources.size(); + + std::vector<CompositorResource *> batch; + batch.reserve(m_pendingResources.size()); + + for (CompositorResource &resource : m_pendingResources) { + if (resource.is_software) + updateBitmap(&resource); + else if (!scheduleUpdateMailbox(&resource)) + batch.push_back(&resource); + } + + if (!batch.empty()) + scheduleUpdateMailboxes(std::move(batch)); +} + +void CompositorResourceTracker::commitResources() +{ + // DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // + // This might be called from a Qt Quick render thread, but the UI thread + // will still be blocked for the duration of this call. + + DCHECK(m_pendingResourceUpdates == 0); + + for (CompositorResource *resource : m_pendingImports) + resource->import_count++; + m_pendingImports.clear(); + + m_committedResources.insert(std::make_move_iterator(m_pendingResources.begin()), + std::make_move_iterator(m_pendingResources.end())); + m_pendingResources.clear(); + + ++m_committedFrameId; +} + +std::vector<viz::ReturnedResource> CompositorResourceTracker::returnResources() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::vector<viz::ReturnedResource> returnedResources; + base::EraseIf(m_committedResources, [&](const CompositorResource &resource) { + if (resource.last_used_for_frame != m_committedFrameId) { + viz::ReturnedResource returnedResource; + returnedResource.id = resource.id; + returnedResource.count = resource.import_count; + returnedResources.push_back(std::move(returnedResource)); + return true; + } + return false; + }); + return returnedResources; +} + +const CompositorResource *CompositorResourceTracker::findResource(CompositorResourceId id) const +{ + auto it = m_committedResources.find(id); + DCHECK(it != m_committedResources.end()); + + const_cast<CompositorResource &>(*it).last_used_for_frame = m_committedFrameId; + + return &*it; +} + +void CompositorResourceTracker::updateBitmap(CompositorResource *resource) +{ + content::BrowserMainLoop *browserMainLoop = content::BrowserMainLoop::GetInstance(); + viz::ServerSharedBitmapManager *bitmapManager = browserMainLoop->GetServerSharedBitmapManager(); + + resource->bitmap = bitmapManager->GetSharedBitmapFromId( + resource->size, + viz::BGRA_8888, + resource->mailbox_holder.mailbox); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +} + +quint32 CompositorResourceTracker::consumeMailbox(const gpu::MailboxHolder &mailboxHolder) +{ +#if QT_CONFIG(opengl) + gpu::MailboxManager *mailboxManager = mailbox_manager(); + DCHECK(mailboxManager); + if (mailboxHolder.sync_token.HasData()) + mailboxManager->PullTextureUpdates(mailboxHolder.sync_token); + return service_id(mailboxManager->ConsumeTexture(mailboxHolder.mailbox)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +bool CompositorResourceTracker::scheduleUpdateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + gpu::SyncPointManager *syncPointManager = WebEngineContext::current()->syncPointManager(); + DCHECK(syncPointManager); + return syncPointManager->WaitOutOfOrder( + resource->mailbox_holder.sync_token, + base::BindOnce(&CompositorResourceTracker::updateMailbox, + m_weakPtrFactory.GetWeakPtr(), + resource)); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailbox(CompositorResource *resource) +{ +#if QT_CONFIG(opengl) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + resource->texture_fence = CompositorResourceFence::create(); + + if (--m_pendingResourceUpdates == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleUpdateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); + DCHECK(gpuTaskRunner); + gpuTaskRunner->PostTask( + FROM_HERE, + base::BindOnce(&CompositorResourceTracker::updateMailboxes, + m_weakPtrFactory.GetWeakPtr(), + std::move(resources))); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::updateMailboxes(std::vector<CompositorResource *> resources) +{ +#if QT_CONFIG(opengl) + for (CompositorResource *resource : resources) + resource->texture_id = consumeMailbox(resource->mailbox_holder); + + scoped_refptr<CompositorResourceFence> fence = CompositorResourceFence::create(); + + for (CompositorResource *resource : resources) + resource->texture_fence = fence; + + if ((m_pendingResourceUpdates -= resources.size()) == 0) + scheduleRunSubmitCallback(); +#else + NOTREACHED(); +#endif // QT_CONFIG(OPENGL) +} + +void CompositorResourceTracker::scheduleRunSubmitCallback() +{ + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&CompositorResourceTracker::runSubmitCallback, + m_weakPtrFactory.GetWeakPtr())); +} + +void CompositorResourceTracker::runSubmitCallback() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + std::move(m_submitCallback).Run(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/compositor/compositor_resource_tracker.h b/src/core/compositor/compositor_resource_tracker.h new file mode 100644 index 000000000..887309395 --- /dev/null +++ b/src/core/compositor/compositor_resource_tracker.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMPOSITOR_RESOURCE_TRACKER_H +#define COMPOSITOR_RESOURCE_TRACKER_H + +#include "compositor_resource.h" +#include "locked_ptr.h" + +#include <base/callback.h> +#include <base/containers/flat_set.h> + +#include <atomic> +#include <vector> + +namespace viz { +class CompositorFrame; +struct ReturnedResource; +} // namespace viz + +namespace gpu { +struct MailboxHolder; +} // namespace gpu + +namespace QtWebEngineCore { + +// Ensures resources are not used before they are ready. +// +// The life cycle of a frame's resources: +// +// Step 1. A new CompositorFrame is received and given to submitResources(). +// The frame's resources will extracted and initialized to a pending state. +// +// Step 2. Once the new resources are ready to be committed, +// CompositorResourceTracker will notify the client by running the callback +// given to submitResources(). +// +// Step 3. Once the client is ready to render, commitResources() should be +// called. This will commit all the pending resources, making them available +// via findResource(). +// +// Step 4. Once all the resources have been used (via findResource()), +// returnResources() may be called to return a list of all the resources which +// were *not* used since the last commitResources(). Go to step 1. +class CompositorResourceTracker final +{ +public: + CompositorResourceTracker(); + ~CompositorResourceTracker(); + + void submitResources(const viz::CompositorFrame &frame, base::OnceClosure callback); + void commitResources(); + std::vector<viz::ReturnedResource> returnResources(); + + // The returned pointer is invalidated by the next call to commitFrame() or + // returnResources(). It should therefore not be stored in data structures + // but used immediately. + // + // Do not ask for resources which do not exist. + const CompositorResource *findResource(CompositorResourceId id) const; + +private: + void updateBitmap(CompositorResource *resource); + + quint32 consumeMailbox(const gpu::MailboxHolder &mailboxHolder); + + bool scheduleUpdateMailbox(CompositorResource *resource); + void updateMailbox(CompositorResource *resource); + + void scheduleUpdateMailboxes(std::vector<CompositorResource *> resources); + void updateMailboxes(std::vector<CompositorResource *> resources); + + void scheduleRunSubmitCallback(); + void runSubmitCallback(); + + base::flat_set<CompositorResource> m_committedResources; + std::vector<CompositorResource> m_pendingResources; + std::vector<CompositorResource *> m_pendingImports; + base::OnceClosure m_submitCallback; + std::atomic<size_t> m_pendingResourceUpdates{0}; + quint32 m_committedFrameId = 0; + + base::LockedPtrFactory<CompositorResourceTracker> m_weakPtrFactory{this}; + + DISALLOW_COPY_AND_ASSIGN(CompositorResourceTracker); +}; + +} // namespace QtWebEngineCore + +#endif // !COMPOSITOR_RESOURCE_TRACKER_H diff --git a/src/core/compositor/content_gpu_client_qt.cpp b/src/core/compositor/content_gpu_client_qt.cpp new file mode 100644 index 000000000..0317b503a --- /dev/null +++ b/src/core/compositor/content_gpu_client_qt.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "content_gpu_client_qt.h" + +#include "web_engine_context.h" + +namespace QtWebEngineCore { + +ContentGpuClientQt::ContentGpuClientQt() +{ +} + +ContentGpuClientQt::~ContentGpuClientQt() +{ +} + +gpu::SyncPointManager *ContentGpuClientQt::GetSyncPointManager() +{ + WebEngineContext *current = WebEngineContext::current(); + Q_ASSERT(current); + return current->syncPointManager(); +} + +} // namespace diff --git a/src/core/compositor/content_gpu_client_qt.h b/src/core/compositor/content_gpu_client_qt.h new file mode 100644 index 000000000..d7ad43881 --- /dev/null +++ b/src/core/compositor/content_gpu_client_qt.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CONTENT_GPU_CLIENT_QT_H +#define CONTENT_GPU_CLIENT_QT_H + +#include "content/public/gpu/content_gpu_client.h" + +namespace QtWebEngineCore { + +class ContentGpuClientQt : public content::ContentGpuClient { +public: + explicit ContentGpuClientQt(); + ~ContentGpuClientQt() override; + + // content::ContentGpuClient implementation. + gpu::SyncPointManager *GetSyncPointManager() override; +}; + +} + +#endif // CONTENT_GPU_CLIENT_QT_H diff --git a/src/core/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp index 3755759a1..571d02773 100644 --- a/src/core/delegated_frame_node.cpp +++ b/src/core/compositor/delegated_frame_node.cpp @@ -49,17 +49,14 @@ #include "delegated_frame_node.h" #include "chromium_gpu_helper.h" -#include "ozone/gl_surface_qt.h" #include "stream_video_node.h" #include "type_conversion.h" #include "yuv_video_node.h" +#include "compositor_resource_tracker.h" #include "base/bind.h" -#include "base/message_loop/message_loop.h" -#include "base/threading/thread_task_runner_handle.h" #include "cc/base/math_util.h" #include "components/viz/common/quads/compositor_frame.h" -#include "components/viz/common/quads/compositor_frame_metadata.h" #include "components/viz/common/quads/debug_border_draw_quad.h" #include "components/viz/common/quads/draw_quad.h" #include "components/viz/common/quads/render_pass_draw_quad.h" @@ -68,14 +65,8 @@ #include "components/viz/common/quads/texture_draw_quad.h" #include "components/viz/common/quads/tile_draw_quad.h" #include "components/viz/common/quads/yuv_video_draw_quad.h" -#include "components/viz/common/resources/returned_resource.h" -#include "components/viz/common/resources/transferable_resource.h" #include "components/viz/service/display/bsp_tree.h" #include "components/viz/service/display_embedder/server_shared_bitmap_manager.h" -#include "content/browser/browser_main_loop.h" -#include "gpu/command_buffer/service/mailbox_manager.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_fence.h" #ifndef QT_NO_OPENGL # include <QOpenGLContext> @@ -93,10 +84,6 @@ #include <EGL/eglext.h> #endif -#ifndef GL_TIMEOUT_IGNORED -#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull -#endif - #ifndef GL_TEXTURE_RECTANGLE #define GL_TEXTURE_RECTANGLE 0x84F5 #endif @@ -125,7 +112,7 @@ namespace QtWebEngineCore { #ifndef QT_NO_OPENGL class MailboxTexture : public QSGTexture, protected QOpenGLFunctions { public: - MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize); + MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target = -1); ~MailboxTexture(); // QSGTexture: int textureId() const override { return m_textureId; } @@ -134,14 +121,9 @@ public: bool hasMipmaps() const override { return false; } void bind() override; - void setHasAlphaChannel(bool hasAlpha) { m_hasAlpha = hasAlpha; } - gpu::MailboxHolder &mailboxHolder() { return m_mailboxHolder; } - void fetchTexture(gpu::MailboxManager *mailboxManager); - void setTarget(GLenum target); - private: - gpu::MailboxHolder m_mailboxHolder; int m_textureId; + scoped_refptr<CompositorResourceFence> m_fence; QSize m_textureSize; bool m_hasAlpha; GLenum m_target; @@ -154,20 +136,6 @@ private: friend class DelegatedFrameNode; }; #endif // QT_NO_OPENGL -class ResourceHolder { -public: - ResourceHolder(const viz::TransferableResource &resource); - QSharedPointer<QSGTexture> initTexture(bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); - QSGTexture *texture() const { return m_texture.data(); } - viz::ReturnedResource returnResource(); - void incImportCount() { ++m_importCount; } - bool needsToFetch() const { return !m_resource.is_software && m_texture && !m_texture.data()->textureId(); } - -private: - QWeakPointer<QSGTexture> m_texture; - viz::TransferableResource m_resource; - int m_importCount; -}; class RectClipNode : public QSGClipNode { @@ -451,99 +419,12 @@ static QSGNode *buildLayerChain(QSGNode *chainParent, const viz::SharedQuadState } #ifndef QT_NO_OPENGL -static void waitChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLCLIENTWAITSYNCKHRPROC eglClientWaitSyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglClientWaitSyncKHR = (PFNEGLCLIENTWAITSYNCKHRPROC)context->getProcAddress("eglClientWaitSyncKHR"); - } - resolved = true; - } - - if (eglClientWaitSyncKHR) - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglClientWaitSyncKHR(sync->egl.display, sync->egl.sync, 0, EGL_FOREVER_KHR); - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP WaitSyncPtr)(GLsync sync, GLbitfield flags, GLuint64 timeout); - static WaitSyncPtr glWaitSync_ = 0; - if (!glWaitSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glWaitSync_ = (WaitSyncPtr)context->getProcAddress("glWaitSync"); - Q_ASSERT(glWaitSync_); - } - glWaitSync_(sync->arb.sync, 0, GL_TIMEOUT_IGNORED); - break; - } -} - -static void deleteChromiumSync(gl::TransferableFence *sync) -{ - // Chromium uses its own GL bindings and stores in in thread local storage. - // For that reason, let chromium_gpu_helper.cpp contain the producing code that will run in the Chromium - // GPU thread, and put the sync consuming code here that will run in the QtQuick SG or GUI thread. - switch (sync->type) { - case gl::TransferableFence::NoSync: - break; - case gl::TransferableFence::EglSync: -#ifdef EGL_KHR_reusable_sync - { - static bool resolved = false; - static PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR = 0; - - if (!resolved) { - if (gl::GLSurfaceQt::HasEGLExtension("EGL_KHR_fence_sync")) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - eglDestroySyncKHR = (PFNEGLDESTROYSYNCKHRPROC)context->getProcAddress("eglDestroySyncKHR"); - } - resolved = true; - } - - if (eglDestroySyncKHR) { - // FIXME: Use the less wasteful eglWaitSyncKHR once we have a device that supports EGL_KHR_wait_sync. - eglDestroySyncKHR(sync->egl.display, sync->egl.sync); - sync->reset(); - } - } -#endif - break; - case gl::TransferableFence::ArbSync: - typedef void (QOPENGLF_APIENTRYP DeleteSyncPtr)(GLsync sync); - static DeleteSyncPtr glDeleteSync_ = 0; - if (!glDeleteSync_) { - QOpenGLContext *context = QOpenGLContext::currentContext(); - glDeleteSync_ = (DeleteSyncPtr)context->getProcAddress("glDeleteSync"); - Q_ASSERT(glDeleteSync_); - } - glDeleteSync_(sync->arb.sync); - sync->reset(); - break; - } - // If Chromium was able to create a sync, we should have been able to handle its type here too. - Q_ASSERT(!*sync); -} - -MailboxTexture::MailboxTexture(const gpu::MailboxHolder &mailboxHolder, const QSize textureSize) - : m_mailboxHolder(mailboxHolder) - , m_textureId(0) - , m_textureSize(textureSize) - , m_hasAlpha(false) - , m_target(GL_TEXTURE_2D) +MailboxTexture::MailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) + : m_textureId(resource->texture_id) + , m_fence(resource->texture_fence) + , m_textureSize(toQt(resource->size)) + , m_hasAlpha(hasAlphaChannel) + , m_target(target >= 0 ? target : GL_TEXTURE_2D) #if defined(USE_OZONE) , m_ownsTexture(false) #endif @@ -574,6 +455,8 @@ MailboxTexture::~MailboxTexture() void MailboxTexture::bind() { + if (m_fence) + m_fence->wait(); glBindTexture(m_target, m_textureId); #ifdef Q_OS_QNX if (m_target == GL_TEXTURE_EXTERNAL_OES) { @@ -590,96 +473,7 @@ void MailboxTexture::bind() } #endif } - -void MailboxTexture::setTarget(GLenum target) -{ - m_target = target; -} - -void MailboxTexture::fetchTexture(gpu::MailboxManager *mailboxManager) -{ - gpu::TextureBase *tex = ConsumeTexture(mailboxManager, m_target, m_mailboxHolder.mailbox); - - // The texture might already have been deleted (e.g. when navigating away from a page). - if (tex) { - m_textureId = service_id(tex); -#ifdef Q_OS_QNX - if (m_target == GL_TEXTURE_EXTERNAL_OES) { - m_eglStreamData = eglstream_connect_consumer(tex); - } -#endif - } -} -#endif //QT_NO_OPENGL - -ResourceHolder::ResourceHolder(const viz::TransferableResource &resource) - : m_resource(resource) - , m_importCount(1) -{ -} - -QSharedPointer<QSGTexture> ResourceHolder::initTexture(bool quadNeedsBlending, RenderWidgetHostViewQtDelegate *apiDelegate) -{ - QSharedPointer<QSGTexture> texture = m_texture.toStrongRef(); - if (!texture) { - if (m_resource.is_software) { - Q_ASSERT(apiDelegate); - std::unique_ptr<viz::SharedBitmap> sharedBitmap = - content::BrowserMainLoop::GetInstance()->GetServerSharedBitmapManager()->GetSharedBitmapFromId( - m_resource.size, viz::BGRA_8888, m_resource.mailbox_holder.mailbox); - // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending - // to draw it but Chromium keeps this information in the quads. - // The input format is currently always Format_ARGB32_Premultiplied, so assume that all - // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion - // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to - // return false. - QImage::Format format = quadNeedsBlending ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; - QImage image = sharedBitmap - ? QImage(sharedBitmap->pixels(), m_resource.size.width(), m_resource.size.height(), format) - : QImage(m_resource.size.width(), m_resource.size.height(), format); - texture.reset(apiDelegate->createTextureFromImage(image.copy())); - } else { -#ifndef QT_NO_OPENGL - texture.reset(new MailboxTexture(m_resource.mailbox_holder, toQt(m_resource.size))); - static_cast<MailboxTexture *>(texture.data())->setHasAlphaChannel(quadNeedsBlending); -#else - Q_UNREACHABLE(); -#endif - } - if (m_resource.filter == GL_NEAREST) - texture->setFiltering(QSGTexture::Nearest); - else if (m_resource.filter == GL_LINEAR) - texture->setFiltering(QSGTexture::Linear); - else { - // Depends on qtdeclarative fix, see QTBUG-71322 -#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1) - texture->setFiltering(QSGTexture::Linear); -#else - texture->setFiltering(QSGTexture::Nearest); -#endif - } - m_texture = texture; - } - // All quads using a resource should request the same blending state. - Q_ASSERT(texture->hasAlphaChannel() || !quadNeedsBlending); - return texture; -} - -viz::ReturnedResource ResourceHolder::returnResource() -{ - viz::ReturnedResource returned; - // The ResourceProvider ensures that the resource isn't used by the parent compositor's GL - // context in the GPU process by inserting a sync point to be waited for by the child - // compositor's GL context. We don't need this since we are triggering the delegated frame - // ack directly from our rendering thread. At this point (in updatePaintNode) we know that - // a frame that was compositing any of those resources has already been swapped and we thus - // don't need to use this mechanism. - returned.id = m_resource.id; - returned.count = m_importCount; - m_importCount = 0; - return returned; -} - +#endif // !QT_NO_OPENGL RectClipNode::RectClipNode(const QRectF &rect) : m_geometry(QSGGeometry::defaultAttributes_Point2D(), 4) @@ -691,9 +485,8 @@ RectClipNode::RectClipNode(const QRectF &rect) } DelegatedFrameNode::DelegatedFrameNode() - : m_numPendingSyncPoints(0) #if defined(USE_OZONE) && !defined(QT_NO_OPENGL) - , m_contextShared(true) + : m_contextShared(true) #endif { setFlag(UsePreprocess); @@ -719,22 +512,6 @@ DelegatedFrameNode::~DelegatedFrameNode() void DelegatedFrameNode::preprocess() { -#ifndef QT_NO_OPENGL - // With the threaded render loop the GUI thread has been unlocked at this point. - // We can now wait for the Chromium GPU thread to produce textures that will be - // rendered on our quads and fetch the IDs from the mailboxes we were given. - QList<MailboxTexture *> mailboxesToFetch; - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator ResourceHolderIterator; - ResourceHolderIterator end = m_chromiumCompositorData->resourceHolders.constEnd(); - for (ResourceHolderIterator it = m_chromiumCompositorData->resourceHolders.constBegin(); it != end ; ++it) { - if ((*it)->needsToFetch()) - mailboxesToFetch.append(static_cast<MailboxTexture *>((*it)->texture())); - } - - if (!mailboxesToFetch.isEmpty()) - fetchAndSyncMailboxes(mailboxesToFetch); -#endif - // Then render any intermediate RenderPass in order. typedef QPair<int, QSharedPointer<QSGLayer> > Pair; for (const Pair &pair : qAsConst(m_sgObjects.renderPassLayers)) { @@ -761,8 +538,8 @@ static bool areSharedQuadStatesEqual(const viz::SharedQuadState *layerState, // Compares if the frame data that we got from the Chromium Compositor is // *structurally* equivalent to the one of the previous frame. // If it is, we will just reuse and update the old nodes where necessary. -static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, - viz::CompositorFrame *previousFrameData) +static bool areRenderPassStructuresEqual(const viz::CompositorFrame *frameData, + const viz::CompositorFrame *previousFrameData) { if (!previousFrameData) return false; @@ -814,12 +591,12 @@ static bool areRenderPassStructuresEqual(viz::CompositorFrame *frameData, return true; } -void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, - std::vector<viz::ReturnedResource> *resourcesToRelease, +void DelegatedFrameNode::commit(const viz::CompositorFrame &pendingFrame, + const viz::CompositorFrame &committedFrame, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { - m_chromiumCompositorData = chromiumCompositorData; - viz::CompositorFrame* frameData = &m_chromiumCompositorData->frameData; + const viz::CompositorFrame* frameData = &pendingFrame; if (frameData->render_pass_list.empty()) return; @@ -827,27 +604,11 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // countering the scale of devicePixel-scaled tiles when rendering them // to the final surface. QMatrix4x4 matrix; - const float devicePixelRatio = m_chromiumCompositorData->frameDevicePixelRatio; + const float devicePixelRatio = frameData->metadata.device_scale_factor; matrix.scale(1 / devicePixelRatio, 1 / devicePixelRatio); if (QSGTransformNode::matrix() != matrix) setMatrix(matrix); - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceCandidates; - qSwap(m_chromiumCompositorData->resourceHolders, resourceCandidates); - - // A frame's resource_list only contains the new resources to be added to the scene. Quads can - // still reference resources that were added in previous frames. Add them to the list of - // candidates to be picked up by quads, it's then our responsibility to return unused resources - // to the producing child compositor. - for (unsigned i = 0; i < frameData->resource_list.size(); ++i) { - const viz::TransferableResource &res = frameData->resource_list.at(i); - if (QSharedPointer<ResourceHolder> resource = resourceCandidates.value(res.id)) - resource->incImportCount(); - else - resourceCandidates[res.id] = QSharedPointer<ResourceHolder>(new ResourceHolder(res)); - } - - frameData->resource_list.clear(); QScopedPointer<DelegatedNodeTreeHandler> nodeHandler; const QSizeF viewportSizeInPt = apiDelegate->screenRect().size(); @@ -861,26 +622,22 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, // Additionally, because we clip (i.e. don't build scene graph nodes for) quads outside // of the visible area, we also have to rebuild the tree whenever the window is resized. const bool buildNewTree = - !areRenderPassStructuresEqual(frameData, &m_chromiumCompositorData->previousFrameData) || + !areRenderPassStructuresEqual(frameData, &committedFrame) || m_sceneGraphNodes.empty() || viewportSize != m_previousViewportSize; - m_chromiumCompositorData->previousFrameData = viz::CompositorFrame(); - SGObjects previousSGObjects; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; if (buildNewTree) { // Keep the old objects in scope to hold a ref on layers, resources and textures // that we can re-use. Destroy the remaining objects before returning. - qSwap(m_sgObjects, previousSGObjects); + qSwap(m_sgObjects, m_previousSGObjects); // Discard the scene graph nodes from the previous frame. while (QSGNode *oldChain = firstChild()) delete oldChain; m_sceneGraphNodes.clear(); nodeHandler.reset(new DelegatedNodeTreeCreator(&m_sceneGraphNodes, apiDelegate)); } else { - // Save the texture strong refs so they only go out of scope when the method returns and - // the new vector of texture strong refs has been filled. - qSwap(m_sgObjects.textureStrongRefs, textureStrongRefs); + qSwap(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures); + qSwap(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures); nodeHandler.reset(new DelegatedNodeTreeUpdater(&m_sceneGraphNodes)); } // The RenderPasses list is actually a tree where a parent RenderPass is connected @@ -900,7 +657,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, if (pass != rootRenderPass) { QSharedPointer<QSGLayer> rpLayer; if (buildNewTree) { - rpLayer = findRenderPassLayer(pass->id, previousSGObjects.renderPassLayers); + rpLayer = findRenderPassLayer(pass->id, m_previousSGObjects.renderPassLayers); if (!rpLayer) { rpLayer = QSharedPointer<QSGLayer>(apiDelegate->createLayer()); // Avoid any premature texture update since we need to wait @@ -929,7 +686,7 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } if (scissorRect.IsEmpty()) { - holdResources(pass, resourceCandidates); + holdResources(pass, resourceTracker); continue; } @@ -955,13 +712,13 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, targetRect.Intersect(quadState->clip_rect); targetRect.Intersect(scissorRect); if (targetRect.IsEmpty()) { - holdResources(quad, resourceCandidates); + holdResources(quad, resourceTracker); continue; } if (quadState->sorting_context_id != currentSortingContextId) { flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); currentSortingContextId = quadState->sorting_context_id; } @@ -983,28 +740,23 @@ void DelegatedFrameNode::commit(ChromiumCompositorData *chromiumCompositorData, } handleQuad(quad, currentLayerChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } flushPolygons(&polygonQueue, renderPassChain, - nodeHandler.data(), resourceCandidates, apiDelegate); + nodeHandler.data(), resourceTracker, apiDelegate); } - // Send resources of remaining candidates back to the child compositors so that - // they can be freed or reused. - typedef QHash<unsigned, QSharedPointer<ResourceHolder> >::const_iterator - ResourceHolderIterator; - ResourceHolderIterator end = resourceCandidates.constEnd(); - for (ResourceHolderIterator it = resourceCandidates.constBegin(); it != end ; ++it) - resourcesToRelease->push_back((*it)->returnResource()); + copyMailboxTextures(); m_previousViewportSize = viewportSize; + m_previousSGObjects = SGObjects(); } void DelegatedFrameNode::flushPolygons( base::circular_deque<std::unique_ptr<viz::DrawPolygon>> *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (polygonQueue->empty()) @@ -1024,7 +776,7 @@ void DelegatedFrameNode::flushPolygons( polygon->TransformToLayerSpace(inverseTransform); handlePolygon(polygon, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); }; viz::BspTree(polygonQueue).TraverseWithActionHandler(&actionHandler); @@ -1034,20 +786,20 @@ void DelegatedFrameNode::handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { const viz::DrawQuad *quad = polygon->original_ref(); if (!polygon->is_split()) { handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } else { std::vector<gfx::QuadF> clipRegionList; polygon->ToQuads2D(&clipRegionList); for (const auto & clipRegion : clipRegionList) handleClippedQuad(quad, clipRegion, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } } @@ -1056,7 +808,7 @@ void DelegatedFrameNode::handleClippedQuad( const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { if (currentLayerChain) { @@ -1074,21 +826,21 @@ void DelegatedFrameNode::handleClippedQuad( currentLayerChain = clipNode; } handleQuad(quad, currentLayerChain, - nodeHandler, resourceCandidates, apiDelegate); + nodeHandler, resourceTracker, apiDelegate); } void DelegatedFrameNode::handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate) { switch (quad->material) { case viz::DrawQuad::RENDER_PASS: { const viz::RenderPassDrawQuad *renderPassQuad = viz::RenderPassDrawQuad::MaterialCast(quad); if (!renderPassQuad->mask_texture_size.IsEmpty()) { - ResourceHolder *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(renderPassQuad->mask_resource_id(), resourceTracker); Q_UNUSED(resource); // FIXME: QTBUG-67652 } QSGLayer *layer = @@ -1101,7 +853,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TEXTURE_CONTENT: { const viz::TextureDrawQuad *tquad = viz::TextureDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); QSGTexture *texture = initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate); QSizeF textureSize; @@ -1152,7 +904,7 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::TILED_CONTENT: { const viz::TileDrawQuad *tquad = viz::TileDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(tquad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(tquad->resource_id(), resourceTracker); nodeHandler->setupTextureContentNode( initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate), toQt(quad->rect), toQt(tquad->tex_coord_rect), @@ -1162,17 +914,17 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::YUV_VIDEO_CONTENT: { const viz::YUVVideoDrawQuad *vquad = viz::YUVVideoDrawQuad::MaterialCast(quad); - ResourceHolder *yResource = - findAndHoldResource(vquad->y_plane_resource_id(), resourceCandidates); - ResourceHolder *uResource = - findAndHoldResource(vquad->u_plane_resource_id(), resourceCandidates); - ResourceHolder *vResource = - findAndHoldResource(vquad->v_plane_resource_id(), resourceCandidates); - ResourceHolder *aResource = 0; + const CompositorResource *yResource = + findAndHoldResource(vquad->y_plane_resource_id(), resourceTracker); + const CompositorResource *uResource = + findAndHoldResource(vquad->u_plane_resource_id(), resourceTracker); + const CompositorResource *vResource = + findAndHoldResource(vquad->v_plane_resource_id(), resourceTracker); + const CompositorResource *aResource = nullptr; // This currently requires --enable-vp8-alpha-playback and // needs a video with alpha data to be triggered. if (vquad->a_plane_resource_id()) - aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceCandidates); + aResource = findAndHoldResource(vquad->a_plane_resource_id(), resourceTracker); nodeHandler->setupYUVVideoNode( initAndHoldTexture(yResource, quad->ShouldDrawWithBlending()), @@ -1188,11 +940,9 @@ void DelegatedFrameNode::handleQuad( } case viz::DrawQuad::STREAM_VIDEO_CONTENT: { const viz::StreamVideoDrawQuad *squad = viz::StreamVideoDrawQuad::MaterialCast(quad); - ResourceHolder *resource = findAndHoldResource(squad->resource_id(), resourceCandidates); + const CompositorResource *resource = findAndHoldResource(squad->resource_id(), resourceTracker); MailboxTexture *texture = static_cast<MailboxTexture *>( - initAndHoldTexture(resource, quad->ShouldDrawWithBlending())); - // since this is not default TEXTURE_2D type - texture->setTarget(GL_TEXTURE_EXTERNAL_OES); + initAndHoldTexture(resource, quad->ShouldDrawWithBlending(), apiDelegate, GL_TEXTURE_EXTERNAL_OES)); nodeHandler->setupStreamVideoNode(texture, toQt(squad->rect), toQt(squad->matrix.matrix()), currentLayerChain); @@ -1207,75 +957,100 @@ void DelegatedFrameNode::handleQuad( } } -ResourceHolder *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +const CompositorResource *DelegatedFrameNode::findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker) { - // ResourceHolders must survive when the scene graph destroys our node branch - QSharedPointer<ResourceHolder> &resource = m_chromiumCompositorData->resourceHolders[resourceId]; - if (!resource) - resource = candidates.take(resourceId); - Q_ASSERT(resource); - return resource.data(); + return resourceTracker->findResource(resourceId); } -void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker) { for (auto resource : quad->resources) - findAndHoldResource(resource, candidates); + findAndHoldResource(resource, resourceTracker); } -void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates) +void DelegatedFrameNode::holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker) { for (const auto &quad : pass->quad_list) - holdResources(quad, candidates); + holdResources(quad, resourceTracker); } -QSGTexture *DelegatedFrameNode::initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate) +template<class Container, class Key> +inline auto &findTexture(Container &map, Container &previousMap, const Key &key) { - // QSGTextures must be destroyed in the scene graph thread as part of the QSGNode tree, - // so we can't store them with the ResourceHolder in m_chromiumCompositorData. - // Hold them through a QSharedPointer solely on the root DelegatedFrameNode of the web view - // and access them through a QWeakPointer from the resource holder to find them later. - m_sgObjects.textureStrongRefs.append(resource->initTexture(quadIsAllOpaque, apiDelegate)); - return m_sgObjects.textureStrongRefs.last().data(); + auto &value = map[key]; + if (value) + return value; + value = previousMap[key]; + return value; } -void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch) +QSGTexture *DelegatedFrameNode::initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate, int target) { -#ifndef QT_NO_OPENGL - QList<gl::TransferableFence> transferredFences; - { - QMutexLocker lock(&m_mutex); - QVector<MailboxTexture *> mailboxesToPull; - mailboxesToPull.reserve(mailboxesToFetch.size()); - - gpu::SyncPointManager *syncPointManager = sync_point_manager(); - scoped_refptr<base::SingleThreadTaskRunner> gpuTaskRunner = gpu_task_runner(); - Q_ASSERT(m_numPendingSyncPoints == 0); - m_numPendingSyncPoints = mailboxesToFetch.count(); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { - gpu::SyncToken &syncToken = mailboxTexture->mailboxHolder().sync_token; - const auto task = base::Bind(&DelegatedFrameNode::pullTexture, this, mailboxTexture); - if (!syncPointManager->WaitOutOfOrder(syncToken, std::move(task))) - mailboxesToPull.append(mailboxTexture); - } - if (!mailboxesToPull.isEmpty()) { - auto task = base::BindOnce(&DelegatedFrameNode::pullTextures, this, std::move(mailboxesToPull)); - gpuTaskRunner->PostTask(FROM_HERE, std::move(task)); - } - - m_mailboxesFetchedWaitCond.wait(&m_mutex); - m_textureFences.swap(transferredFences); + QSGTexture::Filtering filtering; + + if (resource->filter == GL_NEAREST) + filtering = QSGTexture::Nearest; + else if (resource->filter == GL_LINEAR) + filtering = QSGTexture::Linear; + else { + // Depends on qtdeclarative fix, see QTBUG-71322 +#if QT_VERSION >= QT_VERSION_CHECK(5, 12, 1) + filtering = QSGTexture::Linear; +#else + filtering = QSGTexture::Nearest; +#endif } - for (gl::TransferableFence sync : qAsConst(transferredFences)) { - // We need to wait on the fences on the Qt current context, and - // can therefore not use GLFence routines that uses a different - // concept of current context. - waitChromiumSync(&sync); - deleteChromiumSync(&sync); + if (resource->is_software) { + QSharedPointer<QSGTexture> &texture = + findTexture(m_sgObjects.bitmapTextures, m_previousSGObjects.bitmapTextures, resource->id); + if (texture) + return texture.data(); + texture = createBitmapTexture(resource, hasAlphaChannel, apiDelegate); + texture->setFiltering(filtering); + return texture.data(); + } else { + QSharedPointer<MailboxTexture> &texture = + findTexture(m_sgObjects.mailboxTextures, m_previousSGObjects.mailboxTextures, resource->id); + if (texture) + return texture.data(); + texture = createMailboxTexture(resource, hasAlphaChannel, target); + texture->setFiltering(filtering); + return texture.data(); } +} -#if defined(USE_OZONE) +QSharedPointer<QSGTexture> DelegatedFrameNode::createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate) +{ + Q_ASSERT(apiDelegate); + viz::SharedBitmap *sharedBitmap = resource->bitmap.get(); + gfx::Size size = resource->size; + + // QSG interprets QImage::hasAlphaChannel meaning that a node should enable blending + // to draw it but Chromium keeps this information in the quads. + // The input format is currently always Format_ARGB32_Premultiplied, so assume that all + // alpha bytes are 0xff if quads aren't requesting blending and avoid the conversion + // from Format_ARGB32_Premultiplied to Format_RGB32 just to get hasAlphaChannel to + // return false. + QImage::Format format = hasAlphaChannel ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + QImage image = sharedBitmap + ? QImage(sharedBitmap->pixels(), size.width(), size.height(), format) + : QImage(size.width(), size.height(), format); + return QSharedPointer<QSGTexture>(apiDelegate->createTextureFromImage(image.copy())); +} + +QSharedPointer<MailboxTexture> DelegatedFrameNode::createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target) +{ +#ifndef QT_NO_OPENGL + return QSharedPointer<MailboxTexture>::create(resource, hasAlphaChannel, target); +#else + Q_UNREACHABLE(); +#endif +} + +void DelegatedFrameNode::copyMailboxTextures() +{ +#if !defined(QT_NO_OPENGL) && defined(USE_OZONE) // Workaround when context is not shared QTBUG-48969 // Make slow copy between two contexts. if (!m_contextShared) { @@ -1290,13 +1065,17 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe GLuint fbo = 0; funcs->glGenFramebuffers(1, &fbo); - for (MailboxTexture *mailboxTexture : qAsConst(mailboxesToFetch)) { + for (const QSharedPointer<MailboxTexture> &mailboxTexture : qAsConst(m_sgObjects.mailboxTextures)) { + if (mailboxTexture->m_ownsTexture) + continue; + // Read texture into QImage from shared context. // Switch to shared context. sharedContext->makeCurrent(m_offsurface.data()); funcs = sharedContext->functions(); QImage img(mailboxTexture->textureSize(), QImage::Format_RGBA8888_Premultiplied); funcs->glBindFramebuffer(GL_FRAMEBUFFER, fbo); + mailboxTexture->m_fence->wait(); funcs->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mailboxTexture->m_textureId, 0); GLenum status = funcs->glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { @@ -1330,69 +1109,6 @@ void DelegatedFrameNode::fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxe currentContext->makeCurrent(surface); } #endif -#else - Q_UNUSED(mailboxesToFetch) -#endif //QT_NO_OPENGL -} - - -void DelegatedFrameNode::pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> textures) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - for (MailboxTexture *texture : textures) { - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - } - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(textures) -#endif -} - -void DelegatedFrameNode::pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *texture) -{ -#ifndef QT_NO_OPENGL - gpu::MailboxManager *mailboxManager = mailbox_manager(); - gpu::SyncToken &syncToken = texture->mailboxHolder().sync_token; - if (syncToken.HasData()) - mailboxManager->PullTextureUpdates(syncToken); - texture->fetchTexture(mailboxManager); - --frameNode->m_numPendingSyncPoints; - - fenceAndUnlockQt(frameNode); -#else - Q_UNUSED(frameNode) - Q_UNUSED(texture) -#endif -} - -void DelegatedFrameNode::fenceAndUnlockQt(DelegatedFrameNode *frameNode) -{ -#ifndef QT_NO_OPENGL - if (!!gl::GLContext::GetCurrent() && gl::GLFence::IsSupported()) { - // Create a fence on the Chromium GPU-thread and context - std::unique_ptr<gl::GLFence> fence = gl::GLFence::Create(); - // But transfer it to something generic since we need to read it using Qt's OpenGL. - frameNode->m_textureFences.append(fence->Transfer()); - } - if (frameNode->m_numPendingSyncPoints == 0) - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&DelegatedFrameNode::unlockQt, frameNode)); -#else - Q_UNUSED(frameNode) -#endif -} - -void DelegatedFrameNode::unlockQt(DelegatedFrameNode *frameNode) -{ - QMutexLocker lock(&frameNode->m_mutex); - // Signal preprocess() the textures are ready - frameNode->m_mailboxesFetchedWaitCond.wakeOne(); } } // namespace QtWebEngineCore diff --git a/src/core/delegated_frame_node.h b/src/core/compositor/delegated_frame_node.h index e37ad08a3..a39ae864b 100644 --- a/src/core/delegated_frame_node.h +++ b/src/core/compositor/delegated_frame_node.h @@ -43,14 +43,8 @@ #include "base/containers/circular_deque.h" #include "components/viz/common/quads/compositor_frame.h" #include "components/viz/common/quads/render_pass.h" -#include "components/viz/common/resources/transferable_resource.h" -#include "gpu/command_buffer/service/sync_point_manager.h" -#include "ui/gl/gl_fence.h" -#include <QMutex> #include <QSGNode> -#include <QSharedData> #include <QSharedPointer> -#include <QWaitCondition> #include <QtGui/QOffscreenSurface> #include "chromium_gpu_helper.h" @@ -72,77 +66,60 @@ class DrawPolygon; namespace QtWebEngineCore { +class CompositorResource; +class CompositorResourceTracker; class DelegatedNodeTreeHandler; class MailboxTexture; -class ResourceHolder; - -// Separating this data allows another DelegatedFrameNode to reconstruct the QSGNode tree from the mailbox textures -// and render pass information. -class ChromiumCompositorData : public QSharedData { -public: - ChromiumCompositorData() : frameDevicePixelRatio(1) { } - QHash<unsigned, QSharedPointer<ResourceHolder> > resourceHolders; - viz::CompositorFrame frameData; - viz::CompositorFrame previousFrameData; - qreal frameDevicePixelRatio; -}; class DelegatedFrameNode : public QSGTransformNode { public: DelegatedFrameNode(); ~DelegatedFrameNode(); - void preprocess(); - void commit(ChromiumCompositorData *chromiumCompositorData, std::vector<viz::ReturnedResource> *resourcesToRelease, RenderWidgetHostViewQtDelegate *apiDelegate); + void preprocess() override; + void commit(const viz::CompositorFrame &pendingFrame, const viz::CompositorFrame &committedFrame, const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); private: void flushPolygons(base::circular_deque<std::unique_ptr<viz::DrawPolygon> > *polygonQueue, QSGNode *renderPassChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handlePolygon( const viz::DrawPolygon *polygon, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleClippedQuad( const viz::DrawQuad *quad, const gfx::QuadF &clipRegion, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); void handleQuad( const viz::DrawQuad *quad, QSGNode *currentLayerChain, DelegatedNodeTreeHandler *nodeHandler, - QHash<unsigned, QSharedPointer<ResourceHolder> > &resourceCandidates, + const CompositorResourceTracker *resourceTracker, RenderWidgetHostViewQtDelegate *apiDelegate); - void fetchAndSyncMailboxes(QList<MailboxTexture *> &mailboxesToFetch); - // Making those callbacks static bypasses base::Bind's ref-counting requirement - // of the this pointer when the callback is a method. - static void pullTexture(DelegatedFrameNode *frameNode, MailboxTexture *mailbox); - static void pullTextures(DelegatedFrameNode *frameNode, const QVector<MailboxTexture *> mailboxes); - static void fenceAndUnlockQt(DelegatedFrameNode *frameNode); - static void unlockQt(DelegatedFrameNode *frameNode); - ResourceHolder *findAndHoldResource(unsigned resourceId, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::DrawQuad *quad, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - void holdResources(const viz::RenderPass *pass, QHash<unsigned, QSharedPointer<ResourceHolder> > &candidates); - QSGTexture *initAndHoldTexture(ResourceHolder *resource, bool quadIsAllOpaque, RenderWidgetHostViewQtDelegate *apiDelegate = 0); + const CompositorResource *findAndHoldResource(unsigned resourceId, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::DrawQuad *quad, const CompositorResourceTracker *resourceTracker); + void holdResources(const viz::RenderPass *pass, const CompositorResourceTracker *resourceTracker); + QSGTexture *initAndHoldTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate = 0, int target = -1); + QSharedPointer<QSGTexture> createBitmapTexture(const CompositorResource *resource, bool hasAlphaChannel, RenderWidgetHostViewQtDelegate *apiDelegate); + QSharedPointer<MailboxTexture> createMailboxTexture(const CompositorResource *resource, bool hasAlphaChannel, int target); + + void copyMailboxTextures(); - QExplicitlySharedDataPointer<ChromiumCompositorData> m_chromiumCompositorData; struct SGObjects { QVector<QPair<int, QSharedPointer<QSGLayer> > > renderPassLayers; QVector<QSharedPointer<QSGRootNode> > renderPassRootNodes; - QVector<QSharedPointer<QSGTexture> > textureStrongRefs; - } m_sgObjects; + QHash<unsigned, QSharedPointer<QSGTexture> > bitmapTextures; + QHash<unsigned, QSharedPointer<MailboxTexture> > mailboxTextures; + } m_sgObjects, m_previousSGObjects; QVector<QSGNode*> m_sceneGraphNodes; - int m_numPendingSyncPoints; - QWaitCondition m_mailboxesFetchedWaitCond; - QMutex m_mutex; - QList<gl::TransferableFence> m_textureFences; #if defined(USE_OZONE) bool m_contextShared; QScopedPointer<QOffscreenSurface> m_offsurface; diff --git a/src/core/stream_video_node.cpp b/src/core/compositor/stream_video_node.cpp index 29922f866..29922f866 100644 --- a/src/core/stream_video_node.cpp +++ b/src/core/compositor/stream_video_node.cpp diff --git a/src/core/stream_video_node.h b/src/core/compositor/stream_video_node.h index 9d937791f..9d937791f 100644 --- a/src/core/stream_video_node.h +++ b/src/core/compositor/stream_video_node.h diff --git a/src/core/yuv_video_node.cpp b/src/core/compositor/yuv_video_node.cpp index 4a436d952..4a436d952 100644 --- a/src/core/yuv_video_node.cpp +++ b/src/core/compositor/yuv_video_node.cpp diff --git a/src/core/yuv_video_node.h b/src/core/compositor/yuv_video_node.h index dca8fa5e2..dca8fa5e2 100644 --- a/src/core/yuv_video_node.h +++ b/src/core/compositor/yuv_video_node.h diff --git a/src/core/configure.json b/src/core/configure.json index 044d85527..4d2555dd2 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -25,6 +25,8 @@ "webengine-v8-snapshot": "boolean", "webengine-webchannel": "boolean", "webengine-kerberos": "boolean", + "webengine-widgets": "boolean", + "webengine-qml": "boolean", "alsa": { "type": "boolean", "name": "webengine-alsa" }, "pulseaudio": { "type": "boolean", "name": "webengine-pulseaudio" }, "ffmpeg": { "type": "enum", "name": "webengine-system-ffmpeg", "values": { "system": "yes", "qt": "no" } }, @@ -546,15 +548,6 @@ "condition": "config.macos && features.webengine-spellchecker", "output": [ "publicFeature" ] }, - "webengine-ui-delegates": { - "label": "UI Delegates", - "output": [ "privateFeature" ] - }, - "webengine-testsupport": { - "label": "Test Support", - "autoDetect": "features.private_tests || call.isTestsInBuildParts", - "output": [ "privateFeature" ] - }, "webengine-webrtc": { "label": "WebRTC", "purpose": "Provides WebRTC support.", @@ -657,6 +650,19 @@ "label": "Thumb instruction set", "condition": "config.linux && features.webengine-embedded-build && arch.arm && tests.webengine-arm-thumb", "output": [ "privateFeature" ] + }, + "webengine-widgets": { + "label": "Qt WebEngine Widgets", + "purpose": "Provides WebEngine Widgets support.", + "section": "WebEngine", + "condition": "module.widgets", + "output": [ "privateFeature" ] + }, + "webengine-qml": { + "label": "Qt WebEngine Qml", + "purpose": "Provides WebEngine Qml support.", + "section": "WebEngine", + "output": [ "privateFeature" ] } }, @@ -705,8 +711,10 @@ "summary": [ { - "section": "Qt WebEngine", + "section": "Qt WebEngineCore", "entries": [ + "webengine-widgets", + "webengine-qml", "webengine-embedded-build", "webengine-pepper-plugins", "webengine-printing-and-pdf", diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 0a51cc261..beec6dd26 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -95,7 +95,6 @@ #include "login_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #include "net/network_delegate_qt.h" -#include "net/qrc_protocol_handler_qt.h" #include "net/url_request_context_getter_qt.h" #if QT_CONFIG(webengine_printing_and_pdf) #include "printing/printing_message_filter_qt.h" diff --git a/src/core/content_client_qt.cpp b/src/core/content_client_qt.cpp index 1a3be31be..def017916 100644 --- a/src/core/content_client_qt.cpp +++ b/src/core/content_client_qt.cpp @@ -56,7 +56,6 @@ #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" -#include "net/qrc_protocol_handler_qt.h" #include "type_conversion.h" #include <QCoreApplication> diff --git a/src/core/content_main_delegate_qt.cpp b/src/core/content_main_delegate_qt.cpp index 2811d5545..52895e312 100644 --- a/src/core/content_main_delegate_qt.cpp +++ b/src/core/content_main_delegate_qt.cpp @@ -40,17 +40,17 @@ #include "content_main_delegate_qt.h" #include "base/command_line.h" -#include <base/i18n/rtl.h> +#include "base/i18n/rtl.h" #include "base/logging.h" #include "base/path_service.h" #include "base/strings/string_number_conversions.h" -#include <chrome/grit/generated_resources.h> +#include "chrome/grit/generated_resources.h" #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/ui_base_paths.h" #include "ui/base/resource/resource_bundle.h" -#include <ui/base/webui/jstemplate_builder.h> +#include "ui/base/webui/jstemplate_builder.h" #include "net/grit/net_resources.h" #include "net/base/net_module.h" #include "services/service_manager/sandbox/switches.h" @@ -59,6 +59,7 @@ #include "content_client_qt.h" #include "renderer/content_renderer_client_qt.h" #include "type_conversion.h" +#include "web_engine_context.h" #include "web_engine_library_info.h" #if defined(ARCH_CPU_ARM_FAMILY) && (defined(OS_ANDROID) || defined(OS_LINUX)) @@ -167,6 +168,12 @@ content::ContentBrowserClient *ContentMainDelegateQt::CreateContentBrowserClient return m_browserClient.get(); } +content::ContentGpuClient *ContentMainDelegateQt::CreateContentGpuClient() +{ + m_gpuClient.reset(new ContentGpuClientQt); + return m_gpuClient.get(); +} + content::ContentRendererClient *ContentMainDelegateQt::CreateContentRendererClient() { #if defined(OS_LINUX) diff --git a/src/core/content_main_delegate_qt.h b/src/core/content_main_delegate_qt.h index c06afb0fb..4d2f33792 100644 --- a/src/core/content_main_delegate_qt.h +++ b/src/core/content_main_delegate_qt.h @@ -42,6 +42,7 @@ #include "content/public/app/content_main_delegate.h" +#include "compositor/content_gpu_client_qt.h" #include "content_browser_client_qt.h" #include "content_utility_client_qt.h" @@ -56,12 +57,14 @@ public: void PreSandboxStartup() override; content::ContentBrowserClient* CreateContentBrowserClient() override; + content::ContentGpuClient* CreateContentGpuClient() override; content::ContentRendererClient* CreateContentRendererClient() override; content::ContentUtilityClient* CreateContentUtilityClient() override; bool BasicStartupComplete(int* /*exit_code*/) override; private: std::unique_ptr<ContentBrowserClientQt> m_browserClient; + std::unique_ptr<ContentGpuClientQt> m_gpuClient; std::unique_ptr<ContentUtilityClientQt> m_utilityClient; }; diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index e92dd60ff..0aa7fd567 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -38,6 +38,7 @@ qtConfig(egl): CONFIG += egl INCLUDEPATH += $$PWD $$PWD/api SOURCES = \ + accessibility_activation_observer.cpp \ accessibility_tree_formatter_qt.cpp \ authentication_dialog_controller.cpp \ browser_accessibility_manager_qt.cpp \ @@ -46,7 +47,6 @@ SOURCES = \ browser_main_parts_qt.cpp \ browser_message_filter_qt.cpp \ certificate_error_controller.cpp \ - chromium_gpu_helper.cpp \ chromium_overrides.cpp \ client_cert_select_controller.cpp \ clipboard_qt.cpp \ @@ -55,12 +55,15 @@ SOURCES = \ common/qt_ipc_logging.cpp \ common/qt_messages.cpp \ common/user_script_data.cpp \ - compositor.cpp \ + compositor/chromium_gpu_helper.cpp \ + compositor/compositor.cpp \ + compositor/compositor_resource_tracker.cpp \ + compositor/content_gpu_client_qt.cpp \ + compositor/delegated_frame_node.cpp \ content_client_qt.cpp \ content_browser_client_qt.cpp \ content_main_delegate_qt.cpp \ content_utility_client_qt.cpp \ - delegated_frame_node.cpp \ desktop_screen_qt.cpp \ devtools_frontend_qt.cpp \ devtools_manager_delegate_qt.cpp \ @@ -76,13 +79,12 @@ SOURCES = \ net/custom_protocol_handler.cpp \ net/network_delegate_qt.cpp \ net/proxy_config_service_qt.cpp \ - net/qrc_protocol_handler_qt.cpp \ + net/qrc_url_scheme_handler.cpp \ net/ssl_host_state_delegate_qt.cpp \ net/url_request_context_getter_qt.cpp \ net/url_request_custom_job.cpp \ net/url_request_custom_job_delegate.cpp \ net/url_request_custom_job_proxy.cpp \ - net/url_request_qrc_job_qt.cpp \ net/webui_controller_factory_qt.cpp \ ozone/gl_context_qt.cpp \ ozone/gl_ozone_egl_qt.cpp \ @@ -120,12 +122,14 @@ SOURCES = \ web_contents_delegate_qt.cpp \ web_contents_view_qt.cpp \ web_engine_context.cpp \ + web_engine_context_threads.cpp \ web_engine_error.cpp \ web_engine_library_info.cpp \ web_engine_settings.cpp \ web_event_factory.cpp HEADERS = \ + accessibility_activation_observer.h \ authentication_dialog_controller_p.h \ authentication_dialog_controller.h \ build_config_qt.h \ @@ -144,35 +148,38 @@ HEADERS = \ color_chooser_controller.h \ common/qt_messages.h \ common/user_script_data.h \ - compositor.h \ + compositor/chromium_gpu_helper.h \ + compositor/compositor.h \ + compositor/compositor_resource.h \ + compositor/compositor_resource_tracker.h \ + compositor/content_gpu_client_qt.h \ + compositor/delegated_frame_node.h \ content_client_qt.h \ content_browser_client_qt.h \ content_main_delegate_qt.h \ content_utility_client_qt.h \ - delegated_frame_node.h \ desktop_screen_qt.h \ devtools_frontend_qt.h \ devtools_manager_delegate_qt.h \ download_manager_delegate_qt.h \ - chromium_gpu_helper.h \ favicon_manager.h \ file_picker_controller.h \ global_descriptors_qt.h \ javascript_dialog_controller_p.h \ javascript_dialog_controller.h \ javascript_dialog_manager_qt.h \ + locked_ptr.h \ login_delegate_qt.h \ media_capture_devices_dispatcher.h \ net/cookie_monster_delegate_qt.h \ net/custom_protocol_handler.h \ net/network_delegate_qt.h \ - net/qrc_protocol_handler_qt.h \ + net/qrc_url_scheme_handler.h \ net/ssl_host_state_delegate_qt.h \ net/url_request_context_getter_qt.h \ net/url_request_custom_job.h \ net/url_request_custom_job_delegate.h \ net/url_request_custom_job_proxy.h \ - net/url_request_qrc_job_qt.h \ net/webui_controller_factory_qt.h \ ozone/gl_context_qt.h \ ozone/gl_ozone_egl_qt.h \ @@ -266,12 +273,14 @@ qtConfig(webengine-printing-and-pdf) { contains(QT_CONFIG, opengl) { SOURCES += \ - yuv_video_node.cpp \ - stream_video_node.cpp + compositor/compositor_resource_fence.cpp \ + compositor/stream_video_node.cpp \ + compositor/yuv_video_node.cpp HEADERS += \ - yuv_video_node.h \ - stream_video_node.h + compositor/compositor_resource_fence.h \ + compositor/stream_video_node.h \ + compositor/yuv_video_node.h } qtConfig(webengine-geolocation) { diff --git a/src/core/locked_ptr.h b/src/core/locked_ptr.h new file mode 100644 index 000000000..73495435b --- /dev/null +++ b/src/core/locked_ptr.h @@ -0,0 +1,299 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOCKED_PTR_H +#define LOCKED_PTR_H + +#include <base/bind_internal.h> + +#include <QtCore/qreadwritelock.h> + +namespace base { + +struct LockedPtrCore +{ + LockedPtrCore(uintptr_t data) : data(data) {} + + std::atomic<size_t> refCount{1}; + // Atomic so that WeakLockedPtr::get can still read it. + std::atomic<uintptr_t> data; + QReadWriteLock lock{QReadWriteLock::Recursive}; +}; + +enum class LockedPtrMode { Weak, Shared, Exclusive }; + +template<class T, LockedPtrMode mode> class LockedPtr; + +// A WeakLockedPtr<T> is something like shared_ptr<T*>. The T* value can only be +// accessed by atomic read. +template<class T> using WeakLockedPtr = LockedPtr<T, LockedPtrMode::Weak>; + +// A SharedLockedPtr<T> is like WeakLockedPtr<T>, but the T* value is prevented +// from changing for the lifetime of the SharedLockedPtr by holding a +// shared-exclusive mutex in shared mode. +template<class T> using SharedLockedPtr = LockedPtr<T, LockedPtrMode::Shared>; + +// An ExclusiveLockedPtr<T> is like SharedLockedPtr<T>, but the mutex is held in +// exclusive mode. Only in this mode can the T* value be changed. +template<class T> using ExclusiveLockedPtr = LockedPtr<T, LockedPtrMode::Exclusive>; + +template<class T, LockedPtrMode mode> +class LockedPtr +{ + template<class T1> + static constexpr bool canConstructFrom = + std::is_same<T, T1>::value || + std::is_same<T, const T1>::value; + +public: + constexpr LockedPtr() {} + constexpr LockedPtr(std::nullptr_t) {} + + LockedPtr(const LockedPtr &that) + { + m_core = that.m_core; + lock(); + } + + LockedPtr &operator=(const LockedPtr &that) + { + unlock(); + m_core = that.m_core; + lock(); + } + + LockedPtr(LockedPtr &&that) + { + m_core = that.m_core; + that.m_core = nullptr; + } + + LockedPtr &operator=(LockedPtr &&that) + { + unlock(); + m_core = that.m_core; + that.m_core = nullptr; + } + + template<class T1, LockedPtrMode mode1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr(const LockedPtr<T1, mode1> &that) + { + m_core = that.m_core; + lock(); + } + + template<class T1, LockedPtrMode mode1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr &operator=(const LockedPtr<T1, mode1> &that) + { + unlock(); + m_core = that.m_core; + lock(); + } + + template<class T1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr(LockedPtr<T1, mode> &&that) + { + m_core = that.m_core; + that.m_core = nullptr; + } + + template<class T1, + class Enable = std::enable_if_t<canConstructFrom<T1>>> + LockedPtr &operator=(LockedPtr<T1, mode> &&that) + { + unlock(); + m_core = that.m_core; + that.m_core = nullptr; + } + + ~LockedPtr() + { + unlock(); + } + + T *get() const + { + if (m_core) { + if (mode == LockedPtrMode::Weak) + return reinterpret_cast<T *>(m_core->data.load(std::memory_order_acquire)); + else + return reinterpret_cast<T *>(m_core->data.load(std::memory_order_relaxed)); + } + return nullptr; + } + + void set(T *value) + { + static_assert(mode == LockedPtrMode::Exclusive, ""); + DCHECK(m_core); + m_core->data.store(reinterpret_cast<uintptr_t>(value), std::memory_order_release); + } + + T &operator*() const { return *get(); } + T *operator->() const { return get(); } + explicit operator bool() const { return get(); } + + static LockedPtr create(T *value) + { + return new LockedPtrCore(reinterpret_cast<uintptr_t>(value)); + } + +private: + template<class T1, LockedPtrMode mode1> friend class LockedPtr; + + LockedPtr(LockedPtrCore *core) + : m_core(core) + {} + + void lock() + { + if (m_core) { + ++m_core->refCount; + + if (mode == LockedPtrMode::Shared) + m_core->lock.lockForRead(); + else if (mode == LockedPtrMode::Exclusive) + m_core->lock.lockForWrite(); + } + } + + void unlock() + { + if (m_core) { + if (mode != LockedPtrMode::Weak) + m_core->lock.unlock(); + + if (--m_core->refCount == 0) + delete m_core; + } + } + + LockedPtrCore *m_core = nullptr; +}; + +// This makes Bind check the pointer before calling the functor. +template<class T> +struct IsWeakReceiver<WeakLockedPtr<T>> : std::true_type {}; + +// By converting the WeakLockedPtr into a SharedLockedPtr we prevent the +// pointed-to object from being destroyed during the base::Callback::Run call. +// +// Unwrap() is called before checking the pointer, so there's no race condition. +template<class T> +struct BindUnwrapTraits<WeakLockedPtr<T>> +{ + static SharedLockedPtr<T> Unwrap(const WeakLockedPtr<T> &o) + { + return o; + } +}; + +// Like base::WeakPtrFactory, but InvalidateWeakPtrs *waits* until all currently +// executing base::Callbacks are finished. Queued up base::Callbacks are still +// canceled, exactly like with WeakPtrFactory. +// +// Consider, for example, the function +// +// void fun() +// { +// MyClass *myClass = new MyClass; +// myClass->scheduleDoStuff(); +// delete myClass; // ??? +// } +// +// where +// +// class MyClass +// { +// public: +// void scheduleDoStuff() +// { +// content::BrowserThread::PostTask( +// content::BrowserThread::IO, FROM_HERE, +// base::BindOnce(&MyClass::doStuff, m_weakPtrFactory.GetWeakPtr())); +// } +// void doStuff(); +// private: +// //base::WeakPtrFactory m_weakPtrFactory{this}; +// base::LockedPtrFactory m_weakPtrFactory{this}; +// }; +// +// What happens if the 'delete myClass' line is executed concurrently with +// MyClass::doStuff? +// +// With WeakPtrs we get a segfault or perhaps memory corruption. +// +// With LockedPtrs we get no crash and no corruption: LockedPtrFactory's +// destructor will wait until doStuff is done before continuing. +template<class T> +class LockedPtrFactory +{ +public: + explicit LockedPtrFactory(T *value) + : m_ptr(WeakLockedPtr<T>::create(value)) + {} + + ~LockedPtrFactory() + { + InvalidateWeakPtrs(); + } + + WeakLockedPtr<T> GetWeakPtr() { return m_ptr; } + WeakLockedPtr<const T> GetWeakPtr() const { return m_ptr; } + SharedLockedPtr<T> GetSharedPtr() { return m_ptr; } + SharedLockedPtr<const T> GetSharedPtr() const { return m_ptr; } + ExclusiveLockedPtr<T> GetExclusivePtr() { return m_ptr; } + ExclusiveLockedPtr<const T> GetExclusivePtr() const { return m_ptr; } + + void InvalidateWeakPtrs() + { + if (ExclusiveLockedPtr<T> ptr = m_ptr) + ptr.set(nullptr); + } + +private: + WeakLockedPtr<T> m_ptr; +}; + +} // namespace base + +#endif // !LOCKED_PTR_H diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 37309931e..be31b6104 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -81,16 +81,19 @@ WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::Page } } -namespace { - -QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) +static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) { if (resourceType >= 0 && resourceType < content::ResourceType(QWebEngineUrlRequestInfo::ResourceTypeLast)) return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType); return QWebEngineUrlRequestInfo::ResourceTypeUnknown; } -QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) +static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) +{ + return static_cast<content::ResourceType>(resourceType); +} + +static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) { return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType); } @@ -99,16 +102,17 @@ QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::Navigati class URLRequestNotification { public: URLRequestNotification(net::URLRequest *request, - const QUrl &url, bool isMainFrameRequest, - int navigationType, - int frameTreeNodeId, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, net::CompletionOnceCallback callback) : m_request(request) - , m_url(url) , m_isMainFrameRequest(isMainFrameRequest) - , m_navigationType(navigationType) - , m_frameTreeNodeId(frameTreeNodeId) + , m_newUrl(newUrl) + , m_originalUrl(requestInfo.requestUrl()) + , m_requestInfo(std::move(requestInfo)) + , m_webContentsGetter(webContentsGetter) , m_callback(std::move(callback)) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); @@ -148,25 +152,36 @@ private: // May run concurrently with cancel() so no peeking at m_request here. int error = net::OK; - content::WebContents *webContents = content::WebContents::FromFrameTreeNodeId(m_frameTreeNodeId); + content::WebContents *webContents = m_webContentsGetter.Run(); + if (webContents) { - int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; WebContentsAdapterClient *client = - WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); - client->navigationRequested(m_navigationType, - m_url, - navigationRequestAction, - m_isMainFrameRequest); - error = net::ERR_FAILED; - switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { - case WebContentsAdapterClient::AcceptRequest: - error = net::OK; - break; - case WebContentsAdapterClient::IgnoreRequest: - error = net::ERR_ABORTED; - break; + WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); + + client->interceptRequest(m_requestInfo); + if (m_requestInfo.changed()) { + error = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + // We handle the rest of the changes later when we are back in I/O thread + } + + // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources + if (error == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { + int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; + client->navigationRequested(m_requestInfo.navigationType(), + m_requestInfo.requestUrl(), + navigationRequestAction, + m_isMainFrameRequest); + error = net::ERR_FAILED; + switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { + case WebContentsAdapterClient::AcceptRequest: + error = net::OK; + break; + case WebContentsAdapterClient::IgnoreRequest: + error = net::ERR_ABORTED; + break; + } + DCHECK(error != net::ERR_FAILED); } - DCHECK(error != net::ERR_FAILED); } // Run the callback on the IO thread. @@ -181,6 +196,17 @@ private: DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (m_request) { + if (m_requestInfo.changed()) { + if (m_originalUrl != m_requestInfo.d_ptr->url) + *m_newUrl = toGurl(m_requestInfo.d_ptr->url); + + if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { + auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); + for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) + m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } + } + if (m_request->status().status() != net::URLRequestStatus::CANCELED) std::move(m_callback).Run(error); m_request->RemoveUserData(UserData::key); @@ -192,17 +218,16 @@ private: ~URLRequestNotification() {} net::URLRequest *m_request; - QUrl m_url; bool m_isMainFrameRequest; - int m_navigationType; - int m_frameTreeNodeId; + GURL *m_newUrl; + const QUrl m_originalUrl; + QWebEngineUrlRequestInfo m_requestInfo; + content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; net::CompletionOnceCallback m_callback; }; const char URLRequestNotification::UserData::key[] = "QtWebEngineCore::URLRequestNotification"; -} // namespace - NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) : m_profileIOData(data) { @@ -212,7 +237,6 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); Q_ASSERT(m_profileIOData); - const content::ResourceRequestInfo *resourceInfo = content::ResourceRequestInfo::ForRequest(request); content::ResourceType resourceType = content::RESOURCE_TYPE_LAST_TYPE; @@ -231,14 +255,14 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet else firstPartyUrl = toQt(request->site_for_cookies()); - QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor(); - if (interceptor) { - QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), - toQt(navigationType), - qUrl, - firstPartyUrl, - QByteArray::fromStdString(request->method())); - QWebEngineUrlRequestInfo requestInfo(infoPrivate); + QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), + toQt(navigationType), + qUrl, + firstPartyUrl, + QByteArray::fromStdString(request->method())); + QWebEngineUrlRequestInfo requestInfo(infoPrivate); + + if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { interceptor->interceptRequest(requestInfo); m_profileIOData->releaseInterceptor(); if (requestInfo.changed()) { @@ -255,6 +279,8 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet if (result != net::OK) return result; + + requestInfo.resetChanged(); } } else m_profileIOData->releaseInterceptor(); @@ -262,17 +288,16 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet if (!resourceInfo) return net::OK; - int frameTreeNodeId = resourceInfo->GetFrameTreeNodeId(); - // Only intercept MAIN_FRAME and SUB_FRAME with an associated render frame. - if (!content::IsResourceTypeFrame(resourceType) || frameTreeNodeId == -1) + if (!m_profileIOData->hasPageInterceptors() && !content::IsResourceTypeFrame(resourceType)) return net::OK; + auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); new URLRequestNotification( request, - qUrl, resourceInfo->IsMainFrame(), - navigationType, - frameTreeNodeId, + newUrl, + std::move(requestInfo), + webContentsGetter, std::move(callback) ); diff --git a/src/core/net/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp new file mode 100644 index 000000000..74a77a7ec --- /dev/null +++ b/src/core/net/qrc_url_scheme_handler.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qrc_url_scheme_handler.h" + +#include <QtWebEngineCore/qwebengineurlrequestjob.h> + +#include <QFile> +#include <QFileInfo> +#include <QMimeDatabase> +#include <QMimeType> + +namespace QtWebEngineCore { + +void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) +{ + QByteArray requestMethod = job->requestMethod(); + if (requestMethod != "GET") { + job->fail(QWebEngineUrlRequestJob::RequestDenied); + return; + } + + QUrl requestUrl = job->requestUrl(); + QString requestPath = requestUrl.path(); + QScopedPointer<QFile> file(new QFile(':' + requestPath, job)); + QFileInfo fileInfo(*file); + QMimeDatabase mimeDatabase; + QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo); + job->reply(mimeType.name().toUtf8(), file.take()); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/qrc_protocol_handler_qt.h b/src/core/net/qrc_url_scheme_handler.h index f2849c1ef..f6ca92879 100644 --- a/src/core/net/qrc_protocol_handler_qt.h +++ b/src/core/net/qrc_url_scheme_handler.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2018 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtWebEngine module of the Qt Toolkit. @@ -37,34 +37,19 @@ ** ****************************************************************************/ -#ifndef QRC_PROTOCOL_HANDLER_QT_H_ -#define QRC_PROTOCOL_HANDLER_QT_H_ +#ifndef QRC_URL_SCHEME_HANDLER_H +#define QRC_URL_SCHEME_HANDLER_H -#include "net/url_request/url_request_job_factory.h" - -namespace net { - -class NetworkDelegate; -class URLRequestJob; - -} // namespace +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#include <QtWebEngineCore/qwebengineurlschemehandler.h> namespace QtWebEngineCore { -extern const char kQrcSchemeQt[]; - -// Implements a ProtocolHandler for qrc file jobs. If |network_delegate_| is NULL, -// then all file requests will fail with ERR_ACCESS_DENIED. -class QrcProtocolHandlerQt : public net::URLRequestJobFactory::ProtocolHandler { - +class QrcUrlSchemeHandler final : public QWebEngineUrlSchemeHandler { public: - QrcProtocolHandlerQt(); - net::URLRequestJob *MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const override; - -private: - DISALLOW_COPY_AND_ASSIGN(QrcProtocolHandlerQt); + void requestStarted(QWebEngineUrlRequestJob *) override; }; } // namespace QtWebEngineCore -#endif // QRC_PROTOCOL_HANDLER_QT_H_ +#endif // !QRC_URL_SCHEME_HANDLER_H diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index b5f10388c..a80ef8060 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -164,7 +164,7 @@ void URLRequestCustomJobProxy::initialize(GURL url, std::string method, base::Op QWebEngineUrlSchemeHandler *schemeHandler = nullptr; if (m_profileAdapter) - schemeHandler = m_profileAdapter->customUrlSchemeHandlers()[toQByteArray(m_scheme)]; + schemeHandler = m_profileAdapter->urlSchemeHandler(toQByteArray(m_scheme)); if (schemeHandler) { m_delegate = new URLRequestCustomJobDelegate(this, toQt(url), diff --git a/src/core/net/url_request_qrc_job_qt.cpp b/src/core/net/url_request_qrc_job_qt.cpp deleted file mode 100644 index a2712653d..000000000 --- a/src/core/net/url_request_qrc_job_qt.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "url_request_qrc_job_qt.h" - -#include "type_conversion.h" - -#include "base/pending_task.h" -#include "base/threading/thread_task_runner_handle.h" -#include "net/base/net_errors.h" -#include "net/base/io_buffer.h" - -#include <QUrl> -#include <QFileInfo> -#include <QMimeDatabase> -#include <QMimeType> - -using namespace net; -namespace QtWebEngineCore { - -URLRequestQrcJobQt::URLRequestQrcJobQt(URLRequest *request, NetworkDelegate *networkDelegate) - : URLRequestJob(request, networkDelegate) - , m_remainingBytes(0) - , m_weakFactory(this) -{ -} - -URLRequestQrcJobQt::~URLRequestQrcJobQt() -{ - if (m_file.isOpen()) - m_file.close(); -} - -void URLRequestQrcJobQt::Start() -{ - base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, base::Bind(&URLRequestQrcJobQt::startGetHead, m_weakFactory.GetWeakPtr())); -} - -void URLRequestQrcJobQt::Kill() -{ - if (m_file.isOpen()) - m_file.close(); - m_weakFactory.InvalidateWeakPtrs(); - - URLRequestJob::Kill(); -} - -bool URLRequestQrcJobQt::GetMimeType(std::string *mimeType) const -{ - DCHECK(request_); - if (m_mimeType.size() > 0) { - *mimeType = m_mimeType; - return true; - } - return false; -} - -int URLRequestQrcJobQt::ReadRawData(IOBuffer *buf, int bufSize) -{ - DCHECK_GE(m_remainingBytes, 0); - // File has been read finished. - if (!m_remainingBytes || !bufSize) { - return 0; - } - if (m_remainingBytes < bufSize) - bufSize = static_cast<int>(m_remainingBytes); - qint64 rv = m_file.read(buf->data(), bufSize); - if (rv >= 0) { - m_remainingBytes -= rv; - DCHECK_GE(m_remainingBytes, 0); - return static_cast<int>(rv); - } - return static_cast<int>(rv); -} - -void URLRequestQrcJobQt::startGetHead() -{ - // Get qrc file path. - QString qrcFilePath = ':' + toQt(request_->url()).path(); - m_file.setFileName(qrcFilePath); - QFileInfo qrcFileInfo(m_file); - // Get qrc file mime type. - QMimeDatabase mimeDatabase; - QMimeType mimeType = mimeDatabase.mimeTypeForFile(qrcFileInfo); - m_mimeType = mimeType.name().toStdString(); - // Open file - if (m_file.open(QIODevice::ReadOnly)) { - m_remainingBytes = m_file.size(); - set_expected_content_size(m_remainingBytes); - // Notify that the headers are complete - NotifyHeadersComplete(); - } else { - NotifyStartError(URLRequestStatus(URLRequestStatus::FAILED, ERR_INVALID_URL)); - } -} - -} // namespace QtWebEngineCore diff --git a/src/core/ozone/gl_context_qt.cpp b/src/core/ozone/gl_context_qt.cpp index ec49414b0..7e913817e 100644 --- a/src/core/ozone/gl_context_qt.cpp +++ b/src/core/ozone/gl_context_qt.cpp @@ -119,7 +119,13 @@ void* GLContextHelper::getGlXConfig() void* GLContextHelper::getEGLDisplay() { +#ifdef Q_OS_WIN + // Windows QPA plugin does not implement resourceForIntegration for "egldisplay". + // Use resourceForContext instead. + return resourceForContext(QByteArrayLiteral("egldisplay")); +#else return resourceForIntegration(QByteArrayLiteral("egldisplay")); +#endif } void* GLContextHelper::getXDisplay() diff --git a/src/core/printing/print_view_manager_qt.cpp b/src/core/printing/print_view_manager_qt.cpp index ff9fc76be..b9b491e41 100644 --- a/src/core/printing/print_view_manager_qt.cpp +++ b/src/core/printing/print_view_manager_qt.cpp @@ -72,18 +72,18 @@ namespace { static const qreal kMicronsToMillimeter = 1000.0f; -static std::vector<char> -GetStdVectorFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) +static QSharedPointer<QByteArray> +GetByteArrayFromHandle(base::SharedMemoryHandle handle, uint32_t data_size) { std::unique_ptr<base::SharedMemory> shared_buf( new base::SharedMemory(handle, true)); if (!shared_buf->Map(data_size)) { - return std::vector<char>(); + return QSharedPointer<QByteArray>(new QByteArray); } char* data = static_cast<char*>(shared_buf->memory()); - return std::vector<char>(data, data + data_size); + return QSharedPointer<QByteArray>(new QByteArray(data, data_size)); } static scoped_refptr<base::RefCountedBytes> @@ -247,7 +247,7 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, // If there already is a pending print in progress, don't try starting another one. if (m_printSettings) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, std::vector<char>())); + base::Bind(callback, QSharedPointer<QByteArray>())); return; } @@ -255,7 +255,7 @@ void PrintViewManagerQt::PrintToPDFWithCallback(const QPageLayout &pageLayout, if (!PrintToPDFInternal(pageLayout, printInColor, useCustomMargins)) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(callback, std::vector<char>())); + base::Bind(callback, QSharedPointer<QByteArray>())); resetPdfState(); } @@ -358,18 +358,18 @@ void PrintViewManagerQt::OnMetafileReadyForPrinting(content::RenderFrameHost* rf StopWorker(params.document_cookie); // Create local copies so we can reset the state and take a new pdf print job. - base::Callback<void(const std::vector<char>&)> pdf_print_callback = m_pdfPrintCallback; + base::Callback<void(QSharedPointer<QByteArray>)> pdf_print_callback = m_pdfPrintCallback; base::Callback<void(bool)> pdf_save_callback = m_pdfSaveCallback; base::FilePath pdfOutputPath = m_pdfOutputPath; resetPdfState(); if (!pdf_print_callback.is_null()) { - std::vector<char> data_vector = GetStdVectorFromHandle(params.content.metafile_data_handle, - params.content.data_size); + QSharedPointer<QByteArray> data_array = GetByteArrayFromHandle(params.content.metafile_data_handle, + params.content.data_size); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(pdf_print_callback, data_vector)); + base::Bind(pdf_print_callback, data_array)); } else { scoped_refptr<base::RefCountedBytes> data_bytes = GetBytesFromHandle(params.content.metafile_data_handle, params.content.data_size); @@ -394,7 +394,7 @@ void PrintViewManagerQt::NavigationStopped() if (!m_pdfPrintCallback.is_null()) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(m_pdfPrintCallback, std::vector<char>())); + base::Bind(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); PrintViewManagerBaseQt::NavigationStopped(); @@ -406,7 +406,7 @@ void PrintViewManagerQt::RenderProcessGone(base::TerminationStatus status) if (!m_pdfPrintCallback.is_null()) { content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, - base::Bind(m_pdfPrintCallback, std::vector<char>())); + base::Bind(m_pdfPrintCallback, QSharedPointer<QByteArray>())); } resetPdfState(); } diff --git a/src/core/printing/print_view_manager_qt.h b/src/core/printing/print_view_manager_qt.h index 209be8782..b21389691 100644 --- a/src/core/printing/print_view_manager_qt.h +++ b/src/core/printing/print_view_manager_qt.h @@ -56,6 +56,8 @@ #include "content/public/browser/notification_registrar.h" #include "content/public/browser/web_contents_user_data.h" +#include <QSharedPointer> + struct PrintHostMsg_RequestPrintPreview_Params; struct PrintHostMsg_DidPreviewDocument_Params; @@ -83,7 +85,7 @@ class PrintViewManagerQt { public: ~PrintViewManagerQt() override; - typedef base::Callback<void(const std::vector<char> &result)> PrintToPDFCallback; + typedef base::Callback<void(QSharedPointer<QByteArray> result)> PrintToPDFCallback; typedef base::Callback<void(bool success)> PrintToPDFFileCallback; // Method to print a page to a Pdf document with page size \a pageSize in location \a filePath. diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index 462be9f48..cee783118 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -44,10 +44,12 @@ #include "content/public/browser/browsing_data_remover.h" #include "content/public/browser/download_manager.h" +#include "api/qwebengineurlscheme.h" #include "content_client_qt.h" #include "download_manager_delegate_qt.h" #include "net/url_request_context_getter_qt.h" #include "permission_manager_qt.h" +#include "profile_adapter_client.h" #include "profile_qt.h" #include "renderer_host/user_resource_controller_host.h" #include "type_conversion.h" @@ -81,6 +83,7 @@ ProfileAdapter::ProfileAdapter(const QString &storageName): , m_persistentCookiesPolicy(AllowPersistentCookies) , m_visitedLinksPolicy(TrackVisitedLinksOnDisk) , m_httpCacheMaxSize(0) + , m_pageRequestInterceptors(0) { WebEngineContext::current()->addProfileAdapter(this); // creation of profile requires webengine context @@ -88,6 +91,7 @@ ProfileAdapter::ProfileAdapter(const QString &storageName): content::BrowserContext::Initialize(m_profile.data(), toFilePath(dataPath())); // fixme: this should not be here m_profile->m_profileIOData->initializeOnUIThread(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); } ProfileAdapter::~ProfileAdapter() @@ -97,6 +101,7 @@ ProfileAdapter::~ProfileAdapter() m_profile->GetDownloadManager(m_profile.data())->Shutdown(); m_downloadManagerDelegate.reset(); } + Q_ASSERT(m_pageRequestInterceptors == 0); } void ProfileAdapter::setStorageName(const QString &storageName) @@ -173,6 +178,22 @@ void ProfileAdapter::removeClient(ProfileAdapterClient *adapterClient) m_clients.removeOne(adapterClient); } +void ProfileAdapter::addPageRequestInterceptor() +{ + ++m_pageRequestInterceptors; + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateRequestInterceptor(); +} + +void ProfileAdapter::removePageRequestInterceptor() +{ + Q_ASSERT(m_pageRequestInterceptors > 0); + --m_pageRequestInterceptors; + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateRequestInterceptor(); +} + + void ProfileAdapter::cancelDownload(quint32 downloadId) { downloadManagerDelegate()->cancelDownload(downloadId); @@ -400,9 +421,23 @@ void ProfileAdapter::setHttpCacheMaxSize(int maxSize) m_profile->m_profileIOData->updateHttpCache(); } -const QHash<QByteArray, QWebEngineUrlSchemeHandler *> &ProfileAdapter::customUrlSchemeHandlers() const +static bool isInternalScheme(const QByteArray &scheme) +{ + static QSet<QByteArray> internalSchemes{ + QByteArrayLiteral("qrc"), + QByteArrayLiteral("data"), + QByteArrayLiteral("blob"), + QByteArrayLiteral("http"), + QByteArrayLiteral("https"), + QByteArrayLiteral("ftp"), + QByteArrayLiteral("javascript"), + }; + return internalSchemes.contains(scheme); +} + +QWebEngineUrlSchemeHandler *ProfileAdapter::urlSchemeHandler(const QByteArray &scheme) { - return m_customUrlSchemeHandlers; + return m_customUrlSchemeHandlers.value(scheme.toLower()).data(); } const QList<QByteArray> ProfileAdapter::customUrlSchemes() const @@ -416,12 +451,17 @@ void ProfileAdapter::updateCustomUrlSchemeHandlers() m_profile->m_profileIOData->updateJobFactory(); } -bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) +void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { + Q_ASSERT(handler); bool removedOneOrMore = false; auto it = m_customUrlSchemeHandlers.begin(); while (it != m_customUrlSchemeHandlers.end()) { if (it.value() == handler) { + if (isInternalScheme(it.key())) { + qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", it.key().constData()); + continue; + } it = m_customUrlSchemeHandlers.erase(it); removedOneOrMore = true; continue; @@ -430,26 +470,43 @@ bool ProfileAdapter::removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *ha } if (removedOneOrMore) updateCustomUrlSchemeHandlers(); - return removedOneOrMore; } -QWebEngineUrlSchemeHandler *ProfileAdapter::takeCustomUrlSchemeHandler(const QByteArray &scheme) +void ProfileAdapter::removeUrlScheme(const QByteArray &scheme) { - QWebEngineUrlSchemeHandler *handler = m_customUrlSchemeHandlers.take(scheme); - if (handler) + QByteArray canonicalScheme = scheme.toLower(); + if (isInternalScheme(canonicalScheme)) { + qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", scheme.constData()); + return; + } + if (m_customUrlSchemeHandlers.remove(canonicalScheme)) updateCustomUrlSchemeHandlers(); - return handler; } -void ProfileAdapter::addCustomUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) +void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { - m_customUrlSchemeHandlers.insert(scheme, handler); + Q_ASSERT(handler); + QByteArray canonicalScheme = scheme.toLower(); + if (isInternalScheme(canonicalScheme)) { + qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); + return; + } + if (m_customUrlSchemeHandlers.value(canonicalScheme, handler) != handler) { + qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); + return; + } + if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) + qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " + "before installing the custom scheme handler.", scheme.constData()); + + m_customUrlSchemeHandlers.insert(canonicalScheme, handler); updateCustomUrlSchemeHandlers(); } -void ProfileAdapter::clearCustomUrlSchemeHandlers() +void ProfileAdapter::removeAllUrlSchemeHandlers() { m_customUrlSchemeHandlers.clear(); + m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler); updateCustomUrlSchemeHandlers(); } @@ -548,7 +605,37 @@ bool ProfileAdapter::isSpellCheckEnabled() const void ProfileAdapter::resetVisitedLinksManager() { - m_visitedLinksManager.reset(new VisitedLinksManagerQt(this)); + m_visitedLinksManager.reset(new VisitedLinksManagerQt(m_profile.data(), persistVisitedLinks())); +} + +void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable) +{ + if (m_usedForGlobalCertificateVerification == enable) + return; + + static QPointer<ProfileAdapter> profileForglobalCertificateVerification; + + m_usedForGlobalCertificateVerification = enable; + if (enable) { + if (profileForglobalCertificateVerification) { + profileForglobalCertificateVerification->m_usedForGlobalCertificateVerification = false; + for (auto *client : qAsConst(profileForglobalCertificateVerification->m_clients)) + client->useForGlobalCertificateVerificationChanged(); + } + profileForglobalCertificateVerification = this; + } else { + Q_ASSERT(profileForglobalCertificateVerification); + Q_ASSERT(profileForglobalCertificateVerification == this); + profileForglobalCertificateVerification = nullptr; + } + + if (m_profile->m_urlRequestContextGetter.get()) + m_profile->m_profileIOData->updateUsedForGlobalCertificateVerification(); +} + +bool ProfileAdapter::isUsedForGlobalCertificateVerification() const +{ + return m_usedForGlobalCertificateVerification; } } // namespace QtWebEngineCore diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index f2a5cdb49..9849d2788 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -63,6 +63,7 @@ #include "api/qwebenginecookiestore.h" #include "api/qwebengineurlrequestinterceptor.h" #include "api/qwebengineurlschemehandler.h" +#include "net/qrc_url_scheme_handler.h" QT_FORWARD_DECLARE_CLASS(QObject) @@ -168,14 +169,14 @@ public: void setHttpCacheMaxSize(int maxSize); bool trackVisitedLinks() const; - bool persistVisitedLinks() const; - const QHash<QByteArray, QWebEngineUrlSchemeHandler *> &customUrlSchemeHandlers() const; + QWebEngineUrlSchemeHandler *urlSchemeHandler(const QByteArray &scheme); + void installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler); + void removeUrlScheme(const QByteArray &scheme); + void removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler); + void removeAllUrlSchemeHandlers(); + const QList<QByteArray> customUrlSchemes() const; - void clearCustomUrlSchemeHandlers(); - void addCustomUrlSchemeHandler(const QByteArray &, QWebEngineUrlSchemeHandler *); - bool removeCustomUrlSchemeHandler(QWebEngineUrlSchemeHandler *); - QWebEngineUrlSchemeHandler *takeCustomUrlSchemeHandler(const QByteArray &); UserResourceControllerHost *userResourceController(); void permissionRequestReply(const QUrl &origin, PermissionType type, bool reply); @@ -187,12 +188,21 @@ public: void clearHttpCache(); + void setUseForGlobalCertificateVerification(bool enable = true); + bool isUsedForGlobalCertificateVerification() const; + + void addPageRequestInterceptor(); + void removePageRequestInterceptor(); + bool hasPageRequestInterceptor() const { return m_pageRequestInterceptors > 0; } + private: void updateCustomUrlSchemeHandlers(); void resetVisitedLinksManager(); + bool persistVisitedLinks() const; QString m_name; bool m_offTheRecord; + bool m_usedForGlobalCertificateVerification = false; QScopedPointer<ProfileQt> m_profile; QScopedPointer<VisitedLinksManagerQt> m_visitedLinksManager; QScopedPointer<DownloadManagerDelegateQt> m_downloadManagerDelegate; @@ -207,9 +217,11 @@ private: QString m_httpAcceptLanguage; PersistentCookiesPolicy m_persistentCookiesPolicy; VisitedLinksPolicy m_visitedLinksPolicy; - QHash<QByteArray, QWebEngineUrlSchemeHandler *> m_customUrlSchemeHandlers; + QHash<QByteArray, QPointer<QWebEngineUrlSchemeHandler>> m_customUrlSchemeHandlers; QList<ProfileAdapterClient*> m_clients; int m_httpCacheMaxSize; + int m_pageRequestInterceptors; + QrcUrlSchemeHandler m_qrcHandler; Q_DISABLE_COPY(ProfileAdapter) }; diff --git a/src/core/profile_adapter_client.h b/src/core/profile_adapter_client.h index 06051fab6..4711f8bcf 100644 --- a/src/core/profile_adapter_client.h +++ b/src/core/profile_adapter_client.h @@ -142,6 +142,7 @@ public: virtual void downloadRequested(DownloadItemInfo &info) = 0; virtual void downloadUpdated(const DownloadItemInfo &info) = 0; + virtual void useForGlobalCertificateVerificationChanged() {} static QString downloadInterruptReasonToString(DownloadInterruptReason reason); }; diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 7783f1ae7..89d690e9e 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -80,12 +80,20 @@ #include "net/custom_protocol_handler.h" #include "net/network_delegate_qt.h" #include "net/proxy_config_service_qt.h" -#include "net/qrc_protocol_handler_qt.h" #include "net/url_request_context_getter_qt.h" #include "profile_qt.h" #include "resource_context_qt.h" #include "type_conversion.h" +#if defined(USE_NSS_CERTS) +#include "net/cert_net/nss_ocsp.h" +#endif + +#if defined(OS_LINUX) || defined(OS_MACOSX) +#include "net/cert/cert_net_fetcher.h" +#include "net/cert_net/cert_net_fetcher_impl.h" +#endif + namespace QtWebEngineCore { static const char* const kDefaultAuthSchemes[] = { net::kBasicAuthScheme, @@ -171,6 +179,16 @@ ProfileIODataQt::~ProfileIODataQt() { if (content::BrowserThread::IsThreadInitialized(content::BrowserThread::IO)) DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_useForGlobalCertificateVerification) { +#if defined(USE_NSS_CERTS) + net::SetURLRequestContextForNSSHttpIO(nullptr); +#endif +#if defined(OS_LINUX) ||defined(OS_MACOSX) + net::ShutdownGlobalCertNetFetcher(); +#endif + } + m_resourceContext.reset(); if (m_cookieDelegate) m_cookieDelegate->setCookieMonster(0); // this will let CookieMonsterDelegateQt be deleted @@ -213,6 +231,7 @@ void ProfileIODataQt::initializeOnIOThread() m_initialized = true; generateAllStorage(); generateJobFactory(); + setGlobalCertificateVerification(); } void ProfileIODataQt::initializeOnUIThread() @@ -255,6 +274,26 @@ void ProfileIODataQt::generateAllStorage() m_updateAllStorage = false; } +class SSLConfigServiceQt : public net::SSLConfigService { +public: + SSLConfigServiceQt() + { + // Enable revocation checking: + m_defaultConfig.rev_checking_enabled = true; + // Mirroring Android WebView (we have no beef with Symantec, and our users might use them): + m_defaultConfig.symantec_enforcement_disabled = true; + } + ~SSLConfigServiceQt() override = default; + + void GetSSLConfig(net::SSLConfig* config) override + { + *config = m_defaultConfig; + } + +private: + net::SSLConfig m_defaultConfig; +}; + void ProfileIODataQt::generateStorage() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); @@ -300,7 +339,7 @@ void ProfileIODataQt::generateStorage() nullptr /* NetLog */, m_networkDelegate.get())); - m_storage->set_ssl_config_service(std::make_unique<net::SSLConfigServiceDefaults>()); + m_storage->set_ssl_config_service(std::make_unique<SSLConfigServiceQt>()); m_storage->set_transport_security_state(std::make_unique<net::TransportSecurityState>()); if (!m_dataPath.isEmpty()) { @@ -498,9 +537,6 @@ void ProfileIODataQt::generateJobFactory() base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN})); jobFactory->SetProtocolHandler(url::kFileScheme, std::make_unique<net::FileProtocolHandler>(taskRunner)); - jobFactory->SetProtocolHandler(kQrcSchemeQt, - std::unique_ptr<net::URLRequestJobFactory::ProtocolHandler>( - new QrcProtocolHandlerQt())); jobFactory->SetProtocolHandler(url::kFtpScheme, net::FtpProtocolHandler::Create(m_urlRequestContext->host_resolver())); @@ -558,6 +594,21 @@ void ProfileIODataQt::regenerateJobFactory() } } +void ProfileIODataQt::setGlobalCertificateVerification() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + QMutexLocker lock(&m_mutex); + if (m_useForGlobalCertificateVerification) { +#if defined(USE_NSS_CERTS) + // Set request context used by NSS for OCSP requests. + net::SetURLRequestContextForNSSHttpIO(m_urlRequestContext.get()); +#endif +#if defined(OS_LINUX) || defined(OS_MACOSX) + net::SetGlobalCertNetFetcher(net::CreateCertNetFetcher(m_urlRequestContext.get())); +#endif + } +} + void ProfileIODataQt::setRequestContextData(content::ProtocolHandlerMap *protocolHandlers, content::URLRequestInterceptorScopedVector request_interceptors) { @@ -580,6 +631,7 @@ void ProfileIODataQt::setFullConfiguration() m_httpCachePath = m_profileAdapter->httpCachePath(); m_httpCacheMaxSize = m_profileAdapter->httpCacheMaxSize(); m_customUrlSchemes = m_profileAdapter->customUrlSchemes(); + m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); m_dataPath = m_profileAdapter->dataPath(); } @@ -687,6 +739,7 @@ void ProfileIODataQt::updateRequestInterceptor() Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); QMutexLocker lock(&m_mutex); m_requestInterceptor = m_profileAdapter->requestInterceptor(); + m_hasPageInterceptors = m_profileAdapter->hasPageRequestInterceptor(); // We in this case do not need to regenerate any Chromium classes. } @@ -696,6 +749,13 @@ QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() return m_requestInterceptor; } +bool ProfileIODataQt::hasPageInterceptors() +{ + // used in NetworkDelegateQt::OnBeforeURLRequest + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); + return m_hasPageInterceptors; +} + void ProfileIODataQt::releaseInterceptor() { m_mutex.unlock(); @@ -711,4 +771,15 @@ bool ProfileIODataQt::canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) return m_cookieDelegate->canGetCookies(firstPartyUrl, url); } +void ProfileIODataQt::updateUsedForGlobalCertificateVerification() +{ + Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); + QMutexLocker lock(&m_mutex); + m_useForGlobalCertificateVerification = m_profileAdapter->isUsedForGlobalCertificateVerification(); + + if (m_useForGlobalCertificateVerification) + content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE, + base::Bind(&ProfileIODataQt::setGlobalCertificateVerification, m_weakPtr)); +} + } // namespace QtWebEngineCore diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 5b416861c..bcf49e22b 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -91,6 +91,7 @@ public: void regenerateJobFactory(); bool canSetCookie(const QUrl &firstPartyUrl, const QByteArray &cookieLine, const QUrl &url) const; bool canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) const; + void setGlobalCertificateVerification(); // Used in NetworkDelegateQt::OnBeforeURLRequest. QWebEngineUrlRequestInterceptor *acquireInterceptor(); @@ -105,6 +106,8 @@ public: void updateHttpCache(); // runs on ui thread void updateJobFactory(); // runs on ui thread void updateRequestInterceptor(); // runs on ui thread + void updateUsedForGlobalCertificateVerification(); // runs on ui thread + bool hasPageInterceptors(); private: ProfileQt *m_profile; @@ -145,6 +148,8 @@ private: bool m_updateJobFactory = false; bool m_updateUserAgent = false; bool m_ignoreCertificateErrors = false; + bool m_useForGlobalCertificateVerification = false; + bool m_hasPageInterceptors = false; base::WeakPtrFactory<ProfileIODataQt> m_weakPtrFactory; // this should be always the last member QString m_dataPath; DISALLOW_COPY_AND_ASSIGN(ProfileIODataQt); diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 40ed75973..61b1e2a09 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -41,7 +41,7 @@ #include "browser_accessibility_manager_qt.h" #include "chromium_overrides.h" -#include "compositor.h" +#include "compositor/compositor.h" #include "qtwebenginecoreglobal_p.h" #include "render_widget_host_view_qt_delegate.h" #include "type_conversion.h" @@ -93,7 +93,6 @@ #include <QWheelEvent> #include <QWindow> #include <QtGui/private/qinputcontrol_p.h> -#include <QtGui/qaccessible.h> namespace QtWebEngineCore { @@ -244,19 +243,6 @@ private: float dpiScale; }; -bool isAccessibilityEnabled() { - // On Linux accessibility is disabled by default due to performance issues, - // and can be re-enabled by setting the QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY environment - // variable. For details, see QTBUG-59922. -#ifdef Q_OS_LINUX - static bool accessibility_enabled - = qEnvironmentVariableIsSet("QTWEBENGINE_ENABLE_LINUX_ACCESSIBILITY"); -#else - const bool accessibility_enabled = true; -#endif - return accessibility_enabled; -} - RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget) : content::RenderWidgetHostViewBase::RenderWidgetHostViewBase(widget) , m_gestureProvider(QtGestureProviderConfig(), this) @@ -280,13 +266,6 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget base::checked_cast<uint32_t>(widget->GetRoutingID())) { host()->SetView(this); -#ifndef QT_NO_ACCESSIBILITY - if (isAccessibilityEnabled()) { - QAccessible::installActivationObserver(this); - if (QAccessible::isActive()) - content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); - } -#endif // QT_NO_ACCESSIBILITY if (GetTextInputManager()) GetTextInputManager()->AddObserver(this); @@ -303,9 +282,6 @@ RenderWidgetHostViewQt::RenderWidgetHostViewQt(content::RenderWidgetHost *widget RenderWidgetHostViewQt::~RenderWidgetHostViewQt() { QObject::disconnect(m_adapterClientDestroyedConnection); -#ifndef QT_NO_ACCESSIBILITY - QAccessible::removeActivationObserver(this); -#endif // QT_NO_ACCESSIBILITY if (text_input_manager_) text_input_manager_->RemoveObserver(this); @@ -314,7 +290,6 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt() void RenderWidgetHostViewQt::setDelegate(RenderWidgetHostViewQtDelegate* delegate) { m_delegate.reset(delegate); - m_compositor->setViewDelegate(delegate); } void RenderWidgetHostViewQt::setAdapterClient(WebContentsAdapterClient *adapterClient) @@ -696,7 +671,9 @@ void RenderWidgetHostViewQt::SubmitCompositorFrame(const viz::LocalSurfaceId &lo if (dpiScale != 0 && dpiScale != 1) frame.metadata.device_scale_factor /= dpiScale; - m_compositor->submitFrame(std::move(frame)); + m_compositor->submitFrame( + std::move(frame), + base::BindOnce(&RenderWidgetHostViewQtDelegate::update, base::Unretained(m_delegate.get()))); if (m_loadVisuallyCommittedState == NotCommitted) { m_loadVisuallyCommittedState = DidFirstCompositorFrameSwap; @@ -928,7 +905,7 @@ void RenderWidgetHostViewQt::OnDidUpdateVisualPropertiesComplete(const cc::Rende QSGNode *RenderWidgetHostViewQt::updatePaintNode(QSGNode *oldNode) { - return m_compositor->updatePaintNode(oldNode); + return m_compositor->updatePaintNode(oldNode, m_delegate.get()); } void RenderWidgetHostViewQt::notifyResize() @@ -1392,16 +1369,6 @@ void RenderWidgetHostViewQt::handleInputMethodQueryEvent(QInputMethodQueryEvent ev->accept(); } -#ifndef QT_NO_ACCESSIBILITY -void RenderWidgetHostViewQt::accessibilityActiveChanged(bool active) -{ - if (active) - content::BrowserAccessibilityStateImpl::GetInstance()->EnableAccessibility(); - else - content::BrowserAccessibilityStateImpl::GetInstance()->DisableAccessibility(); -} -#endif // QT_NO_ACCESSIBILITY - void RenderWidgetHostViewQt::handleWheelEvent(QWheelEvent *ev) { if (!m_wheelAckPending) { diff --git a/src/core/render_widget_host_view_qt.h b/src/core/render_widget_host_view_qt.h index a12ffe636..3d51731d2 100644 --- a/src/core/render_widget_host_view_qt.h +++ b/src/core/render_widget_host_view_qt.h @@ -57,7 +57,6 @@ #include <QMap> #include <QPoint> #include <QtGlobal> -#include <QtGui/qaccessible.h> #include <QtGui/QTouchEvent> QT_BEGIN_NAMESPACE @@ -94,9 +93,6 @@ class RenderWidgetHostViewQt , public ui::GestureProviderClient , public RenderWidgetHostViewQtDelegateClient , public base::SupportsWeakPtr<RenderWidgetHostViewQt> -#ifndef QT_NO_ACCESSIBILITY - , public QAccessible::ActivationObserver -#endif // QT_NO_ACCESSIBILITY , public content::TextInputManager::Observer { public: @@ -209,9 +205,6 @@ public: // Overridden from content::BrowserAccessibilityDelegate content::BrowserAccessibilityManager* CreateBrowserAccessibilityManager(content::BrowserAccessibilityDelegate* delegate, bool for_root_frame) override; -#ifndef QT_NO_ACCESSIBILITY - void accessibilityActiveChanged(bool active) override; -#endif // QT_NO_ACCESSIBILITY LoadVisuallyCommittedState getLoadVisuallyCommittedState() const { return m_loadVisuallyCommittedState; } void setLoadVisuallyCommittedState(LoadVisuallyCommittedState state) { m_loadVisuallyCommittedState = state; } diff --git a/src/core/visited_links_manager_qt.cpp b/src/core/visited_links_manager_qt.cpp index ac27446b8..d4885e8e8 100644 --- a/src/core/visited_links_manager_qt.cpp +++ b/src/core/visited_links_manager_qt.cpp @@ -39,7 +39,6 @@ #include "visited_links_manager_qt.h" -#include "profile_adapter.h" #include "content_browser_client_qt.h" #include "profile_qt.h" #include "type_conversion.h" @@ -106,14 +105,13 @@ static void ensureDirectoryExists(const base::FilePath &path) errorstr.c_str()); } -VisitedLinksManagerQt::VisitedLinksManagerQt(ProfileAdapter *adapter) +VisitedLinksManagerQt::VisitedLinksManagerQt(ProfileQt *profile, bool persistVisitedLinks) : m_delegate(new VisitedLinkDelegateQt) { - Q_ASSERT(adapter && adapter->profile()); - ProfileQt *profile = adapter->profile(); - if (adapter->persistVisitedLinks()) + Q_ASSERT(profile); + if (persistVisitedLinks) ensureDirectoryExists(profile->GetPath()); - m_visitedLinkMaster.reset(new visitedlink::VisitedLinkMaster(profile, m_delegate.data(), adapter->persistVisitedLinks())); + m_visitedLinkMaster.reset(new visitedlink::VisitedLinkMaster(profile, m_delegate.data(), persistVisitedLinks)); m_visitedLinkMaster->Init(); } diff --git a/src/core/visited_links_manager_qt.h b/src/core/visited_links_manager_qt.h index 8d9a7495b..5bbcc5983 100644 --- a/src/core/visited_links_manager_qt.h +++ b/src/core/visited_links_manager_qt.h @@ -67,14 +67,14 @@ class GURL; namespace QtWebEngineCore { -class ProfileAdapter; +class ProfileQt; class VisitedLinkDelegateQt; class QWEBENGINECORE_PRIVATE_EXPORT VisitedLinksManagerQt { public: virtual~VisitedLinksManagerQt(); - VisitedLinksManagerQt(ProfileAdapter *profileAdapter); + VisitedLinksManagerQt(ProfileQt *profile, bool persistVisitedLinks); void deleteAllVisitedLinkData(); void deleteVisitedLinkDataForUrls(const QList<QUrl> &); diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 21540f5da..0c29b3525 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -210,10 +210,10 @@ static void callbackOnEvaluateJS(WebContentsAdapterClient *adapterClient, quint6 #if QT_CONFIG(webengine_printing_and_pdf) static void callbackOnPrintingFinished(WebContentsAdapterClient *adapterClient, int requestId, - const std::vector<char>& result) + QSharedPointer<QByteArray> result) { if (requestId) - adapterClient->didPrintPage(requestId, QByteArray(result.data(), result.size())); + adapterClient->didPrintPage(requestId, result); } static void callbackOnPdfSavingFinished(WebContentsAdapterClient *adapterClient, diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 55cbe13dd..f9d0c0781 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -66,6 +66,7 @@ QT_FORWARD_DECLARE_CLASS(QKeyEvent) QT_FORWARD_DECLARE_CLASS(QVariant) QT_FORWARD_DECLARE_CLASS(QWebEngineQuotaRequest) QT_FORWARD_DECLARE_CLASS(QWebEngineRegisterProtocolHandlerRequest) +QT_FORWARD_DECLARE_CLASS(QWebEngineUrlRequestInfo) namespace content { struct DropData; @@ -444,7 +445,7 @@ public: virtual void didFetchDocumentMarkup(quint64 requestId, const QString& result) = 0; virtual void didFetchDocumentInnerText(quint64 requestId, const QString& result) = 0; virtual void didFindText(quint64 requestId, int matchCount) = 0; - virtual void didPrintPage(quint64 requestId, const QByteArray &result) = 0; + virtual void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) = 0; virtual void didPrintPageToPdf(const QString &filePath, bool success) = 0; virtual void passOnFocus(bool reverse) = 0; // returns the last QObject (QWidget/QQuickItem) based object in the accessibility @@ -475,6 +476,7 @@ public: virtual ClientType clientType() = 0; virtual void printRequested() = 0; virtual void widgetChanged(RenderWidgetHostViewQtDelegate *newWidget) = 0; + virtual void interceptRequest(QWebEngineUrlRequestInfo &) { } virtual ProfileAdapter *profileAdapter() = 0; virtual WebContentsAdapter* webContentsAdapter() = 0; diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index aee5be42f..e988fa23c 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -54,10 +54,6 @@ #include "components/viz/common/features.h" #include "components/web_cache/browser/web_cache_manager.h" #include "content/browser/devtools/devtools_http_handler.h" -#include "content/browser/gpu/gpu_main_thread_factory.h" -#include "content/browser/renderer_host/render_process_host_impl.h" -#include "content/browser/utility_process_host.h" -#include "content/gpu/in_process_gpu_thread.h" #include "content/public/app/content_main.h" #include "content/public/app/content_main_runner.h" #include "content/public/browser/browser_main_runner.h" @@ -67,9 +63,8 @@ #include "content/public/common/content_paths.h" #include "content/public/common/content_switches.h" #include "content/public/common/main_function_params.h" -#include "content/renderer/in_process_renderer_thread.h" -#include "content/utility/in_process_utility_thread.h" #include "gpu/command_buffer/service/gpu_switches.h" +#include "gpu/command_buffer/service/sync_point_manager.h" #include "gpu/ipc/host/gpu_switches.h" #include "media/audio/audio_manager.h" #include "mojo/core/embedder/embedder.h" @@ -85,16 +80,19 @@ #include "content/public/app/sandbox_helper_win.h" #endif // OS_WIN +#ifndef QT_NO_ACCESSIBILITY +#include "accessibility_activation_observer.h" +#endif #include "api/qwebengineurlscheme.h" -#include "profile_adapter.h" #include "content_browser_client_qt.h" #include "content_client_qt.h" #include "content_main_delegate_qt.h" #include "devtools_manager_delegate_qt.h" #include "media_capture_devices_dispatcher.h" #include "net/webui_controller_factory_qt.h" -#include "type_conversion.h" #include "ozone/gl_context_qt.h" +#include "profile_adapter.h" +#include "type_conversion.h" #include "web_engine_library_info.h" #include <QFileInfo> @@ -214,6 +212,12 @@ void WebEngineContext::destroy() { if (m_devtoolsServer) m_devtoolsServer->stop(); + + // Normally the GPU thread is shut down when the GpuProcessHost is destroyed + // on IO thread (triggered by ~BrowserMainRunner). But by that time the UI + // task runner is not working anymore so we need to do this earlier. + destroyGpuProcess(); + base::MessagePump::Delegate *delegate = static_cast<base::MessageLoop *>(m_runLoop->delegate_); // Flush the UI message loop before quitting. @@ -269,6 +273,8 @@ WebEngineContext *WebEngineContext::current() qAddPostRoutine(WebEngineContext::destroyContextPostRoutine); // Add a false reference so there is no race between unreferencing m_handle and a global QApplication. m_handle->AddRef(); + // This is need since gpu process start requires sync point manager; + m_handle->initialize(); } return m_handle.get(); } @@ -327,6 +333,10 @@ static void appendToFeatureSwitch(base::CommandLine *commandLine, const char *fe WebEngineContext::WebEngineContext() : m_mainDelegate(new ContentMainDelegateQt) , m_globalQObject(new QObject()) + , m_syncPointManager(new gpu::SyncPointManager()) +{} + +void WebEngineContext::initialize() { base::TaskScheduler::Create("Browser"); m_contentRunner.reset(content::ContentMainRunner::Create()); @@ -414,10 +424,23 @@ WebEngineContext::WebEngineContext() // an OpenGL Core Profile context. If the switch is not set, it would always try to create a // Core Profile context, even if Qt uses a legacy profile, which causes // "Could not share GL contexts" warnings, because it's not possible to share between Core and - // legacy profiles. - // Given that Core profile is not currently supported on Windows anyway, pass this switch to - // get rid of the warnings. - parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); + // legacy profiles. See GLContextWGL::Initialize(). + // Given that Desktop GL Core profile is not currently supported on Windows anyway, pass this + // switch to get rid of the warnings. + // + // The switch is also used to determine which version of OpenGL ES to use (2 or 3) when using + // ANGLE. + // If the switch is not set, Chromium will always try to create an ES3 context, even if Qt uses + // an ES2 context, which causes resource sharing issues (black screen), + // see gpu::gles2::GenerateGLContextAttribs(). + // Make sure to disable ES3 context creation when using ES2. + const bool isGLES2Context = qt_gl_global_share_context() + && qt_gl_global_share_context()->isOpenGLES() + && qt_gl_global_share_context()->format().majorVersion() == 2; + const bool isDesktopGLOrSoftware = !usingANGLE(); + + if (isDesktopGLOrSoftware || isGLES2Context) + parsedCommandLine->AppendSwitch(switches::kDisableES3GLContext); #endif // Needed to allow navigations within pages that were set using setHtml(). One example is // tst_QWebEnginePage::acceptNavigationRequest. @@ -450,8 +473,7 @@ WebEngineContext::WebEngineContext() #ifndef QT_NO_OPENGL bool tryGL = - !usingANGLE() - && (!usingSoftwareDynamicGL() + (!usingSoftwareDynamicGL() // If user requested WebGL support instead of using Skia rendering to // bitmaps, use software rendering via software OpenGL. This might be less // performant, but at least provides WebGL support. @@ -461,10 +483,13 @@ WebEngineContext::WebEngineContext() if (tryGL) { if (qt_gl_global_share_context() && qt_gl_global_share_context()->isValid()) { - // If the native handle is QEGLNativeContext try to use GL ES/2, if there is no native handle - // assume we are using wayland and try GL ES/2, and finally Ozone demands GL ES/2 too. + // If the native handle is QEGLNativeContext try to use GL ES/2. + // If there is no native handle, assume we are using wayland and try GL ES/2. + // If we are using ANGLE on Windows, use OpenGL ES (2 or 3). if (qt_gl_global_share_context()->nativeHandle().isNull() - || !strcmp(qt_gl_global_share_context()->nativeHandle().typeName(), "QEGLNativeContext")) + || !strcmp(qt_gl_global_share_context()->nativeHandle().typeName(), + "QEGLNativeContext") + || usingANGLE()) { if (qt_gl_global_share_context()->isOpenGLES()) { glType = gl::kGLImplementationEGLName; @@ -530,9 +555,11 @@ WebEngineContext::WebEngineContext() parsedCommandLine->AppendSwitch(switches::kDisableGpu); } - content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread); - content::RenderProcessHostImpl::RegisterRendererMainThreadFactory(content::CreateInProcessRendererThread); - content::RegisterGpuMainThreadFactory(content::CreateInProcessGpuThread); + bool threadedGpu = true; +#ifndef QT_NO_OPENGL + threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); +#endif + registerMainThreadFactories(threadedGpu); mojo::core::Init(); @@ -584,6 +611,10 @@ WebEngineContext::WebEngineContext() m_printJobManager.reset(new printing::PrintJobManager()); #endif +#ifndef QT_NO_ACCESSIBILITY + m_accessibilityActivationObserver.reset(new AccessibilityActivationObserver()); +#endif + content::WebUIControllerFactory::RegisterFactory(WebUIControllerFactoryQt::GetInstance()); } @@ -593,4 +624,10 @@ printing::PrintJobManager* WebEngineContext::getPrintJobManager() return m_printJobManager.get(); } #endif + +gpu::SyncPointManager *WebEngineContext::syncPointManager() +{ + return m_syncPointManager.get(); +} + } // namespace diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h index 604c85a61..b9af38153 100644 --- a/src/core/web_engine_context.h +++ b/src/core/web_engine_context.h @@ -52,6 +52,14 @@ class RunLoop; namespace content { class BrowserMainRunner; class ContentMainRunner; +class GpuProcess; +class GpuThreadController; +class InProcessChildThreadParams; +} + +namespace gpu { +struct GpuPreferences; +class SyncPointManager; } #if QT_CONFIG(webengine_printing_and_pdf) @@ -64,9 +72,10 @@ QT_FORWARD_DECLARE_CLASS(QObject) namespace QtWebEngineCore { -class ProfileAdapter; +class AccessibilityActivationObserver; class ContentMainDelegateQt; class DevToolsServerQt; +class ProfileAdapter; bool usingSoftwareDynamicGL(); @@ -82,17 +91,23 @@ public: #if QT_CONFIG(webengine_printing_and_pdf) printing::PrintJobManager* getPrintJobManager(); #endif + void initialize(); void destroyProfileAdapter(); void addProfileAdapter(ProfileAdapter *profileAdapter); void removeProfileAdapter(ProfileAdapter *profileAdapter); void destroy(); + gpu::SyncPointManager *syncPointManager(); + private: friend class base::RefCounted<WebEngineContext>; friend class ProfileAdapter; WebEngineContext(); ~WebEngineContext(); + static void registerMainThreadFactories(bool threaded); + static void destroyGpuProcess(); + std::unique_ptr<base::RunLoop> m_runLoop; std::unique_ptr<ContentMainDelegateQt> m_mainDelegate; std::unique_ptr<content::ContentMainRunner> m_contentRunner; @@ -100,7 +115,11 @@ private: std::unique_ptr<QObject> m_globalQObject; std::unique_ptr<ProfileAdapter> m_defaultProfileAdapter; std::unique_ptr<DevToolsServerQt> m_devtoolsServer; + std::unique_ptr<gpu::SyncPointManager> m_syncPointManager; QVector<ProfileAdapter*> m_profileAdapters; +#ifndef QT_NO_ACCESSIBILITY + std::unique_ptr<AccessibilityActivationObserver> m_accessibilityActivationObserver; +#endif #if QT_CONFIG(webengine_printing_and_pdf) std::unique_ptr<printing::PrintJobManager> m_printJobManager; diff --git a/src/core/web_engine_context_threads.cpp b/src/core/web_engine_context_threads.cpp new file mode 100644 index 000000000..3e5d47b92 --- /dev/null +++ b/src/core/web_engine_context_threads.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "web_engine_context.h" + +#include "base/bind.h" +#include "base/threading/platform_thread.h" +#include "base/threading/thread_restrictions.h" +#include "content/browser/gpu/gpu_main_thread_factory.h" +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/utility_process_host.h" +#include "content/gpu/gpu_child_thread.h" +#include "content/gpu/gpu_process.h" +#include "content/gpu/in_process_gpu_thread.h" +#include "content/renderer/in_process_renderer_thread.h" +#include "content/utility/in_process_utility_thread.h" + +#include <memory> + +namespace QtWebEngineCore { + +struct GpuThreadControllerQt : content::GpuThreadController +{ + GpuThreadControllerQt(const content::InProcessChildThreadParams ¶ms, const gpu::GpuPreferences &gpuPreferences) + { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&GpuThreadControllerQt::createGpuProcess, params, gpuPreferences)); + } + ~GpuThreadControllerQt() override + { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, + base::BindOnce(&GpuThreadControllerQt::destroyGpuProcess)); + } + + static void createGpuProcess( + const content::InProcessChildThreadParams ¶ms, + const gpu::GpuPreferences &gpuPreferences) + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (s_gpuProcessDestroyed) + return; + + s_gpuProcess = std::make_unique<content::GpuProcess>(base::ThreadPriority::NORMAL); + auto gpuInit = std::make_unique<gpu::GpuInit>(); + gpuInit->InitializeInProcess(base::CommandLine::ForCurrentProcess(), gpuPreferences); + auto childThread = new content::GpuChildThread(params, std::move(gpuInit)); + childThread->Init(base::Time::Now()); + s_gpuProcess->set_main_thread(childThread); + } + + static void destroyGpuProcess() + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (s_gpuProcessDestroyed) + return; + + // viz::GpuServiceImpl::~GpuServiceImpl waits for io task. + base::ScopedAllowBaseSyncPrimitivesForTesting allow; + s_gpuProcess.reset(); + s_gpuProcessDestroyed = true; + } + + static std::unique_ptr<content::GpuProcess> s_gpuProcess; + static bool s_gpuProcessDestroyed; +}; + +std::unique_ptr<content::GpuProcess> GpuThreadControllerQt::s_gpuProcess; +bool GpuThreadControllerQt::s_gpuProcessDestroyed = false; + +static std::unique_ptr<content::GpuThreadController> createGpuThreadController( + const content::InProcessChildThreadParams ¶ms, + const gpu::GpuPreferences &gpuPreferences) +{ + return std::make_unique<GpuThreadControllerQt>(params, gpuPreferences); +} + +// static +void WebEngineContext::destroyGpuProcess() +{ + GpuThreadControllerQt::destroyGpuProcess(); +} + +// static +void WebEngineContext::registerMainThreadFactories(bool threaded) +{ + content::UtilityProcessHost::RegisterUtilityMainThreadFactory(content::CreateInProcessUtilityThread); + content::RenderProcessHostImpl::RegisterRendererMainThreadFactory(content::CreateInProcessRendererThread); + if (threaded) + content::RegisterGpuMainThreadFactory(content::CreateInProcessGpuThread); + else + content::RegisterGpuMainThreadFactory(createGpuThreadController); +} + +} // namespace QtWebEngineCore diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp index fc6287dd9..339a3ef0e 100644 --- a/src/core/web_event_factory.cpp +++ b/src/core/web_event_factory.cpp @@ -101,7 +101,7 @@ static KeyboardDriver keyboardDriverImpl() if (platformName == QLatin1Literal("xcb") || platformName == QLatin1Literal("wayland")) return KeyboardDriver::Xkb; -#if QT_CONFIG(libinput) && QT_CONFIG(xkbcommon) +#if QT_CONFIG(libinput) // Based on QEglFSIntegration::createInputHandlers and QLibInputKeyboard::processKey. if (platformName == QLatin1Literal("eglfs") && !qEnvironmentVariableIntValue("QT_QPA_EGLFS_NO_LIBINPUT")) return KeyboardDriver::Xkb; diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 32fce325b..6698a9736 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -qtHaveModule(designer):qtHaveModule(webenginewidgets): SUBDIRS += qwebengineview +qtHaveModule(designer): SUBDIRS += qwebengineview diff --git a/src/src.pro b/src/src.pro index 218cdb66d..30562686a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -14,11 +14,7 @@ core.depends = buildtools SUBDIRS += buildtools \ core \ - process \ - webengine \ - webengine_plugin \ - plugins - + process qtConfig(webengine-spellchecker):!qtConfig(webengine-native-spellchecker):!cross_compile { SUBDIRS += qwebengine_convert_dict @@ -26,19 +22,11 @@ qtConfig(webengine-spellchecker):!qtConfig(webengine-native-spellchecker):!cross qwebengine_convert_dict.depends = core } -qtConfig(webengine-testsupport) { - webengine_testsupport_plugin.subdir = webengine/plugin/testsupport - webengine_testsupport_plugin.target = sub-webengine-testsupport-plugin - webengine_testsupport_plugin.depends = webengine - SUBDIRS += webengine_testsupport_plugin -} - -qtConfig(webengine-ui-delegates) { - SUBDIRS += webengine/ui \ - webengine/ui2 +qtConfig(webengine-qml) { + SUBDIRS += webengine } -qtHaveModule(widgets) { - SUBDIRS += webenginewidgets +qtConfig(webengine-widgets) { + SUBDIRS += plugins webenginewidgets plugins.depends = webenginewidgets } diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index ddc71602b..1d93c5070 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -279,6 +279,12 @@ void QQuickWebEngineProfilePrivate::downloadUpdated(const DownloadItemInfo &info } } +void QQuickWebEngineProfilePrivate::useForGlobalCertificateVerificationChanged() +{ + Q_Q(QQuickWebEngineProfile); + Q_EMIT q->useForGlobalCertificateVerificationChanged(); +} + void QQuickWebEngineProfilePrivate::userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script) { Q_ASSERT(p && p->data); @@ -789,6 +795,61 @@ bool QQuickWebEngineProfile::isSpellCheckEnabled() const } /*! + \property QQuickWebEngineProfile::useForGlobalCertificateVerification + \since 5.13 + + This property holds whether this profile is used for downloading and + caching during global certificate verification when using the online + certificate status protocol (OCSP), certificate revokation lists (CRLs), + and authority information access (AIA), for example. + + As long as one profile has this option enabled, all other profiles will be + able to use it for certificate verification. Only one profile at a time can + have this option enabled. It is recommended that the profile has a disk HTTP + cache to avoid needlessly re-downloading. + + By default, no profile has this property enabled. + + Currently, only affects Linux/NSS installations, where having a profile with + this role enables OCSP. +*/ + +/*! + \qmlproperty bool WebEngineProfile::useForGlobalCertificateVerification + \since QtWebEngine 1.9 + + This property holds whether this profile is used for downloading and + caching during global certificate verification when using the online + certificate status protocol (OCSP), certificate revokation lists (CRLs), + and authority information access (AIA), for example. + + As long as one profile has this option enabled, all other profiles will be + able to use it for certificate verification. Only one profile at a time can + have this option enabled. It is recommended that the profile has a disk HTTP + cache to avoid needlessly re-downloading. + + By default, no profile has this property enabled. + + Currently, only affects Linux/NSS installations, where having a profile with + this role enables OCSP. +*/ + +void QQuickWebEngineProfile::setUseForGlobalCertificateVerification(bool enable) +{ + Q_D(QQuickWebEngineProfile); + if (enable != d->profileAdapter()->isUsedForGlobalCertificateVerification()) { + d->profileAdapter()->setUseForGlobalCertificateVerification(enable); + emit useForGlobalCertificateVerificationChanged(); + } +} + +bool QQuickWebEngineProfile::isUsedForGlobalCertificateVerification() const +{ + const Q_D(QQuickWebEngineProfile); + return d->profileAdapter()->isUsedForGlobalCertificateVerification(); +} + +/*! Returns the cookie store for this profile. */ @@ -840,20 +901,7 @@ void QQuickWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestIntercept const QWebEngineUrlSchemeHandler *QQuickWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QQuickWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet<QByteArray> internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->urlSchemeHandler(scheme); } /*! @@ -865,25 +913,7 @@ static bool checkInternalScheme(const QByteArray &scheme) void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { Q_D(QQuickWebEngineProfile); - Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); - return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); - connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->installUrlSchemeHandler(scheme, handler); } /*! @@ -894,10 +924,7 @@ void QQuickWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, Q void QQuickWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { Q_D(QQuickWebEngineProfile); - Q_ASSERT(handler); - if (!d->profileAdapter()->removeCustomUrlSchemeHandler(handler)) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlSchemeHandler(handler); } /*! @@ -908,10 +935,7 @@ void QQuickWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler * void QQuickWebEngineProfile::removeUrlScheme(const QByteArray &scheme) { Q_D(QQuickWebEngineProfile); - QWebEngineUrlSchemeHandler *handler = d->profileAdapter()->takeCustomUrlSchemeHandler(scheme); - if (!handler) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlScheme(scheme); } /*! @@ -920,12 +944,7 @@ void QQuickWebEngineProfile::removeUrlScheme(const QByteArray &scheme) void QQuickWebEngineProfile::removeAllUrlSchemeHandlers() { Q_D(QQuickWebEngineProfile); - d->profileAdapter()->clearCustomUrlSchemeHandlers(); -} - -void QQuickWebEngineProfile::destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj) -{ - removeUrlSchemeHandler(obj); + d->profileAdapter()->removeAllUrlSchemeHandlers(); } QQuickWebEngineSettings *QQuickWebEngineProfile::settings() const diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h index 9fc4f9eca..1a3abe044 100644 --- a/src/webengine/api/qquickwebengineprofile.h +++ b/src/webengine/api/qquickwebengineprofile.h @@ -72,6 +72,11 @@ class Q_WEBENGINE_EXPORT QQuickWebEngineProfile : public QObject { Q_PROPERTY(QStringList spellCheckLanguages READ spellCheckLanguages WRITE setSpellCheckLanguages NOTIFY spellCheckLanguagesChanged FINAL REVISION 3) Q_PROPERTY(bool spellCheckEnabled READ isSpellCheckEnabled WRITE setSpellCheckEnabled NOTIFY spellCheckEnabledChanged FINAL REVISION 3) Q_PROPERTY(QQmlListProperty<QQuickWebEngineScript> userScripts READ userScripts FINAL REVISION 4) + Q_PROPERTY(bool useForGlobalCertificateVerification + READ isUsedForGlobalCertificateVerification + WRITE setUseForGlobalCertificateVerification + NOTIFY useForGlobalCertificateVerificationChanged + FINAL REVISION 5) public: QQuickWebEngineProfile(QObject *parent = Q_NULLPTR); @@ -137,6 +142,9 @@ public: QQmlListProperty<QQuickWebEngineScript> userScripts(); + void setUseForGlobalCertificateVerification(bool b); + bool isUsedForGlobalCertificateVerification() const; + static QQuickWebEngineProfile *defaultProfile(); Q_SIGNALS: @@ -151,13 +159,11 @@ Q_SIGNALS: Q_REVISION(1) void httpAcceptLanguageChanged(); Q_REVISION(3) void spellCheckLanguagesChanged(); Q_REVISION(3) void spellCheckEnabledChanged(); + Q_REVISION(5) void useForGlobalCertificateVerificationChanged(); void downloadRequested(QQuickWebEngineDownloadItem *download); void downloadFinished(QQuickWebEngineDownloadItem *download); -private Q_SLOTS: - void destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj); - private: Q_DECLARE_PRIVATE(QQuickWebEngineProfile) QQuickWebEngineProfile(QQuickWebEngineProfilePrivate *, QObject *parent = Q_NULLPTR); diff --git a/src/webengine/api/qquickwebengineprofile_p.h b/src/webengine/api/qquickwebengineprofile_p.h index d31ded0ec..d59470f46 100644 --- a/src/webengine/api/qquickwebengineprofile_p.h +++ b/src/webengine/api/qquickwebengineprofile_p.h @@ -83,6 +83,8 @@ public: void downloadRequested(DownloadItemInfo &info) override; void downloadUpdated(const DownloadItemInfo &info) override; + void useForGlobalCertificateVerificationChanged() override; + // QQmlListPropertyHelpers static void userScripts_append(QQmlListProperty<QQuickWebEngineScript> *p, QQuickWebEngineScript *script); static int userScripts_count(QQmlListProperty<QQuickWebEngineScript> *p); diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp index b9ae06aeb..353f55974 100644 --- a/src/webengine/api/qquickwebengineview.cpp +++ b/src/webengine/api/qquickwebengineview.cpp @@ -39,7 +39,6 @@ #include "qquickwebengineview_p.h" #include "qquickwebengineview_p_p.h" -#include "qtwebenginecoreglobal_p.h" #include "authentication_dialog_controller.h" #include "profile_adapter.h" #include "certificate_error_controller.h" @@ -1130,12 +1129,12 @@ void QQuickWebEngineViewPrivate::didFindText(quint64 requestId, int matchCount) callback.call(args); } -void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, const QByteArray &result) +void QQuickWebEngineViewPrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { Q_Q(QQuickWebEngineView); QJSValue callback = m_callbacks.take(requestId); QJSValueList args; - args.append(qmlEngine(q)->toScriptValue(result)); + args.append(qmlEngine(q)->toScriptValue(*(result.data()))); callback.call(args); } diff --git a/src/webengine/api/qquickwebengineview_p.h b/src/webengine/api/qquickwebengineview_p.h index ae92b6df0..2528be3f8 100644 --- a/src/webengine/api/qquickwebengineview_p.h +++ b/src/webengine/api/qquickwebengineview_p.h @@ -51,7 +51,6 @@ // We mean it. // -#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> #include <QtWebEngine/private/qtwebengineglobal_p.h> #include "qquickwebenginescript.h" #include <QQuickItem> diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h index cbba9b568..88a670867 100644 --- a/src/webengine/api/qquickwebengineview_p_p.h +++ b/src/webengine/api/qquickwebengineview_p_p.h @@ -128,7 +128,7 @@ public: void didFetchDocumentMarkup(quint64, const QString&) override { } void didFetchDocumentInnerText(quint64, const QString&) override { } void didFindText(quint64, int) override; - void didPrintPage(quint64 requestId, const QByteArray &result) override; + void didPrintPage(quint64 requestId, QSharedPointer<QByteArray>) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void passOnFocus(bool reverse) override; void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override; diff --git a/src/webengine/api/qtwebengineglobal.h b/src/webengine/api/qtwebengineglobal.h index 2d83be674..c2b33778a 100644 --- a/src/webengine/api/qtwebengineglobal.h +++ b/src/webengine/api/qtwebengineglobal.h @@ -41,6 +41,7 @@ #define QTWEBENGINEGLOBAL_H #include <QtCore/qglobal.h> +#include <QtWebEngine/qtwebengine-config.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/api/qtwebengineglobal_p.h b/src/webengine/api/qtwebengineglobal_p.h index 7058bef09..2d30f75b0 100644 --- a/src/webengine/api/qtwebengineglobal_p.h +++ b/src/webengine/api/qtwebengineglobal_p.h @@ -51,7 +51,9 @@ // We mean it. // -#include "qtwebengineglobal.h" +#include <QtWebEngine/qtwebengineglobal.h> +#include <QtCore/private/qglobal_p.h> +#include <QtWebEngine/private/qtwebengine-config_p.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/configure.json b/src/webengine/configure.json new file mode 100644 index 000000000..ec5ad34d9 --- /dev/null +++ b/src/webengine/configure.json @@ -0,0 +1,29 @@ +{ + "module": "webengine", + "depends": [ + "webenginecore-private" + ], + "features": { + "webengine-ui-delegates": { + "label": "UI Delegates", + "section": "WebEngine", + "output": [ "privateFeature" ] + }, + "webengine-testsupport": { + "label": "Test Support", + "autoDetect": "features.private_tests || call.isTestsInBuildParts", + "output": [ "privateFeature" ] + } + }, + "summary": [ + { + "section": "Qt WebEngineQml", + "condition": "features.webengine-qml", + "entries": [ + "webengine-ui-delegates", + "webengine-testsupport" + ] + } + ] +} + diff --git a/src/webengine/module.pro b/src/webengine/module.pro new file mode 100644 index 000000000..b74d63d93 --- /dev/null +++ b/src/webengine/module.pro @@ -0,0 +1,90 @@ +include($$QTWEBENGINE_OUT_ROOT/src/webengine/qtwebengine-config.pri) +QT_FOR_CONFIG += webengine-private + +TARGET = QtWebEngine +MODULE = webengine + +# For our export macros +DEFINES += QT_BUILD_WEBENGINE_LIB + +QT += qml quick webenginecore +QT_PRIVATE += quick-private gui-private core-private webenginecore-private + +QMAKE_DOCS = $$PWD/doc/qtwebengine.qdocconf + +INCLUDEPATH += $$PWD api ../core ../core/api + +SOURCES = \ + api/qquickwebengineaction.cpp \ + api/qquickwebenginecertificateerror.cpp \ + api/qquickwebenginecontextmenurequest.cpp \ + api/qquickwebenginedialogrequests.cpp \ + api/qquickwebenginedownloaditem.cpp \ + api/qquickwebenginehistory.cpp \ + api/qquickwebenginefaviconprovider.cpp \ + api/qquickwebengineloadrequest.cpp \ + api/qquickwebenginenavigationrequest.cpp \ + api/qquickwebenginenewviewrequest.cpp \ + api/qquickwebengineprofile.cpp \ + api/qquickwebenginescript.cpp \ + api/qquickwebenginesettings.cpp \ + api/qquickwebenginesingleton.cpp \ + api/qquickwebengineview.cpp \ + api/qtwebengineglobal.cpp \ + render_widget_host_view_qt_delegate_quick.cpp \ + render_widget_host_view_qt_delegate_quickwindow.cpp \ + ui_delegates_manager.cpp + +HEADERS = \ + api/qtwebengineglobal.h \ + api/qtwebengineglobal_p.h \ + api/qquickwebengineaction_p.h \ + api/qquickwebengineaction_p_p.h \ + api/qquickwebenginecertificateerror_p.h \ + api/qquickwebenginecontextmenurequest_p.h \ + api/qquickwebenginedialogrequests_p.h \ + api/qquickwebenginedownloaditem_p.h \ + api/qquickwebenginedownloaditem_p_p.h \ + api/qquickwebenginehistory_p.h \ + api/qquickwebenginefaviconprovider_p_p.h \ + api/qquickwebengineloadrequest_p.h \ + api/qquickwebenginenavigationrequest_p.h \ + api/qquickwebenginenewviewrequest_p.h \ + api/qquickwebengineprofile.h \ + api/qquickwebengineprofile_p.h \ + api/qquickwebenginescript.h \ + api/qquickwebenginescript_p.h \ + api/qquickwebenginesettings_p.h \ + api/qquickwebenginesingleton_p.h \ + api/qquickwebengineview_p.h \ + api/qquickwebengineview_p_p.h \ + render_widget_host_view_qt_delegate_quick.h \ + render_widget_host_view_qt_delegate_quickwindow.h \ + ui_delegates_manager.h + +qtConfig(webengine-testsupport) { + QT_PRIVATE += testlib + SOURCES += api/qquickwebenginetestsupport.cpp + HEADERS += api/qquickwebenginetestsupport_p.h +} + +!build_pass { + python = $$pythonPathForShell() + chromium_attributions.commands = \ + cd $$shell_quote($$shell_path($$PWD/../3rdparty)) && \ + $$python chromium/tools/licenses.py \ + --file-template ../../tools/about_credits.tmpl \ + --entry-template ../../tools/about_credits_entry.tmpl credits \ + $$shell_quote($$shell_path($$OUT_PWD/chromium_attributions.qdoc)) + chromium_attributions.CONFIG += phony + + QMAKE_EXTRA_TARGETS += chromium_attributions + + prepare_docs { + prepare_docs.depends += chromium_attributions + } else { + html_docs.depends += chromium_attributions + } +} + +load(qt_module) diff --git a/src/webengine/plugin/plugin.cpp b/src/webengine/plugin/plugin.cpp index 84a12c930..9fab3e9ce 100644 --- a/src/webengine/plugin/plugin.cpp +++ b/src/webengine/plugin/plugin.cpp @@ -40,22 +40,21 @@ #include <QtQml/qqmlextensionplugin.h> #include <QtWebEngine/QQuickWebEngineProfile> -#include "qquickwebenginecertificateerror_p.h" -#include "qquickwebenginecontextmenurequest_p.h" -#include "qquickwebenginedialogrequests_p.h" -#include "qquickwebenginedownloaditem_p.h" -#include "qquickwebenginehistory_p.h" -#include "qquickwebenginefaviconprovider_p_p.h" -#include "qquickwebengineloadrequest_p.h" -#include "qquickwebenginenavigationrequest_p.h" -#include "qquickwebenginenewviewrequest_p.h" -#include "qquickwebenginesettings_p.h" -#include "qquickwebenginesingleton_p.h" -#include "qquickwebengineview_p.h" -#include "qquickwebengineaction_p.h" -#include "qwebenginequotarequest.h" -#include "qwebengineregisterprotocolhandlerrequest.h" -#include "qtwebengineversion.h" +#include <QtWebEngine/private/qquickwebenginecertificateerror_p.h> +#include <QtWebEngine/private/qquickwebenginecontextmenurequest_p.h> +#include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> +#include <QtWebEngine/private/qquickwebenginedownloaditem_p.h> +#include <QtWebEngine/private/qquickwebenginehistory_p.h> +#include <QtWebEngine/private/qquickwebenginefaviconprovider_p_p.h> +#include <QtWebEngine/private/qquickwebengineloadrequest_p.h> +#include <QtWebEngine/private/qquickwebenginenavigationrequest_p.h> +#include <QtWebEngine/private/qquickwebenginenewviewrequest_p.h> +#include <QtWebEngine/private/qquickwebenginesettings_p.h> +#include <QtWebEngine/private/qquickwebenginesingleton_p.h> +#include <QtWebEngine/private/qquickwebengineview_p.h> +#include <QtWebEngine/private/qquickwebengineaction_p.h> +#include <QtWebEngineCore/qwebenginequotarequest.h> +#include <QtWebEngineCore/qwebengineregisterprotocolhandlerrequest.h> QT_BEGIN_NAMESPACE @@ -96,6 +95,7 @@ public: qmlRegisterType<QQuickWebEngineProfile, 2>(uri, 1, 3, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 3>(uri, 1, 4, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineProfile, 4>(uri, 1, 5, "WebEngineProfile"); + qmlRegisterType<QQuickWebEngineProfile, 5>(uri, 1, 9, "WebEngineProfile"); qmlRegisterType<QQuickWebEngineScript>(uri, 1, 1, "WebEngineScript"); qmlRegisterUncreatableType<QQuickWebEngineCertificateError>(uri, 1, 1, "WebEngineCertificateError", msgUncreatableType("WebEngineCertificateError")); qmlRegisterUncreatableType<QQuickWebEngineDownloadItem>(uri, 1, 1, "WebEngineDownloadItem", diff --git a/src/webengine/plugin/plugin.pro b/src/webengine/plugin/plugin.pro index b6652fa26..c3702e81d 100644 --- a/src/webengine/plugin/plugin.pro +++ b/src/webengine/plugin/plugin.pro @@ -3,11 +3,9 @@ TARGET = qtwebengineplugin TARGETPATH = QtWebEngine IMPORT_VERSION = 1.8 -QT += webengine qml quick +QT += qml quick QT_PRIVATE += core-private webenginecore-private webengine-private -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/core $$QTWEBENGINE_ROOT/src/core/api $$QTWEBENGINE_ROOT/src/webengine $$QTWEBENGINE_ROOT/src/webengine/api $$QTWEBENGINE_ROOT/include/QtWebEngine - SOURCES = plugin.cpp load(qml_plugin) diff --git a/src/webengine/plugin/testsupport/plugin.cpp b/src/webengine/testsupport/plugin.cpp index d5c43a859..7a1e73d8b 100644 --- a/src/webengine/plugin/testsupport/plugin.cpp +++ b/src/webengine/testsupport/plugin.cpp @@ -39,7 +39,7 @@ #include <QtQml> -#include "qquickwebenginetestsupport_p.h" +#include <QtWebEngine/private/qquickwebenginetestsupport_p.h> QT_BEGIN_NAMESPACE diff --git a/src/webengine/plugin/testsupport/qmldir b/src/webengine/testsupport/qmldir index 7fff80251..7fff80251 100644 --- a/src/webengine/plugin/testsupport/qmldir +++ b/src/webengine/testsupport/qmldir diff --git a/src/webengine/plugin/testsupport/testsupport.pro b/src/webengine/testsupport/testsupport.pro index 2804635f8..a24796675 100644 --- a/src/webengine/plugin/testsupport/testsupport.pro +++ b/src/webengine/testsupport/testsupport.pro @@ -3,11 +3,9 @@ TARGET = qtwebenginetestsupportplugin TARGETPATH = QtWebEngine/testsupport IMPORT_VERSION = 1.0 -QT += webengine qml quick +QT += qml quick QT_PRIVATE += webengine-private gui-private -INCLUDEPATH += $$QTWEBENGINE_ROOT/src/core $$QTWEBENGINE_ROOT/src/webengine $$QTWEBENGINE_ROOT/src/webengine/api - SOURCES = plugin.cpp load(qml_plugin) diff --git a/src/webengine/webengine.pro b/src/webengine/webengine.pro index 418ade9a8..23668229e 100644 --- a/src/webengine/webengine.pro +++ b/src/webengine/webengine.pro @@ -1,89 +1,19 @@ -include($$QTWEBENGINE_OUT_ROOT/src/core/qtwebenginecore-config.pri) # workaround for QTBUG-68093 -QT_FOR_CONFIG += webenginecore-private +TEMPLATE = subdirs -TARGET = QtWebEngine +qml_module.file = module.pro +qml_plugin.file = plugin/plugin.pro -# For our export macros -DEFINES += QT_BUILD_WEBENGINE_LIB +qml_plugin.depends = qml_module -QT += qml quick webenginecore -QT_PRIVATE += quick-private gui-private core-private webenginecore-private - -QMAKE_DOCS = $$PWD/doc/qtwebengine.qdocconf - -INCLUDEPATH += $$PWD api ../core ../core/api - -SOURCES = \ - api/qquickwebengineaction.cpp \ - api/qquickwebenginecertificateerror.cpp \ - api/qquickwebenginecontextmenurequest.cpp \ - api/qquickwebenginedialogrequests.cpp \ - api/qquickwebenginedownloaditem.cpp \ - api/qquickwebenginehistory.cpp \ - api/qquickwebenginefaviconprovider.cpp \ - api/qquickwebengineloadrequest.cpp \ - api/qquickwebenginenavigationrequest.cpp \ - api/qquickwebenginenewviewrequest.cpp \ - api/qquickwebengineprofile.cpp \ - api/qquickwebenginescript.cpp \ - api/qquickwebenginesettings.cpp \ - api/qquickwebenginesingleton.cpp \ - api/qquickwebengineview.cpp \ - api/qtwebengineglobal.cpp \ - render_widget_host_view_qt_delegate_quick.cpp \ - render_widget_host_view_qt_delegate_quickwindow.cpp \ - ui_delegates_manager.cpp - -HEADERS = \ - api/qtwebengineglobal.h \ - api/qtwebengineglobal_p.h \ - api/qquickwebengineaction_p.h \ - api/qquickwebengineaction_p_p.h \ - api/qquickwebenginecertificateerror_p.h \ - api/qquickwebenginecontextmenurequest_p.h \ - api/qquickwebenginedialogrequests_p.h \ - api/qquickwebenginedownloaditem_p.h \ - api/qquickwebenginedownloaditem_p_p.h \ - api/qquickwebenginehistory_p.h \ - api/qquickwebenginefaviconprovider_p_p.h \ - api/qquickwebengineloadrequest_p.h \ - api/qquickwebenginenavigationrequest_p.h \ - api/qquickwebenginenewviewrequest_p.h \ - api/qquickwebengineprofile.h \ - api/qquickwebengineprofile_p.h \ - api/qquickwebenginescript.h \ - api/qquickwebenginescript_p.h \ - api/qquickwebenginesettings_p.h \ - api/qquickwebenginesingleton_p.h \ - api/qquickwebengineview_p.h \ - api/qquickwebengineview_p_p.h \ - render_widget_host_view_qt_delegate_quick.h \ - render_widget_host_view_qt_delegate_quickwindow.h \ - ui_delegates_manager.h +SUBDIRS += qml_module qml_plugin qtConfig(webengine-testsupport) { - QT_PRIVATE += testlib - SOURCES += api/qquickwebenginetestsupport.cpp - HEADERS += api/qquickwebenginetestsupport_p.h + testsupport_plugin.file = testsupport/testsupport.pro + testsupport_plugin.depends = qml_module + SUBDIRS += testsupport_plugin } -!build_pass { - python = $$pythonPathForShell() - chromium_attributions.commands = \ - cd $$shell_quote($$shell_path($$PWD/../3rdparty)) && \ - $$python chromium/tools/licenses.py \ - --file-template ../../tools/about_credits.tmpl \ - --entry-template ../../tools/about_credits_entry.tmpl credits \ - $$shell_quote($$shell_path($$OUT_PWD/chromium_attributions.qdoc)) - chromium_attributions.CONFIG += phony - - QMAKE_EXTRA_TARGETS += chromium_attributions - - prepare_docs { - prepare_docs.depends += chromium_attributions - } else { - html_docs.depends += chromium_attributions - } +qtConfig(webengine-ui-delegates) { + SUBDIRS += ui \ + ui2 } - -load(qt_module) diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 5da39e814..06a708672 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -48,7 +48,7 @@ #include "file_picker_controller.h" #include "javascript_dialog_controller.h" #if QT_CONFIG(webengine_printing_and_pdf) -#include "printing/pdfium_document_wrapper_qt.h" +#include "printer_worker.h" #endif #include "qwebenginecertificateerror.h" #include "qwebenginefullscreenrequest.h" @@ -94,6 +94,7 @@ #include <QMimeData> #if QT_CONFIG(webengine_printing_and_pdf) #include <QPrinter> +#include <QThread> #endif #include <QStandardPaths> #include <QStyle> @@ -106,89 +107,6 @@ using namespace QtWebEngineCore; static const int MaxTooltipLength = 1024; -#if QT_CONFIG(webengine_printing_and_pdf) -static bool printPdfDataOnPrinter(const QByteArray& data, QPrinter& printer) -{ - if (!data.size()) { - qWarning("Failure to print on printer %ls: Print result data is empty.", - qUtf16Printable(printer.printerName())); - return false; - } - - QSize pageSize = printer.pageRect().size(); - PdfiumDocumentWrapperQt pdfiumWrapper(data.constData(), data.size(), pageSize); - - int toPage = printer.toPage(); - int fromPage = printer.fromPage(); - bool ascendingOrder = true; - - if (fromPage == 0 && toPage == 0) { - fromPage = 1; - toPage = pdfiumWrapper.pageCount(); - } - fromPage = qMax(1, fromPage); - toPage = qMin(pdfiumWrapper.pageCount(), toPage); - - if (printer.pageOrder() == QPrinter::LastPageFirst) { - qSwap(fromPage, toPage); - ascendingOrder = false; - } - - int pageCopies = 1; - int documentCopies = 1; - - if (!printer.supportsMultipleCopies()) - documentCopies = printer.copyCount(); - - if (printer.collateCopies()) { - pageCopies = documentCopies; - documentCopies = 1; - } - - QPainter painter; - if (!painter.begin(&printer)) { - qWarning("Failure to print on printer %ls: Could not open printer for painting.", - qUtf16Printable(printer.printerName())); - return false; - } - - for (int printedDocuments = 0; printedDocuments < documentCopies; printedDocuments++) { - int currentPageIndex = fromPage; - while (true) { - for (int printedPages = 0; printedPages < pageCopies; printedPages++) { - if (printer.printerState() == QPrinter::Aborted - || printer.printerState() == QPrinter::Error) - return false; - - QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); - if (currentImage.isNull()) - return false; - - // Painting operations are automatically clipped to the bounds of the drawable part of the page. - painter.drawImage(QRect(0, 0, pageSize.width(), pageSize.height()), currentImage, currentImage.rect()); - if (printedPages < pageCopies - 1) - printer.newPage(); - } - - if (currentPageIndex == toPage) - break; - - if (ascendingOrder) - currentPageIndex++; - else - currentPageIndex--; - - printer.newPage(); - } - if (printedDocuments < documentCopies - 1) - printer.newPage(); - } - painter.end(); - - return true; -} -#endif // QT_CONFIG(webengine_printing_and_pdf) - static QWebEnginePage::WebWindowType toWindowType(WebContentsAdapterClient::WindowOpenDisposition disposition) { switch (disposition) { @@ -241,6 +159,7 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) , webChannelWorldId(QWebEngineScript::MainWorld) , defaultAudioMuted(false) , defaultZoomFactor(1.0) + , requestInterceptor(nullptr) #if QT_CONFIG(webengine_printing_and_pdf) , currentPrinter(nullptr) #endif @@ -260,6 +179,8 @@ QWebEnginePagePrivate::QWebEnginePagePrivate(QWebEngineProfile *_profile) QWebEnginePagePrivate::~QWebEnginePagePrivate() { + if (requestInterceptor) + profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); delete history; delete settings; } @@ -499,19 +420,35 @@ void QWebEnginePagePrivate::didFindText(quint64 requestId, int matchCount) m_callbacks.invoke(requestId, matchCount > 0); } -void QWebEnginePagePrivate::didPrintPage(quint64 requestId, const QByteArray &result) +void QWebEnginePagePrivate::didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) { #if QT_CONFIG(webengine_printing_and_pdf) + Q_Q(QWebEnginePage); + // If no currentPrinter is set that means that were printing to PDF only. if (!currentPrinter) { - m_callbacks.invoke(requestId, result); + if (!result.data()) + return; + m_callbacks.invoke(requestId, *(result.data())); return; } - bool printerResult = printPdfDataOnPrinter(result, *currentPrinter); + QThread *printerThread = new QThread; + QObject::connect(printerThread, &QThread::finished, printerThread, &QThread::deleteLater); + printerThread->start(); + + PrinterWorker *printerWorker = new PrinterWorker(result, currentPrinter); + QObject::connect(printerWorker, &PrinterWorker::resultReady, q, [=](bool success) { + currentPrinter = nullptr; + m_callbacks.invoke(requestId, success); + }); + + QObject::connect(printerWorker, &PrinterWorker::resultReady, printerThread, &QThread::quit); + QObject::connect(printerThread, &QThread::finished, printerWorker, &PrinterWorker::deleteLater); + + printerWorker->moveToThread(printerThread); + QMetaObject::invokeMethod(printerWorker, "print"); - currentPrinter = nullptr; - m_callbacks.invoke(requestId, printerResult); #else // we should never enter this branch, but just for safe-keeping... Q_UNUSED(result); @@ -1850,6 +1787,40 @@ void QWebEnginePagePrivate::printRequested() }); } +/*! + \since 5.13 + + Registers the request interceptor \a interceptor to intercept URL requests. + + The page does not take ownership of the pointer. This interceptor is called + after any interceptors on the profile, and unlike profile interceptors, is run + on the UI thread, making it thread-safer. Only URL requests from this page are + intercepted. + + To unset the request interceptor, set a \c nullptr. + + \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() +*/ + +void QWebEnginePage::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QWebEnginePage); + bool hadInterceptorChanged = bool(d->requestInterceptor) != bool(interceptor); + d->requestInterceptor = interceptor; + if (hadInterceptorChanged) { + if (interceptor) + d->profile->d_ptr->profileAdapter()->addPageRequestInterceptor(); + else + d->profile->d_ptr->profileAdapter()->removePageRequestInterceptor(); + } +} + +void QWebEnginePagePrivate::interceptRequest(QWebEngineUrlRequestInfo &info) +{ + if (requestInterceptor) + requestInterceptor->interceptRequest(info); +} + #if QT_CONFIG(menu) QMenu *QWebEnginePage::createStandardContextMenu() { @@ -2434,10 +2405,7 @@ void QWebEnginePage::printToPdf(const QWebEngineCallback<const QByteArray&> &res It is the users responsibility to ensure the \a printer remains valid until \a resultCallback has been called. - \note The rendering of the current content into a temporary PDF document is asynchronous and does - not block the main thread. However, the subsequent rendering of PDF into \a printer runs on the - main thread and will therefore block the event loop. Moreover, printing runs on the browser - process, which is by default not sandboxed. + \note Printing runs on the browser process, which is by default not sandboxed. The \a resultCallback must take a boolean as parameter. If printing was successful, this boolean will have the value \c true, otherwise, its value will be \c false. diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 69e0ce4a3..4fd195074 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -71,6 +71,7 @@ class QWebEngineQuotaRequest; class QWebEngineRegisterProtocolHandlerRequest; class QWebEngineScriptCollection; class QWebEngineSettings; +class QWebEngineUrlRequestInterceptor; class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject { Q_OBJECT @@ -305,6 +306,8 @@ public: void setDevToolsPage(QWebEnginePage *page); QWebEnginePage *devToolsPage() const; + void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); + const QWebEngineContextMenuData &contextMenuData() const; Q_SIGNALS: diff --git a/src/webenginewidgets/api/qwebenginepage_p.h b/src/webenginewidgets/api/qwebenginepage_p.h index eecbf0b65..ddd9e8342 100644 --- a/src/webenginewidgets/api/qwebenginepage_p.h +++ b/src/webenginewidgets/api/qwebenginepage_p.h @@ -124,7 +124,7 @@ public: void didFetchDocumentMarkup(quint64 requestId, const QString& result) override; void didFetchDocumentInnerText(quint64 requestId, const QString& result) override; void didFindText(quint64 requestId, int matchCount) override; - void didPrintPage(quint64 requestId, const QByteArray &result) override; + void didPrintPage(quint64 requestId, QSharedPointer<QByteArray> result) override; void didPrintPageToPdf(const QString &filePath, bool success) override; void passOnFocus(bool reverse) override; void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override; @@ -151,6 +151,7 @@ public: void printRequested() override; const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; } + void interceptRequest(QWebEngineUrlRequestInfo &) override; void widgetChanged(QtWebEngineCore::RenderWidgetHostViewQtDelegate *newWidget) override; QtWebEngineCore::ProfileAdapter *profileAdapter() override; @@ -193,6 +194,7 @@ public: bool defaultAudioMuted; qreal defaultZoomFactor; QTimer wasShownTimer; + QWebEngineUrlRequestInterceptor *requestInterceptor; QtWebEngineCore::RenderWidgetHostViewQtDelegateWidget *widget = nullptr; mutable QtWebEngineCore::CallbackDirectory m_callbacks; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 03ce5e0bc..7b2f398ba 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -668,20 +668,7 @@ QWebEngineSettings *QWebEngineProfile::settings() const const QWebEngineUrlSchemeHandler *QWebEngineProfile::urlSchemeHandler(const QByteArray &scheme) const { const Q_D(QWebEngineProfile); - if (d->profileAdapter()->customUrlSchemeHandlers().contains(scheme)) - return d->profileAdapter()->customUrlSchemeHandlers().value(scheme); - return 0; -} - -static bool checkInternalScheme(const QByteArray &scheme) -{ - static QSet<QByteArray> internalSchemes; - if (internalSchemes.isEmpty()) { - internalSchemes << QByteArrayLiteral("qrc") << QByteArrayLiteral("data") << QByteArrayLiteral("blob") - << QByteArrayLiteral("http") << QByteArrayLiteral("https") << QByteArrayLiteral("ftp") - << QByteArrayLiteral("javascript"); - } - return internalSchemes.contains(scheme); + return d->profileAdapter()->urlSchemeHandler(scheme); } /*! @@ -695,25 +682,7 @@ static bool checkInternalScheme(const QByteArray &scheme) void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler) { Q_D(QWebEngineProfile); - Q_ASSERT(handler); - QByteArray canonicalScheme = scheme.toLower(); - if (checkInternalScheme(canonicalScheme)) { - qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData()); - return; - } - - if (d->profileAdapter()->customUrlSchemeHandlers().contains(canonicalScheme)) { - if (d->profileAdapter()->customUrlSchemeHandlers().value(canonicalScheme) != handler) - qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData()); - return; - } - - if (QWebEngineUrlScheme::schemeByName(canonicalScheme) == QWebEngineUrlScheme()) - qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " - "before installing the custom scheme handler.", scheme.constData()); - - d->profileAdapter()->addCustomUrlSchemeHandler(canonicalScheme, handler); - connect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->installUrlSchemeHandler(scheme, handler); } /*! @@ -726,10 +695,7 @@ void QWebEngineProfile::installUrlSchemeHandler(const QByteArray &scheme, QWebEn void QWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler) { Q_D(QWebEngineProfile); - Q_ASSERT(handler); - if (!d->profileAdapter()->removeCustomUrlSchemeHandler(handler)) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlSchemeHandler(handler); } /*! @@ -742,10 +708,7 @@ void QWebEngineProfile::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handl void QWebEngineProfile::removeUrlScheme(const QByteArray &scheme) { Q_D(QWebEngineProfile); - QWebEngineUrlSchemeHandler *handler = d->profileAdapter()->takeCustomUrlSchemeHandler(scheme); - if (!handler) - return; - disconnect(handler, SIGNAL(_q_destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*)), this, SLOT(destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler*))); + d->profileAdapter()->removeUrlScheme(scheme); } /*! @@ -756,12 +719,42 @@ void QWebEngineProfile::removeUrlScheme(const QByteArray &scheme) void QWebEngineProfile::removeAllUrlSchemeHandlers() { Q_D(QWebEngineProfile); - d->profileAdapter()->clearCustomUrlSchemeHandlers(); + d->profileAdapter()->removeAllUrlSchemeHandlers(); +} + +/*! + \since 5.13 + + Sets this profile to be used for downloading and caching when needed during + certificate verification, for instance for OCSP, CRLs, and AIA. + + Only one QWebEngineProfile can do this at a time, and it is recommended + that the profile fullfilling this role has a disk HTTP cache to avoid + needlessly re-downloading. + + Currently only affects Linux/NSS installations where it enables OCSP. + + As long as one profile has this option enabled, all other profiles will be + able to use it for their certificate verification. + + \sa isUsedForGlobalCertificateVerification(), httpCacheType() +*/ +void QWebEngineProfile::setUseForGlobalCertificateVerification() +{ + Q_D(QWebEngineProfile); + d->profileAdapter()->setUseForGlobalCertificateVerification(); } -void QWebEngineProfile::destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj) +/*! + \since 5.13 + + Returns \c true if this profile is currently being used for global + certificate verification. +*/ +bool QWebEngineProfile::isUsedForGlobalCertificateVerification() const { - removeUrlSchemeHandler(obj); + Q_D(const QWebEngineProfile); + return d->profileAdapter()->isUsedForGlobalCertificateVerification(); } /*! diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h index f9a564cd2..7ec28ac0a 100644 --- a/src/webenginewidgets/api/qwebengineprofile.h +++ b/src/webenginewidgets/api/qwebengineprofile.h @@ -128,19 +128,20 @@ public: void setSpellCheckEnabled(bool enabled); bool isSpellCheckEnabled() const; + void setUseForGlobalCertificateVerification(); + bool isUsedForGlobalCertificateVerification() const; + static QWebEngineProfile *defaultProfile(); Q_SIGNALS: void downloadRequested(QWebEngineDownloadItem *download); -private Q_SLOTS: - void destroyedUrlSchemeHandler(QWebEngineUrlSchemeHandler *obj); - private: Q_DISABLE_COPY(QWebEngineProfile) Q_DECLARE_PRIVATE(QWebEngineProfile) QWebEngineProfile(QWebEngineProfilePrivate *, QObject *parent = Q_NULLPTR); + friend class QWebEnginePage; friend class QWebEnginePagePrivate; friend class QWebEngineUrlSchemeHandler; QScopedPointer<QWebEngineProfilePrivate> d_ptr; diff --git a/src/webenginewidgets/printer_worker.cpp b/src/webenginewidgets/printer_worker.cpp new file mode 100644 index 000000000..3a670ad49 --- /dev/null +++ b/src/webenginewidgets/printer_worker.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "printer_worker.h" + +#include "printing/pdfium_document_wrapper_qt.h" + +#include <QPainter> +#include <QPrinter> + +namespace QtWebEngineCore { + +PrinterWorker::PrinterWorker(QSharedPointer<QByteArray> data, QPrinter *printer) + : m_data(data) + , m_printer(printer) +{ +} + +PrinterWorker::~PrinterWorker() +{ +} + +void PrinterWorker::print() +{ + if (!m_data->size()) { + qWarning("Failure to print on printer %ls: Print result data is empty.", + qUtf16Printable(m_printer->printerName())); + Q_EMIT resultReady(false); + return; + } + + QSize pageSize = m_printer->pageRect().size(); + PdfiumDocumentWrapperQt pdfiumWrapper(m_data->constData(), m_data->size(), pageSize); + + int toPage = m_printer->toPage(); + int fromPage = m_printer->fromPage(); + bool ascendingOrder = true; + + if (fromPage == 0 && toPage == 0) { + fromPage = 1; + toPage = pdfiumWrapper.pageCount(); + } + fromPage = qMax(1, fromPage); + toPage = qMin(pdfiumWrapper.pageCount(), toPage); + + if (m_printer->pageOrder() == QPrinter::LastPageFirst) { + qSwap(fromPage, toPage); + ascendingOrder = false; + } + + int pageCopies = 1; + int documentCopies = 1; + + if (!m_printer->supportsMultipleCopies()) + documentCopies = m_printer->copyCount(); + + if (m_printer->collateCopies()) { + pageCopies = documentCopies; + documentCopies = 1; + } + + QPainter painter; + if (!painter.begin(m_printer)) { + qWarning("Failure to print on printer %ls: Could not open printer for painting.", + qUtf16Printable(m_printer->printerName())); + Q_EMIT resultReady(false); + return; + } + + for (int printedDocuments = 0; printedDocuments < documentCopies; printedDocuments++) { + int currentPageIndex = fromPage; + while (true) { + for (int printedPages = 0; printedPages < pageCopies; printedPages++) { + if (m_printer->printerState() == QPrinter::Aborted + || m_printer->printerState() == QPrinter::Error) { + Q_EMIT resultReady(false); + return; + } + + QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); + if (currentImage.isNull()) { + Q_EMIT resultReady(false); + return; + } + + // Painting operations are automatically clipped to the bounds of the drawable part of the page. + painter.drawImage(QRect(0, 0, pageSize.width(), pageSize.height()), currentImage, currentImage.rect()); + if (printedPages < pageCopies - 1) + m_printer->newPage(); + } + + if (currentPageIndex == toPage) + break; + + if (ascendingOrder) + currentPageIndex++; + else + currentPageIndex--; + + m_printer->newPage(); + } + if (printedDocuments < documentCopies - 1) + m_printer->newPage(); + } + painter.end(); + + Q_EMIT resultReady(true); + return; +} + +} // namespace QtWebEngineCore diff --git a/src/webenginewidgets/printer_worker.h b/src/webenginewidgets/printer_worker.h new file mode 100644 index 000000000..96025c90e --- /dev/null +++ b/src/webenginewidgets/printer_worker.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PRINTER_WORKER_H +#define PRINTER_WORKER_H + +#include "qtwebenginecoreglobal_p.h" + +#include <QSharedPointer> + +QT_BEGIN_NAMESPACE +class QPrinter; +QT_END_NAMESPACE + +namespace QtWebEngineCore { + +class PrinterWorker : public QObject +{ + Q_OBJECT +public: + PrinterWorker(QSharedPointer<QByteArray> data, QPrinter *printer); + virtual ~PrinterWorker(); + +public Q_SLOTS: + void print(); + +Q_SIGNALS: + void resultReady(bool success); + +private: + Q_DISABLE_COPY(PrinterWorker) + + QSharedPointer<QByteArray> m_data; + QPrinter *m_printer; +}; + +} // namespace QtWebEngineCore + +Q_DECLARE_METATYPE(QtWebEngineCore::PrinterWorker*) + +#endif // PRINTER_WORKER_H diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp index 900ed3324..5b464a461 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -132,25 +132,31 @@ RenderWidgetHostViewQtDelegateWidget::RenderWidgetHostViewQtDelegateWidget(Rende "QSurfaceFormat before the QtGui application instance is created."); } #endif - - // Make sure the OpenGL profile of the QQuickWidget matches the shared context profile. - if (sharedFormat.profile() == QSurfaceFormat::CoreProfile) { - int major; - int minor; - QSurfaceFormat::OpenGLContextProfile profile; - + int major; + int minor; + QSurfaceFormat::OpenGLContextProfile profile; #ifdef Q_OS_MACOS - // Due to QTBUG-63180, requesting the sharedFormat.majorVersion() on macOS will lead to - // a failed creation of QQuickWidget shared context. Thus make sure to request the - // major version specified in the defaultFormat instead. - major = defaultFormat.majorVersion(); - minor = defaultFormat.minorVersion(); - profile = defaultFormat.profile(); + // Due to QTBUG-63180, requesting the sharedFormat.majorVersion() on macOS will lead to + // a failed creation of QQuickWidget shared context. Thus make sure to request the + // major version specified in the defaultFormat instead. + major = defaultFormat.majorVersion(); + minor = defaultFormat.minorVersion(); + profile = defaultFormat.profile(); #else - major = sharedFormat.majorVersion(); - minor = sharedFormat.minorVersion(); - profile = sharedFormat.profile(); + major = sharedFormat.majorVersion(); + minor = sharedFormat.minorVersion(); + profile = sharedFormat.profile(); +#endif + + // Make sure the OpenGL profile of the QQuickWidget matches the shared context profile. + // It covers the following cases: + // 1) Desktop OpenGL Core Profile. + // 2) Windows ANGLE OpenGL ES profile. + if (sharedFormat.profile() == QSurfaceFormat::CoreProfile +#ifdef Q_OS_WIN + || globalSharedContext->isOpenGLES() #endif + ) { format.setMajorVersion(major); format.setMinorVersion(minor); format.setProfile(profile); diff --git a/src/webenginewidgets/webenginewidgets.pro b/src/webenginewidgets/webenginewidgets.pro index e61575d3a..4669c2bce 100644 --- a/src/webenginewidgets/webenginewidgets.pro +++ b/src/webenginewidgets/webenginewidgets.pro @@ -49,6 +49,9 @@ HEADERS = \ qtConfig(webengine-printing-and-pdf) { QT += printsupport + + SOURCES += printer_worker.cpp + HEADERS += printer_worker.h } load(qt_module) |