diff options
37 files changed, 420 insertions, 195 deletions
diff --git a/.qmake.conf b/.qmake.conf index dceae50da..d804257e8 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -5,4 +5,4 @@ QTWEBENGINE_OUT_ROOT = $$shadowed($$PWD) load(qt_build_config) CONFIG += warning_clean -MODULE_VERSION = 5.13.1 +MODULE_VERSION = 5.13.2 diff --git a/configure.pri b/configure.pri index 23f72c5c6..1e7e35178 100644 --- a/configure.pri +++ b/configure.pri @@ -294,8 +294,3 @@ defineTest(qtConfTest_hasThumbFlag) { # no flag assume mthumb return(true) } - -defineTest(qtConfTest_hasGcc6OrNewer) { - greaterThan(QMAKE_GCC_MAJOR_VERSION, 5):return(true) - return(false) -} diff --git a/dist/changes-5.13.2 b/dist/changes-5.13.2 new file mode 100644 index 000000000..aeac5b000 --- /dev/null +++ b/dist/changes-5.13.2 @@ -0,0 +1,81 @@ +Qt 5.13.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.13.0 through 5.13.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qt-5/index.html + +The Qt version 5.13 series is binary compatible with the 5.12.x series. +Applications compiled for 5.12 will continue to run with 5.13. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.13.2 Changes * +**************************************************************************** + +General +------- + + - [QTBUG-76329] Fixes extensions and PDF viewer when building with GCC 5. + - [QTBUG-77072, QTBUG-78084] Fixed flashing when closing combo box on + macOS. + - [QTBUG-77827] Fixed unbundling of libjpeg on Linux. + - [QTBUG-77977] Fixed invalid URL on profile change. + - [QTBUG-78016] Fixed crash with desktop sharing. + - [QTBUG-78206] Fixed accessibility events for combo boxes. + - [QTBUG-78316] Fixed XMLHttpRequest status with custom schemes. + + +Chromium +-------- + + - Security fixes from Chromium up to version 77.0.3865.120, including: + + - CVE-2019-5869 + - CVE-2019-5870 + - CVE-2019-5872 + - CVE-2019-5875 + - CVE-2019-5876 + - CVE-2019-13659 + - CVE-2019-13660 + - CVE-2019-13663 + - CVE-2019-13664 + - CVE-2019-13665 + - CVE-2019-13673 + - CVE-2019-13674 + - CVE-2019-13675 + - CVE-2019-13678/CVE-2019-13681 + - CVE-2019-13687 + - CVE-2019-13688 + - CVE-2019-13691 + - CVE-2019-13692 + - CVE-2019-13693 + - CVE-2019-13694 + - CVE-2019-13695 + - CVE-2019-13697 + - Security issue 946351 + - Security issue 946978 + - Security issue 957160 + - Security issue 960354 + - Security issue 964938 + - Security issue 971904 + - Security issue 973628 + - Security issue 974354 + - Security issue 979373 + - Security issue 981459 + - Security issue 981597 + - Security issue 986727 + - Security issue 990234 + +Qt WebEngine Widgets +-------------------- + + - [QTBUG-77209] Fixed QWebEnginePage visibility state. diff --git a/examples/webengine/customdialogs/main.cpp b/examples/webengine/customdialogs/main.cpp index 82a3eca63..5aad8affb 100644 --- a/examples/webengine/customdialogs/main.cpp +++ b/examples/webengine/customdialogs/main.cpp @@ -67,9 +67,10 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - Application app(argc, argv); QtWebEngine::initialize(); + Application app(argc, argv); + QQmlApplicationEngine engine; Server *server = new Server(&engine); diff --git a/examples/webengine/minimal/main.cpp b/examples/webengine/minimal/main.cpp index 9db6ea6aa..348517ee0 100644 --- a/examples/webengine/minimal/main.cpp +++ b/examples/webengine/minimal/main.cpp @@ -56,9 +56,8 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); - QtWebEngine::initialize(); + QGuiApplication app(argc, argv); QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/examples/webengine/quicknanobrowser/main.cpp b/examples/webengine/quicknanobrowser/main.cpp index 8d032f5d8..d09abba3c 100644 --- a/examples/webengine/quicknanobrowser/main.cpp +++ b/examples/webengine/quicknanobrowser/main.cpp @@ -80,11 +80,10 @@ int main(int argc, char **argv) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QtWebEngine::initialize(); Application app(argc, argv); - QtWebEngine::initialize(); - QQmlApplicationEngine appEngine; Utils utils; appEngine.rootContext()->setContextProperty("utils", &utils); diff --git a/examples/webengine/recipebrowser/main.cpp b/examples/webengine/recipebrowser/main.cpp index 6e6d69804..e61b9e0f5 100644 --- a/examples/webengine/recipebrowser/main.cpp +++ b/examples/webengine/recipebrowser/main.cpp @@ -58,9 +58,10 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); QtWebEngine::initialize(); + QGuiApplication app(argc, argv); + QQuickStyle::setStyle(QStringLiteral("Material")); QQmlApplicationEngine engine; diff --git a/examples/webengine/webengineaction/main.cpp b/examples/webengine/webengineaction/main.cpp index ce723a99b..f2dec9655 100644 --- a/examples/webengine/webengineaction/main.cpp +++ b/examples/webengine/webengineaction/main.cpp @@ -56,10 +56,10 @@ int main(int argc, char *argv[]) { QCoreApplication::setOrganizationName("QtExamples"); QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); - QtWebEngine::initialize(); + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); diff --git a/src/3rdparty b/src/3rdparty -Subproject 0240cfc1a59deb5b612923d47ccef72f10504fe +Subproject d6e5fc10e417efdf8665d9fba57c269f0534072 diff --git a/src/core/api/qtwebenginecoreglobal.cpp b/src/core/api/qtwebenginecoreglobal.cpp index 25d0bd3be..3c7fc54a1 100644 --- a/src/core/api/qtwebenginecoreglobal.cpp +++ b/src/core/api/qtwebenginecoreglobal.cpp @@ -108,7 +108,7 @@ Q_WEBENGINECORE_PRIVATE_EXPORT void initialize() QCoreApplication *app = QCoreApplication::instance(); if (!app) { - qFatal("QtWebEngine::initialize() must be called after the construction of the application object."); + qFatal("QtWebEngine::initialize() but no core application instance."); return; } diff --git a/src/core/browser_accessibility_manager_qt.cpp b/src/core/browser_accessibility_manager_qt.cpp index 7fb1386c5..8e3ee5940 100644 --- a/src/core/browser_accessibility_manager_qt.cpp +++ b/src/core/browser_accessibility_manager_qt.cpp @@ -146,6 +146,24 @@ void BrowserAccessibilityManagerQt::FireBlinkEvent(ax::mojom::Event event_type, break; } } + +void BrowserAccessibilityManagerQt::FireGeneratedEvent(ui::AXEventGenerator::Event event_type, + BrowserAccessibility* node) +{ + BrowserAccessibilityQt *iface = static_cast<BrowserAccessibilityQt*>(node); + + switch (event_type) { + case ui::AXEventGenerator::Event::VALUE_CHANGED: + if (iface->role() == QAccessible::EditableText) { + QAccessibleTextUpdateEvent event(iface, -1, QString(), QString()); + QAccessible::updateAccessibility(&event); + } + break; + default: + break; + } +} + #endif // QT_NO_ACCESSIBILITY } diff --git a/src/core/browser_accessibility_manager_qt.h b/src/core/browser_accessibility_manager_qt.h index 87c8875ba..16e2d1fe7 100644 --- a/src/core/browser_accessibility_manager_qt.h +++ b/src/core/browser_accessibility_manager_qt.h @@ -60,6 +60,8 @@ public: ~BrowserAccessibilityManagerQt() override; void FireBlinkEvent(ax::mojom::Event event_type, BrowserAccessibility* node) override; + void FireGeneratedEvent(ui::AXEventGenerator::Event event_type, + BrowserAccessibility* node) override; QAccessibleInterface *rootParentAccessible(); bool isValid() const { return m_valid; } diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri index 22cb5991f..f45c418fe 100644 --- a/src/core/config/linux.pri +++ b/src/core/config/linux.pri @@ -178,7 +178,7 @@ host_build { } else { gn_args += use_system_harfbuzz=false } - !qtConfig(webengine-system-glib): gn_args += use_glib=false + gn_args += use_glib=false qtConfig(webengine-pulseaudio) { gn_args += use_pulseaudio=true } else { diff --git a/src/core/configure.json b/src/core/configure.json index be686850b..cd3c5c661 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -334,10 +334,6 @@ "label": "thumb instruction set", "type": "hasThumbFlag" }, - "webengine-extensions-gcc-version" : { - "label": "GCC 6 or newer", - "type": "hasGcc6OrNewer" - }, "webengine-noexecstack" : { "label": "linker supports -z noexecstack", "type": "linkerSupportsFlag", @@ -566,7 +562,7 @@ "label": "Extensions", "purpose": "Enables Chromium extensions within certain limits. Currently used for enabling the pdf viewer.", "section": "WebEngine", - "condition": "features.webengine-printing-and-pdf && (tests.webengine-extensions-gcc-version || config.clang || !config.gcc)", + "condition": "features.webengine-printing-and-pdf", "autoDetect": "features.webengine-printing-and-pdf", "output": [ "publicFeature" ] }, diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 16945020b..6d1bf07a9 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -196,7 +196,7 @@ public: void* GetHandle() override { return m_handle; } // Qt currently never creates contexts using robustness attributes. - bool WasAllocatedUsingRobustnessExtension() override { return false; } + unsigned int CheckStickyGraphicsResetStatus() override { return 0 /*GL_NO_ERROR*/; } // We don't care about the rest, this context shouldn't be used except for its handle. bool Initialize(gl::GLSurface *, const gl::GLContextAttribs &) override { Q_UNREACHABLE(); return false; } @@ -235,7 +235,7 @@ void ShareGroupQtQuick::AboutToAddFirstContext() // This currently has to be setup by ::main in all applications using QQuickWebEngineView with delegated rendering. QOpenGLContext *shareContext = qt_gl_global_share_context(); if (!shareContext) { - qFatal("QWebEngine: OpenGL resource sharing is not set up in QtQuick. Please make sure to call QtWebEngine::initialize() in your main() function."); + qFatal("QWebEngine: OpenGL resource sharing is not set up in QtQuick. Please make sure to call QtWebEngine::initialize() in your main() function before QCoreApplication is created."); } m_shareContextQtQuick = new QtShareGLContext(shareContext); #endif diff --git a/src/core/core_common.pri b/src/core/core_common.pri index c92278657..e548aefd1 100644 --- a/src/core/core_common.pri +++ b/src/core/core_common.pri @@ -17,6 +17,3 @@ CONFIG -= ltcg # Chromium requires C++14 CONFIG += c++14 -#QTBUG-73216 ci has to be updated with latest yocto -boot2qt: CONFIG -= use_gold_linker - diff --git a/src/core/media_capture_devices_dispatcher.cpp b/src/core/media_capture_devices_dispatcher.cpp index ecc46f244..29230c27b 100644 --- a/src/core/media_capture_devices_dispatcher.cpp +++ b/src/core/media_capture_devices_dispatcher.cpp @@ -87,22 +87,25 @@ const blink::MediaStreamDevice *findDeviceWithId(const blink::MediaStreamDevices return 0; } -// Based on chrome/browser/media/desktop_capture_access_handler.cc: -void getDevicesForDesktopCapture(blink::MediaStreamDevices *devices, content::DesktopMediaID mediaId, bool captureAudio) +// Based on chrome/browser/media/webrtc/desktop_capture_devices_util.cc: +void getDevicesForDesktopCapture(blink::MediaStreamDevices *devices, + content::DesktopMediaID mediaId, + bool captureAudio, + blink::MediaStreamType videoType, + blink::MediaStreamType audioType) { DCHECK_CURRENTLY_ON(BrowserThread::UI); // Add selected desktop source to the list. - devices->push_back(blink::MediaStreamDevice(blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE, mediaId.ToString(), "Screen")); + devices->push_back(blink::MediaStreamDevice(videoType, mediaId.ToString(), mediaId.ToString())); if (captureAudio) { if (mediaId.type == content::DesktopMediaID::TYPE_WEB_CONTENTS) { devices->push_back( - blink::MediaStreamDevice(blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, - mediaId.ToString(), "Tab audio")); + blink::MediaStreamDevice(audioType, mediaId.ToString(), "Tab audio")); } else { // Use the special loopback device ID for system audio capture. devices->push_back(blink::MediaStreamDevice( - blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE, + audioType, media::AudioDeviceDescription::kLoopbackInputDeviceId, "System Audio")); } @@ -151,19 +154,27 @@ content::DesktopMediaID getDefaultScreenId() WebContentsAdapterClient::MediaRequestFlags mediaRequestFlagsForRequest(const content::MediaStreamRequest &request) { - WebContentsAdapterClient::MediaRequestFlags requestFlags = WebContentsAdapterClient::MediaNone; + if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE && + request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) + return {WebContentsAdapterClient::MediaAudioCapture, WebContentsAdapterClient::MediaVideoCapture}; - if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE) - requestFlags |= WebContentsAdapterClient::MediaAudioCapture; - else if (request.audio_type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) - requestFlags |= WebContentsAdapterClient::MediaDesktopAudioCapture; + if (request.audio_type == blink::MEDIA_DEVICE_AUDIO_CAPTURE && + request.video_type == blink::MEDIA_NO_SERVICE) + return {WebContentsAdapterClient::MediaAudioCapture}; - if (request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) - requestFlags |= WebContentsAdapterClient::MediaVideoCapture; - else if (request.video_type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) - requestFlags |= WebContentsAdapterClient::MediaDesktopVideoCapture; + if (request.audio_type == blink::MEDIA_NO_SERVICE && + request.video_type == blink::MEDIA_DEVICE_VIDEO_CAPTURE) + return {WebContentsAdapterClient::MediaVideoCapture}; - return requestFlags; + if (request.audio_type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE && + request.video_type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE) + return {WebContentsAdapterClient::MediaDesktopAudioCapture, WebContentsAdapterClient::MediaDesktopVideoCapture}; + + if (request.video_type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || + request.video_type == blink::MEDIA_DISPLAY_VIDEO_CAPTURE) + return {WebContentsAdapterClient::MediaDesktopVideoCapture}; + + return {}; } } // namespace @@ -198,14 +209,13 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: if (!securityOriginsMatch) qWarning("Security origin mismatch for media access permission: %s requested and %s provided\n", qPrintable(requestSecurityOrigin.toString()), qPrintable(securityOrigin.toString())); - bool microphoneRequested = - (request.audio_type && authorizationFlags & WebContentsAdapterClient::MediaAudioCapture); - bool webcamRequested = - (request.video_type && authorizationFlags & WebContentsAdapterClient::MediaVideoCapture); - bool desktopAudioRequested = - (request.audio_type && authorizationFlags & WebContentsAdapterClient::MediaDesktopAudioCapture); - bool desktopVideoRequested = - (request.video_type && authorizationFlags & WebContentsAdapterClient::MediaDesktopVideoCapture); + WebContentsAdapterClient::MediaRequestFlags requestFlags = mediaRequestFlagsForRequest(request); + WebContentsAdapterClient::MediaRequestFlags finalFlags = requestFlags & authorizationFlags; + + bool microphoneRequested = finalFlags.testFlag(WebContentsAdapterClient::MediaAudioCapture); + bool webcamRequested = finalFlags.testFlag(WebContentsAdapterClient::MediaVideoCapture); + bool desktopAudioRequested = finalFlags.testFlag(WebContentsAdapterClient::MediaDesktopAudioCapture); + bool desktopVideoRequested = finalFlags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture); if (securityOriginsMatch) { if (microphoneRequested || webcamRequested) { @@ -221,7 +231,8 @@ void MediaCaptureDevicesDispatcher::handleMediaAccessPermissionResponse(content: break; } } else if (desktopVideoRequested) { - getDevicesForDesktopCapture(&devices, getDefaultScreenId(), desktopAudioRequested); + getDevicesForDesktopCapture(&devices, getDefaultScreenId(), desktopAudioRequested, + request.video_type, request.audio_type); } } @@ -274,13 +285,13 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(WebContentsAdapter { DCHECK_CURRENTLY_ON(BrowserThread::UI); - // Let's not support tab capture for now. - if (request.video_type == blink::MEDIA_GUM_TAB_VIDEO_CAPTURE || request.audio_type == blink::MEDIA_GUM_TAB_AUDIO_CAPTURE) { + WebContentsAdapterClient::MediaRequestFlags flags = mediaRequestFlagsForRequest(request); + if (!flags) { std::move(callback).Run(blink::MediaStreamDevices(), blink::MEDIA_DEVICE_NOT_SUPPORTED, std::unique_ptr<content::MediaStreamUI>()); return; } - if (request.video_type == blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || request.audio_type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE) { + if (flags.testFlag(WebContentsAdapterClient::MediaDesktopVideoCapture)) { const bool screenCaptureEnabled = adapterClient->webEngineSettings()->testAttribute(WebEngineSettings::ScreenCaptureEnabled); const bool originIsSecure = content::IsOriginSecure(request.security_origin); @@ -298,18 +309,13 @@ void MediaCaptureDevicesDispatcher::processMediaAccessRequest(WebContentsAdapter enqueueMediaAccessRequest(webContents, request, std::move(callback)); // We might not require this approval for pepper requests. - adapterClient->runMediaAccessPermissionRequest(toQt(request.security_origin), mediaRequestFlagsForRequest(request)); + adapterClient->runMediaAccessPermissionRequest(toQt(request.security_origin), flags); } void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content::WebContents *webContents, const content::MediaStreamRequest &request, content::MediaResponseCallback callback) { blink::MediaStreamDevices devices; - if (request.video_type != blink::MEDIA_GUM_DESKTOP_VIDEO_CAPTURE || request.requested_video_device_id.empty()) { - std::move(callback).Run(devices, blink::MEDIA_DEVICE_INVALID_STATE, std::unique_ptr<content::MediaStreamUI>()); - return; - } - content::WebContents *const web_contents_for_stream = content::WebContents::FromRenderFrameHost( content::RenderFrameHost::FromID(request.render_process_id, request.render_frame_id)); content::RenderFrameHost *const main_frame = web_contents_for_stream ? web_contents_for_stream->GetMainFrame() : NULL; @@ -334,7 +340,7 @@ void MediaCaptureDevicesDispatcher::processDesktopCaptureAccessRequest(content:: // Audio is only supported for screen capture streams. bool capture_audio = (mediaId.type == content::DesktopMediaID::TYPE_SCREEN && request.audio_type == blink::MEDIA_GUM_DESKTOP_AUDIO_CAPTURE); - getDevicesForDesktopCapture(&devices, mediaId, capture_audio); + getDevicesForDesktopCapture(&devices, mediaId, capture_audio, request.video_type, request.audio_type); std::move(callback).Run(devices, devices.empty() ? blink::MEDIA_DEVICE_INVALID_STATE : blink::MEDIA_DEVICE_OK, std::unique_ptr<content::MediaStreamUI>()); diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 7f278fd92..2cca152e6 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -136,11 +136,11 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet // Deprecated =begin // quick peek if deprecated - QWebEngineUrlRequestInterceptor* profileInterceptor = m_profileIOData->requestInterceptor(); - if (profileInterceptor && profileInterceptor->property("deprecated").toBool()) { - profileInterceptor = nullptr; - if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { - interceptor->interceptRequest(requestInfo); + + if (m_profileIOData->isInterceptorDeprecated()) { + QWebEngineUrlRequestInterceptor* profileInterceptor = m_profileIOData->acquireInterceptor(); + if (profileInterceptor && m_profileIOData->isInterceptorDeprecated()) { + profileInterceptor->interceptRequest(requestInfo); m_profileIOData->releaseInterceptor(); if (requestInfo.changed()) { int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; @@ -174,7 +174,9 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet if (!resourceInfo) return net::OK; - if (!m_profileIOData->hasPageInterceptors() && !profileInterceptor && !content::IsResourceTypeFrame(resourceType)) + // try to bail out + if (!m_profileIOData->hasPageInterceptors() && (!m_profileIOData->requestInterceptor() || m_profileIOData->isInterceptorDeprecated()) && + !content::IsResourceTypeFrame(resourceType)) return net::OK; auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); @@ -185,7 +187,7 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet std::move(requestInfo), webContentsGetter, std::move(callback), - profileInterceptor ? m_profileIOData->profileAdapter() : nullptr + m_profileIOData->profileAdapter() ); // We'll run the callback after we notified the UI thread. diff --git a/src/core/net/url_request_custom_job.cpp b/src/core/net/url_request_custom_job.cpp index cba9b4dc5..d8442707e 100644 --- a/src/core/net/url_request_custom_job.cpp +++ b/src/core/net/url_request_custom_job.cpp @@ -40,10 +40,12 @@ #include "url_request_custom_job.h" #include "url_request_custom_job_proxy.h" +#include "base/strings/stringprintf.h" #include "base/task/post_task.h" #include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_thread.h" #include "net/base/io_buffer.h" +#include "net/http/http_util.h" #include <QIODevice> @@ -136,6 +138,22 @@ bool URLRequestCustomJob::GetCharset(std::string* charset) return false; } +void URLRequestCustomJob::GetResponseInfo(HttpResponseInfo* info) +{ + if (m_error) + return; + + std::string headers; + if (m_redirect.is_valid()) { + headers += "HTTP/1.1 303 See Other\n"; + headers += base::StringPrintf("Location: %s\n", m_redirect.spec().c_str()); + } else { + headers += "HTTP/1.1 200 OK\n"; + } + + info->headers = new HttpResponseHeaders(HttpUtil::AssembleRawHeaders(headers.c_str(), headers.size())); +} + bool URLRequestCustomJob::IsRedirectResponse(GURL* location, int* http_status_code, bool* /*insecure_scheme_was_upgraded*/) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); diff --git a/src/core/net/url_request_custom_job.h b/src/core/net/url_request_custom_job.h index 16be76f29..c800d1595 100644 --- a/src/core/net/url_request_custom_job.h +++ b/src/core/net/url_request_custom_job.h @@ -64,6 +64,7 @@ public: int ReadRawData(net::IOBuffer *buf, int buf_size) override; bool GetMimeType(std::string *mimeType) const override; bool GetCharset(std::string *charset) override; + void GetResponseInfo(net::HttpResponseInfo* info) override; bool IsRedirectResponse(GURL* location, int* http_status_code, bool* insecure_scheme_was_upgraded) override; protected: diff --git a/src/core/net/url_request_notification.cpp b/src/core/net/url_request_notification.cpp index e37ad35bc..b59339441 100644 --- a/src/core/net/url_request_notification.cpp +++ b/src/core/net/url_request_notification.cpp @@ -49,6 +49,7 @@ #include "profile_io_data_qt.h" #include "qwebengineurlrequestinfo_p.h" #include "type_conversion.h" +#include <QVariant> namespace QtWebEngineCore { @@ -107,7 +108,7 @@ void URLRequestNotification::notify() if (webContents) { - if (m_profileAdapter) { + if (m_profileAdapter && m_profileAdapter->requestInterceptor()) { QWebEngineUrlRequestInterceptor* interceptor = m_profileAdapter->requestInterceptor(); if (!interceptor->property("deprecated").toBool()) interceptor->interceptRequest(m_requestInfo); diff --git a/src/core/printing/pdfium_document_wrapper_qt.cpp b/src/core/printing/pdfium_document_wrapper_qt.cpp index 6f415b50b..dae6ec44b 100644 --- a/src/core/printing/pdfium_document_wrapper_qt.cpp +++ b/src/core/printing/pdfium_document_wrapper_qt.cpp @@ -48,64 +48,6 @@ namespace QtWebEngineCore { int PdfiumDocumentWrapperQt::m_libraryUsers = 0; -class PdfiumPageWrapperQt { -public: - PdfiumPageWrapperQt(FPDF_DOCUMENT data, int pageIndex) - : m_pageData(FPDF_LoadPage(data, pageIndex)) - , m_width(FPDF_GetPageWidth(m_pageData)) - , m_height(FPDF_GetPageHeight(m_pageData)) - , m_image(createImage()) - { - } - - PdfiumPageWrapperQt() - : m_pageData(nullptr) - , m_width(-1) - , m_height(-1) - , m_image(QImage()) - { - } - - virtual ~PdfiumPageWrapperQt() - { - FPDF_ClosePage(m_pageData); - } - - QImage image() - { - return m_image; - } - -private: - QImage createImage() - { - Q_ASSERT(m_pageData); - - QImage image(m_width * 2, m_height * 2, QImage::Format_ARGB32); - Q_ASSERT(!image.isNull()); - image.fill(0xFFFFFFFF); - - FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(image.width(), image.height(), - FPDFBitmap_BGRA, - image.scanLine(0), image.bytesPerLine()); - Q_ASSERT(bitmap); - - FPDF_RenderPageBitmap(bitmap, m_pageData, - 0, 0, image.width(), image.height(), - 0, 0); - FPDFBitmap_Destroy(bitmap); - bitmap = nullptr; - return image; - } - -private: - FPDF_PAGE m_pageData; - int m_width; - int m_height; - QImage m_image; -}; - - PdfiumDocumentWrapperQt::PdfiumDocumentWrapperQt(const void *pdfData, size_t size, const char *password) { @@ -118,27 +60,41 @@ PdfiumDocumentWrapperQt::PdfiumDocumentWrapperQt(const void *pdfData, size_t siz m_pageCount = FPDF_GetPageCount((FPDF_DOCUMENT)m_documentHandle); } -QImage PdfiumDocumentWrapperQt::pageAsQImage(size_t index) +QImage PdfiumDocumentWrapperQt::pageAsQImage(size_t pageIndex,int width , int height) { if (!m_documentHandle || !m_pageCount) { qWarning("Failure to generate QImage from invalid or empty PDF document."); return QImage(); } - if (static_cast<int>(index) >= m_pageCount) { + if (static_cast<int>(pageIndex) >= m_pageCount) { qWarning("Failure to generate QImage from PDF data: index out of bounds."); return QImage(); } - PdfiumPageWrapperQt pageWrapper((FPDF_DOCUMENT)m_documentHandle, index); - return pageWrapper.image(); + FPDF_PAGE pageData(FPDF_LoadPage((FPDF_DOCUMENT)m_documentHandle, pageIndex)); + QImage image(width, height, QImage::Format_ARGB32); + Q_ASSERT(!image.isNull()); + image.fill(0xFFFFFFFF); + + FPDF_BITMAP bitmap = FPDFBitmap_CreateEx(width, height, + FPDFBitmap_BGRA, + image.scanLine(0), image.bytesPerLine()); + Q_ASSERT(bitmap); + FPDF_RenderPageBitmap(bitmap, pageData, + 0, 0, width, height, + 0, 0); + FPDFBitmap_Destroy(bitmap); + bitmap = nullptr; + FPDF_ClosePage(pageData); + return image; } -bool PdfiumDocumentWrapperQt::pageIsLandscape(size_t index) +QSizeF PdfiumDocumentWrapperQt::pageSize(size_t index) { - double width = 0, height = 0; - FPDF_GetPageSizeByIndex((FPDF_DOCUMENT)m_documentHandle, index, &width, &height); - return (width > height); + QSizeF size; + FPDF_GetPageSizeByIndex((FPDF_DOCUMENT)m_documentHandle, index, &size.rwidth(), &size.rheight()); + return size; } PdfiumDocumentWrapperQt::~PdfiumDocumentWrapperQt() @@ -147,5 +103,4 @@ PdfiumDocumentWrapperQt::~PdfiumDocumentWrapperQt() if (--m_libraryUsers == 0) FPDF_DestroyLibrary(); } - } diff --git a/src/core/printing/pdfium_document_wrapper_qt.h b/src/core/printing/pdfium_document_wrapper_qt.h index 121742aa3..e0778c32b 100644 --- a/src/core/printing/pdfium_document_wrapper_qt.h +++ b/src/core/printing/pdfium_document_wrapper_qt.h @@ -56,15 +56,14 @@ #include <QtGui/qimage.h> namespace QtWebEngineCore { -class PdfiumPageWrapperQt; class Q_WEBENGINECORE_PRIVATE_EXPORT PdfiumDocumentWrapperQt { public: PdfiumDocumentWrapperQt(const void *pdfData, size_t size, const char *password = nullptr); virtual ~PdfiumDocumentWrapperQt(); - QImage pageAsQImage(size_t index); - bool pageIsLandscape(size_t index); + QImage pageAsQImage(size_t index, int width , int height); + QSizeF pageSize(size_t index); int pageCount() const { return m_pageCount; } private: diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index ebb533206..dbe76f0d1 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -179,7 +179,16 @@ void ProfileAdapter::setRequestInterceptor(QWebEngineUrlRequestInterceptor *inte { if (m_requestInterceptor == interceptor) return; + + if (m_requestInterceptor) + disconnect(m_requestInterceptor, &QObject::destroyed, this, nullptr); m_requestInterceptor = interceptor; + if (m_requestInterceptor) + connect(m_requestInterceptor, &QObject::destroyed, this, [this] () { + m_profile->m_profileIOData->updateRequestInterceptor(); + Q_ASSERT(!m_profile->m_profileIOData->requestInterceptor()); + }); + if (m_profile->m_urlRequestContextGetter.get()) m_profile->m_profileIOData->updateRequestInterceptor(); } diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 27c97a986..a2613d3fc 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -614,7 +614,6 @@ void ProfileIODataQt::setRequestContextData(content::ProtocolHandlerMap *protoco void ProfileIODataQt::setFullConfiguration() { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); - m_requestInterceptor = m_profileAdapter->requestInterceptor(); m_persistentCookiesPolicy = m_profileAdapter->persistentCookiesPolicy(); m_cookiesPath = m_profileAdapter->cookiesPath(); m_channelIdPath = m_profileAdapter->channelIdPath(); @@ -741,9 +740,18 @@ void ProfileIODataQt::updateRequestInterceptor() QMutexLocker lock(&m_mutex); m_requestInterceptor = m_profileAdapter->requestInterceptor(); m_hasPageInterceptors = m_profileAdapter->hasPageRequestInterceptor(); + if (m_requestInterceptor) + m_isInterceptorDeprecated = m_requestInterceptor->property("deprecated").toBool(); + else + m_isInterceptorDeprecated = false; // We in this case do not need to regenerate any Chromium classes. } +bool ProfileIODataQt::isInterceptorDeprecated() const +{ + return m_isInterceptorDeprecated; +} + QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() { m_mutex.lock(); diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 570365085..a1b123771 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -120,7 +120,8 @@ public: void setGlobalCertificateVerification(); // Used in NetworkDelegateQt::OnBeforeURLRequest. - QWebEngineUrlRequestInterceptor *acquireInterceptor(); + bool isInterceptorDeprecated() const; // Remove for Qt6 + QWebEngineUrlRequestInterceptor *acquireInterceptor(); // Remove for Qt6 void releaseInterceptor(); QWebEngineUrlRequestInterceptor *requestInterceptor(); @@ -191,6 +192,7 @@ private: base::WeakPtrFactory<ProfileIODataQt> m_weakPtrFactory; // this should be always the last member QString m_dataPath; bool m_pendingStorageRequestGeneration = false; + volatile bool m_isInterceptorDeprecated = false; // Remove for Qt6 DISALLOW_COPY_AND_ASSIGN(ProfileIODataQt); friend class BrowsingDataRemoverObserverQt; diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp index 5977a28a8..cd8ee8110 100644 --- a/src/core/profile_qt.cpp +++ b/src/core/profile_qt.cpp @@ -272,6 +272,7 @@ net::URLRequestContextGetter *ProfileQt::CreateRequestContext( m_profileIOData->setRequestContextData(protocol_handlers, std::move(request_interceptors)); m_profileIOData->updateStorageSettings(); + m_profileIOData->updateRequestInterceptor(); m_urlRequestContextGetter = new URLRequestContextGetterQt(m_profileIOData.get()); return m_urlRequestContextGetter.get(); } diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index ca2479965..d1fe786f6 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -1414,8 +1414,7 @@ void WebContentsAdapter::startDragging(QObject *dragSource, const content::DropD }); QMimeData *mimeData = mimeDataFromDropData(*m_currentDropData); - if (handleDropDataFileContents(dropData, mimeData)) - allowedActions = Qt::MoveAction; + handleDropDataFileContents(dropData, mimeData); drag->setMimeData(mimeData); if (!pixmap.isNull()) { diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index a3a5881bf..07e0d3ba8 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -362,6 +362,7 @@ ProxyAuthentication WebEngineContext::qProxyNetworkAuthentication(QString host, const static char kChromiumFlagsEnv[] = "QTWEBENGINE_CHROMIUM_FLAGS"; const static char kDisableSandboxEnv[] = "QTWEBENGINE_DISABLE_SANDBOX"; +const static char kDisableInProcGpuThread[] = "QTWEBENGINE_DISABLE_GPU_THREAD"; static void appendToFeatureList(std::string &featureList, const char *feature) { @@ -608,6 +609,7 @@ WebEngineContext::WebEngineContext() #ifndef QT_NO_OPENGL threadedGpu = QOpenGLContext::supportsThreadedOpenGL(); #endif + threadedGpu = threadedGpu && !qEnvironmentVariableIsSet(kDisableInProcGpuThread); registerMainThreadFactories(threadedGpu); SetContentClient(new ContentClientQt); diff --git a/src/webengine/api/qtwebengineglobal.cpp b/src/webengine/api/qtwebengineglobal.cpp index a11618dba..4346832c9 100644 --- a/src/webengine/api/qtwebengineglobal.cpp +++ b/src/webengine/api/qtwebengineglobal.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qtwebengineglobal.h" +#include <QCoreApplication> namespace QtWebEngineCore { @@ -62,8 +63,8 @@ namespace QtWebEngine { /*! \fn QtWebEngine::initialize() - Sets up an OpenGL Context that can be shared between threads. This has to be done after - QGuiApplication is created, but before a Qt Quick window is created. + Sets up an OpenGL Context that can be shared between threads. This has to be done before + QGuiApplication is created and before window's QPlatformOpenGLContext is created. This has the same effect as setting the Qt::AA_ShareOpenGLContexts attribute with QCoreApplication::setAttribute before constructing @@ -71,7 +72,15 @@ namespace QtWebEngine { */ void initialize() { - QtWebEngineCore::initialize(); + QCoreApplication *app = QCoreApplication::instance(); + if (app) { + qWarning("QtWebEngine::initialize() called with QCoreApplication object already created and should be call before. "\ + "This is depreciated and may fail in the future."); + QtWebEngineCore::initialize(); + return; + } + // call initialize the same way as widgets do + qAddPreRoutine(QtWebEngineCore::initialize); } } // namespace QtWebEngine diff --git a/src/webengine/doc/src/qtwebengine-features.qdoc b/src/webengine/doc/src/qtwebengine-features.qdoc index 00b9cb496..8744f312a 100644 --- a/src/webengine/doc/src/qtwebengine-features.qdoc +++ b/src/webengine/doc/src/qtwebengine-features.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the documentation of the Qt Toolkit. @@ -37,6 +37,7 @@ \li \l{Audio and Video Codecs} \li \l{Chromium DevTools} \li \l{Client Certificates} + \li \l{Custom Schemes} \li \l{Drag and Drop} \li \l{Fullscreen} \li \l{HTML5 DRM} diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 7b66ac876..2b18d63b1 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -2539,16 +2539,13 @@ void QContextMenuBuilder::addMenuItem(ContextMenuItem menuItem) switch (menuItem) { case ContextMenuItem::Back: - action = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), QWebEnginePage::tr("&Back"), m_menu); - QObject::connect(action, &QAction::triggered, thisRef->d_ptr->view, &QWebEngineView::back); + action = thisRef->action(QWebEnginePage::Back); break; case ContextMenuItem::Forward: - action = new QAction(QIcon::fromTheme(QStringLiteral("go-next")), QWebEnginePage::tr("&Forward"), m_menu); - QObject::connect(action, &QAction::triggered, thisRef->d_ptr->view, &QWebEngineView::forward); + action = thisRef->action(QWebEnginePage::Forward); break; case ContextMenuItem::Reload: - action = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), QWebEnginePage::tr("&Reload"), m_menu); - QObject::connect(action, &QAction::triggered, thisRef->d_ptr->view, &QWebEngineView::reload); + action = thisRef->action(QWebEnginePage::Reload); break; case ContextMenuItem::Cut: action = thisRef->action(QWebEnginePage::Cut); diff --git a/src/webenginewidgets/printer_worker.cpp b/src/webenginewidgets/printer_worker.cpp index 94a862cda..8e1c2a985 100644 --- a/src/webenginewidgets/printer_worker.cpp +++ b/src/webenginewidgets/printer_worker.cpp @@ -94,17 +94,9 @@ void PrinterWorker::print() documentCopies = 1; } - bool isLandscape = pdfiumWrapper.pageIsLandscape(0); - QPageLayout::Orientation prevOrientation = m_printer->pageLayout().orientation(); - m_printer->setPageOrientation(isLandscape ? QPageLayout::Landscape : QPageLayout::Portrait); + qreal resolution = m_printer->resolution() / 72.0; // pdfium uses points so 1/72 inch 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++) { if (printedDocuments > 0) @@ -113,11 +105,20 @@ void PrinterWorker::print() int currentPageIndex = fromPage; for (int i = 0; true; i++) { - prevOrientation = m_printer->pageLayout().orientation(); - isLandscape = pdfiumWrapper.pageIsLandscape(currentPageIndex - 1); + QSizeF documentSize = (pdfiumWrapper.pageSize(currentPageIndex - 1) * resolution); + bool isLandscape = documentSize.width() > documentSize.height(); m_printer->setPageOrientation(isLandscape ? QPageLayout::Landscape : QPageLayout::Portrait); - - QSize pageSize = m_printer->pageRect().size(); + QRectF pageRect = m_printer->pageRect(QPrinter::DevicePixel); + documentSize = documentSize.scaled(pageRect.size(), Qt::KeepAspectRatio); + + // setPageOrientation has to be called before qpainter.begin() or before qprinter.newPage() so correct metrics is used, + // therefore call begin now for only first page + if (!painter.isActive() && !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; + } if (i > 0) m_printer->newPage(); @@ -132,21 +133,12 @@ void PrinterWorker::print() if (printedPages > 0) m_printer->newPage(); - QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1); + QImage currentImage = pdfiumWrapper.pageAsQImage(currentPageIndex - 1,documentSize.width(),documentSize.height()); if (currentImage.isNull()) { Q_EMIT resultReady(false); return; } - - QRect targetRect = currentImage.rect(); - // Scale down currentImage by both width and height to fit into the drawable area of the page. - float scaleFactor = (float)pageSize.width() / (float)targetRect.width(); - targetRect = QRect(0, 0, targetRect.width() * scaleFactor, targetRect.height() * scaleFactor); - scaleFactor = (float)pageSize.height() / (float)targetRect.height(); - targetRect = QRect(0, 0, targetRect.width() * scaleFactor, targetRect.height() * scaleFactor); - - // Painting operations are automatically clipped to the bounds of the drawable part of the page. - painter.drawImage(targetRect, currentImage, currentImage.rect()); + painter.drawImage(0,0, currentImage); } if (currentPageIndex == toPage) @@ -156,8 +148,6 @@ void PrinterWorker::print() currentPageIndex++; else currentPageIndex--; - - m_printer->setPageOrientation(prevOrientation); } } painter.end(); diff --git a/tests/auto/quick/dialogs/tst_dialogs.cpp b/tests/auto/quick/dialogs/tst_dialogs.cpp index eee6b2bb6..82ea3be37 100644 --- a/tests/auto/quick/dialogs/tst_dialogs.cpp +++ b/tests/auto/quick/dialogs/tst_dialogs.cpp @@ -28,6 +28,7 @@ #include "testhandler.h" #include "server.h" +#include "util.h" #include <QtWebEngine/private/qquickwebenginedialogrequests_p.h> #include <QtWebEngine/private/qquickwebenginecontextmenurequest_p.h> #include <QQuickWebEngineProfile> @@ -43,6 +44,7 @@ class tst_Dialogs : public QObject { public: tst_Dialogs(){} + private slots: void initTestCase(); void init(); @@ -64,7 +66,6 @@ private: void tst_Dialogs::initTestCase() { - QtWebEngine::initialize(); QQuickWebEngineProfile::defaultProfile()->setOffTheRecord(true); qmlRegisterType<TestHandler>("io.qt.tester", 1, 0, "TestHandler"); m_engine.reset(new QQmlApplicationEngine()); @@ -230,5 +231,5 @@ void tst_Dialogs::javaScriptDialogRequested() } #include "tst_dialogs.moc" -QTEST_MAIN(tst_Dialogs) +W_QTEST_MAIN(tst_Dialogs) diff --git a/tests/auto/quick/shared/util.h b/tests/auto/quick/shared/util.h index c2e7d3e19..bc5ae445b 100644 --- a/tests/auto/quick/shared/util.h +++ b/tests/auto/quick/shared/util.h @@ -36,6 +36,7 @@ #include <QtTest/QtTest> #include <QtWebEngine/private/qquickwebengineview_p.h> #include <QtWebEngine/private/qquickwebengineloadrequest_p.h> +#include <QGuiApplication> #if !defined(TESTS_SOURCE_DIR) #define TESTS_SOURCE_DIR "" @@ -182,4 +183,15 @@ inline QString activeElementId(QQuickWebEngineView *webEngineView) return arguments.at(1).toString(); } +#define W_QTEST_MAIN(TestObject) \ +int main(int argc, char *argv[]) \ +{ \ + QtWebEngine::initialize(); \ + QGuiApplication app(argc, argv); \ + app.setAttribute(Qt::AA_Use96Dpi, true); \ + TestObject tc; \ + QTEST_SET_MAIN_SOURCE_PATH \ + return QTest::qExec(&tc, argc, argv); \ +} #endif /* UTIL_H */ + diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 6373bf2d9..8d5b486b2 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -1051,6 +1051,12 @@ void tst_QWebEnginePage::comboBoxPopupPositionAfterMove() auto jsViewPosition = [&view]() { QLatin1String script("(function() { return [window.screenX, window.screenY]; })()"); QVariantList posList = evaluateJavaScriptSync(view.page(), script).toList(); + + if (posList.count() != 2) { + qWarning("jsViewPosition failed."); + return QPoint(); + } + return QPoint(posList.at(0).toInt(), posList.at(1).toInt()); }; @@ -1247,16 +1253,21 @@ public: load(QUrl("qrc:///resources/content.html")); } - void jsGetUserMedia(const QString & constraints) + void jsGetMedia(const QString &call) { evaluateJavaScriptSync(this, QStringLiteral( "var promiseFulfilled = false;" "var promiseRejected = false;" - "navigator.mediaDevices.getUserMedia(%1)" + "navigator.mediaDevices.%1" ".then(stream => { promiseFulfilled = true})" ".catch(err => { promiseRejected = true})") - .arg(constraints)); + .arg(call)); + } + + void jsGetUserMedia(const QString &constraints) + { + jsGetMedia(QStringLiteral("getUserMedia(%1)").arg(constraints)); } bool jsPromiseFulfilled() @@ -1313,32 +1324,34 @@ private: void tst_QWebEnginePage::getUserMediaRequest_data() { - QTest::addColumn<QString>("constraints"); + QTest::addColumn<QString>("call"); QTest::addColumn<QWebEnginePage::Feature>("feature"); QTest::addRow("device audio") - << "{audio: true}" << QWebEnginePage::MediaAudioCapture; + << "getUserMedia({audio: true})" << QWebEnginePage::MediaAudioCapture; QTest::addRow("device video") - << "{video: true}" << QWebEnginePage::MediaVideoCapture; + << "getUserMedia({video: true})" << QWebEnginePage::MediaVideoCapture; QTest::addRow("device audio+video") - << "{audio: true, video: true}" << QWebEnginePage::MediaAudioVideoCapture; + << "getUserMedia({audio: true, video: true})" << QWebEnginePage::MediaAudioVideoCapture; QTest::addRow("desktop video") - << "{video: { mandatory: { chromeMediaSource: 'desktop' }}}" + << "getUserMedia({video: { mandatory: { chromeMediaSource: 'desktop' }}})" << QWebEnginePage::DesktopVideoCapture; QTest::addRow("desktop audio+video") - << "{audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}}" + << "getUserMedia({audio: { mandatory: { chromeMediaSource: 'desktop' }}, video: { mandatory: { chromeMediaSource: 'desktop' }}})" << QWebEnginePage::DesktopAudioVideoCapture; + QTest::addRow("display video") + << "getDisplayMedia()" << QWebEnginePage::DesktopVideoCapture; } void tst_QWebEnginePage::getUserMediaRequest() { - QFETCH(QString, constraints); + QFETCH(QString, call); QFETCH(QWebEnginePage::Feature, feature); GetUserMediaTestPage page; + QWebEngineView view; if (feature == QWebEnginePage::DesktopVideoCapture || feature == QWebEnginePage::DesktopAudioVideoCapture) { // Desktop capture needs to be on a desktop. - QWebEngineView view; view.setPage(&page); view.resize(640, 480); view.show(); @@ -1349,7 +1362,7 @@ void tst_QWebEnginePage::getUserMediaRequest() page.settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); // 1. Rejecting request on C++ side should reject promise on JS side. - page.jsGetUserMedia(constraints); + page.jsGetMedia(call); QTRY_VERIFY(page.gotFeatureRequest(feature)); page.rejectPendingRequest(); QTRY_VERIFY(!page.jsPromiseFulfilled() && page.jsPromiseRejected()); @@ -1359,13 +1372,13 @@ void tst_QWebEnginePage::getUserMediaRequest() // deeper in the content layer we cannot guarantee that the promise will // always be fulfilled, however in this case an error should be returned to // JS instead of leaving the Promise in limbo. - page.jsGetUserMedia(constraints); + page.jsGetMedia(call); QTRY_VERIFY(page.gotFeatureRequest(feature)); page.acceptPendingRequest(); QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected()); // 3. Media feature permissions are not remembered. - page.jsGetUserMedia(constraints); + page.jsGetMedia(call); QTRY_VERIFY(page.gotFeatureRequest(feature)); page.acceptPendingRequest(); QTRY_VERIFY(page.jsPromiseFulfilled() || page.jsPromiseRejected()); diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp index 8b75067ee..921831700 100644 --- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp +++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp @@ -40,6 +40,12 @@ #include <QtWebEngineWidgets/qwebengineview.h> #include <QtWebEngineWidgets/qwebenginedownloaditem.h> +#if QT_CONFIG(webengine_webchannel) +#include <QWebChannel> +#endif + +#include <map> + class tst_QWebEngineProfile : public QObject { Q_OBJECT @@ -58,6 +64,7 @@ private Q_SLOTS: void urlSchemeHandlerStreaming(); void urlSchemeHandlerRequestHeaders(); void urlSchemeHandlerInstallation(); + void urlSchemeHandlerXhrStatus(); void customUserAgent(); void httpAcceptLanguage(); void downloadItem(); @@ -568,6 +575,109 @@ void tst_QWebEngineProfile::urlSchemeHandlerInstallation() profile.removeUrlScheme("tst"); } +#if QT_CONFIG(webengine_webchannel) +class XhrStatusHost : public QObject +{ + Q_OBJECT +public: + std::map<QUrl, int> requests; + + bool isReady() + { + static const auto sig = QMetaMethod::fromSignal(&XhrStatusHost::load); + return isSignalConnected(sig); + } + +Q_SIGNALS: + void load(QUrl url); + +public Q_SLOTS: + void loadFinished(QUrl url, int status) + { + requests[url] = status; + } + +private: +}; + +class XhrStatusUrlSchemeHandler : public QWebEngineUrlSchemeHandler +{ +public: + void requestStarted(QWebEngineUrlRequestJob *job) + { + QString path = job->requestUrl().path(); + if (path == "/") { + QBuffer *buffer = new QBuffer(job); + buffer->open(QBuffer::ReadWrite); + buffer->write(QByteArrayLiteral(R"( +<html> + <body> + <script src="qwebchannel.js"></script> + <script> + new QWebChannel(qt.webChannelTransport, (channel) => { + const host = channel.objects.host; + host.load.connect((url) => { + const xhr = new XMLHttpRequest(); + xhr.onload = () => { host.loadFinished(url, xhr.status); }; + xhr.onerror = () => { host.loadFinished(url, -1); }; + xhr.open("GET", url, true); + xhr.send(); + }); + }); + </script> + </body> +</html> +)")); + buffer->seek(0); + job->reply("text/html", buffer); + } else if (path == "/qwebchannel.js") { + QFile *file = new QFile(":/qtwebchannel/qwebchannel.js", job); + file->open(QFile::ReadOnly); + job->reply("application/javascript", file); + } else if (path == "/ok") { + QBuffer *buffer = new QBuffer(job); + buffer->buffer() = QByteArrayLiteral("ok"); + job->reply("text/plain", buffer); + } else if (path == "/redirect") { + QUrl url = job->requestUrl(); + url.setPath("/ok"); + job->redirect(url); + } else if (path == "/fail") { + job->fail(QWebEngineUrlRequestJob::RequestFailed); + } else { + job->fail(QWebEngineUrlRequestJob::UrlNotFound); + } + } +}; +#endif + +void tst_QWebEngineProfile::urlSchemeHandlerXhrStatus() +{ +#if QT_CONFIG(webengine_webchannel) + XhrStatusUrlSchemeHandler handler; + XhrStatusHost host; + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QWebChannel channel; + channel.registerObject("host", &host); + profile.installUrlSchemeHandler("aviancarrier", &handler); + page.setWebChannel(&channel); + page.load(QUrl("aviancarrier:/")); + QTRY_VERIFY(host.isReady()); + host.load(QUrl("aviancarrier:/ok")); + host.load(QUrl("aviancarrier:/redirect")); + host.load(QUrl("aviancarrier:/fail")); + host.load(QUrl("aviancarrier:/notfound")); + QTRY_COMPARE(host.requests.size(), 4u); + QCOMPARE(host.requests[QUrl("aviancarrier:/ok")], 200); + QCOMPARE(host.requests[QUrl("aviancarrier:/redirect")], 200); + QCOMPARE(host.requests[QUrl("aviancarrier:/fail")], -1); + QCOMPARE(host.requests[QUrl("aviancarrier:/notfound")], -1); +#else + QSKIP("No QtWebChannel"); +#endif +} + void tst_QWebEngineProfile::customUserAgent() { QString defaultUserAgent = QWebEngineProfile::defaultProfile()->httpUserAgent(); |