diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-07-25 11:12:16 +0200 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2019-07-25 14:50:41 +0200 |
commit | 83fa1d5f9b890ba251f7dc8f82acd950e9527343 (patch) | |
tree | c9beec83e2454bc7d001bdc116620b3cc323d7a4 | |
parent | 9cfde2edf4736ae9533e7e1fac495a23ad905419 (diff) | |
parent | 562b294373a99d979df363e9bbbab2e5b77c67df (diff) | |
download | qtwebengine-83fa1d5f9b890ba251f7dc8f82acd950e9527343.tar.gz |
Merge "Merge remote-tracking branch 'origin/dev' into wip/qt6"
43 files changed, 413 insertions, 155 deletions
diff --git a/mkspecs/features/configure.prf b/mkspecs/features/configure.prf index b8d9d43ce..cc84182b7 100644 --- a/mkspecs/features/configure.prf +++ b/mkspecs/features/configure.prf @@ -94,13 +94,9 @@ defineTest(runConfigure) { } } - !qtConfig(webengine-embedded-build): qtConfig(xcb) { - for(package, $$list("libdrm xcomposite xcursor xi xtst")) { - !qtConfig(webengine-system-$$package) { - skipBuild("A suitable version of $$package could not be found.") - return(false) - } - } + !qtConfig(webengine-embedded-build): qtConfig(xcb) : !qtConfig(webengine-ozone-x11) { + skipBuild("Could not find all necessary libraries for qpa-xcb support") + return(false) } } } diff --git a/mkspecs/features/functions.prf b/mkspecs/features/functions.prf index 9efa8958f..d3eda85b2 100644 --- a/mkspecs/features/functions.prf +++ b/mkspecs/features/functions.prf @@ -121,11 +121,3 @@ defineReplace(pkgConfigHostExecutable) { return($$system_quote($$system_path($$wrapper_name))) } -defineTest(hasX11Dependencies) { - for(package, $$list("libdrm xcomposite xcursor xi xtst")) { - !qtConfig(webengine-system-$$package) { - return(false) - } - } - return(true) -} diff --git a/mkspecs/features/platform.prf b/mkspecs/features/platform.prf index 53ea51014..ef1c86d39 100644 --- a/mkspecs/features/platform.prf +++ b/mkspecs/features/platform.prf @@ -29,8 +29,8 @@ defineTest(isPlatformSupported) { skipBuild("Qt WebEngine on Windows requires MSVC or Clang (MSVC mode).") return(false) } - !isMinWinSDKVersion(10, 16299): { - skipBuild("Qt WebEngine on Windows requires a Windows SDK version 10.0.16299 or newer.") + !isMinWinSDKVersion(10, 17763): { + skipBuild("Qt WebEngine on Windows requires a Windows SDK version 10.0.17763 or newer.") return(false) } !qtConfig(webengine-winversion) { diff --git a/src/3rdparty b/src/3rdparty -Subproject be2f3bb839aab9ad8ba7b545b8a2bc91ce6702d +Subproject f5613a4bc321972b8f72654d4c4bc9ba0c36ffb diff --git a/src/core/api/qtwebenginecoreglobal.cpp b/src/core/api/qtwebenginecoreglobal.cpp index abc2bd406..0fddacb15 100644 --- a/src/core/api/qtwebenginecoreglobal.cpp +++ b/src/core/api/qtwebenginecoreglobal.cpp @@ -80,6 +80,8 @@ static QOpenGLContext *shareContext; static void deleteShareContext() { + if (qt_gl_global_share_context() == shareContext) + qt_gl_set_global_share_context(nullptr); delete shareContext; shareContext = 0; } diff --git a/src/core/api/qwebengineurlscheme.cpp b/src/core/api/qwebengineurlscheme.cpp index f4efad717..f73992c6b 100644 --- a/src/core/api/qwebengineurlscheme.cpp +++ b/src/core/api/qwebengineurlscheme.cpp @@ -59,6 +59,7 @@ ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::NoAccessAllowed, url::CustomScheme::NoAc ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ServiceWorkersAllowed, url::CustomScheme::ServiceWorkersAllowed) ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ViewSourceAllowed, url::CustomScheme::ViewSourceAllowed) ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::ContentSecurityPolicyIgnored, url::CustomScheme::ContentSecurityPolicyIgnored) +ASSERT_ENUMS_MATCH(QWebEngineUrlScheme::CorsEnabled, url::CustomScheme::CorsEnabled) static bool g_schemesLocked = false; @@ -190,6 +191,13 @@ public: \value ContentSecurityPolicyIgnored Indicates that accesses to this scheme should bypass all Content-Security-Policy checks. + + \value CorsEnabled + Enables cross-origin resource sharing (CORS) for this scheme. This flag is + required in order to, for example, use the scheme with the \l + {https://fetch.spec.whatwg.org/}{Fetch API}, or to deliver CSS fonts to a + different origin. The appropriate CORS headers are generated automatically by + the QWebEngineUrlRequestJob class. (Added in Qt 5.14) */ QWebEngineUrlScheme::QWebEngineUrlScheme(QWebEngineUrlSchemePrivate *d) : d(d) {} @@ -374,7 +382,7 @@ void QWebEngineUrlScheme::registerScheme(const QWebEngineUrlScheme &scheme) return; } - if (url::IsStandard(scheme.d->name.data(), url::Component(0, scheme.d->name.size()))) { + if (url::IsStandard(scheme.d->name.data(), url::Component(0, static_cast<int>(scheme.d->name.size())))) { qWarning() << "QWebEngineUrlScheme::registerScheme: Scheme" << scheme.name() << "is a standard scheme"; return; } diff --git a/src/core/api/qwebengineurlscheme.h b/src/core/api/qwebengineurlscheme.h index 095b47320..ecac44184 100644 --- a/src/core/api/qwebengineurlscheme.h +++ b/src/core/api/qwebengineurlscheme.h @@ -76,6 +76,7 @@ public: ServiceWorkersAllowed = 0x10, ViewSourceAllowed = 0x20, ContentSecurityPolicyIgnored = 0x40, + CorsEnabled = 0x80, }; Q_DECLARE_FLAGS(Flags, Flag) Q_FLAG(Flags) diff --git a/src/core/compositor/compositor_resource_tracker.cpp b/src/core/compositor/compositor_resource_tracker.cpp index 73242c693..741c2717c 100644 --- a/src/core/compositor/compositor_resource_tracker.cpp +++ b/src/core/compositor/compositor_resource_tracker.cpp @@ -174,7 +174,8 @@ quint32 CompositorResourceTracker::consumeMailbox(const gpu::MailboxHolder &mail DCHECK(mailboxManager); if (mailboxHolder.sync_token.HasData()) mailboxManager->PullTextureUpdates(mailboxHolder.sync_token); - return service_id(mailboxManager->ConsumeTexture(mailboxHolder.mailbox)); + gpu::TextureBase *tex = mailboxManager->ConsumeTexture(mailboxHolder.mailbox); + return tex ? service_id(tex) : 0; #else NOTREACHED(); #endif // QT_CONFIG(OPENGL) diff --git a/src/core/compositor/delegated_frame_node.cpp b/src/core/compositor/delegated_frame_node.cpp index dc67b03e2..19e8d1b82 100644 --- a/src/core/compositor/delegated_frame_node.cpp +++ b/src/core/compositor/delegated_frame_node.cpp @@ -195,12 +195,10 @@ public: { Q_ASSERT(layer); Q_ASSERT(m_nodeIterator != m_sceneGraphNodes->end()); - QSGInternalImageNode *imageNode = static_cast<QSGInternalImageNode*>(*m_nodeIterator++); - imageNode->setTargetRect(rect); - imageNode->setInnerTargetRect(rect); - imageNode->setSubSourceRect(layer->convertToNormalizedSourceRect(sourceRect)); + QSGImageNode *imageNode = static_cast<QSGImageNode*>(*m_nodeIterator++); + imageNode->setRect(rect); + imageNode->setSourceRect(sourceRect); imageNode->setTexture(layer); - imageNode->update(); } void setupTextureContentNode(QSGTexture *texture, const QRect &rect, const QRectF &sourceRect, @@ -281,13 +279,10 @@ public: QSGNode *layerChain) override { Q_ASSERT(layer); - // Only QSGInternalImageNode currently supports QSGLayer textures. - QSGInternalImageNode *imageNode = m_apiDelegate->createInternalImageNode(); - imageNode->setTargetRect(rect); - imageNode->setInnerTargetRect(rect); - imageNode->setSubSourceRect(layer->convertToNormalizedSourceRect(sourceRect)); + QSGImageNode *imageNode = m_apiDelegate->createImageNode(); + imageNode->setRect(rect); + imageNode->setSourceRect(sourceRect); imageNode->setTexture(layer); - imageNode->update(); layerChain->appendChildNode(imageNode); m_sceneGraphNodes->append(imageNode); diff --git a/src/core/config/common.pri b/src/core/config/common.pri index dea904ef8..edebdb33c 100644 --- a/src/core/config/common.pri +++ b/src/core/config/common.pri @@ -127,3 +127,7 @@ qtConfig(webengine-kerberos) { } else { gn_args += use_kerberos=false } + +ccache { + gn_args += cc_wrapper=\"ccache\" +} diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri index 40bc0b73c..998aedc40 100644 --- a/src/core/config/linux.pri +++ b/src/core/config/linux.pri @@ -180,7 +180,7 @@ host_build { } !packagesExist(libpci): gn_args += use_libpci=false - qtConfig(webengine-system-x11): hasX11Dependencies() { + qtConfig(webengine-ozone-x11) { gn_args += ozone_platform_x11=true packagesExist(xscrnsaver): gn_args += use_xscrnsaver=true } diff --git a/src/core/configure.json b/src/core/configure.json index 98eee347f..66e39ddc2 100644 --- a/src/core/configure.json +++ b/src/core/configure.json @@ -11,6 +11,7 @@ "options": { "webengine-alsa": "boolean", "webengine-embedded-build": "boolean", + "webengine-full-debug-info": "boolean", "webengine-icu": { "type": "enum", "name": "webengine-system-icu", "values": { "system": "yes", "qt": "no" } }, "webengine-ffmpeg": { "type": "enum", "name": "webengine-system-ffmpeg", "values": { "system": "yes", "qt": "no" } }, "webengine-opus": { "type": "enum", "name": "webengine-system-opus", "values": { "system": "yes", "qt": "no" } }, @@ -650,6 +651,17 @@ "condition": "config.unix && libs.webengine-x11", "output": [ "privateFeature" ] }, + "webengine-ozone-x11" : { + "label": "Support qpa-xcb", + "condition": "config.unix + && features.webengine-system-x11 + && features.webengine-system-libdrm + && features.webengine-system-xcomposite + && features.webengine-system-xcursor + && features.webengine-system-xi + && features.webengine-system-xtst", + "output": [ "privateFeature" ] + }, "webengine-sanitizer" : { "label": "Sanitizer", "autoDetect": "config.sanitizer && tests.webengine-sanitizer", @@ -685,6 +697,16 @@ "section": "WebEngine", "output": [ "privateFeature" ] }, + "webengine-full-debug-info": { + "label": "Full debug information", + "purpose": "Enables debug information for Blink and V8.", + "autoDetect": false, + "condition": "config.debug || features.debug_and_release || features.force_debug_info", + "output": [ + { "type": "privateConfig", "name": "v8base_debug" }, + { "type": "privateConfig", "name": "webcore_debug" } + ] + }, "webengine-noexecstack": { "label": "linker supports -z noexecstack", "condition": "config.unix && tests.webengine-noexecstack", @@ -742,6 +764,7 @@ "webengine-widgets", "webengine-qml", "webengine-embedded-build", + "webengine-full-debug-info", "webengine-pepper-plugins", "webengine-printing-and-pdf", "webengine-proprietary-codecs", @@ -756,6 +779,11 @@ "webengine-extensions", { "type": "feature", + "args": "webengine-ozone-x11", + "condition": "config.unix" + }, + { + "type": "feature", "args": "webengine-v8-snapshot-support", "condition": "config.unix && config.cross_compile && features.webengine-v8-snapshot" }, @@ -826,8 +854,7 @@ "webengine-system-png", "webengine-system-jpeg", "webengine-system-harfbuzz", - "webengine-system-freetype", - "webengine-system-x11" + "webengine-system-freetype" ] }, { @@ -845,6 +872,7 @@ "section": "Required system libraries for qpa-xcb", "condition": "config.unix && !config.macos", "entries": [ + "webengine-system-x11", "webengine-system-libdrm", "webengine-system-xcomposite", "webengine-system-xcursor", diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index f99513adf..908387788 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -246,7 +246,7 @@ HEADERS = \ web_engine_settings.h \ web_event_factory.h -qtConfig(webengine-system-x11): hasX11Dependencies() { +qtConfig(webengine-ozone-x11) { HEADERS += ozone/gl_ozone_glx_qt.h \ ozone/gl_surface_glx_qt.h SOURCES += ozone/gl_surface_glx_qt.cpp \ diff --git a/src/core/core_module.pro b/src/core/core_module.pro index 02aa6ac38..b220af4a5 100644 --- a/src/core/core_module.pro +++ b/src/core/core_module.pro @@ -42,11 +42,15 @@ LIBS_PRIVATE += $$NINJA_LIB_DIRS $$NINJA_LIBS unix:qtConfig(webengine-noexecstack): \ QMAKE_LFLAGS += -Wl,-z,noexecstack linux { - QMAKE_LFLAGS += -Wl,--gc-sections -Wl,-O1 -Wl,-z,now - # Embedded address sanitizer symbols are undefined and are picked up by the dynamic link loader - # at runtime. Thus we do not to pass the linker flag below, because the linker would complain - # about the undefined sanitizer symbols. - !sanitizer: QMAKE_LFLAGS += -Wl,-z,defs + # add chromium flags + for(flag, NINJA_LFLAGS) { + # filter out some flags + !contains(flag, .*noexecstack$): \ + !contains(flag, .*as-needed$): \ + !contains(flag, ^-B.*): \ + !contains(flag, ^-fuse-ld.*): \ + QMAKE_LFLAGS += $$flag + } } else { QMAKE_LFLAGS += $$NINJA_LFLAGS } diff --git a/src/core/net/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp index 74a77a7ec..73bf24f1d 100644 --- a/src/core/net/qrc_url_scheme_handler.cpp +++ b/src/core/net/qrc_url_scheme_handler.cpp @@ -59,6 +59,11 @@ void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) QUrl requestUrl = job->requestUrl(); QString requestPath = requestUrl.path(); QScopedPointer<QFile> file(new QFile(':' + requestPath, job)); + if (!file->exists() || file->size() == 0) { + qWarning("QResource '%s' not found or is empty", qUtf8Printable(requestPath)); + job->fail(QWebEngineUrlRequestJob::UrlNotFound); + return; + } QFileInfo fileInfo(*file); QMimeDatabase mimeDatabase; QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo); diff --git a/src/core/net/url_request_custom_job.cpp b/src/core/net/url_request_custom_job.cpp index cba9b4dc5..dd213d4f8 100644 --- a/src/core/net/url_request_custom_job.cpp +++ b/src/core/net/url_request_custom_job.cpp @@ -40,10 +40,13 @@ #include "url_request_custom_job.h" #include "url_request_custom_job_proxy.h" +#include "api/qwebengineurlscheme.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> @@ -62,6 +65,9 @@ URLRequestCustomJob::URLRequestCustomJob(URLRequest *request, , m_pendingReadSize(0) , m_pendingReadPos(0) , m_pendingReadBuffer(nullptr) + , m_corsEnabled(QWebEngineUrlScheme::schemeByName(QByteArray::fromStdString(scheme)) + .flags().testFlag(QWebEngineUrlScheme::CorsEnabled)) + , m_httpStatusCode(500) { } @@ -136,6 +142,26 @@ bool URLRequestCustomJob::GetCharset(std::string* charset) return false; } +void URLRequestCustomJob::GetResponseInfo(HttpResponseInfo* info) +{ + // Based on net::URLRequestRedirectJob::StartAsync() + + if (!m_corsEnabled) + return; + + std::string headers; + headers += base::StringPrintf("HTTP/1.1 %i OK\n", m_httpStatusCode); + if (m_redirect.is_valid()) + headers += base::StringPrintf("Location: %s\n", m_redirect.spec().c_str()); + std::string origin; + if (request_->extra_request_headers().GetHeader("Origin", &origin)) { + headers += base::StringPrintf("Access-Control-Allow-Origin: %s\n", origin.c_str()); + headers += "Access-Control-Allow-Credentials: true\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..e1e8e9fba 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: @@ -80,6 +81,8 @@ private: int m_pendingReadSize; int m_pendingReadPos; net::IOBuffer *m_pendingReadBuffer; + const bool m_corsEnabled; + int m_httpStatusCode; friend class URLRequestCustomJobProxy; diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index 72d14450e..b9ccf7ea4 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -100,6 +100,7 @@ void URLRequestCustomJobProxy::reply(std::string mimeType, QIODevice *device) m_job->set_expected_content_size(size); if (m_job->m_device && m_job->m_device->isReadable()) { m_started = true; + m_job->m_httpStatusCode = 200; m_job->NotifyHeadersComplete(); } else { fail(ERR_INVALID_URL); @@ -114,6 +115,7 @@ void URLRequestCustomJobProxy::redirect(GURL url) if (m_job->m_device || m_job->m_error) return; m_job->m_redirect = url; + m_job->m_httpStatusCode = 303; m_started = true; m_job->NotifyHeadersComplete(); } @@ -138,6 +140,7 @@ void URLRequestCustomJobProxy::fail(int error) if (!m_job) return; m_job->m_error = error; + m_job->m_httpStatusCode = 500; if (m_job->m_device) m_job->m_device->close(); if (!m_started) diff --git a/src/core/pref_service_adapter.cpp b/src/core/pref_service_adapter.cpp index 0cf53dda5..8a33a4b60 100644 --- a/src/core/pref_service_adapter.cpp +++ b/src/core/pref_service_adapter.cpp @@ -137,6 +137,11 @@ void PrefServiceAdapter::setup(const ProfileAdapter &profileAdapter) content::BrowserContext::CreateRandomMediaDeviceIDSalt()); } +#if QT_CONFIG(webengine_spellchecker) + // Ignore stored values for these options to preserve backwards compatibility. + m_prefService->ClearPref(spellcheck::prefs::kSpellCheckEnable); + m_prefService->ClearPref(spellcheck::prefs::kSpellCheckDictionaries); +#endif // QT_CONFIG(webengine_spellchecker) } void PrefServiceAdapter::commit() diff --git a/src/core/profile_adapter.cpp b/src/core/profile_adapter.cpp index fdbdbfdcb..551663cf0 100644 --- a/src/core/profile_adapter.cpp +++ b/src/core/profile_adapter.cpp @@ -438,7 +438,7 @@ void ProfileAdapter::setHttpCacheMaxSize(int maxSize) m_profile->m_profileIOData->updateHttpCache(); } -enum class SchemeType { Protected, Overridable, Custom }; +enum class SchemeType { Protected, Overridable, Custom, Unknown }; static SchemeType schemeType(const QByteArray &canonicalScheme) { static const QSet<QByteArray> blacklist{ @@ -463,7 +463,9 @@ static SchemeType schemeType(const QByteArray &canonicalScheme) return SchemeType::Overridable; if (blacklisted || (standardSyntax && !customScheme)) return SchemeType::Protected; - return SchemeType::Custom; + if (customScheme) + return SchemeType::Custom; + return SchemeType::Unknown; } QWebEngineUrlSchemeHandler *ProfileAdapter::urlSchemeHandler(const QByteArray &scheme) @@ -530,7 +532,7 @@ void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngin return; } - if (type == SchemeType::Custom) + if (type == SchemeType::Unknown) qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() " "before installing the custom scheme handler.", scheme.constData()); diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index a5ae0a04a..0242506ee 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -238,7 +238,10 @@ public: float GetHistoricalTouchMajor(size_t pointer_index, size_t historical_index) const override { return 0; } float GetHistoricalX(size_t pointer_index, size_t historical_index) const override { return 0; } float GetHistoricalY(size_t pointer_index, size_t historical_index) const override { return 0; } - ToolType GetToolType(size_t pointer_index) const override { return ui::MotionEvent::ToolType::FINGER; } + ToolType GetToolType(size_t pointer_index) const override { + return (touchPoints.at(pointer_index).flags() & QTouchEvent::TouchPoint::InfoFlag::Pen) ? ui::MotionEvent::ToolType::STYLUS + : ui::MotionEvent::ToolType::FINGER; + } int GetButtonState() const override { return 0; } private: diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h index 6066284d9..4ee790ce9 100644 --- a/src/core/render_widget_host_view_qt_delegate.h +++ b/src/core/render_widget_host_view_qt_delegate.h @@ -65,7 +65,6 @@ class QSGTexture; class QVariant; class QInputMethodEvent; -class QSGInternalImageNode; class QSGImageNode; QT_END_NAMESPACE @@ -102,7 +101,6 @@ public: virtual QWindow* window() const = 0; virtual QSGTexture *createTextureFromImage(const QImage &) = 0; virtual QSGLayer *createLayer() = 0; - virtual QSGInternalImageNode *createInternalImageNode() = 0; virtual QSGImageNode *createImageNode() = 0; virtual QSGRectangleNode *createRectangleNode() = 0; virtual void update() = 0; diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index 2295d33c0..ee0b8cc39 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -693,19 +693,23 @@ void WebContentsAdapter::load(const QWebEngineHttpRequest &request) } } - auto navigate = [](WebContentsAdapter *adapter, const content::NavigationController::LoadURLParams ¶ms) { + auto navigate = [](QWeakPointer<WebContentsAdapter> weakAdapter, const content::NavigationController::LoadURLParams ¶ms) { + WebContentsAdapter *adapter = weakAdapter.data(); + if (!adapter) + return; adapter->webContents()->GetController().LoadURLWithParams(params); // Follow chrome::Navigate and invalidate the URL immediately. adapter->m_webContentsDelegate->NavigationStateChanged(adapter->webContents(), content::INVALIDATE_TYPE_URL); adapter->focusIfNecessary(); }; + QWeakPointer<WebContentsAdapter> weakThis(sharedFromThis()); if (resizeNeeded) { // Schedule navigation on the event loop. base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(navigate, this, std::move(params))); + base::BindOnce(navigate, std::move(weakThis), std::move(params))); } else { - navigate(this, params); + navigate(std::move(weakThis), params); } } @@ -1051,6 +1055,7 @@ quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitiv // waiting for it forever. // Assume that any unfinished find has been unsuccessful when a new one is started // to cover that case. + m_webContentsDelegate->setLastReceivedFindReply(m_lastFindRequestId); m_adapterClient->didFindText(m_lastFindRequestId, 0); } @@ -1071,6 +1076,7 @@ quint64 WebContentsAdapter::findText(const QString &subString, bool caseSensitiv void WebContentsAdapter::stopFinding() { CHECK_INITIALIZED(); + m_webContentsDelegate->setLastReceivedFindReply(m_lastFindRequestId); m_webContentsDelegate->setLastSearchedString(QString()); m_webContents->StopFinding(content::STOP_FIND_ACTION_KEEP_SELECTION); } diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index 93f441636..f18617f8e 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -234,6 +234,11 @@ void WebContentsDelegateQt::AddNewContents(content::WebContents* source, std::un { Q_UNUSED(source) QSharedPointer<WebContentsAdapter> newAdapter = createWindow(std::move(new_contents), disposition, initial_pos, user_gesture); + // Chromium can forget to pass user-agent override settings to new windows (see QTBUG-61774 and QTBUG-76249), + // so set it here. Note the actual value doesn't really matter here. Only the second value does, but we try + // to give the correct user-agent anyway. + if (newAdapter) + newAdapter->webContents()->SetUserAgentOverride(newAdapter->profileAdapter()->httpUserAgent().toStdString(), true); if (newAdapter && !newAdapter->isInitialized()) newAdapter->loadDefault(); if (was_blocked) @@ -575,7 +580,7 @@ void WebContentsDelegateQt::FindReply(content::WebContents *source, int request_ Q_UNUSED(source) Q_UNUSED(selection_rect) Q_UNUSED(active_match_ordinal) - if (final_update) { + if (final_update && request_id > m_lastReceivedFindReply) { m_lastReceivedFindReply = request_id; m_viewClient->didFindText(request_id, number_of_matches); } diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index 11b35c549..00b715c30 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -115,6 +115,7 @@ public: QString lastSearchedString() const { return m_lastSearchedString; } void setLastSearchedString(const QString &s) { m_lastSearchedString = s; } int lastReceivedFindReply() const { return m_lastReceivedFindReply; } + void setLastReceivedFindReply(int id) { m_lastReceivedFindReply = id; } QUrl url() const { return m_url; } QString title() const { return m_title; } diff --git a/src/webengine/doc/qtwebengine.qdocconf b/src/webengine/doc/qtwebengine.qdocconf index 50f2e2ceb..be5db9c19 100644 --- a/src/webengine/doc/qtwebengine.qdocconf +++ b/src/webengine/doc/qtwebengine.qdocconf @@ -1,4 +1,5 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) +include($QT_INSTALL_DOCS/config/exampleurl-qtwebengine.qdocconf) project = QtWebEngine description = Qt WebEngine Reference Documentation @@ -60,12 +61,14 @@ depends += qtcore \ qtwidgets headerdirs += .. \ - ../../core \ - ../../webenginewidgets + ../../core/api \ + ../../webenginewidgets/api sourcedirs += .. \ - ../../core/ \ - ../../webenginewidgets \ + ../../core/api \ + ../../core/doc \ + ../../webenginewidgets/api \ + ../../webenginewidgets/doc exampledirs += . \ ../../../examples \ @@ -84,4 +87,4 @@ navigation.qmltypespage = "Qt WebEngine QML Types" # \QWE macro expands to 'Qt WebEngine' without auto-linking anywhere. macro.QWE = "Qt \\WebEngine" -Cpp.ignoretokens += Q_WEBENGINE_EXPORT QWEBENGINEWIDGETS_EXPORT +Cpp.ignoretokens += Q_WEBENGINE_EXPORT Q_WEBENGINECORE_EXPORT QWEBENGINEWIDGETS_EXPORT diff --git a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc index fefb47795..5b7d750ff 100644 --- a/src/webengine/doc/src/qtwebengine-platform-notes.qdoc +++ b/src/webengine/doc/src/qtwebengine-platform-notes.qdoc @@ -69,7 +69,12 @@ \section2 Windows - On Windows, Visual Studio 2017 and Windows 10 SDK are required. + On Windows, the following additional tools are required: + + \list + \li Visual Studio 2017 version 15.8 or later + \li Windows 10 SDK + \endlist \QWE can only be built on 64-bit Windows, with a x64-bit toolchain. For building \QWE for x86 applications, you need to configure diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp index b636448b3..3a103b9aa 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp @@ -170,12 +170,6 @@ QSGLayer *RenderWidgetHostViewQtDelegateQuick::createLayer() return renderContext->sceneGraphContext()->createLayer(renderContext); } -QSGInternalImageNode *RenderWidgetHostViewQtDelegateQuick::createInternalImageNode() -{ - QSGRenderContext *renderContext = QQuickWindowPrivate::get(QQuickItem::window())->context; - return renderContext->sceneGraphContext()->createInternalImageNode(); -} - QSGImageNode *RenderWidgetHostViewQtDelegateQuick::createImageNode() { return QQuickItem::window()->createImageNode(); diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.h b/src/webengine/render_widget_host_view_qt_delegate_quick.h index 00158b3ac..b55b2d658 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quick.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quick.h @@ -71,7 +71,6 @@ public: QWindow* window() const override; QSGTexture *createTextureFromImage(const QImage &) override; QSGLayer *createLayer() override; - QSGInternalImageNode *createInternalImageNode() override; QSGImageNode *createImageNode() override; QSGRectangleNode *createRectangleNode() override; void update() override; diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp index c085aacd7..23b9e02c2 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.cpp @@ -131,11 +131,6 @@ QSGLayer *RenderWidgetHostViewQtDelegateQuickWindow::createLayer() return m_realDelegate->createLayer(); } -QSGInternalImageNode *RenderWidgetHostViewQtDelegateQuickWindow::createInternalImageNode() -{ - return m_realDelegate->createInternalImageNode(); -} - QSGImageNode *RenderWidgetHostViewQtDelegateQuickWindow::createImageNode() { return m_realDelegate->createImageNode(); diff --git a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h index ab583bd63..bebbfa439 100644 --- a/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h +++ b/src/webengine/render_widget_host_view_qt_delegate_quickwindow.h @@ -68,7 +68,6 @@ public: QWindow* window() const override; QSGTexture *createTextureFromImage(const QImage &) override; QSGLayer *createLayer() override; - QSGInternalImageNode *createInternalImageNode() override; QSGImageNode *createImageNode() override; QSGRectangleNode *createRectangleNode() override; void update() override; 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 817d6e408..894dca4fa 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp @@ -77,14 +77,6 @@ protected: { m_client->forwardEvent(event); } - void keyPressEvent(QKeyEvent *event) override - { - m_client->forwardEvent(event); - } - void keyReleaseEvent(QKeyEvent *event) override - { - m_client->forwardEvent(event); - } void inputMethodEvent(QInputMethodEvent *event) override { m_client->forwardEvent(event); @@ -309,12 +301,6 @@ QSGLayer *RenderWidgetHostViewQtDelegateWidget::createLayer() return renderContext->sceneGraphContext()->createLayer(renderContext); } -QSGInternalImageNode *RenderWidgetHostViewQtDelegateWidget::createInternalImageNode() -{ - QSGRenderContext *renderContext = QQuickWindowPrivate::get(quickWindow())->context; - return renderContext->sceneGraphContext()->createInternalImageNode(); -} - QSGImageNode *RenderWidgetHostViewQtDelegateWidget::createImageNode() { return quickWindow()->createImageNode(); @@ -479,7 +465,7 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event) // where we can simply ignore the DblClick event. QMouseEvent *dblClick = static_cast<QMouseEvent *>(event); QMouseEvent press(QEvent::MouseButtonPress, dblClick->localPos(), dblClick->windowPos(), dblClick->screenPos(), - dblClick->button(), dblClick->buttons(), dblClick->modifiers()); + dblClick->button(), dblClick->buttons(), dblClick->modifiers(), dblClick->source()); press.setTimestamp(dblClick->timestamp()); handled = m_client->forwardEvent(&press); } else @@ -487,6 +473,8 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event) if (!handled) return QQuickWidget::event(event); + // Most events are accepted by default, but tablet events are not: + event->accept(); return true; } diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h index 7746c4405..18f848da5 100644 --- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h +++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.h @@ -78,7 +78,6 @@ public: QWindow* window() const override; QSGTexture *createTextureFromImage(const QImage &) override; QSGLayer *createLayer() override; - QSGInternalImageNode *createInternalImageNode() override; QSGImageNode *createImageNode() override; QSGRectangleNode *createRectangleNode() override; void update() override; diff --git a/tests/auto/quick/dialogs/WebView.qml b/tests/auto/quick/dialogs/WebView.qml index 4f8b7a0ce..01f4ac297 100644 --- a/tests/auto/quick/dialogs/WebView.qml +++ b/tests/auto/quick/dialogs/WebView.qml @@ -56,11 +56,12 @@ Window { WebEngineView { id: view anchors.fill: parent - onLoadingChanged: function(reqeust) { - if (reqeust.status === WebEngineView.LoadSucceededStatus) { + onLoadingChanged: function(request) { + if (request.status === WebEngineView.LoadSucceededStatus) { handler.ready = true - } else { - console.log("Wooohooo loading page from qrc failed !") + } else if (request.status === WebEngineView.LoadFailedStatus) { + console.log("Page was not successfully loaded from qrc! Status: " + request.status + + ", error [code: " + request.errorCode + "]: '" + request.errorString + "'") } } diff --git a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp index 8e23e86e8..922c7769e 100644 --- a/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp +++ b/tests/auto/quick/inspectorserver/tst_inspectorserver.cpp @@ -167,7 +167,7 @@ void tst_InspectorServer::openRemoteDebuggingSession() // - The page list didn't return a valid inspector URL // - Or the front-end couldn't be loaded through the inspector HTTP server // - Or the web socket connection couldn't be established between the front-end and the page through the inspector server - QTRY_VERIFY(inspectorWebView->title().startsWith("DevTools -")); + QTRY_VERIFY_WITH_TIMEOUT(inspectorWebView->title().startsWith("DevTools -"), 20000); } QTEST_MAIN(tst_InspectorServer) diff --git a/tests/auto/quick/qmltests/BLACKLIST b/tests/auto/quick/qmltests/BLACKLIST index 0a71e9f27..46bc65923 100644 --- a/tests/auto/quick/qmltests/BLACKLIST +++ b/tests/auto/quick/qmltests/BLACKLIST @@ -1,7 +1,2 @@ -[WebViewFindText::test_findTextInterruptedByLoad] -linux -qemu -b2qt -windows [WebEngineViewSource::test_viewSourceURL] * diff --git a/tests/auto/quick/qmltests/data/tst_findText.qml b/tests/auto/quick/qmltests/data/tst_findText.qml index 1ec574fae..14053a675 100644 --- a/tests/auto/quick/qmltests/data/tst_findText.qml +++ b/tests/auto/quick/qmltests/data/tst_findText.qml @@ -43,13 +43,21 @@ TestWebEngineView { matchCount = -1 } + function findCallbackCalled() { return matchCount != -1 } + function findTextCallback(matchCount) { + // If this starts to fail then either clear was not called before findText + // or unexpected callback was triggered from some search. + // On c++ side callback id can be checked to verify + testcase.verify(!findCallbackCalled(), 'Unexpected callback call or uncleared state before findText call!') + webEngineView.matchCount = matchCount findFailed = matchCount == 0 } TestCase { + id: testcase name: "WebViewFindText" function getBodyInnerHTML() { @@ -207,13 +215,17 @@ TestWebEngineView { webEngineView.findText("hello", findFlags, webEngineView.findTextCallback); // This should not crash. - webEngineView.url = "https://www.qt.io"; - if (!webEngineView.waitForLoadSucceeded(12000)) - skip("Couldn't load page from network, skipping test."); + webEngineView.loadHtml("<html><body>New page with same hello text</body></html>") + verify(webEngineView.waitForLoadSucceeded()) // The callback is not supposed to be called, see QTBUG-61506. - // Check whether the callback was called (-1 = no, other values = yes). - tryVerify(function() { return webEngineView.matchCount == -1; }, 20000); + expectFailContinue('', 'No unexpected findText callback calls occurred.') + tryVerify(function() { return webEngineView.findCallbackCalled() }) + verify(!webEngineView.findCallbackCalled()) + + webEngineView.clear(); + webEngineView.findText('New page', findFlags, webEngineView.findTextCallback) + tryCompare(webEngineView, 'matchCount', 1) } } } diff --git a/tests/auto/widgets/origins/resources/redirect.css b/tests/auto/widgets/origins/resources/redirect.css new file mode 100644 index 000000000..41d7560cc --- /dev/null +++ b/tests/auto/widgets/origins/resources/redirect.css @@ -0,0 +1,8 @@ +@font-face { + font-family: 'MyWebFont'; + src: url('redirect1:/resources/Akronim-Regular.woff2') format('woff2'); +} + +body { + font-family: 'MyWebFont', Fallback, sans-serif; +} diff --git a/tests/auto/widgets/origins/resources/redirect.html b/tests/auto/widgets/origins/resources/redirect.html new file mode 100644 index 000000000..04948e14b --- /dev/null +++ b/tests/auto/widgets/origins/resources/redirect.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<html> + <head> + <title>redirect</title> + <link rel="stylesheet" href="redirect1:/resources/redirect.css"> + </head> + <body> + Text + </body> +</html> diff --git a/tests/auto/widgets/origins/tst_origins.cpp b/tests/auto/widgets/origins/tst_origins.cpp index c1307c5e6..02d5bfba3 100644 --- a/tests/auto/widgets/origins/tst_origins.cpp +++ b/tests/auto/widgets/origins/tst_origins.cpp @@ -122,6 +122,18 @@ void registerSchemes() scheme.setDefaultPort(42); QWebEngineUrlScheme::registerScheme(scheme); } + + { + QWebEngineUrlScheme scheme(QBAL("redirect1")); + scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(scheme); + } + + { + QWebEngineUrlScheme scheme(QBAL("redirect2")); + scheme.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(scheme); + } } Q_CONSTRUCTOR_FUNCTION(registerSchemes) @@ -145,13 +157,26 @@ public: profile->installUrlSchemeHandler(QBAL("HostSyntax-ContentSecurityPolicyIgnored"), this); profile->installUrlSchemeHandler(QBAL("HostAndPortSyntax"), this); profile->installUrlSchemeHandler(QBAL("HostPortAndUserInformationSyntax"), this); + profile->installUrlSchemeHandler(QBAL("redirect1"), this); + profile->installUrlSchemeHandler(QBAL("redirect2"), this); } + QVector<QUrl> &requests() { return m_requests; } + private: void requestStarted(QWebEngineUrlRequestJob *job) override { + QUrl url = job->requestUrl(); + m_requests << url; + + if (url.scheme() == QBAL("redirect1")) { + url.setScheme(QBAL("redirect2")); + job->redirect(url); + return; + } + QString pathPrefix = QSL(THIS_DIR); - QString pathSuffix = job->requestUrl().path(); + QString pathSuffix = url.path(); QFile *file = new QFile(pathPrefix + pathSuffix, job); if (!file->open(QIODevice::ReadOnly)) { job->fail(QWebEngineUrlRequestJob::RequestFailed); @@ -160,8 +185,12 @@ private: QByteArray mimeType = QBAL("text/html"); if (pathSuffix.endsWith(QSL(".js"))) mimeType = QBAL("application/javascript"); + else if (pathSuffix.endsWith(QSL(".css"))) + mimeType = QBAL("text/css"); job->reply(mimeType, file); } + + QVector<QUrl> m_requests; }; class tst_Origins final : public QObject { @@ -169,6 +198,7 @@ class tst_Origins final : public QObject { private Q_SLOTS: void initTestCase(); + void cleanup(); void cleanupTestCase(); void jsUrlCanon(); @@ -187,6 +217,7 @@ private Q_SLOTS: void serviceWorker(); void viewSource(); void createObjectURL(); + void redirect(); private: bool load(const QUrl &url) @@ -209,10 +240,19 @@ private: void tst_Origins::initTestCase() { + QTest::ignoreMessage( + QtWarningMsg, + QRegularExpression("Please register the custom scheme 'tst'.*")); + m_page = new QWebEnginePage(&m_profile, nullptr); m_handler = new TstUrlSchemeHandler(&m_profile); } +void tst_Origins::cleanup() +{ + m_handler->requests().clear(); +} + void tst_Origins::cleanupTestCase() { delete m_handler; @@ -346,11 +386,7 @@ void tst_Origins::jsUrlOrigin() QCOMPARE(eval(QSL("new URL(\"file:/etc/passwd\").origin")), QVariant(QSL("file://"))); QCOMPARE(eval(QSL("new URL(\"file://foo.com/etc/passwd\").origin")), QVariant(QSL("file://"))); - // The qrc scheme should behave like file. - QCOMPARE(eval(QSL("new URL(\"qrc:/crysis.css\").origin")), QVariant(QSL("qrc://"))); - QCOMPARE(eval(QSL("new URL(\"qrc://foo.com/crysis.css\").origin")), QVariant(QSL("qrc://"))); - - // Unregistered schemes behaves like opaque origins. + // Unregistered schemes behave like file. QCOMPARE(eval(QSL("new URL(\"tst:/banana\").origin")), QVariant(QSL("tst://"))); QCOMPARE(eval(QSL("new URL(\"tst://foo.com/banana\").origin")), QVariant(QSL("tst://"))); @@ -367,8 +403,9 @@ void tst_Origins::jsUrlOrigin() QVariant(QSL("hostportanduserinformationsyntax://foo"))); // A PathSyntax scheme should have a 'universal' origin. - QCOMPARE(eval(QSL("new URL(\"PathSyntax:foo\").origin")), - QVariant(QSL("pathsyntax://"))); + QCOMPARE(eval(QSL("new URL(\"PathSyntax:foo\").origin")), QVariant(QSL("pathsyntax:"))); + QCOMPARE(eval(QSL("new URL(\"qrc:/crysis.css\").origin")), QVariant(QSL("qrc:"))); + QCOMPARE(eval(QSL("new URL(\"qrc://foo.com/crysis.css\").origin")), QVariant(QSL("qrc:"))); // The NoAccessAllowed flag forces opaque origins. QCOMPARE(eval(QSL("new URL(\"PathSyntax-NoAccessAllowed:foo\").origin")), @@ -709,7 +746,7 @@ void tst_Origins::serviceWorker() QVERIFY(load(QSL("qrc:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); QVERIFY(eval(QSL("error")).toString() - .contains(QSL("The URL protocol of the current origin ('qrc://') is not supported."))); + .contains(QSL("The URL protocol of the current origin ('qrc:') is not supported."))); QVERIFY(load(QSL("tst:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); @@ -724,7 +761,7 @@ void tst_Origins::serviceWorker() QVERIFY(load(QSL("PathSyntax-Secure:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); QVERIFY(eval(QSL("error")).toString() - .contains(QSL("The URL protocol of the current origin ('pathsyntax-secure://') is not supported."))); + .contains(QSL("The URL protocol of the current origin ('pathsyntax-secure:') is not supported."))); QVERIFY(load(QSL("PathSyntax-ServiceWorkersAllowed:/resources/serviceWorker.html"))); QTRY_VERIFY(eval(QSL("done")).toBool()); @@ -775,5 +812,18 @@ void tst_Origins::createObjectURL() QVERIFY(eval(QSL("result")).toString().startsWith(QSL("blob:tst:"))); } +void tst_Origins::redirect() +{ + QVERIFY(load(QSL("redirect1:/resources/redirect.html"))); + QTRY_COMPARE(m_handler->requests().size(), 7); + QCOMPARE(m_handler->requests()[0], QUrl(QStringLiteral("redirect1:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[1], QUrl(QStringLiteral("redirect2:/resources/redirect.html"))); + QCOMPARE(m_handler->requests()[2], QUrl(QStringLiteral("redirect1:/resources/redirect.css"))); + QCOMPARE(m_handler->requests()[3], QUrl(QStringLiteral("redirect2:/resources/redirect.css"))); + QCOMPARE(m_handler->requests()[4], QUrl(QStringLiteral("redirect1:/resources/Akronim-Regular.woff2"))); + QCOMPARE(m_handler->requests()[5], QUrl(QStringLiteral("redirect1:/resources/Akronim-Regular.woff2"))); + QCOMPARE(m_handler->requests()[6], QUrl(QStringLiteral("redirect2:/resources/Akronim-Regular.woff2"))); +} + QTEST_MAIN(tst_Origins) #include "tst_origins.moc" diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 27f2a9c2b..713feca6d 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -220,6 +220,8 @@ private Q_SLOTS: void editActionsWithFocusOnIframe(); void editActionsWithoutSelection(); + void customUserAgentInNewTab(); + private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -932,6 +934,7 @@ void tst_QWebEnginePage::findText() QTRY_COMPARE(loadSpy.count(), 1); // Select whole page contents. + QTRY_VERIFY(m_view->page()->action(QWebEnginePage::SelectAll)->isEnabled()); m_view->page()->triggerAction(QWebEnginePage::SelectAll); QTRY_COMPARE(m_view->hasSelection(), true); @@ -1709,12 +1712,15 @@ void tst_QWebEnginePage::runJavaScriptFromSlot() { QWebEngineProfile profile; QWebEnginePage page(&profile); + page.settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, true); QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished); page.setHtml("<html><body>" " <input type='text' id='input1' value='QtWebEngine' size='50' />" "</body></html>"); QTRY_COMPARE(loadFinishedSpy.count(), 1); + // Workaround for QTBUG-74718 + QTRY_VERIFY(page.action(QWebEnginePage::SelectAll)->isEnabled()); QVariant result(-1); connect(&page, &QWebEnginePage::selectionChanged, [&]() { @@ -4180,6 +4186,69 @@ void tst_QWebEnginePage::editActionsWithoutSelection() QVERIFY(page->action(QWebEnginePage::Unselect)->isEnabled()); } +void tst_QWebEnginePage::customUserAgentInNewTab() +{ + HttpServer server; + QByteArray lastUserAgent; + connect(&server, &HttpServer::newRequest, [&](HttpReqRep *rr) { + QCOMPARE(rr->requestMethod(), "GET"); + lastUserAgent = rr->requestHeader("user-agent"); + rr->setResponseBody(QByteArrayLiteral("<html><body>Test</body></html>")); + rr->sendResponse(); + }); + QVERIFY(server.start()); + + class Page : public QWebEnginePage { + public: + QWebEngineProfile *targetProfile = nullptr; + QScopedPointer<QWebEnginePage> newPage; + Page(QWebEngineProfile *profile) : QWebEnginePage(profile) {} + private: + QWebEnginePage *createWindow(WebWindowType) override + { + newPage.reset(new QWebEnginePage(targetProfile ? targetProfile : profile(), nullptr)); + return newPage.data(); + } + }; + QWebEngineProfile profile1, profile2; + profile1.setHttpUserAgent(QStringLiteral("custom 1")); + profile2.setHttpUserAgent(QStringLiteral("custom 2")); + Page page(&profile1); + QWebEngineView view; + view.resize(500, 500); + view.setPage(&page); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QSignalSpy spy(&page, &QWebEnginePage::loadFinished); + + // First check we can get the user-agent passed through normally + page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + + server.url("/test1").toEncoded() + + QString("'>link</a></body></html>")); + QTRY_COMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().value(0).toBool()); + QCOMPARE(evaluateJavaScriptSync(&page, QStringLiteral("navigator.userAgent")).toString(), profile1.httpUserAgent()); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, elementCenter(&page, "link")); + QTRY_VERIFY(page.newPage); + QTRY_VERIFY(!lastUserAgent.isEmpty()); + QCOMPARE(lastUserAgent, profile1.httpUserAgent().toUtf8()); + + // Now check we can get the new user-agent of the profile + page.newPage.reset(); + page.targetProfile = &profile2; + spy.clear(); + lastUserAgent = { }; + page.setHtml(QString("<html><body><a id='link' target='_blank' href='") + + server.url("/test2").toEncoded() + + QString("'>link</a></body></html>")); + QTRY_COMPARE(spy.count(), 1); + QVERIFY(spy.takeFirst().value(0).toBool()); + QTest::mouseClick(view.focusProxy(), Qt::LeftButton, 0, elementCenter(&page, "link")); + QTRY_VERIFY(page.newPage); + QTRY_VERIFY(!lastUserAgent.isEmpty()); + QCOMPARE(lastUserAgent, profile2.httpUserAgent().toUtf8()); +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index 4334498b9..fa179f2f8 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -1829,11 +1829,14 @@ void tst_QWebEngineView::softwareInputPanel() void tst_QWebEngineView::inputContextQueryInput() { - TestInputContext testContext; QWebEngineView view; view.resize(640, 480); view.show(); + // testContext will be destroyed before the view, so no events are sent accidentally + // when the view is destroyed. + TestInputContext testContext; + QSignalSpy selectionChangedSpy(&view, SIGNAL(selectionChanged())); QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool))); view.setHtml("<html><body>" @@ -1891,7 +1894,7 @@ void tst_QWebEngineView::inputContextQueryInput() QApplication::sendEvent(view.focusProxy(), &event); } QTRY_COMPARE(testContext.infos.count(), 2); - QCOMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.count(), 1); // As a first step, Chromium moves the cursor to the start of the selection. // We don't filter this in QtWebEngine because we don't know yet if this is part of a selection. @@ -1917,7 +1920,7 @@ void tst_QWebEngineView::inputContextQueryInput() QApplication::sendEvent(view.focusProxy(), &event); } QTRY_COMPARE(testContext.infos.count(), 1); - QCOMPARE(selectionChangedSpy.count(), 1); + QTRY_COMPARE(selectionChangedSpy.count(), 1); QCOMPARE(testContext.infos[0].cursorPosition, 0); QCOMPARE(testContext.infos[0].anchorPosition, 0); QCOMPARE(testContext.infos[0].surroundingText, QStringLiteral("QtWebEngine!")); diff --git a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp index b6582083d..64df05d89 100644 --- a/tests/auto/widgets/spellchecking/tst_spellchecking.cpp +++ b/tests/auto/widgets/spellchecking/tst_spellchecking.cpp @@ -72,10 +72,7 @@ class tst_Spellchecking : public QObject private Q_SLOTS: void init(); void cleanup(); - void initTestCase(); - void spellCheckLanguage(); - void spellCheckLanguages(); - void spellCheckEnabled(); + void settings(); void spellcheck(); void spellcheck_data(); @@ -84,19 +81,8 @@ private: WebView *m_view; }; -void tst_Spellchecking::initTestCase() -{ - QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); - QVERIFY(profile); - QVERIFY(!profile->isSpellCheckEnabled()); - QVERIFY(profile->spellCheckLanguages().isEmpty()); -} - void tst_Spellchecking::init() { - QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); - profile->setSpellCheckEnabled(false); - profile->setSpellCheckLanguages(QStringList()); m_view = new WebView(); } @@ -106,7 +92,6 @@ void tst_Spellchecking::load() m_view->show(); QSignalSpy spyFinished(m_view->page(), &QWebEnginePage::loadFinished); QVERIFY(spyFinished.wait()); - } void tst_Spellchecking::cleanup() @@ -114,29 +99,57 @@ void tst_Spellchecking::cleanup() delete m_view; } -void tst_Spellchecking::spellCheckLanguage() +void tst_Spellchecking::settings() { - QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); - QVERIFY(profile); - profile->setSpellCheckLanguages({"en-US"}); - QVERIFY(profile->spellCheckLanguages() == QStringList({"en-US"})); -} + // Default profile has spellchecking disabled -void tst_Spellchecking::spellCheckLanguages() -{ - QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); - QVERIFY(profile); - profile->setSpellCheckLanguages({"en-US","de-DE"}); - QVERIFY(profile->spellCheckLanguages() == QStringList({"en-US","de-DE"})); -} + QVERIFY(!QWebEngineProfile::defaultProfile()->isSpellCheckEnabled()); + QVERIFY(QWebEngineProfile::defaultProfile()->spellCheckLanguages().isEmpty()); + // New named profiles have spellchecking disabled -void tst_Spellchecking::spellCheckEnabled() -{ - QWebEngineProfile *profile = QWebEngineProfile::defaultProfile(); - QVERIFY(profile); - profile->setSpellCheckEnabled(true); - QVERIFY(profile->isSpellCheckEnabled()); + auto profile1 = std::make_unique<QWebEngineProfile>(QStringLiteral("Profile1")); + QVERIFY(!profile1->isSpellCheckEnabled()); + QVERIFY(profile1->spellCheckLanguages().isEmpty()); + + auto profile2 = std::make_unique<QWebEngineProfile>(QStringLiteral("Profile2")); + QVERIFY(!profile2->isSpellCheckEnabled()); + QVERIFY(profile2->spellCheckLanguages().isEmpty()); + + // New otr profiles have spellchecking disabled + + auto profile3 = std::make_unique<QWebEngineProfile>(); + QVERIFY(!profile2->isSpellCheckEnabled()); + QVERIFY(profile2->spellCheckLanguages().isEmpty()); + + // Settings can be changed + + profile1->setSpellCheckEnabled(true); + QVERIFY(profile1->isSpellCheckEnabled()); + + profile1->setSpellCheckLanguages({"en-US"}); + QVERIFY(profile1->spellCheckLanguages() == QStringList({"en-US"})); + + profile1->setSpellCheckLanguages({"en-US","de-DE"}); + QVERIFY(profile1->spellCheckLanguages() == QStringList({"en-US","de-DE"})); + + // Settings are per profile + + QVERIFY(!profile2->isSpellCheckEnabled()); + QVERIFY(profile2->spellCheckLanguages().isEmpty()); + + QVERIFY(!profile3->isSpellCheckEnabled()); + QVERIFY(profile3->spellCheckLanguages().isEmpty()); + + // Settings are not persisted + + // TODO(juvaldma): Write from dtor currently usually happens *after* the + // read from the ctor, so this test would pass even if settings were + // persisted. It would start to fail on the second run though. + profile1.reset(); + profile1 = std::make_unique<QWebEngineProfile>(QStringLiteral("Profile1")); + QVERIFY(!profile1->isSpellCheckEnabled()); + QVERIFY(profile1->spellCheckLanguages().isEmpty()); } void tst_Spellchecking::spellcheck() @@ -174,14 +187,41 @@ void tst_Spellchecking::spellcheck() QString result = evaluateJavaScriptSync(m_view->page(), "text();").toString(); QVERIFY(result == text); - // open menu on misspelled word - m_view->activateMenu(m_view->focusWidget(), rect.center()); - QSignalSpy spyMenuReady(m_view, &WebView::menuReady); - QVERIFY(spyMenuReady.wait()); - - // check if menu is valid - QVERIFY(m_view->data().isValid()); - QVERIFY(m_view->data().isContentEditable()); + bool gotMisspelledWord = false; // clumsy QTRY_VERIFY still execs expr after first success + QString detail; + + // check that spellchecker has done text processing and filled misspelled word + QTRY_VERIFY2([&] () { + detail.clear(); + if (gotMisspelledWord) + return true; + + // open menu on misspelled word + m_view->activateMenu(m_view->focusWidget(), rect.center()); + QSignalSpy spyMenuReady(m_view, &WebView::menuReady); + if (!spyMenuReady.wait()) { + detail = "menu was not shown"; + return false; + } + + if (!m_view->data().isValid()) { + detail = "invalid data"; + return false; + } + + if (!m_view->data().isContentEditable()) { + detail = "content is not editable"; + return false; + } + + if (m_view->data().misspelledWord().isEmpty()) { + detail = "no misspelled word"; + return false; + }; + + gotMisspelledWord = true; + return true; + } (), qPrintable(QString("Context menu: %1").arg(detail))); // check misspelled word QCOMPARE(m_view->data().misspelledWord(), QStringLiteral("lowe")); |