diff options
32 files changed, 707 insertions, 253 deletions
diff --git a/src/core/api/qwebengineurlrequestinfo.cpp b/src/core/api/qwebengineurlrequestinfo.cpp index c3e5b5445..dc2d07740 100644 --- a/src/core/api/qwebengineurlrequestinfo.cpp +++ b/src/core/api/qwebengineurlrequestinfo.cpp @@ -96,8 +96,8 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q interceptor on the profile enables intercepting, blocking, and modifying URL requests before they reach the networking stack of Chromium. - You can install the interceptor on a profile via QWebEngineProfile::setRequestInterceptor() - or QQuickWebEngineProfile::setRequestInterceptor(). + You can install the interceptor on a profile via QWebEngineProfile::setUrlRequestInterceptor() + or QQuickWebEngineProfile::setUrlRequestInterceptor(). When using the \l{Qt WebEngine Widgets Module}, \l{QWebEnginePage::acceptNavigationRequest()} offers further options to accept or block requests. @@ -115,11 +115,7 @@ ASSERT_ENUMS_MATCH(QtWebEngineCore::WebContentsAdapterClient::OtherNavigation, Q \fn void QWebEngineUrlRequestInterceptor::interceptRequest(QWebEngineUrlRequestInfo &info) Reimplementing this virtual function makes it possible to intercept URL - requests. For interceptors installed on a QWebEngineProfile, the function is executed - on the I/O thread, and thus it may not be thread-safe to interact with pages. If the - interceptor was installed on a QWebEnginePage, the function is executed on the main - application thread, and can safely interact with other user classes. Both versions will - be stalling the URL request until handled. + requests. This method will be stalling the URL request until handled. \a info contains the information about the URL request and will track internally whether its members have been altered. diff --git a/src/core/api/qwebengineurlrequestinterceptor.h b/src/core/api/qwebengineurlrequestinterceptor.h index dc2a15ee3..2b07681ca 100644 --- a/src/core/api/qwebengineurlrequestinterceptor.h +++ b/src/core/api/qwebengineurlrequestinterceptor.h @@ -55,7 +55,7 @@ class QWEBENGINECORE_EXPORT QWebEngineUrlRequestInterceptor : public QObject Q_OBJECT Q_DISABLE_COPY(QWebEngineUrlRequestInterceptor) public: - explicit QWebEngineUrlRequestInterceptor(QObject *p = Q_NULLPTR) + explicit QWebEngineUrlRequestInterceptor(QObject *p = nullptr) : QObject (p) { } diff --git a/src/core/command_line_pref_store_qt.cpp b/src/core/command_line_pref_store_qt.cpp new file mode 100644 index 000000000..5c5c82e1a --- /dev/null +++ b/src/core/command_line_pref_store_qt.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "command_line_pref_store_qt.h" + +#include "chrome/common/chrome_switches.h" +#include "components/proxy_config/proxy_config_dictionary.h" +#include "components/proxy_config/proxy_config_pref_names.h" +#include "content/public/common/content_switches.h" +#include <QDebug> + +CommandLinePrefStoreQt::CommandLinePrefStoreQt(const base::CommandLine *commandLine) + : CommandLinePrefStore(commandLine) +{ + + if (commandLine->HasSwitch(switches::kNoProxyServer)) { + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreateDirect()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyPacUrl)) { + std::string pac_script_url = + commandLine->GetSwitchValueASCII(switches::kProxyPacUrl); + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreatePacScript( + pac_script_url, false)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyAutoDetect)) { + SetValue(proxy_config::prefs::kProxy, + std::make_unique<base::Value>( + ProxyConfigDictionary::CreateAutoDetect()), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } else if (commandLine->HasSwitch(switches::kProxyServer)) { + std::string proxy_server = + commandLine->GetSwitchValueASCII(switches::kProxyServer); + std::string bypass_list = + commandLine->GetSwitchValueASCII(switches::kProxyBypassList); + SetValue( + proxy_config::prefs::kProxy, + std::make_unique<base::Value>(ProxyConfigDictionary::CreateFixedServers( + proxy_server, bypass_list)), + WriteablePrefStore::DEFAULT_PREF_WRITE_FLAGS); + } + + if (commandLine->HasSwitch(switches::kNoProxyServer) && (commandLine->HasSwitch(switches::kProxyAutoDetect) || commandLine->HasSwitch(switches::kProxyServer) || commandLine->HasSwitch(switches::kProxyPacUrl) || commandLine->HasSwitch(switches::kProxyBypassList))) { + qWarning("Additional command-line proxy switches specified when --%s was also specified", + qPrintable(switches::kNoProxyServer)); + } +} + +CommandLinePrefStoreQt::~CommandLinePrefStoreQt() = default; diff --git a/src/core/command_line_pref_store_qt.h b/src/core/command_line_pref_store_qt.h new file mode 100644 index 000000000..a509f8ca9 --- /dev/null +++ b/src/core/command_line_pref_store_qt.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef COMMAND_LINE_PREF_STORE_QT_H +#define COMMAND_LINE_PREF_STORE_QT_H + +#include "base/command_line.h" +#include "components/prefs/command_line_pref_store.h" + +class CommandLinePrefStoreQt : public CommandLinePrefStore +{ +public: + explicit CommandLinePrefStoreQt(const base::CommandLine *commandLine); + +protected: + ~CommandLinePrefStoreQt() override; + DISALLOW_COPY_AND_ASSIGN(CommandLinePrefStoreQt); +}; + +#endif // COMMAND_LINE_PREF_STORE_QT_H diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp index 846f3b908..3861d1c95 100644 --- a/src/core/content_browser_client_qt.cpp +++ b/src/core/content_browser_client_qt.cpp @@ -760,13 +760,19 @@ scoped_refptr<content::LoginDelegate> ContentBrowserClientQt::CreateLoginDelegat return loginDelegate; } +bool ContentBrowserClientQt::ShouldIsolateErrorPage(bool in_main_frame) +{ + Q_UNUSED(in_main_frame); + return false; +} + bool ContentBrowserClientQt::ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) { #if BUILDFLAG(ENABLE_EXTENSIONS) - return true; -#else - return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url); + if (effective_url.SchemeIs(extensions::kExtensionScheme)) + return true; #endif + return ContentBrowserClient::ShouldUseProcessPerSite(browser_context, effective_url); } } // namespace QtWebEngineCore diff --git a/src/core/content_browser_client_qt.h b/src/core/content_browser_client_qt.h index e90b687cc..fc938f882 100644 --- a/src/core/content_browser_client_qt.h +++ b/src/core/content_browser_client_qt.h @@ -175,6 +175,7 @@ public: #if QT_CONFIG(webengine_geolocation) std::unique_ptr<device::LocationProvider> OverrideSystemLocationProvider() override; #endif + bool ShouldIsolateErrorPage(bool in_main_frame) override; bool ShouldUseProcessPerSite(content::BrowserContext* browser_context, const GURL& effective_url) override; #if defined(Q_OS_LINUX) diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri index 18cedc63e..edf6806a3 100644 --- a/src/core/core_chromium.pri +++ b/src/core/core_chromium.pri @@ -53,6 +53,7 @@ SOURCES = \ clipboard_qt.cpp \ color_chooser_qt.cpp \ color_chooser_controller.cpp \ + command_line_pref_store_qt.cpp \ common/qt_ipc_logging.cpp \ common/qt_messages.cpp \ common/user_script_data.cpp \ @@ -86,6 +87,7 @@ SOURCES = \ net/url_request_custom_job.cpp \ net/url_request_custom_job_delegate.cpp \ net/url_request_custom_job_proxy.cpp \ + net/url_request_notification.cpp \ net/webui_controller_factory_qt.cpp \ ozone/gl_context_qt.cpp \ ozone/gl_ozone_egl_qt.cpp \ @@ -150,6 +152,7 @@ HEADERS = \ client_cert_override_p.h \ client_cert_select_controller.h \ clipboard_qt.h \ + command_line_pref_store_qt.h \ color_chooser_qt.h \ color_chooser_controller_p.h \ color_chooser_controller.h \ @@ -187,6 +190,7 @@ HEADERS = \ net/url_request_custom_job.h \ net/url_request_custom_job_delegate.h \ net/url_request_custom_job_proxy.h \ + net/url_request_notification.h \ net/webui_controller_factory_qt.h \ ozone/gl_context_qt.h \ ozone/gl_ozone_egl_qt.h \ diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp index 73f3ff818..3641cb845 100644 --- a/src/core/net/network_delegate_qt.cpp +++ b/src/core/net/network_delegate_qt.cpp @@ -58,6 +58,7 @@ #include "type_conversion.h" #include "web_contents_adapter_client.h" #include "web_contents_view_qt.h" +#include "url_request_notification.h" namespace QtWebEngineCore { @@ -91,146 +92,11 @@ static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourc return QWebEngineUrlRequestInfo::ResourceTypeUnknown; } -static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) -{ - return static_cast<content::ResourceType>(resourceType); -} - static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) { return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType); } -// Notifies WebContentsAdapterClient of a new URLRequest. -class URLRequestNotification { -public: - URLRequestNotification(net::URLRequest *request, - bool isMainFrameRequest, - GURL *newUrl, - QWebEngineUrlRequestInfo &&requestInfo, - content::ResourceRequestInfo::WebContentsGetter webContentsGetter, - net::CompletionOnceCallback callback) - : m_request(request) - , m_isMainFrameRequest(isMainFrameRequest) - , m_newUrl(newUrl) - , m_originalUrl(requestInfo.requestUrl()) - , m_requestInfo(std::move(requestInfo)) - , m_webContentsGetter(webContentsGetter) - , m_callback(std::move(callback)) - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - m_request->SetUserData(UserData::key, std::make_unique<UserData>(this)); - - base::PostTaskWithTraits( - FROM_HERE, - {content::BrowserThread::UI}, - base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); - } - -private: - // Calls cancel() when the URLRequest is destroyed. - class UserData : public base::SupportsUserData::Data { - public: - UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} - ~UserData() { m_ptr->cancel(); } - static const char key[]; - private: - URLRequestNotification *m_ptr; - }; - - void cancel() - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - // May run concurrently with notify() but we only touch m_request here. - - m_request = nullptr; - } - - void notify() - { - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - - // May run concurrently with cancel() so no peeking at m_request here. - - int error = net::OK; - content::WebContents *webContents = m_webContentsGetter.Run(); - - if (webContents) { - WebContentsAdapterClient *client = - WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); - - client->interceptRequest(m_requestInfo); - if (m_requestInfo.changed()) { - error = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; - // We handle the rest of the changes later when we are back in I/O thread - } - - // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources - if (error == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { - int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; - client->navigationRequested(m_requestInfo.navigationType(), - m_requestInfo.requestUrl(), - navigationRequestAction, - m_isMainFrameRequest); - error = net::ERR_FAILED; - switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { - case WebContentsAdapterClient::AcceptRequest: - error = net::OK; - break; - case WebContentsAdapterClient::IgnoreRequest: - error = net::ERR_ABORTED; - break; - } - DCHECK(error != net::ERR_FAILED); - } - } - - // Run the callback on the IO thread. - base::PostTaskWithTraits( - FROM_HERE, - {content::BrowserThread::IO}, - base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), error)); - } - - void complete(int error) - { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - - if (m_request) { - if (m_requestInfo.changed()) { - if (m_originalUrl != m_requestInfo.d_ptr->url) - *m_newUrl = toGurl(m_requestInfo.d_ptr->url); - - if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { - auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); - for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) - m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); - } - } - - if (m_request->status().status() != net::URLRequestStatus::CANCELED) - std::move(m_callback).Run(error); - m_request->RemoveUserData(UserData::key); - } - - delete this; - } - - ~URLRequestNotification() {} - - net::URLRequest *m_request; - bool m_isMainFrameRequest; - GURL *m_newUrl; - const QUrl m_originalUrl; - QWebEngineUrlRequestInfo m_requestInfo; - content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; - net::CompletionOnceCallback m_callback; -}; - -const char URLRequestNotification::UserData::key[] = "QtWebEngineCore::URLRequestNotification"; - NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) : m_profileIOData(data) { @@ -265,33 +131,41 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet QByteArray::fromStdString(request->method())); QWebEngineUrlRequestInfo requestInfo(infoPrivate); - if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { - interceptor->interceptRequest(requestInfo); - m_profileIOData->releaseInterceptor(); - if (requestInfo.changed()) { - int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + // 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); + m_profileIOData->releaseInterceptor(); + if (requestInfo.changed()) { + int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + + if (qUrl != infoPrivate->url) + *newUrl = toGurl(infoPrivate->url); + + if (!infoPrivate->extraHeaders.isEmpty()) { + auto end = infoPrivate->extraHeaders.constEnd(); + for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) + request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } - if (qUrl != infoPrivate->url) - *newUrl = toGurl(infoPrivate->url); + if (result != net::OK) + return result; - if (!infoPrivate->extraHeaders.isEmpty()) { - auto end = infoPrivate->extraHeaders.constEnd(); - for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) - request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + requestInfo.resetChanged(); } - - if (result != net::OK) - return result; - - requestInfo.resetChanged(); + } else { + m_profileIOData->releaseInterceptor(); } - } else - m_profileIOData->releaseInterceptor(); + } + // Deprecated =cut if (!resourceInfo) return net::OK; - if (!m_profileIOData->hasPageInterceptors() && !content::IsResourceTypeFrame(resourceType)) + if (!m_profileIOData->hasPageInterceptors() && !profileInterceptor && !content::IsResourceTypeFrame(resourceType)) return net::OK; auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); @@ -301,7 +175,8 @@ int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::Complet newUrl, std::move(requestInfo), webContentsGetter, - std::move(callback) + std::move(callback), + profileInterceptor ? m_profileIOData->profileAdapter() : nullptr ); // We'll run the callback after we notified the UI thread. diff --git a/src/core/net/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp index 13b969281..ff8ab20aa 100644 --- a/src/core/net/proxy_config_service_qt.cpp +++ b/src/core/net/proxy_config_service_qt.cpp @@ -47,6 +47,7 @@ #include "base/bind.h" #include "content/public/browser/browser_thread.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" using content::BrowserThread; @@ -68,10 +69,13 @@ net::ProxyServer ProxyConfigServiceQt::fromQNetworkProxy(const QNetworkProxy &qt } } -ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService) +ProxyConfigServiceQt::ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, + const net::ProxyConfigWithAnnotation& initialConfig, ProxyPrefs::ConfigState initialState) : m_baseService(baseService.release()), m_usesSystemConfiguration(false), - m_registeredObserver(false) + m_registeredObserver(false), + m_prefConfig(initialConfig), + m_perfState(initialState) { } @@ -100,8 +104,10 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy ConfigAvailability systemAvailability = net::ProxyConfigService::CONFIG_UNSET; if (m_baseService.get()) systemAvailability = m_baseService->GetLatestProxyConfig(&systemConfig); - *config = systemConfig; - // make sure to get updates via OnProxyConfigChanged + ProxyPrefs::ConfigState configState; + systemAvailability = PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( + m_perfState, m_prefConfig, systemAvailability, systemConfig, + false, &configState, config); RegisterObserver(); return systemAvailability; } diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h index 961927b89..09e88d445 100644 --- a/src/core/net/proxy_config_service_qt.h +++ b/src/core/net/proxy_config_service_qt.h @@ -43,8 +43,10 @@ #include "base/memory/ref_counted.h" #include "base/observer_list.h" +#include "net/proxy_resolution/proxy_config.h" #include "net/proxy_resolution/proxy_config_service.h" #include "net/proxy_resolution/proxy_config_with_annotation.h" +#include "components/proxy_config/proxy_prefs.h" #include <QNetworkProxy> @@ -55,7 +57,9 @@ public: static net::ProxyServer fromQNetworkProxy(const QNetworkProxy &); - explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService); + explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, + const net::ProxyConfigWithAnnotation& initialConfig, + ProxyPrefs::ConfigState initialState); ~ProxyConfigServiceQt() override; // ProxyConfigService implementation: @@ -86,6 +90,10 @@ private: // Indicates whether the base service registration is done. bool m_registeredObserver; + // Configuration as defined by prefs. + net::ProxyConfigWithAnnotation m_prefConfig; + ProxyPrefs::ConfigState m_perfState; + DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceQt); }; diff --git a/src/core/net/url_request_notification.cpp b/src/core/net/url_request_notification.cpp new file mode 100644 index 000000000..6da661cff --- /dev/null +++ b/src/core/net/url_request_notification.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "url_request_notification.h" + +#include "base/supports_user_data.h" +#include "base/task/post_task.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" +#include "net/url_request/url_request.h" +#include "web_contents_adapter_client.h" +#include "web_contents_view_qt.h" +#include "profile_io_data_qt.h" +#include "qwebengineurlrequestinfo_p.h" +#include "type_conversion.h" + +namespace QtWebEngineCore { + +// Calls cancel() when the URLRequest is destroyed. +class UserData : public base::SupportsUserData::Data { +public: + UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} + ~UserData() { m_ptr->cancel(); } + static const char key[]; +private: + URLRequestNotification *m_ptr; +}; + +const char UserData::key[] = "QtWebEngineCore::URLRequestNotification"; + +static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) +{ + return static_cast<content::ResourceType>(resourceType); +} + +URLRequestNotification::URLRequestNotification(net::URLRequest *request, + bool isMainFrameRequest, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, + net::CompletionOnceCallback callback, + QPointer<ProfileAdapter> adapter) + : m_request(request) + , m_isMainFrameRequest(isMainFrameRequest) + , m_newUrl(newUrl) + , m_originalUrl(requestInfo.requestUrl()) + , m_requestInfo(std::move(requestInfo)) + , m_webContentsGetter(webContentsGetter) + , m_callback(std::move(callback)) + , m_profileAdapter(adapter) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + m_request->SetUserData(UserData::key, std::make_unique<UserData>(this)); + + base::PostTaskWithTraits( + FROM_HERE, + {content::BrowserThread::UI}, + base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); +} + + +void URLRequestNotification::notify() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + // May run concurrently with cancel() so no peeking at m_request here. + + int result = net::OK; + content::WebContents *webContents = m_webContentsGetter.Run(); + + if (webContents) { + + if (m_profileAdapter) { + QWebEngineUrlRequestInterceptor* interceptor = m_profileAdapter->requestInterceptor(); + interceptor->interceptRequest(m_requestInfo); + } + + WebContentsAdapterClient *client = + WebContentsViewQt::from(static_cast<content::WebContentsImpl*>(webContents)->GetView())->client(); + + if (!m_requestInfo.changed()) { + client->interceptRequest(m_requestInfo); + } + + if (m_requestInfo.changed()) { + result = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; + // We handle the rest of the changes later when we are back in I/O thread + } + + // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources + if (result == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { + int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; + client->navigationRequested(m_requestInfo.navigationType(), + m_requestInfo.requestUrl(), + navigationRequestAction, + m_isMainFrameRequest); + result = net::ERR_FAILED; + switch (static_cast<WebContentsAdapterClient::NavigationRequestAction>(navigationRequestAction)) { + case WebContentsAdapterClient::AcceptRequest: + result = net::OK; + break; + case WebContentsAdapterClient::IgnoreRequest: + result = net::ERR_ABORTED; + break; + } + DCHECK(result != net::ERR_FAILED); + } + } + + // Run the callback on the IO thread. + base::PostTaskWithTraits( + FROM_HERE, + {content::BrowserThread::IO}, + base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), result)); +} + +void URLRequestNotification::cancel() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + // May run concurrently with notify() but we only touch m_request here. + + m_request = nullptr; +} + +void URLRequestNotification::complete(int error) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (m_request) { + if (m_requestInfo.changed()) { + if (m_originalUrl != m_requestInfo.d_ptr->url) + *m_newUrl = toGurl(m_requestInfo.d_ptr->url); + + if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { + auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); + for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) + m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); + } + } + + if (m_request->status().status() != net::URLRequestStatus::CANCELED) + std::move(m_callback).Run(error); + m_request->RemoveUserData(UserData::key); + } + + delete this; +} + +} diff --git a/src/core/net/url_request_notification.h b/src/core/net/url_request_notification.h new file mode 100644 index 000000000..1d9acf12f --- /dev/null +++ b/src/core/net/url_request_notification.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWebEngine module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef URL_REQUEST_NOTIFIACTION_H +#define URL_REQUEST_NOTIFIACTION_H + +#include "content/public/browser/resource_request_info.h" +#include "net/base/completion_once_callback.h" +#include "qwebengineurlrequestinfo.h" +#include <QPointer> + +class GURL; + +namespace net { +class URLRequest; +} + +namespace QtWebEngineCore { + +class ProfileAdapter; +class ProfileIoDataQt; + +// Notifies WebContentsAdapterClient of a new URLRequest. +class URLRequestNotification { +public: + URLRequestNotification(net::URLRequest *request, + bool isMainFrameRequest, + GURL *newUrl, + QWebEngineUrlRequestInfo &&requestInfo, + content::ResourceRequestInfo::WebContentsGetter webContentsGetter, + net::CompletionOnceCallback callback, + QPointer<ProfileAdapter> adapter); + ~URLRequestNotification() = default; + void cancel(); + void notify(); + void complete(int error); + +private: + net::URLRequest *m_request; //used only by io thread + bool m_isMainFrameRequest; + GURL *m_newUrl; + const QUrl m_originalUrl; + QWebEngineUrlRequestInfo m_requestInfo; + content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; + net::CompletionOnceCallback m_callback; + QPointer<ProfileAdapter> m_profileAdapter; +}; +} +#endif diff --git a/src/core/permission_manager_qt.cpp b/src/core/permission_manager_qt.cpp index b6d055e27..cf3041e7a 100644 --- a/src/core/permission_manager_qt.cpp +++ b/src/core/permission_manager_qt.cpp @@ -48,6 +48,7 @@ #include "type_conversion.h" #include "web_contents_delegate_qt.h" +#include "web_engine_settings.h" namespace QtWebEngineCore { @@ -69,8 +70,11 @@ ProfileAdapter::PermissionType toQt(content::PermissionType type) case content::PermissionType::BACKGROUND_SYNC: case content::PermissionType::SENSORS: case content::PermissionType::ACCESSIBILITY_EVENTS: + break; case content::PermissionType::CLIPBOARD_READ: + return ProfileAdapter::ClipboardRead; case content::PermissionType::CLIPBOARD_WRITE: + return ProfileAdapter::ClipboardWrite; case content::PermissionType::PAYMENT_HANDLER: case content::PermissionType::BACKGROUND_FETCH: case content::PermissionType::NUM: @@ -154,19 +158,28 @@ int PermissionManagerQt::RequestPermission(content::PermissionType permission, bool /*user_gesture*/, const base::Callback<void(blink::mojom::PermissionStatus)>& callback) { - int request_id = ++m_requestIdCount; + WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>( + content::WebContents::FromRenderFrameHost(frameHost)->GetDelegate()); + Q_ASSERT(contentsDelegate); + ProfileAdapter::PermissionType permissionType = toQt(permission); if (permissionType == ProfileAdapter::UnsupportedPermission) { callback.Run(blink::mojom::PermissionStatus::DENIED); return content::PermissionController::kNoPendingOperation; + } else if (permissionType == ProfileAdapter::ClipboardRead) { + WebEngineSettings *settings = contentsDelegate->webEngineSettings(); + if (settings->testAttribute(WebEngineSettings::JavascriptCanAccessClipboard) + && settings->testAttribute(WebEngineSettings::JavascriptCanPaste)) + callback.Run(blink::mojom::PermissionStatus::GRANTED); + else + callback.Run(blink::mojom::PermissionStatus::DENIED); + return content::PermissionController::kNoPendingOperation; } // Audio and video-capture should not come this way currently Q_ASSERT(permissionType != ProfileAdapter::AudioCapturePermission && permissionType != ProfileAdapter::VideoCapturePermission); - content::WebContents *webContents = frameHost->GetRenderViewHost()->GetDelegate()->GetAsWebContents(); - WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); - Q_ASSERT(contentsDelegate); + int request_id = ++m_requestIdCount; RequestOrSubscription request = { permissionType, toQt(requesting_origin), @@ -184,6 +197,10 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio bool /*user_gesture*/, const base::Callback<void(const std::vector<blink::mojom::PermissionStatus>&)>& callback) { + WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>( + content::WebContents::FromRenderFrameHost(frameHost)->GetDelegate()); + Q_ASSERT(contentsDelegate); + bool answerable = true; std::vector<blink::mojom::PermissionStatus> result; result.reserve(permissions.size()); @@ -191,7 +208,14 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio const ProfileAdapter::PermissionType permissionType = toQt(permission); if (permissionType == ProfileAdapter::UnsupportedPermission) result.push_back(blink::mojom::PermissionStatus::DENIED); - else { + else if (permissionType == ProfileAdapter::ClipboardRead) { + WebEngineSettings *settings = contentsDelegate->webEngineSettings(); + if (settings->testAttribute(WebEngineSettings::JavascriptCanAccessClipboard) + && settings->testAttribute(WebEngineSettings::JavascriptCanPaste)) + result.push_back(blink::mojom::PermissionStatus::GRANTED); + else + result.push_back(blink::mojom::PermissionStatus::DENIED); + } else { answerable = false; break; } @@ -202,9 +226,6 @@ int PermissionManagerQt::RequestPermissions(const std::vector<content::Permissio } int request_id = ++m_requestIdCount; - content::WebContents *webContents = frameHost->GetRenderViewHost()->GetDelegate()->GetAsWebContents(); - WebContentsDelegateQt* contentsDelegate = static_cast<WebContentsDelegateQt*>(webContents->GetDelegate()); - Q_ASSERT(contentsDelegate); MultiRequest request = { permissions, toQt(requesting_origin), @@ -241,6 +262,18 @@ blink::mojom::PermissionStatus PermissionManagerQt::GetPermissionStatusForFrame( content::RenderFrameHost *render_frame_host, const GURL &requesting_origin) { + if (permission == content::PermissionType::CLIPBOARD_READ || + permission == content::PermissionType::CLIPBOARD_WRITE) { + WebContentsDelegateQt *delegate = static_cast<WebContentsDelegateQt *>( + content::WebContents::FromRenderFrameHost(render_frame_host)->GetDelegate()); + if (!delegate->webEngineSettings()->testAttribute(WebEngineSettings::JavascriptCanAccessClipboard)) + return blink::mojom::PermissionStatus::DENIED; + if (permission == content::PermissionType::CLIPBOARD_READ && + !delegate->webEngineSettings()->testAttribute(WebEngineSettings::JavascriptCanPaste)) + return blink::mojom::PermissionStatus::DENIED; + return blink::mojom::PermissionStatus::GRANTED; + } + return GetPermissionStatus( permission, requesting_origin, diff --git a/src/core/profile_adapter.h b/src/core/profile_adapter.h index 3b1b9bc91..800058bc5 100644 --- a/src/core/profile_adapter.h +++ b/src/core/profile_adapter.h @@ -157,6 +157,8 @@ public: // NotificationPermission = 2, AudioCapturePermission = 3, VideoCapturePermission = 4, + ClipboardRead = 5, + ClipboardWrite = 6, }; HttpCacheType httpCacheType() const; diff --git a/src/core/profile_io_data_qt.cpp b/src/core/profile_io_data_qt.cpp index 83886ca54..4815b8749 100644 --- a/src/core/profile_io_data_qt.cpp +++ b/src/core/profile_io_data_qt.cpp @@ -49,6 +49,7 @@ #include "content/public/common/content_features.h" #include "chrome/browser/custom_handlers/protocol_handler_registry_factory.h" #include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -194,6 +195,11 @@ ProfileIODataQt::~ProfileIODataQt() delete m_proxyConfigService.fetchAndStoreAcquire(0); } +QPointer<ProfileAdapter> ProfileIODataQt::profileAdapter() +{ + return m_profileAdapter; +} + void ProfileIODataQt::shutdownOnUIThread() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); @@ -647,10 +653,15 @@ void ProfileIODataQt::updateStorageSettings() // must synchronously run on the glib message loop. This will be passed to // the URLRequestContextStorage on the IO thread in GetURLRequestContext(). Q_ASSERT(m_proxyConfigService == 0); + net::ProxyConfigWithAnnotation initialConfig; + ProxyPrefs::ConfigState initialConfigState = PrefProxyConfigTrackerImpl::ReadPrefConfig( + m_profileAdapter->profile()->GetPrefs(), &initialConfig); + m_proxyConfigService = new ProxyConfigServiceQt( net::ProxyResolutionService::CreateSystemProxyConfigService( - base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}))); + base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO})), + initialConfig, initialConfigState); //pass interface to io thread m_proxyResolverFactoryInterface = ChromeMojoProxyResolverFactory::CreateWithStrongBinding().PassInterface(); if (m_initialized) @@ -742,6 +753,11 @@ QWebEngineUrlRequestInterceptor *ProfileIODataQt::acquireInterceptor() return m_requestInterceptor; } +QWebEngineUrlRequestInterceptor *ProfileIODataQt::requestInterceptor() +{ + return m_requestInterceptor; +} + bool ProfileIODataQt::hasPageInterceptors() { // used in NetworkDelegateQt::OnBeforeURLRequest diff --git a/src/core/profile_io_data_qt.h b/src/core/profile_io_data_qt.h index 23497c476..407d0d6f2 100644 --- a/src/core/profile_io_data_qt.h +++ b/src/core/profile_io_data_qt.h @@ -80,6 +80,7 @@ public: ProfileIODataQt(ProfileQt *profile); // runs on ui thread virtual ~ProfileIODataQt(); + QPointer<ProfileAdapter> profileAdapter(); content::ResourceContext *resourceContext(); net::URLRequestContext *urlRequestContext(); #if BUILDFLAG(ENABLE_EXTENSIONS) @@ -105,6 +106,7 @@ public: // Used in NetworkDelegateQt::OnBeforeURLRequest. QWebEngineUrlRequestInterceptor *acquireInterceptor(); void releaseInterceptor(); + QWebEngineUrlRequestInterceptor *requestInterceptor(); void setRequestContextData(content::ProtocolHandlerMap *protocolHandlers, content::URLRequestInterceptorScopedVector request_interceptors); diff --git a/src/core/profile_qt.cpp b/src/core/profile_qt.cpp index 68e933d85..0ede1df55 100644 --- a/src/core/profile_qt.cpp +++ b/src/core/profile_qt.cpp @@ -41,6 +41,7 @@ #include "profile_adapter.h" #include "browsing_data_remover_delegate_qt.h" +#include "command_line_pref_store_qt.h" #include "download_manager_delegate_qt.h" #include "net/ssl_host_state_delegate_qt.h" #include "net/url_request_context_getter_qt.h" @@ -48,6 +49,7 @@ #include "qtwebenginecoreglobal_p.h" #include "type_conversion.h" #include "web_engine_library_info.h" +#include "web_engine_context.h" #include "base/time/time.h" #include "content/public/browser/browser_thread.h" @@ -62,6 +64,7 @@ #include "components/prefs/pref_service_factory.h" #include "components/prefs/pref_registry_simple.h" #include "components/user_prefs/user_prefs.h" +#include "components/proxy_config/pref_proxy_config_tracker_impl.h" #if QT_CONFIG(webengine_spellchecker) #include "chrome/browser/spellchecker/spellcheck_service.h" #include "chrome/common/pref_names.h" @@ -89,8 +92,10 @@ ProfileQt::ProfileQt(ProfileAdapter *profileAdapter) { PrefServiceFactory factory; factory.set_user_prefs(new InMemoryPrefStore); + factory.set_command_line_prefs(base::MakeRefCounted<CommandLinePrefStoreQt>( + WebEngineContext::commandLine())); PrefRegistrySimple *registry = new PrefRegistrySimple(); - + PrefProxyConfigTrackerImpl::RegisterPrefs(registry); #if QT_CONFIG(webengine_spellchecker) // Initial spellcheck settings registry->RegisterStringPref(prefs::kAcceptLanguages, std::string()); diff --git a/src/core/qtwebengine.gni b/src/core/qtwebengine.gni index b7f10833a..7c0ca763e 100644 --- a/src/core/qtwebengine.gni +++ b/src/core/qtwebengine.gni @@ -27,6 +27,7 @@ deps = [ "//components/web_cache/browser", "//components/web_cache/renderer", "//components/spellcheck:buildflags", + "//components/proxy_config", "//content/public/app:browser", "//content", "//media:media_buildflags", diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp index 9bf767437..63a1637fe 100644 --- a/src/core/web_engine_context.cpp +++ b/src/core/web_engine_context.cpp @@ -361,6 +361,7 @@ WebEngineContext::WebEngineContext() base::TaskScheduler::Create("Browser"); m_contentRunner.reset(content::ContentMainRunner::Create()); m_browserRunner.reset(content::BrowserMainRunner::Create()); + #ifdef Q_OS_LINUX // Call qputenv before BrowserMainRunnerImpl::Initialize is called. // http://crbug.com/245466 @@ -376,33 +377,18 @@ WebEngineContext::WebEngineContext() // Allow us to inject javascript like any webview toolkit. content::RenderFrameHost::AllowInjectingJavaScriptForAndroidWebView(); - base::CommandLine::CreateEmpty(); - base::CommandLine* parsedCommandLine = base::CommandLine::ForCurrentProcess(); QStringList appArgs = QCoreApplication::arguments(); - if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) { - appArgs = appArgs.mid(0, 1); // Take application name and drop the rest - appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' ')); - } - bool enableWebGLSoftwareRendering = - appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering")); + bool enableWebGLSoftwareRendering = appArgs.contains(QStringLiteral("--enable-webgl-software-rendering")); bool useEmbeddedSwitches = false; #if defined(QTWEBENGINE_EMBEDDED_SWITCHES) - useEmbeddedSwitches = !appArgs.removeAll(QStringLiteral("--disable-embedded-switches")); -#else - useEmbeddedSwitches = appArgs.removeAll(QStringLiteral("--enable-embedded-switches")); -#endif - base::CommandLine::StringVector argv; - argv.resize(appArgs.size()); -#if defined(Q_OS_WIN) - for (int i = 0; i < appArgs.size(); ++i) - argv[i] = toString16(appArgs[i]); + useEmbeddedSwitches = !appArgs.contains(QStringLiteral("--disable-embedded-switches")); #else - for (int i = 0; i < appArgs.size(); ++i) - argv[i] = appArgs[i].toStdString(); + useEmbeddedSwitches = appArgs.contains(QStringLiteral("--enable-embedded-switches")); #endif - parsedCommandLine->InitFromArgv(argv); + + base::CommandLine* parsedCommandLine = commandLine(); parsedCommandLine->AppendSwitchPath(switches::kBrowserSubprocessPath, WebEngineLibraryInfo::getPath(content::CHILD_PROCESS_EXE)); @@ -665,4 +651,34 @@ gpu::SyncPointManager *WebEngineContext::syncPointManager() return s_syncPointManager.load(); } +base::CommandLine* WebEngineContext::commandLine() { + if (base::CommandLine::CreateEmpty()) { + base::CommandLine* parsedCommandLine = base::CommandLine::ForCurrentProcess(); + QStringList appArgs = QCoreApplication::arguments(); + if (qEnvironmentVariableIsSet(kChromiumFlagsEnv)) { + appArgs = appArgs.mid(0, 1); // Take application name and drop the rest + appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' ')); + } +#ifdef Q_OS_WIN + appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering")); +#endif + appArgs.removeAll(QStringLiteral("--disable-embedded-switches")); + appArgs.removeAll(QStringLiteral("--enable-embedded-switches")); + + base::CommandLine::StringVector argv; + argv.resize(appArgs.size()); +#if defined(Q_OS_WIN) + for (int i = 0; i < appArgs.size(); ++i) + argv[i] = toString16(appArgs[i]); +#else + for (int i = 0; i < appArgs.size(); ++i) + argv[i] = appArgs[i].toStdString(); +#endif + parsedCommandLine->InitFromArgv(argv); + return parsedCommandLine; + } else { + return base::CommandLine::ForCurrentProcess(); + } +} + } // namespace diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h index ad02ddf4d..4dc5251cc 100644 --- a/src/core/web_engine_context.h +++ b/src/core/web_engine_context.h @@ -47,6 +47,7 @@ namespace base { class RunLoop; +class CommandLine; } namespace content { @@ -95,6 +96,7 @@ public: void addProfileAdapter(ProfileAdapter *profileAdapter); void removeProfileAdapter(ProfileAdapter *profileAdapter); void destroy(); + static base::CommandLine* commandLine(); static gpu::SyncPointManager *syncPointManager(); diff --git a/src/webengine/api/qquickwebengineprofile.cpp b/src/webengine/api/qquickwebengineprofile.cpp index 4448d44d1..8a6c20f67 100644 --- a/src/webengine/api/qquickwebengineprofile.cpp +++ b/src/webengine/api/qquickwebengineprofile.cpp @@ -922,20 +922,45 @@ void QQuickWebEngineProfile::clearHttpCache() d->profileAdapter()->clearHttpCache(); } - +#if QT_DEPRECATED_SINCE(5, 13) /*! Registers a request interceptor singleton \a interceptor to intercept URL requests. The profile does not take ownership of the pointer. + \obsolete + + Interceptors installed with this method will call + QWebEngineUrlRequestInterceptor::interceptRequest on the I/O thread. Therefore + the user has to provide thread-safe interaction with the other user classes. + Use setUrlRequestInterceptor instead. + \sa QWebEngineUrlRequestInterceptor + */ void QQuickWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QQuickWebEngineProfile); + interceptor->setProperty("deprecated", true); + d->profileAdapter()->setRequestInterceptor(interceptor); + qWarning("Use of deprecated not tread-safe setter, use setUrlRequestInterceptor instead."); +} +#endif + +/*! + Registers a request interceptor singleton \a interceptor to intercept URL requests. + + The profile does not take ownership of the pointer. + + \sa QWebEngineUrlRequestInfo QWebEngineUrlRequestInterceptor +*/ +void QQuickWebEngineProfile::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QQuickWebEngineProfile); d->profileAdapter()->setRequestInterceptor(interceptor); } + /*! Returns the custom URL scheme handler register for the URL scheme \a scheme. */ diff --git a/src/webengine/api/qquickwebengineprofile.h b/src/webengine/api/qquickwebengineprofile.h index 1e2e3e030..f4460ba18 100644 --- a/src/webengine/api/qquickwebengineprofile.h +++ b/src/webengine/api/qquickwebengineprofile.h @@ -126,7 +126,10 @@ public: QWebEngineCookieStore *cookieStore() const; +#if QT_DEPRECATED_SINCE(5, 13) void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); +#endif + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); const QWebEngineUrlSchemeHandler *urlSchemeHandler(const QByteArray &) const; void installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *); diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 3bc1a0b80..22ef8fffe 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -1797,7 +1797,7 @@ void QWebEnginePagePrivate::printRequested() \sa QWebEngineUrlRequestInfo, QWebEngineProfile::setRequestInterceptor() */ -void QWebEnginePage::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +void QWebEnginePage::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QWebEnginePage); bool hadInterceptorChanged = bool(d->requestInterceptor) != bool(interceptor); diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 4fd195074..55450e438 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -77,8 +77,6 @@ class QWEBENGINEWIDGETS_EXPORT QWebEnginePage : public QObject { Q_OBJECT Q_PROPERTY(QString selectedText READ selectedText) Q_PROPERTY(bool hasSelection READ hasSelection) - - // Ex-QWebFrame properties Q_PROPERTY(QUrl requestedUrl READ requestedUrl) Q_PROPERTY(qreal zoomFactor READ zoomFactor WRITE setZoomFactor) Q_PROPERTY(QString title READ title) @@ -306,7 +304,7 @@ public: void setDevToolsPage(QWebEnginePage *page); QWebEnginePage *devToolsPage() const; - void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); const QWebEngineContextMenuData &contextMenuData() const; diff --git a/src/webenginewidgets/api/qwebengineprofile.cpp b/src/webenginewidgets/api/qwebengineprofile.cpp index 7e80f9720..e9703ffe8 100644 --- a/src/webenginewidgets/api/qwebengineprofile.cpp +++ b/src/webenginewidgets/api/qwebengineprofile.cpp @@ -550,19 +550,43 @@ QWebEngineCookieStore* QWebEngineProfile::cookieStore() return d->profileAdapter()->cookieStore(); } - +#if QT_DEPRECATED_SINCE(5, 13) /*! Registers a request interceptor singleton \a interceptor to intercept URL requests. The profile does not take ownership of the pointer. + \obsolete + + Interceptors installed with this method will call + QWebEngineUrlRequestInterceptor::interceptRequest on the I/O thread. Therefore + the user has to provide thread-safe interaction with the other user classes. + Use setUrlRequestInterceptor instead. + \since 5.6 \sa QWebEngineUrlRequestInfo -*/ +*/ void QWebEngineProfile::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) { Q_D(QWebEngineProfile); + interceptor->setProperty("deprecated", true); + d->profileAdapter()->setRequestInterceptor(interceptor); + qWarning("Use of deprecated not tread-safe setter, use setUrlRequestInterceptor instead."); +} +#endif +/*! + Registers a request interceptor singleton \a interceptor to intercept URL requests. + + The profile does not take ownership of the pointer. + + \since 5.13 + \sa QWebEngineUrlRequestInfo QWebEngineUrlRequestInterceptor +*/ + +void QWebEngineProfile::setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor) +{ + Q_D(QWebEngineProfile); d->profileAdapter()->setRequestInterceptor(interceptor); } diff --git a/src/webenginewidgets/api/qwebengineprofile.h b/src/webenginewidgets/api/qwebengineprofile.h index 9fc509851..79e83c377 100644 --- a/src/webenginewidgets/api/qwebengineprofile.h +++ b/src/webenginewidgets/api/qwebengineprofile.h @@ -106,7 +106,10 @@ public: void setHttpCacheMaximumSize(int maxSize); QWebEngineCookieStore* cookieStore(); +#if QT_DEPRECATED_SINCE(5, 13) void setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); +#endif + void setUrlRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor); void clearAllVisitedLinks(); void clearVisitedLinks(const QList<QUrl> &urls); diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index 7b7fec6f4..5629998fd 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -107,6 +107,7 @@ public: void interceptRequest(QWebEngineUrlRequestInfo &info) override { + QVERIFY(QThread::currentThread() == QCoreApplication::instance()->thread()); // Since 63 we also intercept some unrelated blob requests.. if (info.requestUrl().scheme() == QLatin1String("blob")) return; @@ -167,7 +168,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() QWebEngineProfile profile; profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -197,7 +198,7 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() // Make sure that registering an observer does not modify the request. TestRequestInterceptor observer(/* intercept */ false); - profile.setRequestInterceptor(&observer); + profile.setUrlRequestInterceptor(&observer); page.load(QUrl("qrc:///resources/__placeholder__")); QTRY_COMPARE(loadSpy.count(), 1); success = loadSpy.takeFirst().takeFirst(); @@ -230,7 +231,7 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() { QWebEngineProfile profile; LocalhostContentProvider contentProvider; - profile.setRequestInterceptor(&contentProvider); + profile.setUrlRequestInterceptor(&contentProvider); QWebEnginePage page(&profile); QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); @@ -264,11 +265,11 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); TestRequestInterceptor interceptor(/* intercept */ true); if (!interceptInPage) - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); if (interceptInPage) - page.setRequestInterceptor(&interceptor); + page.setUrlRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -303,11 +304,11 @@ void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ true); if (!interceptInPage) - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); if (interceptInPage) - page.setRequestInterceptor(&interceptor); + page.setUrlRequestInterceptor(&interceptor); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); page.setUrl(QUrl("qrc:///resources/__placeholder__")); @@ -336,7 +337,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() { QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); @@ -370,7 +371,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -431,7 +432,7 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); @@ -449,7 +450,7 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() { QWebEngineProfile profile; TestRequestInterceptor interceptor(/* intercept */ false); - profile.setRequestInterceptor(&interceptor); + profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); diff --git a/tests/auto/quick/qquickwebengineview/BLACKLIST b/tests/auto/quick/qquickwebengineview/BLACKLIST index 5bb38576a..166a6894e 100644 --- a/tests/auto/quick/qquickwebengineview/BLACKLIST +++ b/tests/auto/quick/qquickwebengineview/BLACKLIST @@ -15,7 +15,3 @@ opensuse-leap [javascriptClipboard:canPaste] opensuse-leap - -[changeLocale] -* - diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index d467cd8ae..9817e7d6c 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -1001,6 +1001,7 @@ void tst_QQuickWebEngineView::changeLocale() viewDE->setUrl(url); QVERIFY(waitForLoadFailed(viewDE.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar Die Server-IP-Adresse von non.existent wurde nicht gefunden.")); @@ -1010,9 +1011,10 @@ void tst_QQuickWebEngineView::changeLocale() viewEN->setUrl(url); QVERIFY(waitForLoadFailed(viewEN.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").isNull()); errorLines = evaluateJavaScriptSync(viewEN.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached")); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("This site can\xE2\x80\x99t be reached non.existent\xE2\x80\x99s server IP address could not be found.")); // Reset error page viewDE->setUrl(QUrl("about:blank")); @@ -1022,9 +1024,10 @@ void tst_QQuickWebEngineView::changeLocale() viewDE->setUrl(url); QVERIFY(waitForLoadFailed(viewDE.data())); + QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body").isNull()); QTRY_VERIFY(!evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").isNull()); errorLines = evaluateJavaScriptSync(viewDE.data(), "document.body.innerText").toString().split(QRegularExpression("[\r\n]"), QString::SkipEmptyParts); - QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar")); + QCOMPARE(errorLines.first().toUtf8(), QByteArrayLiteral("Die Website ist nicht erreichbar Die Server-IP-Adresse von non.existent wurde nicht gefunden.")); } void tst_QQuickWebEngineView::userScripts() @@ -1110,6 +1113,39 @@ void tst_QQuickWebEngineView::javascriptClipboard() QCOMPARE(evaluateJavaScriptSync(view, "document.execCommand('paste')").toBool(), pasteResult); QCOMPARE(evaluateJavaScriptSync(view, "document.getElementById('myInput').value").toString(), (pasteResult ? QString("AnotherText") : QString("OriginalText"))); + + // Test settings on clipboard permissions + evaluateJavaScriptSync(view, + QStringLiteral( + "var accessGranted = false;" + "var accessDenied = false;" + "var accessPrompt = false;" + "navigator.permissions.query({name:'clipboard-write'})" + ".then(result => {" + "if (result.state == 'granted') accessGranted = true;" + "if (result.state == 'denied') accessDenied = true;" + "if (result.state == 'prompt') accessPrompt = true;" + "})")); + + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), copyResult); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); + + evaluateJavaScriptSync(view, + QStringLiteral( + "accessGranted = false;" + "accessDenied = false;" + "accessPrompt = false;" + "navigator.permissions.query({name:'clipboard-read'})" + ".then(result => {" + "if (result.state == 'granted') accessGranted = true;" + "if (result.state == 'denied') accessDenied = true;" + "if (result.state == 'prompt') accessPrompt = true;" + "})")); + + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessGranted").toBool(), pasteResult); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessDenied").toBool(), !javascriptCanAccessClipboard || !javascriptCanPaste); + QTRY_COMPARE(evaluateJavaScriptSync(view, "accessPrompt").toBool(), false); } QTEST_MAIN(tst_QQuickWebEngineView) diff --git a/tests/auto/widgets/proxypac/proxypac.pro b/tests/auto/widgets/proxypac/proxypac.pro index 00ae90977..1c2958d3a 100644 --- a/tests/auto/widgets/proxypac/proxypac.pro +++ b/tests/auto/widgets/proxypac/proxypac.pro @@ -3,26 +3,8 @@ QT += webengine HEADERS += proxyserver.h SOURCES += proxyserver.cpp -# QTBUG-71229 -xgd_desktop.name=XDG_CURRENT_DESKTOP -xgd_desktop.value=KDE -QT_TOOL_ENV += xgd_desktop +proxy_pac.name = QTWEBENGINE_CHROMIUM_FLAGS +proxy_pac.value = --proxy-pac-url="file://$$PWD/proxy.pac" -kde_home.name=KDEHOME -kde_home.value=$$OUT_PWD -QT_TOOL_ENV += kde_home - -PROXY_CONFIG= \ - "[Proxy Settings]" \ - "Proxy Config Script=$$PWD/proxy.pac" \ - "ProxyType=2" - -mkpath($$OUT_PWD/share/config) -KDE_FILE = $$OUT_PWD/share/config/kioslaverc - -!build_pass { - write_file($$KDE_FILE, PROXY_CONFIG) -} - -QMAKE_DISTCLEAN += $$KDE_FILE +QT_TOOL_ENV += proxy_pac diff --git a/tests/auto/widgets/qwebengineview/BLACKLIST b/tests/auto/widgets/qwebengineview/BLACKLIST index e8758abcc..7c86a72d6 100644 --- a/tests/auto/widgets/qwebengineview/BLACKLIST +++ b/tests/auto/widgets/qwebengineview/BLACKLIST @@ -6,6 +6,3 @@ osx [textSelectionOutOfInputField] * - -[changeLocale] -* diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro index eec8bb389..0addb9671 100644 --- a/tests/auto/widgets/widgets.pro +++ b/tests/auto/widgets/widgets.pro @@ -9,6 +9,7 @@ SUBDIRS += \ faviconmanager \ loadsignals \ origins \ + proxypac \ schemes \ shutdown \ qwebenginedownloaditem \ @@ -27,9 +28,6 @@ qtConfig(webengine-printing-and-pdf) { SUBDIRS += printing } -# QTBUG-71229 -linux:!boot2qt: SUBDIRS += proxypac - qtConfig(webengine-spellchecker):!cross_compile { !qtConfig(webengine-native-spellchecker) { SUBDIRS += spellchecking @@ -43,4 +41,4 @@ boot2qt: SUBDIRS -= accessibility defaultsurfaceformat devtools \ faviconmanager qwebenginepage qwebenginehistory \ qwebengineprofile qwebenginescript \ qwebengineview qwebenginedownloaditem qwebenginesettings \ - schemes origins loadsignals + schemes origins loadsignals proxypac |