diff options
Diffstat (limited to 'src/core/net')
46 files changed, 3376 insertions, 2591 deletions
diff --git a/src/core/net/client_cert_override.cpp b/src/core/net/client_cert_override.cpp deleted file mode 100644 index afb7ab5af..000000000 --- a/src/core/net/client_cert_override.cpp +++ /dev/null @@ -1,176 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "client_cert_override.h" - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/task/post_task.h" -#include "base/callback_forward.h" -#include "content/public/browser/browser_task_traits.h" -#include "net/ssl/client_cert_store.h" -#include "net/ssl/ssl_cert_request_info.h" -#include "net/ssl/ssl_private_key.h" -#include "net/cert/x509_certificate.h" -#include "third_party/boringssl/src/include/openssl/pem.h" -#include "third_party/boringssl/src/include/openssl/err.h" -#include "third_party/boringssl/src/include/openssl/evp.h" - -#include "client_cert_store_data.h" -#include "profile_io_data_qt.h" - -#include <QtNetwork/qtnetworkglobal.h> - -#if defined(USE_NSS_CERTS) -#include "net/ssl/client_cert_store_nss.h" -#endif - -#if defined(OS_WIN) -#include "net/ssl/client_cert_store_win.h" -#endif - -#if defined(OS_MACOSX) -#include "net/ssl/client_cert_store_mac.h" -#endif - -namespace { - -class ClientCertIdentityOverride : public net::ClientCertIdentity -{ -public: - ClientCertIdentityOverride(scoped_refptr<net::X509Certificate> cert, scoped_refptr<net::SSLPrivateKey> key) - : net::ClientCertIdentity(std::move(cert)), m_key(std::move(key)) {} - ~ClientCertIdentityOverride() override = default; - - void AcquirePrivateKey(base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)> private_key_callback) override - { - std::move(private_key_callback).Run(m_key); - } - -#if defined(OS_MACOSX) - SecIdentityRef sec_identity_ref() const override - { - return nullptr; - } -#endif - -private: - scoped_refptr<net::SSLPrivateKey> m_key; -}; - -} // namespace - -namespace QtWebEngineCore { - -ClientCertOverrideStore::ClientCertOverrideStore(ClientCertificateStoreData *storeData) - : ClientCertStore() - , m_storeData(storeData) - , m_nativeStore(createNativeStore()) -{ -} - -ClientCertOverrideStore::~ClientCertOverrideStore() = default; - -#if QT_CONFIG(ssl) -net::ClientCertIdentityList ClientCertOverrideStore::GetClientCertsOnUIThread(const net::SSLCertRequestInfo &cert_request_info) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::UI); - const auto &clientCertOverrideData = m_storeData->extraCerts; - // Look for certificates in memory store - for (int i = 0; i < clientCertOverrideData.length(); i++) { - scoped_refptr<net::X509Certificate> cert = clientCertOverrideData[i]->certPtr; - if (cert != NULL && cert->IsIssuedByEncoded(cert_request_info.cert_authorities)) { - net::ClientCertIdentityList selected_identities; - selected_identities.push_back(std::make_unique<ClientCertIdentityOverride>(cert, clientCertOverrideData[i]->keyPtr)); - return selected_identities; - } - } - return net::ClientCertIdentityList(); -} - -void ClientCertOverrideStore::GetClientCertsReturn(const net::SSLCertRequestInfo &cert_request_info, - ClientCertListCallback callback, - net::ClientCertIdentityList &&result) -{ - // Continue with native cert store if matching certificatse were not found in memory - if (result.empty() && m_nativeStore) - m_nativeStore->GetClientCerts(cert_request_info, std::move(callback)); - else - std::move(callback).Run(std::move(result)); -} - -#endif // QT_CONFIG(ssl) - -void ClientCertOverrideStore::GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, - ClientCertListCallback callback) -{ -#if QT_CONFIG(ssl) - // Access the user-provided data from the UI thread, but return on whatever thread this is. - if (base::PostTaskWithTraitsAndReplyWithResult( - FROM_HERE, { content::BrowserThread::UI }, - base::BindOnce(&ClientCertOverrideStore::GetClientCertsOnUIThread, - base::Unretained(this), std::cref(cert_request_info)), - base::BindOnce(&ClientCertOverrideStore::GetClientCertsReturn, - base::Unretained(this), std::cref(cert_request_info), std::move(callback))) - ) { - return; - } -#endif // QT_CONFIG(ssl) - - // Continue with native cert store if we failed to post task - if (m_nativeStore) - m_nativeStore->GetClientCerts(cert_request_info, std::move(callback)); - else - std::move(callback).Run(net::ClientCertIdentityList()); -} - -// static -std::unique_ptr<net::ClientCertStore> ClientCertOverrideStore::createNativeStore() -{ -#if defined(USE_NSS_CERTS) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(net::ClientCertStoreNSS::PasswordDelegateFactory())); -#elif defined(OS_WIN) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin()); -#elif defined(OS_MACOSX) - return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac()); -#else - return nullptr; -#endif -} -} // namespace QtWebEngineCore diff --git a/src/core/net/client_cert_override.h b/src/core/net/client_cert_override.h deleted file mode 100644 index 4f2734485..000000000 --- a/src/core/net/client_cert_override.h +++ /dev/null @@ -1,75 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef CLIENT_CERT_OVERRIDE_P_H -#define CLIENT_CERT_OVERRIDE_P_H - -#include "net/ssl/client_cert_store.h" -#include "base/callback_forward.h" -#include "net/cert/x509_certificate.h" - -namespace net { -class SSLCertRequestInfo; -} // namespace net - -namespace QtWebEngineCore { -struct ClientCertificateStoreData; - -class ClientCertOverrideStore : public net::ClientCertStore -{ -public: - ClientCertOverrideStore(ClientCertificateStoreData *storeData); - virtual ~ClientCertOverrideStore() override; - void GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, - ClientCertListCallback callback) override; -private: - static std::unique_ptr<net::ClientCertStore> createNativeStore(); - net::ClientCertIdentityList GetClientCertsOnUIThread(const net::SSLCertRequestInfo &request); - void GetClientCertsReturn(const net::SSLCertRequestInfo &cert_request_info, - ClientCertListCallback callback, - net::ClientCertIdentityList &&result); - ClientCertificateStoreData *m_storeData; - std::unique_ptr<net::ClientCertStore> m_nativeStore; -}; - -} // QtWebEngineCore - -#endif - - diff --git a/src/core/net/client_cert_qt.cpp b/src/core/net/client_cert_qt.cpp new file mode 100644 index 000000000..044e5618e --- /dev/null +++ b/src/core/net/client_cert_qt.cpp @@ -0,0 +1,148 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "client_cert_qt.h" + +#include "base/functional/bind.h" +#include "base/functional/callback_forward.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/browser_task_traits.h" +#include "crypto/crypto_buildflags.h" +#include "net/ssl/client_cert_store.h" +#include "net/ssl/ssl_cert_request_info.h" +#include "net/ssl/ssl_private_key.h" +#include "net/cert/x509_certificate.h" +#include "third_party/boringssl/src/include/openssl/pem.h" +#include "third_party/boringssl/src/include/openssl/err.h" +#include "third_party/boringssl/src/include/openssl/evp.h" + +#include "client_cert_store_data.h" + +#include <QtNetwork/qtnetworkglobal.h> + +#if BUILDFLAG(USE_NSS_CERTS) +#include "net/ssl/client_cert_store_nss.h" +#endif + +#if defined(Q_OS_WIN) +#include "net/ssl/client_cert_store_win.h" +#endif + +#if BUILDFLAG(IS_MAC) +#include "net/ssl/client_cert_store_mac.h" +#endif + +namespace { + +class ClientCertIdentityQt : public net::ClientCertIdentity +{ +public: + ClientCertIdentityQt(scoped_refptr<net::X509Certificate> cert, scoped_refptr<net::SSLPrivateKey> key) + : net::ClientCertIdentity(std::move(cert)), m_key(std::move(key)) {} + ~ClientCertIdentityQt() override = default; + + void AcquirePrivateKey(base::OnceCallback<void(scoped_refptr<net::SSLPrivateKey>)> private_key_callback) override + { + std::move(private_key_callback).Run(m_key); + } + +private: + scoped_refptr<net::SSLPrivateKey> m_key; +}; + +} // namespace + +namespace QtWebEngineCore { + +ClientCertStoreQt::ClientCertStoreQt(ClientCertificateStoreData *storeData) + : ClientCertStore() + , m_storeData(storeData) + , m_nativeStore(createNativeStore()) +{ +} + +ClientCertStoreQt::~ClientCertStoreQt() = default; + +#if QT_CONFIG(ssl) +net::ClientCertIdentityList ClientCertStoreQt::GetClientCertsOnUIThread(const net::SSLCertRequestInfo &cert_request_info) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + const auto &clientCertOverrideData = m_storeData->extraCerts; + + // Look for certificates in memory store + net::ClientCertIdentityList selected_identities; + for (int i = 0; i < clientCertOverrideData.length(); i++) { + scoped_refptr<net::X509Certificate> cert = clientCertOverrideData[i]->certPtr; + if (cert) { + if (cert->HasExpired()) { + qWarning() << "Expired certificate" << clientCertOverrideData[i]; + continue; + } + if (cert_request_info.cert_authorities.empty() + || cert->IsIssuedByEncoded(cert_request_info.cert_authorities)) { + selected_identities.push_back(std::make_unique<ClientCertIdentityQt>( + cert, clientCertOverrideData[i]->keyPtr)); + } + } + } + return selected_identities; +} + +void ClientCertStoreQt::GetClientCertsReturn(const net::SSLCertRequestInfo &cert_request_info, + ClientCertListCallback callback, + net::ClientCertIdentityList &&result) +{ + // Continue with native cert store and append them after memory certificates + if (m_nativeStore) { + ClientCertListCallback callback2 = base::BindOnce( + [](ClientCertStoreQt::ClientCertListCallback callback, + net::ClientCertIdentityList result1, net::ClientCertIdentityList result2) { + while (!result2.empty()) { + result1.push_back(std::move(result2.back())); + result2.pop_back(); + } + std::move(callback).Run(std::move(result1)); + }, + std::move(callback), std::move(result)); + m_nativeStore->GetClientCerts(cert_request_info, std::move(callback2)); + } else { + std::move(callback).Run(std::move(result)); + } +} + +#endif // QT_CONFIG(ssl) + +void ClientCertStoreQt::GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, + ClientCertListCallback callback) +{ +#if QT_CONFIG(ssl) + // Access the user-provided data from the UI thread, but return on whatever thread this is. + bool ok = content::GetUIThreadTaskRunner({})->PostTaskAndReplyWithResult( + FROM_HERE, + base::BindOnce(&ClientCertStoreQt::GetClientCertsOnUIThread, + base::Unretained(this), std::cref(cert_request_info)), + base::BindOnce(&ClientCertStoreQt::GetClientCertsReturn, + base::Unretained(this), std::cref(cert_request_info), std::move(callback))); + DCHECK(ok); // callback is already moved and we can't really recover here. +#else + if (m_nativeStore) + m_nativeStore->GetClientCerts(cert_request_info, std::move(callback)); + else + std::move(callback).Run(net::ClientCertIdentityList()); +#endif // QT_CONFIG(ssl) +} + +// static +std::unique_ptr<net::ClientCertStore> ClientCertStoreQt::createNativeStore() +{ +#if BUILDFLAG(USE_NSS_CERTS) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreNSS(net::ClientCertStoreNSS::PasswordDelegateFactory())); +#elif defined(Q_OS_WIN) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreWin()); +#elif BUILDFLAG(IS_MAC) + return std::unique_ptr<net::ClientCertStore>(new net::ClientCertStoreMac()); +#else + return nullptr; +#endif +} +} // namespace QtWebEngineCore diff --git a/src/core/net/client_cert_qt.h b/src/core/net/client_cert_qt.h new file mode 100644 index 000000000..96579fae6 --- /dev/null +++ b/src/core/net/client_cert_qt.h @@ -0,0 +1,37 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef CLIENT_CERT_QT_P_H +#define CLIENT_CERT_QT_P_H + +#include "base/functional/callback_forward.h" +#include "net/cert/x509_certificate.h" +#include "net/ssl/client_cert_store.h" + +namespace net { +class SSLCertRequestInfo; +} // namespace net + +namespace QtWebEngineCore { +struct ClientCertificateStoreData; + +class ClientCertStoreQt : public net::ClientCertStore +{ +public: + ClientCertStoreQt(ClientCertificateStoreData *storeData); + virtual ~ClientCertStoreQt() override; + void GetClientCerts(const net::SSLCertRequestInfo &cert_request_info, + ClientCertListCallback callback) override; +private: + static std::unique_ptr<net::ClientCertStore> createNativeStore(); + net::ClientCertIdentityList GetClientCertsOnUIThread(const net::SSLCertRequestInfo &request); + void GetClientCertsReturn(const net::SSLCertRequestInfo &cert_request_info, + ClientCertListCallback callback, + net::ClientCertIdentityList &&result); + ClientCertificateStoreData *m_storeData; + std::unique_ptr<net::ClientCertStore> m_nativeStore; +}; + +} // QtWebEngineCore + +#endif diff --git a/src/core/net/client_cert_store_data.cpp b/src/core/net/client_cert_store_data.cpp index 5a62cb6fe..0de6885df 100644 --- a/src/core/net/client_cert_store_data.cpp +++ b/src/core/net/client_cert_store_data.cpp @@ -1,48 +1,9 @@ -/**************************************************************************** -** -** 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) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "net/client_cert_store_data.h" #if QT_CONFIG(ssl) -#include "base/logging.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" #include "net/base/net_errors.h" #include "net/cert/x509_certificate.h" #include "net/ssl/ssl_platform_key_util.h" @@ -59,15 +20,16 @@ namespace { -class SSLPlatformKeyOverride : public net::ThreadedSSLPrivateKey::Delegate { +class SSLPlatformKeyQt : public net::ThreadedSSLPrivateKey::Delegate +{ public: - SSLPlatformKeyOverride(const QByteArray &sslKeyInBytes) + SSLPlatformKeyQt(const QByteArray &sslKeyInBytes) { m_mem = BIO_new_mem_buf(sslKeyInBytes, -1); m_key = PEM_read_bio_PrivateKey(m_mem, nullptr, nullptr, nullptr); } - ~SSLPlatformKeyOverride() override + ~SSLPlatformKeyQt() override { if (m_key) EVP_PKEY_free(m_key); @@ -103,8 +65,8 @@ public: std::vector<uint16_t> GetAlgorithmPreferences() override { - return { SSL_SIGN_RSA_PKCS1_SHA1, SSL_SIGN_RSA_PKCS1_SHA512 - , SSL_SIGN_RSA_PKCS1_SHA384, SSL_SIGN_RSA_PKCS1_SHA256 }; + return net::SSLPrivateKey::DefaultAlgorithmPreferences(EVP_PKEY_id(m_key), + /* supports pss */ true); } std::string GetProviderName() override { return "qtwebengine"; @@ -112,8 +74,6 @@ public: private: EVP_PKEY *m_key; BIO *m_mem; - - DISALLOW_COPY_AND_ASSIGN(SSLPlatformKeyOverride); }; scoped_refptr<net::SSLPrivateKey> wrapOpenSSLPrivateKey(const QByteArray &sslKeyInBytes) @@ -122,11 +82,11 @@ scoped_refptr<net::SSLPrivateKey> wrapOpenSSLPrivateKey(const QByteArray &sslKey return nullptr; return base::MakeRefCounted<net::ThreadedSSLPrivateKey>( - std::make_unique<SSLPlatformKeyOverride>(sslKeyInBytes), + std::make_unique<SSLPlatformKeyQt>(sslKeyInBytes), net::GetSSLPlatformKeyTaskRunner()); } -} // namespace +} // namespace namespace QtWebEngineCore { @@ -137,7 +97,8 @@ void ClientCertificateStoreData::add(const QSslCertificate &certificate, const Q Entry *data = new Entry; data->keyPtr = wrapOpenSSLPrivateKey(sslKeyInBytes); - data->certPtr = net::X509Certificate::CreateFromBytes(certInBytes.data(), certInBytes.length()); + data->certPtr = net::X509Certificate::CreateFromBytes(base::make_span((const unsigned char *)certInBytes.data(), + (unsigned long)certInBytes.length())); data->key = privateKey; data->certificate = certificate; extraCerts.append(data); diff --git a/src/core/net/client_cert_store_data.h b/src/core/net/client_cert_store_data.h index 7f83f4b60..c2e28ac18 100644 --- a/src/core/net/client_cert_store_data.h +++ b/src/core/net/client_cert_store_data.h @@ -1,52 +1,16 @@ -/**************************************************************************** -** -** 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) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef CLIENT_CERT_STORE_DATA_H #define CLIENT_CERT_STORE_DATA_H -#include "qtwebenginecoreglobal.h" -#include "qtnetworkglobal.h" +#include <QtWebEngineCore/qtwebenginecoreglobal.h> +#include <QtNetwork/qtnetworkglobal.h> #if QT_CONFIG(ssl) #include "base/memory/ref_counted.h" -#include <QtCore/qvector.h> +#include <QtCore/qlist.h> #include <QtNetwork/qsslcertificate.h> #include <QtNetwork/qsslkey.h> @@ -57,8 +21,10 @@ class X509Certificate; namespace QtWebEngineCore { -struct ClientCertificateStoreData { - struct Entry { +struct ClientCertificateStoreData +{ + struct Entry + { QSslKey key; QSslCertificate certificate; scoped_refptr<net::X509Certificate> certPtr; @@ -69,7 +35,7 @@ struct ClientCertificateStoreData { void remove(const QSslCertificate &certificate); void clear(); - QVector<Entry*> extraCerts; + QList<Entry *> extraCerts; }; } // namespace QtWebEngineCore diff --git a/src/core/net/cookie_monster_delegate_qt.cpp b/src/core/net/cookie_monster_delegate_qt.cpp index 5f7b75f57..d107c520c 100644 --- a/src/core/net/cookie_monster_delegate_qt.cpp +++ b/src/core/net/cookie_monster_delegate_qt.cpp @@ -1,129 +1,112 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "cookie_monster_delegate_qt.h" -#include "base/bind.h" -#include "base/memory/ptr_util.h" -#include "base/task/post_task.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" +#include "base/functional/bind.h" #include "net/cookies/cookie_util.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" #include "api/qwebenginecookiestore.h" #include "api/qwebenginecookiestore_p.h" #include "type_conversion.h" +#include <QNetworkCookie> + namespace QtWebEngineCore { -static GURL sourceUrlForCookie(const QNetworkCookie &cookie) { +class CookieChangeListener : public network::mojom::CookieChangeListener +{ +public: + CookieChangeListener(CookieMonsterDelegateQt *delegate) : m_delegate(delegate) { } + ~CookieChangeListener() override = default; + + // network::mojom::CookieChangeListener: + void OnCookieChange(const net::CookieChangeInfo &change) override + { + m_delegate->OnCookieChanged(change); + } + +private: + CookieMonsterDelegateQt *m_delegate; +}; + +class CookieAccessFilter : public network::mojom::CookieRemoteAccessFilter +{ +public: + CookieAccessFilter(CookieMonsterDelegateQt *delegate) : m_delegate(delegate) { } + ~CookieAccessFilter() override = default; + + void AllowedAccess(const GURL &url, const net::SiteForCookies &site_for_cookies, AllowedAccessCallback callback) override + { + bool allow = m_delegate->canGetCookies(toQt(site_for_cookies.first_party_url()), toQt(url)); + std::move(callback).Run(allow); + } + +private: + CookieMonsterDelegateQt *m_delegate; +}; + + +static GURL sourceUrlForCookie(const QNetworkCookie &cookie) +{ QString urlFragment = QStringLiteral("%1%2").arg(cookie.domain()).arg(cookie.path()); return net::cookie_util::CookieOriginToURL(urlFragment.toStdString(), /* is_https */ cookie.isSecure()); } CookieMonsterDelegateQt::CookieMonsterDelegateQt() - : m_client(0) - , m_cookieMonster(nullptr) + : m_client(nullptr) + , m_listener(new CookieChangeListener(this)) + , m_filter(new CookieAccessFilter(this)) + , m_receiver(m_listener.get()) + , m_filterReceiver(m_filter.get()) + , m_hasFilter(false) { } CookieMonsterDelegateQt::~CookieMonsterDelegateQt() { - } void CookieMonsterDelegateQt::AddStore(net::CookieStore *store) { - std::unique_ptr<net::CookieChangeSubscription> sub = - store->GetChangeDispatcher().AddCallbackForAllChanges( - base::Bind(&CookieMonsterDelegateQt::OnCookieChanged, - // this object's destruction will deregister the subscription. - base::Unretained(this))); + std::unique_ptr<net::CookieChangeSubscription> sub = store->GetChangeDispatcher().AddCallbackForAllChanges( + base::BindRepeating(&CookieMonsterDelegateQt::OnCookieChanged, + // this object's destruction will deregister the subscription. + base::Unretained(this))); m_subscriptions.push_back(std::move(sub)); } bool CookieMonsterDelegateQt::hasCookieMonster() { - return m_cookieMonster; + return m_mojoCookieManager.is_bound(); } -void CookieMonsterDelegateQt::getAllCookies(quint64 callbackId) +void CookieMonsterDelegateQt::getAllCookies() { - net::CookieMonster::GetCookieListCallback callback = - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread, this, callbackId); - - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesOnIOThread, this, std::move(callback))); + m_mojoCookieManager->GetAllCookies(net::CookieStore::GetAllCookiesCallback()); } -void CookieMonsterDelegateQt::GetAllCookiesOnIOThread(net::CookieMonster::GetCookieListCallback callback) -{ - if (m_cookieMonster) - m_cookieMonster->GetAllCookiesAsync(std::move(callback)); -} - -void CookieMonsterDelegateQt::setCookie(quint64 callbackId, const QNetworkCookie &cookie, const QUrl &origin) +void CookieMonsterDelegateQt::setCookie(const QNetworkCookie &cookie, const QUrl &origin) { Q_ASSERT(hasCookieMonster()); Q_ASSERT(m_client); - net::CookieStore::SetCookiesCallback callback; - if (callbackId != CallbackDirectory::NoCallbackId) - callback = base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnIOThread, this, callbackId); - GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin); + std::string cookie_line = cookie.toRawForm().toStdString(); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&CookieMonsterDelegateQt::SetCookieOnIOThread, this, - gurl, cookie.toRawForm().toStdString(), std::move(callback))); -} - -void CookieMonsterDelegateQt::SetCookieOnIOThread( - const GURL& url, const std::string& cookie_line, - net::CookieMonster::SetCookiesCallback callback) -{ + net::CookieInclusionStatus inclusion; + auto canonCookie = net::CanonicalCookie::Create(gurl, cookie_line, base::Time::Now(), + absl::nullopt, absl::nullopt, true, &inclusion); + if (!canonCookie || !inclusion.IsInclude()) { + LOG(WARNING) << "QWebEngineCookieStore::setCookie() - Tried to set invalid cookie"; + return; + } net::CookieOptions options; options.set_include_httponly(); - - if (m_cookieMonster) - m_cookieMonster->SetCookieWithOptionsAsync(url, cookie_line, options, std::move(callback)); + options.set_same_site_cookie_context(net::CookieOptions::SameSiteCookieContext::MakeInclusiveForSet()); + m_mojoCookieManager->SetCanonicalCookie(*canonCookie.get(), gurl, options, net::CookieStore::SetCookiesCallback()); } void CookieMonsterDelegateQt::deleteCookie(const QNetworkCookie &cookie, const QUrl &origin) @@ -132,89 +115,69 @@ void CookieMonsterDelegateQt::deleteCookie(const QNetworkCookie &cookie, const Q Q_ASSERT(m_client); GURL gurl = origin.isEmpty() ? sourceUrlForCookie(cookie) : toGurl(origin); - - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookieOnIOThread, this, - gurl, cookie.name().toStdString())); + std::string cookie_name = cookie.name().toStdString(); + auto filter = network::mojom::CookieDeletionFilter::New(); + filter->url = gurl; + filter->cookie_name = cookie_name; + m_mojoCookieManager->DeleteCookies(std::move(filter), network::mojom::CookieManager::DeleteCookiesCallback()); } -void CookieMonsterDelegateQt::DeleteCookieOnIOThread(const GURL& url, const std::string& cookie_name) -{ - if (m_cookieMonster) { - net::CookieMonster::GetCookieListCallback callback = - base::BindOnce(&CookieMonsterDelegateQt::GetCookiesToDeleteCallback, this, cookie_name); - m_cookieMonster->GetAllCookiesForURLAsync(url, std::move(callback)); - } -} - -void CookieMonsterDelegateQt::GetCookiesToDeleteCallback(const std::string& cookie_name, const net::CookieList &cookies, const net::CookieStatusList &statusList) -{ - Q_UNUSED(statusList); - if (!m_cookieMonster) - return; - - net::CookieList cookiesToDelete; - for (auto cookie : cookies) { - if (cookie.Name() == cookie_name) - cookiesToDelete.push_back(cookie); - } - for (auto cookie : cookiesToDelete) - m_cookieMonster->DeleteCanonicalCookieAsync(cookie, base::DoNothing()); -} - - -void CookieMonsterDelegateQt::deleteSessionCookies(quint64 callbackId) +void CookieMonsterDelegateQt::deleteSessionCookies() { Q_ASSERT(hasCookieMonster()); Q_ASSERT(m_client); - net::CookieMonster::DeleteCallback callback = - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread, this, std::move(callback))); + auto filter = network::mojom::CookieDeletionFilter::New(); + filter->session_control = network::mojom::CookieDeletionSessionControl::SESSION_COOKIES; + m_mojoCookieManager->DeleteCookies(std::move(filter), network::mojom::CookieManager::DeleteCookiesCallback()); } -void CookieMonsterDelegateQt::DeleteSessionCookiesOnIOThread(net::CookieMonster::DeleteCallback callback) -{ - if (m_cookieMonster) - m_cookieMonster->DeleteSessionCookiesAsync(std::move(callback)); -} - -void CookieMonsterDelegateQt::deleteAllCookies(quint64 callbackId) +void CookieMonsterDelegateQt::deleteAllCookies() { Q_ASSERT(hasCookieMonster()); Q_ASSERT(m_client); - net::CookieMonster::DeleteCallback callback = - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread, this, callbackId); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&CookieMonsterDelegateQt::DeleteAllOnIOThread, this, std::move(callback))); + auto filter = network::mojom::CookieDeletionFilter::New(); + m_mojoCookieManager->DeleteCookies(std::move(filter), network::mojom::CookieManager::DeleteCookiesCallback()); } -void CookieMonsterDelegateQt::DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback) +void CookieMonsterDelegateQt::setMojoCookieManager(mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_info) { - if (m_cookieMonster) - m_cookieMonster->DeleteAllAsync(std::move(callback)); -} + if (m_mojoCookieManager.is_bound()) + unsetMojoCookieManager(); -void CookieMonsterDelegateQt::setCookieMonster(net::CookieMonster* monster) -{ - if (monster == m_cookieMonster) - return; + Q_ASSERT(!m_mojoCookieManager.is_bound()); + Q_ASSERT(!m_receiver.is_bound()); - m_subscriptions.clear(); - if (monster) - AddStore(monster); + m_mojoCookieManager.Bind(std::move(cookie_manager_info)); - m_cookieMonster = monster; + m_mojoCookieManager->AddGlobalChangeListener(m_receiver.BindNewPipeAndPassRemote()); + if (m_hasFilter) + m_mojoCookieManager->SetRemoteFilter(m_filterReceiver.BindNewPipeAndPassRemote()); - if (!m_client) + if (m_client) + m_client->d_func()->processPendingUserCookies(); +} + +void CookieMonsterDelegateQt::setHasFilter(bool hasFilter) +{ + m_hasFilter = hasFilter; + if (!m_mojoCookieManager.is_bound()) return; + if (m_hasFilter) { + if (!m_filterReceiver.is_bound()) + m_mojoCookieManager->SetRemoteFilter(m_filterReceiver.BindNewPipeAndPassRemote()); + } else { + if (m_filterReceiver.is_bound()) + m_filterReceiver.reset(); + } +} - if (monster) - m_client->d_func()->processPendingUserCookies(); - else - m_client->d_func()->rejectPendingUserCookies(); +void CookieMonsterDelegateQt::unsetMojoCookieManager() +{ + m_receiver.reset(); + m_filterReceiver.reset(); + m_mojoCookieManager.reset(); } void CookieMonsterDelegateQt::setClient(QWebEngineCookieStore *client) @@ -246,54 +209,11 @@ bool CookieMonsterDelegateQt::canGetCookies(const QUrl &firstPartyUrl, const QUr return m_client->d_func()->canAccessCookies(firstPartyUrl, url); } -void CookieMonsterDelegateQt::OnCookieChanged(const net::CanonicalCookie& cookie, net::CookieChangeCause cause) +void CookieMonsterDelegateQt::OnCookieChanged(const net::CookieChangeInfo &change) { if (!m_client) return; - m_client->d_func()->onCookieChanged(toQt(cookie), cause != net::CookieChangeCause::INSERTED); -} - -void CookieMonsterDelegateQt::GetAllCookiesCallbackOnIOThread(qint64 callbackId, const net::CookieList &cookies, const net::CookieStatusList &statusList) -{ - QByteArray rawCookies; - for (auto &&cookie : cookies) - rawCookies += toQt(cookie).toRawForm() % QByteArrayLiteral("\n"); - - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread, this, callbackId, rawCookies)); + m_client->d_func()->onCookieChanged(toQt(change.cookie), change.cause != net::CookieChangeCause::INSERTED); } -void CookieMonsterDelegateQt::SetCookieCallbackOnIOThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status) -{ - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&CookieMonsterDelegateQt::SetCookieCallbackOnUIThread, this, callbackId, status)); -} - -void CookieMonsterDelegateQt::DeleteCookiesCallbackOnIOThread(qint64 callbackId, uint numCookies) -{ - base::PostTaskWithTraits( - FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread, this, callbackId, numCookies)); -} - -void CookieMonsterDelegateQt::GetAllCookiesCallbackOnUIThread(qint64 callbackId, const QByteArray &cookies) -{ - if (m_client) - m_client->d_func()->onGetAllCallbackResult(callbackId, cookies); -} - -void CookieMonsterDelegateQt::SetCookieCallbackOnUIThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status) -{ - if (m_client) - m_client->d_func()->onSetCallbackResult(callbackId, - status == net::CanonicalCookie::CookieInclusionStatus::INCLUDE); -} - -void CookieMonsterDelegateQt::DeleteCookiesCallbackOnUIThread(qint64 callbackId, uint numCookies) -{ - if (m_client) - m_client->d_func()->onDeleteCallbackResult(callbackId, numCookies); -} -} +} // namespace QtWebEngineCore diff --git a/src/core/net/cookie_monster_delegate_qt.h b/src/core/net/cookie_monster_delegate_qt.h index 2ac04acb4..f6872323d 100644 --- a/src/core/net/cookie_monster_delegate_qt.h +++ b/src/core/net/cookie_monster_delegate_qt.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // // W A R N I N G @@ -53,70 +17,66 @@ #include "qtwebenginecoreglobal_p.h" -QT_WARNING_PUSH -// For some reason adding -Wno-unused-parameter to QMAKE_CXXFLAGS has no -// effect with clang, so use a pragma for these dirty chromium headers -QT_WARNING_DISABLE_CLANG("-Wunused-parameter") +// We need to work around Chromium using 'signals' as a variable name in headers: +#ifdef signals +#define StAsH_signals signals +#undef signals +#endif #include "base/memory/ref_counted.h" -#include "net/cookies/cookie_monster.h" -QT_WARNING_POP +#include "mojo/public/cpp/bindings/receiver.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "net/cookies/cookie_store.h" +#include "services/network/public/mojom/cookie_manager.mojom-forward.h" +#include "services/network/public/mojom/cookie_manager.mojom.h" +#ifdef StAsH_signals +#define signals StAsH_signals +#undef StAsH_signals +#endif -#include <QNetworkCookie> #include <QPointer> +QT_FORWARD_DECLARE_CLASS(QNetworkCookie) QT_FORWARD_DECLARE_CLASS(QWebEngineCookieStore) namespace QtWebEngineCore { -// Extends net::CookieMonster::kDefaultCookieableSchemes with qrc, without enabling -// cookies for the file:// scheme, which is disabled by default in Chromium. -// Since qrc:// is similar to file:// and there are some unknowns about how -// to correctly handle file:// cookies, qrc:// should only be used for testing. -static const char* const kCookieableSchemes[] = - { "http", "https", "qrc", "ws", "wss" }; +class CookieMonsterDelegateQtPrivate; -class Q_WEBENGINECORE_PRIVATE_EXPORT CookieMonsterDelegateQt : public base::RefCountedThreadSafe<CookieMonsterDelegateQt> { +class Q_WEBENGINECORE_EXPORT CookieMonsterDelegateQt : public base::RefCountedThreadSafe<CookieMonsterDelegateQt> +{ QPointer<QWebEngineCookieStore> m_client; - net::CookieMonster *m_cookieMonster; std::vector<std::unique_ptr<net::CookieChangeSubscription>> m_subscriptions; + + mojo::Remote<network::mojom::CookieManager> m_mojoCookieManager; + std::unique_ptr<network::mojom::CookieChangeListener> m_listener; + std::unique_ptr<network::mojom::CookieRemoteAccessFilter> m_filter; + mojo::Receiver<network::mojom::CookieChangeListener> m_receiver; + mojo::Receiver<network::mojom::CookieRemoteAccessFilter> m_filterReceiver; + bool m_hasFilter; public: CookieMonsterDelegateQt(); ~CookieMonsterDelegateQt(); bool hasCookieMonster(); - void setCookie(quint64 callbackId, const QNetworkCookie &cookie, const QUrl &origin); + void setCookie(const QNetworkCookie &cookie, const QUrl &origin); void deleteCookie(const QNetworkCookie &cookie, const QUrl &origin); - void getAllCookies(quint64 callbackId); - void deleteSessionCookies(quint64 callbackId); - void deleteAllCookies(quint64 callbackId); + void getAllCookies(); + void deleteSessionCookies(); + void deleteAllCookies(); - void setCookieMonster(net::CookieMonster* monster); void setClient(QWebEngineCookieStore *client); + void setMojoCookieManager(mojo::PendingRemote<network::mojom::CookieManager> cookie_manager_info); + void unsetMojoCookieManager(); + void setHasFilter(bool b); bool canSetCookie(const QUrl &firstPartyUrl, const QByteArray &cookieLine, const QUrl &url) const; bool canGetCookies(const QUrl &firstPartyUrl, const QUrl &url) const; void AddStore(net::CookieStore *store); - void OnCookieChanged(const net::CanonicalCookie &cookie, net::CookieChangeCause cause); - -private: - void GetAllCookiesOnIOThread(net::CookieMonster::GetCookieListCallback callback); - void SetCookieOnIOThread(const GURL& url, const std::string& cookie_line, net::CookieMonster::SetCookiesCallback callback); - void DeleteCookieOnIOThread(const GURL& url, const std::string& cookie_name); - void DeleteSessionCookiesOnIOThread(net::CookieMonster::DeleteCallback callback); - void DeleteAllOnIOThread(net::CookieMonster::DeleteCallback callback); - - void GetCookiesToDeleteCallback(const std::string& cookie_name, const net::CookieList &cookies, const net::CookieStatusList &statusList); - void GetAllCookiesCallbackOnIOThread(qint64 callbackId, const net::CookieList &cookies, const net::CookieStatusList &statusList); - void SetCookieCallbackOnIOThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status); - void DeleteCookiesCallbackOnIOThread(qint64 callbackId, uint numCookies); - - void GetAllCookiesCallbackOnUIThread(qint64 callbackId, const QByteArray &cookies); - void SetCookieCallbackOnUIThread(qint64 callbackId, net::CanonicalCookie::CookieInclusionStatus status); - void DeleteCookiesCallbackOnUIThread(qint64 callbackId, uint numCookies); + void OnCookieChanged(const net::CookieChangeInfo &change); }; -} +} // namespace QtWebEngineCore #endif // COOKIE_MONSTER_DELEGATE_QT_H diff --git a/src/core/net/custom_protocol_handler.cpp b/src/core/net/custom_protocol_handler.cpp deleted file mode 100644 index 7e8ee47ab..000000000 --- a/src/core/net/custom_protocol_handler.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "custom_protocol_handler.h" -#include "url_request_custom_job.h" - -#include "net/base/net_errors.h" -#include "net/url_request/url_request.h" -#include "net/url_request/url_request_error_job.h" - -namespace QtWebEngineCore { - -CustomProtocolHandler::CustomProtocolHandler(QPointer<ProfileAdapter> profileAdapter) - : m_profileAdapter(profileAdapter) -{ -} - -net::URLRequestJob *CustomProtocolHandler::MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const -{ - if (!networkDelegate) - return new net::URLRequestErrorJob(request, nullptr, net::ERR_ACCESS_DENIED); - - return new URLRequestCustomJob(request, networkDelegate, request->url().scheme(), m_profileAdapter); -} - -} // namespace diff --git a/src/core/net/custom_protocol_handler.h b/src/core/net/custom_protocol_handler.h deleted file mode 100644 index 7b189763c..000000000 --- a/src/core/net/custom_protocol_handler.h +++ /dev/null @@ -1,88 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#ifndef CUSTOM_PROTOCOL_HANDLER_H_ -#define CUSTOM_PROTOCOL_HANDLER_H_ - -#include "qtwebenginecoreglobal_p.h" -#include "net/url_request/url_request_job_factory.h" - -#include <QtCore/QByteArray> -#include <QtCore/QObject> -#include <QtCore/QPointer> - -QT_FORWARD_DECLARE_CLASS(QIODevice) - -namespace net { -class NetworkDelegate; -class URLRequestJob; -} // namespace - -namespace QtWebEngineCore { - -class ProfileAdapter; - -// Implements a ProtocolHandler for custom URL schemes. -// If |network_delegate_| is NULL then all file requests will fail with ERR_ACCESS_DENIED. -class Q_WEBENGINECORE_PRIVATE_EXPORT CustomProtocolHandler : public net::URLRequestJobFactory::ProtocolHandler { - -public: - CustomProtocolHandler(QPointer<ProfileAdapter> profileAdapter); - - net::URLRequestJob *MaybeCreateJob(net::URLRequest *request, net::NetworkDelegate *networkDelegate) const override; - -private: - DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); - QPointer<ProfileAdapter> m_profileAdapter; -}; - -} // namespace - -#endif // CUSTOM_PROTOCOL_HANDLER_H_ diff --git a/src/core/net/custom_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp new file mode 100644 index 000000000..b91a1289b --- /dev/null +++ b/src/core/net/custom_url_loader_factory.cpp @@ -0,0 +1,536 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "custom_url_loader_factory.h" + +#include "base/strings/stringprintf.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "mojo/public/cpp/bindings/pending_receiver.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/system/data_pipe.h" +#include "mojo/public/cpp/system/simple_watcher.h" +#include "net/base/net_errors.h" +#include "net/http/http_status_code.h" +#include "net/http/http_util.h" +#include "services/network/public/cpp/cors/cors.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "services/network/public/mojom/url_response_head.mojom.h" +#include "url/url_util.h" +#include "url/url_util_qt.h" + +#include "api/qwebengineurlscheme.h" +#include "net/url_request_custom_job_proxy.h" +#include "profile_adapter.h" +#include "qwebengineloadinginfo.h" +#include "type_conversion.h" +#include "web_contents_adapter_client.h" +#include "web_contents_delegate_qt.h" +#include "web_contents_view_qt.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qfile.h> +#include <QtCore/qfileinfo.h> +#include <QtCore/qiodevice.h> +#include <QtCore/qmimedatabase.h> +#include <QtCore/qmimedata.h> +#include <QtCore/qpointer.h> +#include <QtCore/qurl.h> + +namespace QtWebEngineCore { + +namespace { + +class CustomURLLoader : public network::mojom::URLLoader + , private URLRequestCustomJobProxy::Client +{ +public: + static void CreateAndStart(const network::ResourceRequest &request, + mojo::PendingReceiver<network::mojom::URLLoader> loader, + mojo::PendingRemote<network::mojom::URLLoaderClient> client_remote, + QPointer<ProfileAdapter> profileAdapter, + content::WebContents *webContents) + { + // CustomURLLoader will handle its own life-cycle, and delete when + // the client lets go. + auto *customUrlLoader = new CustomURLLoader(request, std::move(loader), std::move(client_remote), profileAdapter, webContents); + customUrlLoader->Start(); + } + + // network::mojom::URLLoader: + void FollowRedirect(const std::vector<std::string> &removed_headers, + const net::HttpRequestHeaders &modified_headers, + const net::HttpRequestHeaders &modified_cors_exempt_headers, // FIXME: do something with this? + const absl::optional<GURL> &new_url) override + { + // We can be asked for follow our own redirect + scoped_refptr<URLRequestCustomJobProxy> proxy = new URLRequestCustomJobProxy(this, m_proxy->m_scheme, m_proxy->m_profileAdapter); + m_proxy->m_client = nullptr; +// m_taskRunner->PostTask(FROM_HERE, base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); + m_proxy = std::move(proxy); + if (new_url) + m_request.url = *new_url; + else + m_request.url = m_redirect; + m_redirect = GURL(); + for (const std::string &header: removed_headers) + m_request.headers.RemoveHeader(header); + m_request.headers.MergeFrom(modified_headers); + Start(); + } + void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override { } + void PauseReadingBodyFromNet() override { } + void ResumeReadingBodyFromNet() override { } + +private: + CustomURLLoader(const network::ResourceRequest &request, + mojo::PendingReceiver<network::mojom::URLLoader> loader, + mojo::PendingRemote<network::mojom::URLLoaderClient> client_remote, + QPointer<ProfileAdapter> profileAdapter, + content::WebContents *webContents) + // ### We can opt to run the url-loader on the UI thread instead + : m_taskRunner(content::GetIOThreadTaskRunner({})) + , m_proxy(new URLRequestCustomJobProxy(this, request.url.scheme(), profileAdapter)) + , m_webContents(webContents) + , m_receiver(this, std::move(loader)) + , m_client(std::move(client_remote)) + , m_request(request) + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_receiver.set_disconnect_handler( + base::BindOnce(&CustomURLLoader::OnConnectionError, m_weakPtrFactory.GetWeakPtr())); + m_firstBytePosition = 0; + m_device = nullptr; + m_error = 0; + QWebEngineUrlScheme scheme = QWebEngineUrlScheme::schemeByName(QByteArray::fromStdString(request.url.scheme())); + m_corsEnabled = scheme.flags().testFlag(QWebEngineUrlScheme::CorsEnabled); + m_isLocal = scheme.flags().testFlag(QWebEngineUrlScheme::LocalScheme); + } + + ~CustomURLLoader() override = default; + + void Start() + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + + if (network::cors::IsCorsEnabledRequestMode(m_request.mode)) { + // CORS mode requires a valid request_initiator. + if (!m_request.request_initiator) + return CompleteWithFailure(net::ERR_INVALID_ARGUMENT); + + if (m_isLocal) { + std::string fromScheme = m_request.request_initiator->GetTupleOrPrecursorTupleIfOpaque().scheme(); + const std::vector<std::string> &localSchemes = url::GetLocalSchemes(); + bool fromLocal = base::Contains(localSchemes, fromScheme); + bool hasLocalAccess = fromLocal; + if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(fromScheme)) + hasLocalAccess = cs->flags & (url::CustomScheme::LocalAccessAllowed | url::CustomScheme::Local); + if (!hasLocalAccess) + return CompleteWithFailure(net::ERR_ACCESS_DENIED); + } else if (!m_corsEnabled && !m_request.request_initiator->IsSameOriginWith(url::Origin::Create(m_request.url))) { + // Custom schemes are not covered by CorsURLLoader, so we need to reject CORS requests manually. + return CompleteWithFailure(network::CorsErrorStatus(network::mojom::CorsError::kCorsDisabledScheme)); + } + } + + if (mojo::CreateDataPipe(nullptr, m_pipeProducerHandle, m_pipeConsumerHandle) != MOJO_RESULT_OK) + return CompleteWithFailure(net::ERR_FAILED); + + m_head = network::mojom::URLResponseHead::New(); + m_head->request_start = base::TimeTicks::Now(); + + if (!m_pipeConsumerHandle.is_valid()) + return CompleteWithFailure(net::ERR_FAILED); + + std::map<std::string, std::string> headers; + net::HttpRequestHeaders::Iterator it(m_request.headers); + while (it.GetNext()) + headers.emplace(it.name(), it.value()); + if (!m_request.referrer.is_empty()) + headers.emplace("Referer", m_request.referrer.spec()); + + std::string rangeHeader; + if (ParseRange(m_request.headers)) + m_firstBytePosition = m_byteRange.first_byte_position(); + +// m_taskRunner->PostTask(FROM_HERE, + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::initialize, m_proxy, m_request.url, + m_request.method, m_request.request_initiator, std::move(headers), + m_request.request_body)); + } + + void CompleteWithFailure(network::CorsErrorStatus cors_error) + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_client->OnComplete(network::URLLoaderCompletionStatus(cors_error)); + ClearProxyAndClient(false); + } + + void CompleteWithFailure(net::Error net_error) + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_client->OnComplete(network::URLLoaderCompletionStatus(net_error)); + ClearProxyAndClient(false); + } + + void OnConnectionError() + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_receiver.reset(); + if (m_client.is_bound()) + ClearProxyAndClient(false); + else + delete this; + } + + void OnTransferComplete(MojoResult result) + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + if (result == MOJO_RESULT_OK) { + network::URLLoaderCompletionStatus status(net::OK); + status.encoded_data_length = m_totalBytesRead + m_headerBytesRead; + status.encoded_body_length = m_totalBytesRead; + status.decoded_body_length = m_totalBytesRead; + m_client->OnComplete(status); + } else { + m_client->OnComplete(network::URLLoaderCompletionStatus(net::ERR_FAILED)); + } + ClearProxyAndClient(false /* result == MOJO_RESULT_OK */); + } + + void ClearProxyAndClient(bool wait_for_loader_error = false) + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_proxy->m_client = nullptr; + m_client.reset(); + if (m_device && m_device->isOpen()) + m_device->close(); + m_device = nullptr; +// m_taskRunner->PostTask(FROM_HERE, base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); + content::GetUIThreadTaskRunner({})->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); + if (!wait_for_loader_error || !m_receiver.is_bound()) + delete this; + } + + // URLRequestCustomJobProxy::Client: + void notifyExpectedContentSize(qint64 size) override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_totalSize = size; + if (m_byteRange.IsValid()) { + if (!m_byteRange.ComputeBounds(size)) { + CompleteWithFailure(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE); + } else { + m_maxBytesToRead = m_byteRange.last_byte_position() - m_byteRange.first_byte_position() + 1; + m_head->content_length = m_maxBytesToRead; + } + } else { + m_head->content_length = size; + } + } + void notifyHeadersComplete() override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + DCHECK(!m_error); + m_head->response_start = base::TimeTicks::Now(); + + std::string headers; + if (!m_redirect.is_empty()) { + headers += "HTTP/1.1 303 See Other\n"; + headers += base::StringPrintf("Location: %s\n", m_redirect.spec().c_str()); + } else { + if (m_byteRange.IsValid() && m_totalSize > 0) { + headers += "HTTP/1.1 206 Partial Content\n"; + headers += net::HttpResponseHeaders::kContentRange; + headers += base::StringPrintf(": bytes %lld-%lld/%lld", + qlonglong{m_byteRange.first_byte_position()}, + qlonglong{m_byteRange.last_byte_position()}, + qlonglong{m_totalSize}); + headers += "\n"; + } else { + headers += "HTTP/1.1 200 OK\n"; + } + if (m_mimeType.size() > 0) { + headers += net::HttpRequestHeaders::kContentType; + headers += base::StringPrintf(": %s", m_mimeType.c_str()); + if (m_charset.size() > 0) + headers += base::StringPrintf("; charset=%s", m_charset.c_str()); + headers += "\n"; + } + } + if (m_corsEnabled) { + std::string origin; + if (m_request.headers.GetHeader("Origin", &origin)) { + headers += base::StringPrintf("Access-Control-Allow-Origin: %s\n", origin.c_str()); + headers += "Access-Control-Allow-Credentials: true\n"; + } + } + for (auto it = m_additionalResponseHeaders.cbegin(); + it != m_additionalResponseHeaders.cend(); ++it) { + headers += it.key().toLower().toStdString() + ": " + it.value().toLower().toStdString() + + "\n"; + } + m_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(net::HttpUtil::AssembleRawHeaders(headers)); + m_head->encoded_data_length = m_head->headers->raw_headers().length(); + + if (!m_redirect.is_empty()) { + m_head->content_length = {}; + m_head->encoded_body_length = {}; + net::RedirectInfo::FirstPartyURLPolicy first_party_url_policy = + m_request.update_first_party_url_on_redirect ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT + : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL; + net::RedirectInfo redirectInfo = net::RedirectInfo::ComputeRedirectInfo( + m_request.method, m_request.url, + m_request.site_for_cookies, + first_party_url_policy, m_request.referrer_policy, + m_request.referrer.spec(), net::HTTP_SEE_OTHER, + m_redirect, absl::nullopt, false /*insecure_scheme_was_upgraded*/); + m_client->OnReceiveRedirect(redirectInfo, std::move(m_head)); + m_head = nullptr; + // ### should m_request be updated with RedirectInfo? (see FollowRedirect) + return; + } + DCHECK(m_device); + m_head->mime_type = m_mimeType; + m_head->charset = m_charset; + m_headerBytesRead = m_head->headers->raw_headers().length(); + m_client->OnReceiveResponse(std::move(m_head), std::move(m_pipeConsumerHandle), absl::nullopt); + m_head = nullptr; + + m_watcher = std::make_unique<mojo::SimpleWatcher>( + FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL, m_taskRunner); + m_watcher->Watch(m_pipeProducerHandle.get(), MOJO_HANDLE_SIGNAL_WRITABLE, + MOJO_WATCH_CONDITION_SATISFIED, + base::BindRepeating(&CustomURLLoader::notifyReadyWrite, + m_weakPtrFactory.GetWeakPtr())); + + readAvailableData(); // May delete this + } + void notifyCanceled() override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + OnTransferComplete(MOJO_RESULT_CANCELLED); + } + void notifyAborted() override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + notifyStartFailure(net::ERR_ABORTED); + } + void notifyStartFailure(int error) override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + m_head->response_start = base::TimeTicks::Now(); + std::string headers; + switch (error) { + case net::ERR_INVALID_URL: + headers = "HTTP/1.1 400 Bad Request\n"; + break; + case net::ERR_FILE_NOT_FOUND: + headers = "HTTP/1.1 404 Not Found\n"; + break; + case net::ERR_ABORTED: + headers = "HTTP/1.1 503 Request Aborted\n"; + break; + case net::ERR_ACCESS_DENIED: + headers = "HTTP/1.1 403 Forbidden\n"; + break; + case net::ERR_FAILED: + headers = "HTTP/1.1 400 Request Failed\n"; + break; + default: + headers = "HTTP/1.1 500 Internal Error\n"; + break; + } + m_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(net::HttpUtil::AssembleRawHeaders(headers)); + m_head->encoded_data_length = m_head->headers->raw_headers().length(); + m_head->content_length = {}; + m_head->encoded_body_length = {}; + m_client->OnReceiveResponse(std::move(m_head), mojo::ScopedDataPipeConsumerHandle(), absl::nullopt); + CompleteWithFailure(net::Error(error)); + } + void notifySuccess() override + { + if (m_webContents) { + WebContentsDelegateQt *delegate = + static_cast<WebContentsDelegateQt *>(m_webContents->GetDelegate()); + delegate->emitLoadSucceeded(toQt(m_request.url)); + } + } + void notifyReadyRead() override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + readAvailableData(); + } + void notifyReadyWrite(MojoResult result, const mojo::HandleSignalsState &state) + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + if (result != MOJO_RESULT_OK) { + CompleteWithFailure(net::ERR_FAILED); + return; + } + readAvailableData(); + } + bool readAvailableData() + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + for (;;) { + if (m_error || !m_device) + break; + + void *buffer = nullptr; + uint32_t bufferSize = 0; + MojoResult beginResult = m_pipeProducerHandle->BeginWriteData( + &buffer, &bufferSize, MOJO_BEGIN_WRITE_DATA_FLAG_NONE); + if (beginResult == MOJO_RESULT_SHOULD_WAIT) { + m_watcher->ArmOrNotify(); + return false; // Wait for pipe watcher + } + if (beginResult != MOJO_RESULT_OK) + break; + if (m_maxBytesToRead > 0 && m_maxBytesToRead <= int64_t{std::numeric_limits<uint32_t>::max()}) + bufferSize = std::min(bufferSize, uint32_t(m_maxBytesToRead)); + + int readResult = m_device->read(static_cast<char *>(buffer), bufferSize); + uint32_t bytesRead = std::max(readResult, 0); + m_pipeProducerHandle->EndWriteData(bytesRead); + m_totalBytesRead += bytesRead; + m_client->OnTransferSizeUpdated(m_totalBytesRead); + + const bool deviceAtEnd = m_device->atEnd(); + if ((deviceAtEnd && !m_device->isSequential()) + || (m_maxBytesToRead > 0 && m_totalBytesRead >= m_maxBytesToRead)) { + OnTransferComplete(MOJO_RESULT_OK); + return true; // Done with reading + } + + if (readResult == 0) + return false; // Wait for readyRead + if (readResult < 0 && deviceAtEnd && m_device->isSequential()) { + // Failure on read, and sequential device claiming to be at end, so treat it as a successful end-of-data. + OnTransferComplete(MOJO_RESULT_OK); + return true; // Done with reading + } + if (readResult < 0) + break; + } + + CompleteWithFailure(m_error ? net::Error(m_error) : net::ERR_FAILED); + return true; // Done with reading + } + bool ParseRange(const net::HttpRequestHeaders &headers) + { + std::string range_header; + if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) { + std::vector<net::HttpByteRange> ranges; + if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) { + // Chromium doesn't support multirange requests. + if (ranges.size() == 1) { + m_byteRange = ranges[0]; + return true; + } + } + } + return false; + } + base::SequencedTaskRunner *taskRunner() override + { + DCHECK(m_taskRunner->RunsTasksInCurrentSequence()); + return m_taskRunner.get(); + } + + scoped_refptr<base::SequencedTaskRunner> m_taskRunner; + scoped_refptr<URLRequestCustomJobProxy> m_proxy; + content::WebContents *m_webContents; + + mojo::Receiver<network::mojom::URLLoader> m_receiver; + mojo::Remote<network::mojom::URLLoaderClient> m_client; + mojo::ScopedDataPipeProducerHandle m_pipeProducerHandle; + mojo::ScopedDataPipeConsumerHandle m_pipeConsumerHandle; + std::unique_ptr<mojo::SimpleWatcher> m_watcher; + + net::HttpByteRange m_byteRange; + int64_t m_totalSize = 0; + int64_t m_maxBytesToRead = -1; + network::ResourceRequest m_request; + network::mojom::URLResponseHeadPtr m_head; + qint64 m_headerBytesRead = 0; + qint64 m_totalBytesRead = 0; + bool m_corsEnabled; + bool m_isLocal; + + base::WeakPtrFactory<CustomURLLoader> m_weakPtrFactory{this}; +}; + +class CustomURLLoaderFactory : public network::mojom::URLLoaderFactory { +public: + CustomURLLoaderFactory(ProfileAdapter *profileAdapter, content::WebContents *webContents, mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) + : m_taskRunner(content::GetIOThreadTaskRunner({})) + , m_profileAdapter(profileAdapter) + , m_webContents(webContents) + { + m_receivers.set_disconnect_handler(base::BindRepeating( + &CustomURLLoaderFactory::OnDisconnect, base::Unretained(this))); + m_receivers.Add(this, std::move(receiver)); + } + ~CustomURLLoaderFactory() override = default; + + // network::mojom::URLLoaderFactory: + void CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, + const network::ResourceRequest &request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override + { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + Q_UNUSED(request_id); + Q_UNUSED(options); + Q_UNUSED(traffic_annotation); + + m_taskRunner->PostTask(FROM_HERE, + base::BindOnce(&CustomURLLoader::CreateAndStart, request, + std::move(loader), std::move(client), + m_profileAdapter, m_webContents)); + + } + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) override + { + m_receivers.Add(this, std::move(receiver)); + } + + void OnDisconnect() + { + if (m_receivers.empty()) + delete this; + } + + static mojo::PendingRemote<network::mojom::URLLoaderFactory> Create(ProfileAdapter *profileAdapter, content::WebContents *webContents) + { + mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_remote; + new CustomURLLoaderFactory(profileAdapter, webContents, pending_remote.InitWithNewPipeAndPassReceiver()); + return pending_remote; + } + + const scoped_refptr<base::SequencedTaskRunner> m_taskRunner; + mojo::ReceiverSet<network::mojom::URLLoaderFactory> m_receivers; + QPointer<ProfileAdapter> m_profileAdapter; + content::WebContents *m_webContents; +}; + +} // namespace + +mojo::PendingRemote<network::mojom::URLLoaderFactory> CreateCustomURLLoaderFactory(ProfileAdapter *profileAdapter, content::WebContents *webContents) +{ + return CustomURLLoaderFactory::Create(profileAdapter, webContents); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/custom_url_loader_factory.h b/src/core/net/custom_url_loader_factory.h new file mode 100644 index 000000000..e4a767c85 --- /dev/null +++ b/src/core/net/custom_url_loader_factory.h @@ -0,0 +1,36 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef CUSTOM_URL_LOADER_FACTORY_H_ +#define CUSTOM_URL_LOADER_FACTORY_H_ + +#include "mojo/public/cpp/bindings/pending_remote.h" + +namespace content { +class WebContents; +} +namespace network { +namespace mojom { +class URLLoaderFactory; +} // namespace mojom +} // namespace network + +namespace QtWebEngineCore { +class ProfileAdapter; + +mojo::PendingRemote<network::mojom::URLLoaderFactory> CreateCustomURLLoaderFactory(ProfileAdapter *profileAdapter, content::WebContents *webContents); + +} // namespace QtWebEngineCore + +#endif // CUSTOM_URL_LOADER_FACTORY_H_ diff --git a/src/core/net/network_delegate_qt.cpp b/src/core/net/network_delegate_qt.cpp deleted file mode 100644 index 68bf34d31..000000000 --- a/src/core/net/network_delegate_qt.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "network_delegate_qt.h" - -#include "base/task/post_task.h" -#include "content/browser/web_contents/web_contents_impl.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" -#include "content/public/browser/render_view_host.h" -#include "content/public/browser/resource_request_info.h" -#include "net/base/load_flags.h" -#include "net/url_request/url_request.h" -#include "ui/base/page_transition_types.h" - -#include "profile_adapter.h" -#include "cookie_monster_delegate_qt.h" -#include "profile_io_data_qt.h" -#include "qwebengineurlrequestinfo.h" -#include "qwebengineurlrequestinfo_p.h" -#include "qwebengineurlrequestinterceptor.h" -#include "type_conversion.h" -#include "web_contents_adapter_client.h" -#include "web_contents_view_qt.h" -#include "url_request_notification.h" - -namespace QtWebEngineCore { - -WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition) -{ - if (ui::PageTransitionIsRedirect(transition)) - return WebContentsAdapterClient::RedirectNavigation; - - int32_t qualifier = ui::PageTransitionGetQualifier(transition); - - if (qualifier & ui::PAGE_TRANSITION_FORWARD_BACK) - return WebContentsAdapterClient::BackForwardNavigation; - - ui::PageTransition strippedTransition = ui::PageTransitionStripQualifier(transition); - - switch (strippedTransition) { - case ui::PAGE_TRANSITION_LINK: - return WebContentsAdapterClient::LinkNavigation; - case ui::PAGE_TRANSITION_TYPED: - return WebContentsAdapterClient::TypedNavigation; - case ui::PAGE_TRANSITION_FORM_SUBMIT: - return WebContentsAdapterClient::FormSubmittedNavigation; - case ui::PAGE_TRANSITION_RELOAD: - return WebContentsAdapterClient::ReloadNavigation; - default: - return WebContentsAdapterClient::OtherNavigation; - } -} - -static QWebEngineUrlRequestInfo::ResourceType toQt(content::ResourceType resourceType) -{ - if (resourceType >= content::ResourceType::kMainFrame && resourceType <= content::ResourceType::kMaxValue) - return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType); - return QWebEngineUrlRequestInfo::ResourceTypeUnknown; -} - -static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) -{ - return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType); -} - -NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) - : m_profileIOData(data) -{ -} - -int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::CompletionOnceCallback callback, GURL *newUrl) -{ - Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - Q_ASSERT(m_profileIOData); - content::ResourceRequestInfo *resourceInfo = content::ResourceRequestInfo::ForRequest(request); - - content::ResourceType resourceType = content::ResourceType::kMaxValue; - WebContentsAdapterClient::NavigationType navigationType = WebContentsAdapterClient::OtherNavigation; - - if (resourceInfo) { - resourceType = resourceInfo->GetResourceType(); - navigationType = pageTransitionToNavigationType(resourceInfo->GetPageTransition()); - } - - const QUrl qUrl = toQt(request->url()); - - QUrl firstPartyUrl = QUrl(); - if (resourceType == content::ResourceType::kSubFrame) - firstPartyUrl = toQt(request->first_party_url()); - else - firstPartyUrl = toQt(request->site_for_cookies()); - - const QUrl initiator = request->initiator().has_value() ? toQt(request->initiator()->GetURL()) : QUrl(); - - QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), - toQt(navigationType), - qUrl, - firstPartyUrl, - initiator, - QByteArray::fromStdString(request->method())); - QWebEngineUrlRequestInfo requestInfo(infoPrivate); - - // 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) { - std::string h = header.key().toStdString(); - if (base::LowerCaseEqualsASCII(h, "referer")) { - request->SetReferrer(header.value().toStdString()); - } else { - request->SetExtraRequestHeaderByName(h, header.value().toStdString(), /* overwrite */ true); - } - } - } - - if (result != net::OK) - return result; - - requestInfo.resetChanged(); - } - } else { - m_profileIOData->releaseInterceptor(); - } - } - // Deprecated =cut - - if (!resourceInfo) - return net::OK; - - if (!m_profileIOData->hasPageInterceptors() && !profileInterceptor && !content::IsResourceTypeFrame(resourceType)) - return net::OK; - - auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); - new URLRequestNotification( - request, - resourceInfo->IsMainFrame(), - newUrl, - std::move(requestInfo), - webContentsGetter, - std::move(callback), - profileInterceptor ? m_profileIOData->profileAdapter() : nullptr - ); - - // We'll run the callback after we notified the UI thread. - return net::ERR_IO_PENDING; -} - -void NetworkDelegateQt::OnURLRequestDestroyed(net::URLRequest*) -{ -} - -void NetworkDelegateQt::OnCompleted(net::URLRequest */*request*/, bool /*started*/, int /*net_error*/) -{ -} - -bool NetworkDelegateQt::OnCanSetCookie(const net::URLRequest& request, - const net::CanonicalCookie & /*cookie*/, - net::CookieOptions*, - bool allowedFromCaller) -{ - if (!allowedFromCaller) - return false; - return canSetCookies(request.site_for_cookies(), request.url(), std::string()); -} - -bool NetworkDelegateQt::OnCanGetCookies(const net::URLRequest& request, const net::CookieList&, bool allowedFromCaller) -{ - if (!allowedFromCaller) - return false; - return canGetCookies(request.site_for_cookies(), request.url()); -} - -bool NetworkDelegateQt::OnForcePrivacyMode(const GURL &url, const GURL &site_for_cookies) const -{ - return false; -// FIXME: This is what the NetworkContext implementation does (changes tst_QWebEngineCookieStore tests since 72) -// return !canGetCookies(site_for_cookies, url); -} - -bool NetworkDelegateQt::canSetCookies(const GURL &first_party, const GURL &url, const std::string &cookie_line) const -{ - Q_ASSERT(m_profileIOData); - return m_profileIOData->canSetCookie(toQt(first_party), QByteArray::fromStdString(cookie_line), toQt(url)); -} - -bool NetworkDelegateQt::canGetCookies(const GURL &first_party, const GURL &url) const -{ - Q_ASSERT(m_profileIOData); - return m_profileIOData->canGetCookies(toQt(first_party), toQt(url)); -} - -int NetworkDelegateQt::OnBeforeStartTransaction(net::URLRequest *, net::CompletionOnceCallback, net::HttpRequestHeaders *) -{ - return net::OK; -} - -void NetworkDelegateQt::OnBeforeSendHeaders(net::URLRequest* request, const net::ProxyInfo& proxy_info, - const net::ProxyRetryInfoMap& proxy_retry_info, net::HttpRequestHeaders* headers) -{ -} - -void NetworkDelegateQt::OnStartTransaction(net::URLRequest *request, const net::HttpRequestHeaders &headers) -{ -} - -int NetworkDelegateQt::OnHeadersReceived(net::URLRequest*, net::CompletionOnceCallback, const net::HttpResponseHeaders*, scoped_refptr<net::HttpResponseHeaders>*, GURL*) -{ - return net::OK; -} - -void NetworkDelegateQt::OnBeforeRedirect(net::URLRequest*, const GURL&) -{ -} - -void NetworkDelegateQt::OnResponseStarted(net::URLRequest*, int) -{ -} - -void NetworkDelegateQt::OnNetworkBytesReceived(net::URLRequest*, int64_t) -{ -} - -void NetworkDelegateQt::OnNetworkBytesSent(net::URLRequest*, int64_t) -{ -} - -void NetworkDelegateQt::OnPACScriptError(int, const base::string16&) -{ -} - -net::NetworkDelegate::AuthRequiredResponse NetworkDelegateQt::OnAuthRequired(net::URLRequest*, const net::AuthChallengeInfo&, AuthCallback, net::AuthCredentials*) -{ - return AUTH_REQUIRED_RESPONSE_NO_ACTION; -} - -bool NetworkDelegateQt::OnCanAccessFile(const net::URLRequest&, const base::FilePath&, const base::FilePath&) const -{ - return true; -} - -bool NetworkDelegateQt::OnCancelURLRequestWithPolicyViolatingReferrerHeader(const net::URLRequest&, const GURL&, const GURL&) const -{ - return false; -} - -bool NetworkDelegateQt::OnCanQueueReportingReport(const url::Origin& origin) const -{ - return false; -} - -void NetworkDelegateQt::OnCanSendReportingReports(std::set<url::Origin> origins, base::OnceCallback<void(std::set<url::Origin>)> result_callback) const -{ - std::move(result_callback).Run(std::set<url::Origin>()); -} - -bool NetworkDelegateQt::OnCanSetReportingClient(const url::Origin& origin, const GURL& endpoint) const -{ - return false; -} - -bool NetworkDelegateQt::OnCanUseReportingClient(const url::Origin& origin, const GURL& endpoint) const -{ - return false; -} - -} // namespace QtWebEngineCore diff --git a/src/core/net/network_delegate_qt.h b/src/core/net/network_delegate_qt.h deleted file mode 100644 index 53debadcd..000000000 --- a/src/core/net/network_delegate_qt.h +++ /dev/null @@ -1,94 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef NETWORK_DELEGATE_QT_H -#define NETWORK_DELEGATE_QT_H - -#include "net/base/network_delegate.h" -#include "net/base/net_errors.h" - -#include <QUrl> -#include <QSet> - -namespace content { -class WebContents; -} - -namespace QtWebEngineCore { - -class ProfileIODataQt; - -class NetworkDelegateQt : public net::NetworkDelegate { - ProfileIODataQt *m_profileIOData; -public: - NetworkDelegateQt(ProfileIODataQt *data); - - // net::NetworkDelegate implementation - int OnBeforeURLRequest(net::URLRequest* request, net::CompletionOnceCallback callback, GURL* new_url) override; - void OnURLRequestDestroyed(net::URLRequest* request) override; - bool OnCanSetCookie(const net::URLRequest& request, const net::CanonicalCookie& cookie, net::CookieOptions* options, bool) override; - int OnBeforeStartTransaction(net::URLRequest *request, const net::CompletionOnceCallback callback, net::HttpRequestHeaders *headers) override; - void OnBeforeSendHeaders(net::URLRequest* request, const net::ProxyInfo& proxy_info, - const net::ProxyRetryInfoMap& proxy_retry_info, net::HttpRequestHeaders* headers) override; - void OnStartTransaction(net::URLRequest *request, const net::HttpRequestHeaders &headers) override; - int OnHeadersReceived(net::URLRequest*, net::CompletionOnceCallback, const net::HttpResponseHeaders*, scoped_refptr<net::HttpResponseHeaders>*, GURL*) override; - void OnBeforeRedirect(net::URLRequest*, const GURL&) override; - void OnResponseStarted(net::URLRequest*, int) override; - void OnNetworkBytesReceived(net::URLRequest*, int64_t) override; - void OnNetworkBytesSent(net::URLRequest *, int64_t) override; - void OnCompleted(net::URLRequest *request, bool started, int net_error) override; - void OnPACScriptError(int, const base::string16&) override; - net::NetworkDelegate::AuthRequiredResponse OnAuthRequired(net::URLRequest*, const net::AuthChallengeInfo&, AuthCallback, net::AuthCredentials*) override; - bool OnCanGetCookies(const net::URLRequest&, const net::CookieList&, bool) override; - bool OnCanAccessFile(const net::URLRequest&, const base::FilePath&, const base::FilePath&) const override; - bool OnForcePrivacyMode(const GURL&, const GURL&) const override; - bool OnCancelURLRequestWithPolicyViolatingReferrerHeader(const net::URLRequest&, const GURL&, const GURL&) const override; - - bool OnCanQueueReportingReport(const url::Origin& origin) const override; - void OnCanSendReportingReports(std::set<url::Origin> origins, base::OnceCallback<void(std::set<url::Origin>)> result_callback) const override; - bool OnCanSetReportingClient(const url::Origin& origin, const GURL& endpoint) const override; - bool OnCanUseReportingClient(const url::Origin& origin, const GURL& endpoint) const override; - - bool canSetCookies(const GURL &first_party, const GURL &url, const std::string &cookie_line) const; - bool canGetCookies(const GURL &first_party, const GURL &url) const; -}; - -} // namespace QtWebEngineCore - -#endif // NETWORK_DELEGATE_QT_H diff --git a/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp new file mode 100644 index 000000000..319d3a566 --- /dev/null +++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.cpp @@ -0,0 +1,199 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// based on chrome/browser/plugins/plugin_response_interceptor_url_loader_throttle.cc +// Copyright 2018 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 "plugin_response_interceptor_url_loader_throttle.h" + +#include "base/functional/bind.h" +#include "base/uuid.h" +#include "chrome/browser/extensions/api/streams_private/streams_private_api.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/download_manager.h" +#include "content/public/browser/download_request_utils.h" +#include "content/public/browser/download_utils.h" +#include "extensions/browser/guest_view/mime_handler_view/mime_handler_view_attach_helper.h" +#include "extensions/common/constants.h" +#include "extensions/common/extension.h" +#include "extensions/common/manifest_handlers/mime_types_handler.h" +#include "third_party/blink/public/mojom/loader/transferrable_url_loader.mojom.h" + +#include "extensions/extension_system_qt.h" +#include "web_contents_delegate_qt.h" +#include "web_engine_settings.h" + +#include <string> +#include <tuple> + +namespace { + +constexpr uint32_t kFullPageMimeHandlerDataPipeSize = 512U; + +void ClearAllButFrameAncestors(network::mojom::URLResponseHead *response_head) +{ + response_head->headers->RemoveHeader("Content-Security-Policy"); + response_head->headers->RemoveHeader("Content-Security-Policy-Report-Only"); + + if (!response_head->parsed_headers) + return; + + std::vector<network::mojom::ContentSecurityPolicyPtr> &csp = + response_head->parsed_headers->content_security_policy; + std::vector<network::mojom::ContentSecurityPolicyPtr> cleared; + + for (auto &policy : csp) { + auto frame_ancestors = policy->directives.find(network::mojom::CSPDirectiveName::FrameAncestors); + if (frame_ancestors == policy->directives.end()) + continue; + + auto cleared_policy = network::mojom::ContentSecurityPolicy::New(); + cleared_policy->self_origin = std::move(policy->self_origin); + cleared_policy->header = std::move(policy->header); + cleared_policy->header->header_value = ""; + cleared_policy->directives[network::mojom::CSPDirectiveName::FrameAncestors] = std::move(frame_ancestors->second); + + auto raw_frame_ancestors = policy->raw_directives.find(network::mojom::CSPDirectiveName::FrameAncestors); + DCHECK(raw_frame_ancestors != policy->raw_directives.end()); + + cleared_policy->header->header_value = "frame-ancestors " + raw_frame_ancestors->second; + response_head->headers->AddHeader( + cleared_policy->header->type == network::mojom::ContentSecurityPolicyType::kEnforce + ? "Content-Security-Policy" + : "Content-Security-Policy-Report-Only", + cleared_policy->header->header_value); + cleared_policy->raw_directives[network::mojom::CSPDirectiveName::FrameAncestors] = + std::move(raw_frame_ancestors->second); + + cleared.push_back(std::move(cleared_policy)); + } + + csp.swap(cleared); +} +} // namespace + + +namespace QtWebEngineCore { + +PluginResponseInterceptorURLLoaderThrottle::PluginResponseInterceptorURLLoaderThrottle( + network::mojom::RequestDestination request_destination, + int frame_tree_node_id) + : m_request_destination(request_destination), m_frame_tree_node_id(frame_tree_node_id) +{} + +void PluginResponseInterceptorURLLoaderThrottle::WillProcessResponse(const GURL &response_url, + network::mojom::URLResponseHead *response_head, + bool *defer) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::WebContents *web_contents = content::WebContents::FromFrameTreeNodeId(m_frame_tree_node_id); + if (!web_contents) + return; + + if (content::download_utils::MustDownload( + web_contents->GetBrowserContext(), + response_url, response_head->headers.get(), response_head->mime_type)) + return; + + std::string extension_id; + if (response_head->mime_type == "application/pdf") + extension_id = extension_misc::kPdfExtensionId; + if (extension_id.empty()) + return; + + WebContentsDelegateQt *contentsDelegate = static_cast<WebContentsDelegateQt *>(web_contents->GetDelegate()); + if (!contentsDelegate) + return; + + WebEngineSettings *settings = contentsDelegate->webEngineSettings(); + if (!settings->testAttribute(QWebEngineSettings::PdfViewerEnabled) + || !settings->testAttribute(QWebEngineSettings::PluginsEnabled)) { + // PluginServiceFilterQt will inform the URLLoader about the disabled state of plugins + // and we can expect the download to be triggered automatically. It's unnecessary to + // go further and start the guest view embedding process. + return; + } + + // Chrome's PDF Extension does not work properly in the face of a restrictive + // Content-Security-Policy, and does not currently respect the policy anyway. + // Ignore CSP served on a PDF response. https://crbug.com/271452 + if (extension_id == extension_misc::kPdfExtensionId && response_head->headers) + ClearAllButFrameAncestors(response_head); + + const std::string stream_id = base::Uuid::GenerateRandomV4().AsLowercaseString(); + + mojo::PendingRemote<network::mojom::URLLoader> dummy_new_loader; + std::ignore = dummy_new_loader.InitWithNewPipeAndPassReceiver(); + mojo::Remote<network::mojom::URLLoaderClient> new_client; + mojo::PendingReceiver<network::mojom::URLLoaderClient> new_client_receiver = + new_client.BindNewPipeAndPassReceiver(); + + const std::string internal_id = base::UnguessableToken::Create().ToString(); + // Provide the MimeHandlerView code a chance to override the payload. This is + // the case where the resource is handled by frame-based MimeHandlerView. + const std::string payload = + extensions::MimeHandlerViewAttachHelper::OverrideBodyForInterceptedResponse( + m_frame_tree_node_id, response_url, response_head->mime_type, stream_id, + internal_id, + base::BindOnce(&PluginResponseInterceptorURLLoaderThrottle::ResumeLoad, + weak_factory_.GetWeakPtr())); + *defer = true; + + mojo::ScopedDataPipeProducerHandle producer_handle; + mojo::ScopedDataPipeConsumerHandle consumer_handle; + CHECK_EQ(MOJO_RESULT_OK, mojo::CreateDataPipe(kFullPageMimeHandlerDataPipeSize, producer_handle, consumer_handle)); + + uint32_t len = static_cast<uint32_t>(payload.size()); + CHECK_EQ(MOJO_RESULT_OK, + producer_handle->WriteData( + payload.c_str(), &len, MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)); + + network::URLLoaderCompletionStatus status(net::OK); + status.decoded_body_length = len; + new_client->OnComplete(status); + + mojo::PendingRemote<network::mojom::URLLoader> original_loader; + mojo::PendingReceiver<network::mojom::URLLoaderClient> original_client; + mojo::ScopedDataPipeConsumerHandle body = std::move(consumer_handle); + delegate_->InterceptResponse(std::move(dummy_new_loader), + std::move(new_client_receiver), + &original_loader, &original_client, + &body); + + // Make a deep copy of URLResponseHead before passing it cross-thread. + auto deep_copied_response = response_head->Clone(); + if (response_head->headers) { + deep_copied_response->headers = + base::MakeRefCounted<net::HttpResponseHeaders>( + response_head->headers->raw_headers()); + } + + auto transferrable_loader = blink::mojom::TransferrableURLLoader::New(); + transferrable_loader->url = GURL( + extensions::Extension::GetBaseURLFromExtensionId(extension_id).spec() + + base::Uuid::GenerateRandomV4().AsLowercaseString()); + transferrable_loader->url_loader = std::move(original_loader); + transferrable_loader->url_loader_client = std::move(original_client); + transferrable_loader->head = std::move(deep_copied_response); + transferrable_loader->head->intercepted_by_plugin = true; + transferrable_loader->body = std::move(body); + + bool embedded = m_request_destination != + network::mojom::RequestDestination::kDocument; + content::GetUIThreadTaskRunner({})->PostTask( + FROM_HERE, + base::BindOnce(&extensions::StreamsPrivateAPI::SendExecuteMimeTypeHandlerEvent, + extension_id, stream_id, embedded, m_frame_tree_node_id, + std::move(transferrable_loader), response_url, internal_id)); +} + +void PluginResponseInterceptorURLLoaderThrottle::ResumeLoad() +{ + delegate_->Resume(); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/plugin_response_interceptor_url_loader_throttle.h b/src/core/net/plugin_response_interceptor_url_loader_throttle.h new file mode 100644 index 000000000..fb3918c45 --- /dev/null +++ b/src/core/net/plugin_response_interceptor_url_loader_throttle.h @@ -0,0 +1,41 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_ +#define PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_ + +#include "base/memory/weak_ptr.h" +#include "services/network/public/mojom/fetch_api.mojom-shared.h" +#include "third_party/blink/public/common/loader/url_loader_throttle.h" + +namespace content { +class BrowserContext; +} + +namespace QtWebEngineCore { + +class PluginResponseInterceptorURLLoaderThrottle : public blink::URLLoaderThrottle +{ +public: + PluginResponseInterceptorURLLoaderThrottle(network::mojom::RequestDestination request_destination, + int frame_tree_node_id); + ~PluginResponseInterceptorURLLoaderThrottle() override = default; + +private: + // content::URLLoaderThrottle overrides; + void WillProcessResponse(const GURL &response_url, network::mojom::URLResponseHead *response_head, bool *defer) override; + + // Resumes loading for an intercepted response. This would give the extension + // layer chance to initialize its browser side state. + void ResumeLoad(); + + const network::mojom::RequestDestination m_request_destination; + const int m_frame_tree_node_id; + + base::WeakPtrFactory<PluginResponseInterceptorURLLoaderThrottle> + weak_factory_{this}; +}; + +} // namespace QtWebEngineCore + +#endif // PLUGIN_RESPONSE_INTERCEPTOR_URL_LOADER_THROTTLE_H_ diff --git a/src/core/net/proxy_config_monitor.cpp b/src/core/net/proxy_config_monitor.cpp new file mode 100644 index 000000000..8315b7bf2 --- /dev/null +++ b/src/core/net/proxy_config_monitor.cpp @@ -0,0 +1,82 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// originally based on chrome/browser/net/proxy_config_monitor.cc +// Copyright 2017 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 "profile_qt.h" +#include "proxy_config_monitor.h" +#include "proxy_config_service_qt.h" + +#include "content/public/browser/browser_task_traits.h" +#include "components/prefs/pref_service.h" +#include "content/public/browser/browser_thread.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "net/proxy_resolution/proxy_config_with_annotation.h" +#include "services/network/public/mojom/network_context.mojom.h" + +#include <utility> + +using content::BrowserThread; + +ProxyConfigMonitor::ProxyConfigMonitor(PrefService *prefs) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + proxy_config_service_.reset(new ProxyConfigServiceQt(prefs, content::GetUIThreadTaskRunner({}))); + + proxy_config_service_->AddObserver(this); +} + +ProxyConfigMonitor::~ProxyConfigMonitor() +{ + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) + || !BrowserThread::IsThreadInitialized(BrowserThread::UI)); + proxy_config_service_->RemoveObserver(this); +} + +void ProxyConfigMonitor::AddToNetworkContextParams( + network::mojom::NetworkContextParams *network_context_params) +{ + mojo::PendingRemote<network::mojom::ProxyConfigClient> proxy_config_client; + network_context_params->proxy_config_client_receiver = + proxy_config_client.InitWithNewPipeAndPassReceiver(); + proxy_config_client_set_.Add(std::move(proxy_config_client)); + + poller_receiver_set_.Add(this, + network_context_params->proxy_config_poller_client.InitWithNewPipeAndPassReceiver()); + + net::ProxyConfigWithAnnotation proxy_config; + net::ProxyConfigService::ConfigAvailability availability = + proxy_config_service_->GetLatestProxyConfig(&proxy_config); + if (availability != net::ProxyConfigService::CONFIG_PENDING) + network_context_params->initial_proxy_config = proxy_config; +} + +void ProxyConfigMonitor::OnProxyConfigChanged( + const net::ProxyConfigWithAnnotation &config, + net::ProxyConfigService::ConfigAvailability availability) +{ + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) + || !BrowserThread::IsThreadInitialized(BrowserThread::UI)); + for (const auto &proxy_config_client : proxy_config_client_set_) { + switch (availability) { + case net::ProxyConfigService::CONFIG_VALID: + proxy_config_client->OnProxyConfigUpdated(config); + break; + case net::ProxyConfigService::CONFIG_UNSET: + proxy_config_client->OnProxyConfigUpdated(net::ProxyConfigWithAnnotation::CreateDirect()); + break; + case net::ProxyConfigService::CONFIG_PENDING: + NOTREACHED(); + break; + } + } +} + +void ProxyConfigMonitor::OnLazyProxyConfigPoll() +{ + proxy_config_service_->OnLazyPoll(); +} diff --git a/src/core/net/proxy_config_monitor.h b/src/core/net/proxy_config_monitor.h new file mode 100644 index 000000000..585e4b7ed --- /dev/null +++ b/src/core/net/proxy_config_monitor.h @@ -0,0 +1,59 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// originally based on chrome/browser/net/proxy_config_monitor.h +// Copyright 2017 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. + +#ifndef PROXY_CONFIG_MONITOR_H +#define PROXY_CONFIG_MONITOR_H + +#include <memory> + +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "mojo/public/cpp/bindings/remote_set.h" +#include "net/proxy_resolution/proxy_config_service.h" +#include "services/network/public/mojom/network_context.mojom-forward.h" +#include "services/network/public/mojom/proxy_config_with_annotation.mojom.h" + +namespace net { +class ProxyConfigWithAnnotation; +} + +class PrefService; +class ProxyConfigServiceQt; + +// Tracks the ProxyConfig to use, and passes any updates to a NetworkContext's +// ProxyConfigClient. This also responds to errors related to proxy settings +// from the NetworkContext, and forwards them any listening Chrome extensions +// associated with the profile. +class ProxyConfigMonitor : public net::ProxyConfigService::Observer, + public network::mojom::ProxyConfigPollerClient +{ +public: + explicit ProxyConfigMonitor(PrefService *prefs = nullptr); + + ~ProxyConfigMonitor() override; + + // Populates proxy-related fields of |network_context_params|. Updated + // ProxyConfigs will be sent to a NetworkContext created with those params + // whenever the configuration changes. Can be called more than once to inform + // multiple NetworkContexts of proxy changes. + void AddToNetworkContextParams(network::mojom::NetworkContextParams *network_context_params); + +private: + // net::ProxyConfigService::Observer implementation: + void OnProxyConfigChanged(const net::ProxyConfigWithAnnotation &config, + net::ProxyConfigService::ConfigAvailability availability) override; + + // network::mojom::ProxyConfigPollerClient implementation: + void OnLazyProxyConfigPoll() override; + + std::unique_ptr<ProxyConfigServiceQt> proxy_config_service_; + + mojo::ReceiverSet<network::mojom::ProxyConfigPollerClient> poller_receiver_set_; + mojo::RemoteSet<network::mojom::ProxyConfigClient> proxy_config_client_set_; +}; + +#endif // PROXY_CONFIG_MONITOR_H diff --git a/src/core/net/proxy_config_service_qt.cpp b/src/core/net/proxy_config_service_qt.cpp index 00ff1c54d..d74ec699d 100644 --- a/src/core/net/proxy_config_service_qt.cpp +++ b/src/core/net/proxy_config_service_qt.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only //================ Based on ChromeProxyConfigService ======================= @@ -45,58 +9,63 @@ #include "proxy_config_service_qt.h" -#include "base/bind.h" -#include "content/public/browser/browser_thread.h" #include "components/proxy_config/pref_proxy_config_tracker_impl.h" +#include "net/base/proxy_server.h" -using content::BrowserThread; +#include <QNetworkProxy> net::ProxyServer ProxyConfigServiceQt::fromQNetworkProxy(const QNetworkProxy &qtProxy) { - net::HostPortPair hostPortPair(qtProxy.hostName().toStdString(), qtProxy.port()); + std::string host = qtProxy.hostName().toStdString(); + uint16_t port = qtProxy.port(); switch (qtProxy.type()) { case QNetworkProxy::Socks5Proxy: - return net::ProxyServer(net::ProxyServer::SCHEME_SOCKS5, hostPortPair); + return net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_SOCKS5, host, port); case QNetworkProxy::HttpProxy: case QNetworkProxy::HttpCachingProxy: case QNetworkProxy::FtpCachingProxy: - return net::ProxyServer(net::ProxyServer::SCHEME_HTTP, hostPortPair); + return net::ProxyServer::FromSchemeHostAndPort(net::ProxyServer::SCHEME_HTTP, host, port); case QNetworkProxy::NoProxy: case QNetworkProxy::DefaultProxy: - return net::ProxyServer(net::ProxyServer::SCHEME_DIRECT, net::HostPortPair()); default: return net::ProxyServer(net::ProxyServer::SCHEME_INVALID, net::HostPortPair()); } } -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_prefConfig(initialConfig), - m_perfState(initialState) +ProxyConfigServiceQt::ProxyConfigServiceQt(PrefService *prefService, + const scoped_refptr<base::SequencedTaskRunner> &taskRunner) + : m_baseService(net::ProxyConfigService::CreateSystemProxyConfigService(taskRunner)) + , m_usesSystemConfiguration(false) + , m_registeredObserver(false) + , m_prefState(prefService + ? PrefProxyConfigTrackerImpl::ReadPrefConfig(prefService, &m_prefConfig) + : ProxyPrefs::CONFIG_UNSET) { + DETACH_FROM_SEQUENCE(m_sequenceChecker); } ProxyConfigServiceQt::~ProxyConfigServiceQt() { + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); if (m_registeredObserver && m_baseService.get()) m_baseService->RemoveObserver(this); } void ProxyConfigServiceQt::AddObserver(net::ProxyConfigService::Observer *observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); m_observers.AddObserver(observer); } void ProxyConfigServiceQt::RemoveObserver(net::ProxyConfigService::Observer *observer) { + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); m_observers.RemoveObserver(observer); } net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxyConfig(net::ProxyConfigWithAnnotation *config) { + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); m_usesSystemConfiguration = QNetworkProxyFactory::usesSystemConfiguration(); if (m_usesSystemConfiguration) { // Use Chromium's base service to retrieve system settings @@ -106,7 +75,7 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy systemAvailability = m_baseService->GetLatestProxyConfig(&systemConfig); ProxyPrefs::ConfigState configState; systemAvailability = PrefProxyConfigTrackerImpl::GetEffectiveProxyConfig( - m_perfState, m_prefConfig, systemAvailability, systemConfig, + m_prefState, m_prefConfig, systemAvailability, systemConfig, false, &configState, config); RegisterObserver(); return systemAvailability; @@ -151,7 +120,7 @@ net::ProxyConfigService::ConfigAvailability ProxyConfigServiceQt::GetLatestProxy void ProxyConfigServiceQt::OnLazyPoll() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); // We need to update if // - setUseSystemConfiguration() was called in between @@ -168,7 +137,7 @@ void ProxyConfigServiceQt::OnLazyPoll() // Called when the base service changed void ProxyConfigServiceQt::OnProxyConfigChanged(const net::ProxyConfigWithAnnotation &config, ConfigAvailability availability) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); Q_UNUSED(config); if (!m_usesSystemConfiguration) @@ -180,6 +149,7 @@ void ProxyConfigServiceQt::OnProxyConfigChanged(const net::ProxyConfigWithAnnota // Update our observers void ProxyConfigServiceQt::Update() { + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); net::ProxyConfigWithAnnotation actual_config; ConfigAvailability availability = GetLatestProxyConfig(&actual_config); if (availability == CONFIG_PENDING) @@ -193,7 +163,7 @@ void ProxyConfigServiceQt::Update() // in the constructor. void ProxyConfigServiceQt::RegisterObserver() { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK_CALLED_ON_VALID_SEQUENCE(m_sequenceChecker); if (!m_registeredObserver && m_baseService.get()) { m_baseService->AddObserver(this); m_registeredObserver = true; diff --git a/src/core/net/proxy_config_service_qt.h b/src/core/net/proxy_config_service_qt.h index 09e88d445..49c9877a5 100644 --- a/src/core/net/proxy_config_service_qt.h +++ b/src/core/net/proxy_config_service_qt.h @@ -1,48 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef PROXY_CONFIG_SERVICE_QT_H #define PROXY_CONFIG_SERVICE_QT_H -#include "base/memory/ref_counted.h" #include "base/observer_list.h" - +#include "base/task/sequenced_task_runner.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" @@ -50,16 +13,17 @@ #include <QNetworkProxy> +class PrefService; + class ProxyConfigServiceQt - : public net::ProxyConfigService - , public net::ProxyConfigService::Observer { + : public net::ProxyConfigService + , public net::ProxyConfigService::Observer +{ public: - static net::ProxyServer fromQNetworkProxy(const QNetworkProxy &); - explicit ProxyConfigServiceQt(std::unique_ptr<ProxyConfigService> baseService, - const net::ProxyConfigWithAnnotation& initialConfig, - ProxyPrefs::ConfigState initialState); + explicit ProxyConfigServiceQt(PrefService *prefService, + const scoped_refptr<base::SequencedTaskRunner> &taskRunner); ~ProxyConfigServiceQt() override; // ProxyConfigService implementation: @@ -92,9 +56,9 @@ private: // Configuration as defined by prefs. net::ProxyConfigWithAnnotation m_prefConfig; - ProxyPrefs::ConfigState m_perfState; + ProxyPrefs::ConfigState m_prefState; - DISALLOW_COPY_AND_ASSIGN(ProxyConfigServiceQt); + SEQUENCE_CHECKER(m_sequenceChecker); }; #endif // PROXY_CONFIG_SERVICE_QT_H diff --git a/src/core/net/proxying_restricted_cookie_manager_qt.cpp b/src/core/net/proxying_restricted_cookie_manager_qt.cpp new file mode 100644 index 000000000..29e6de968 --- /dev/null +++ b/src/core/net/proxying_restricted_cookie_manager_qt.cpp @@ -0,0 +1,165 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// originally based on android_webview/browser/network_service/aw_proxying_restricted_cookie_manager.cc: +// Copyright 2019 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 "proxying_restricted_cookie_manager_qt.h" + +#include "api/qwebenginecookiestore.h" +#include "api/qwebenginecookiestore_p.h" +#include "profile_io_data_qt.h" +#include "type_conversion.h" + +#include "base/memory/ptr_util.h" +#include "content/public/browser/browser_task_traits.h" +#include "content/public/browser/browser_thread.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" + +namespace QtWebEngineCore { + +// static +void ProxyingRestrictedCookieManagerQt::CreateAndBind(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + content::GetIOThreadTaskRunner({})->PostTask(FROM_HERE, + base::BindOnce(&ProxyingRestrictedCookieManagerQt::CreateAndBindOnIoThread, + profileIoData, + std::move(underlying_rcm), + std::move(receiver))); +} + + +// static +void ProxyingRestrictedCookieManagerQt::CreateAndBindOnIoThread(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + auto wrapper = base::WrapUnique(new ProxyingRestrictedCookieManagerQt( + profileIoData->getWeakPtrOnIOThread(), + std::move(underlying_rcm))); + mojo::MakeSelfOwnedReceiver(std::move(wrapper), std::move(receiver)); +} + +ProxyingRestrictedCookieManagerQt::ProxyingRestrictedCookieManagerQt( + base::WeakPtr<ProfileIODataQt> profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlyingRestrictedCookieManager) + : m_profileIoData(std::move(profileIoData)) + , underlying_restricted_cookie_manager_(std::move(underlyingRestrictedCookieManager)) + , weak_factory_(this) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); +} + +ProxyingRestrictedCookieManagerQt::~ProxyingRestrictedCookieManagerQt() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); +} + +void ProxyingRestrictedCookieManagerQt::GetAllForUrl(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, bool has_storage_access, + network::mojom::CookieManagerGetOptionsPtr options, + bool is_ad_tagged, + GetAllForUrlCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (allowCookies(url, site_for_cookies)) { + underlying_restricted_cookie_manager_->GetAllForUrl(url, site_for_cookies, top_frame_origin, has_storage_access, + std::move(options), is_ad_tagged, std::move(callback)); + } else { + std::move(callback).Run(std::vector<net::CookieWithAccessResult>()); + } +} + +void ProxyingRestrictedCookieManagerQt::SetCanonicalCookie(const net::CanonicalCookie &cookie, + const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + net::CookieInclusionStatus status, + SetCanonicalCookieCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (allowCookies(url, site_for_cookies)) { + underlying_restricted_cookie_manager_->SetCanonicalCookie(cookie, url, site_for_cookies, top_frame_origin, + has_storage_access, status, std::move(callback)); + } else { + std::move(callback).Run(false); + } +} + +void ProxyingRestrictedCookieManagerQt::AddChangeListener(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + mojo::PendingRemote<network::mojom::CookieChangeListener> listener, + AddChangeListenerCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + underlying_restricted_cookie_manager_->AddChangeListener(url, site_for_cookies, top_frame_origin, has_storage_access, + std::move(listener), std::move(callback)); +} + +void ProxyingRestrictedCookieManagerQt::SetCookieFromString(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, bool has_storage_access, + const std::string &cookie, + SetCookieFromStringCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (allowCookies(url, site_for_cookies)) { + underlying_restricted_cookie_manager_->SetCookieFromString(url, site_for_cookies, top_frame_origin, has_storage_access, + cookie, std::move(callback)); + } else { + std::move(callback).Run(); + } +} + +void ProxyingRestrictedCookieManagerQt::GetCookiesString(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, bool get_version_shared_memory, + bool is_ad_tagged, + GetCookiesStringCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + + if (allowCookies(url, site_for_cookies)) { + underlying_restricted_cookie_manager_->GetCookiesString(url, site_for_cookies, top_frame_origin, + has_storage_access, get_version_shared_memory, + is_ad_tagged, + std::move(callback)); + } else { + std::move(callback).Run(network::mojom::kInvalidCookieVersion, base::ReadOnlySharedMemoryRegion(), ""); + } +} + +void ProxyingRestrictedCookieManagerQt::CookiesEnabledFor(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin & /*top_frame_origin*/, + bool /*has_storage_access*/, + CookiesEnabledForCallback callback) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + std::move(callback).Run(allowCookies(url, site_for_cookies)); +} + +bool ProxyingRestrictedCookieManagerQt::allowCookies(const GURL &url, const net::SiteForCookies &site_for_cookies) const +{ + if (!m_profileIoData) + return false; + return m_profileIoData->canGetCookies(toQt(site_for_cookies.first_party_url()), toQt(url)); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/proxying_restricted_cookie_manager_qt.h b/src/core/net/proxying_restricted_cookie_manager_qt.h new file mode 100644 index 000000000..ba30a448e --- /dev/null +++ b/src/core/net/proxying_restricted_cookie_manager_qt.h @@ -0,0 +1,86 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H +#define PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H + +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/remote.h" +#include "services/network/public/mojom/restricted_cookie_manager.mojom.h" +#include "url/gurl.h" + +namespace QtWebEngineCore { + +class ProfileIODataQt; + +class ProxyingRestrictedCookieManagerQt : public network::mojom::RestrictedCookieManager +{ +public: + // Expects to be called on the UI thread. + static void CreateAndBind(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver); + + ~ProxyingRestrictedCookieManagerQt() override; + + // network::mojom::RestrictedCookieManager interface: + void GetAllForUrl(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + network::mojom::CookieManagerGetOptionsPtr options, + bool is_ad_tagged, + GetAllForUrlCallback callback) override; + + void SetCanonicalCookie(const net::CanonicalCookie& cookie, + const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + net::CookieInclusionStatus status, + SetCanonicalCookieCallback callback) override; + void AddChangeListener(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + mojo::PendingRemote<network::mojom::CookieChangeListener> listener, + AddChangeListenerCallback callback) override; + void SetCookieFromString(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + const std::string &cookie, + SetCookieFromStringCallback callback) override; + void GetCookiesString(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, bool get_version_shared_memory, + bool is_ad_tagged, + GetCookiesStringCallback callback) override; + void CookiesEnabledFor(const GURL &url, + const net::SiteForCookies &site_for_cookies, + const url::Origin &top_frame_origin, + bool has_storage_access, + CookiesEnabledForCallback callback) override; + + // Internal: + bool allowCookies(const GURL &url, const net::SiteForCookies &site_for_cookies) const; + +private: + ProxyingRestrictedCookieManagerQt(base::WeakPtr<ProfileIODataQt> profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm); + + static void CreateAndBindOnIoThread(ProfileIODataQt *profileIoData, + mojo::PendingRemote<network::mojom::RestrictedCookieManager> underlying_rcm, + mojo::PendingReceiver<network::mojom::RestrictedCookieManager> receiver); + + base::WeakPtr<ProfileIODataQt> m_profileIoData; + + mojo::Remote<network::mojom::RestrictedCookieManager> underlying_restricted_cookie_manager_; + + base::WeakPtrFactory<ProxyingRestrictedCookieManagerQt> weak_factory_; +}; + +} // namespace QtWebEngineCore + +#endif // PROXYING_RESTRICTED_COOKIE_MANAGER_QT_H diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp new file mode 100644 index 000000000..220f44023 --- /dev/null +++ b/src/core/net/proxying_url_loader_factory_qt.cpp @@ -0,0 +1,603 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "proxying_url_loader_factory_qt.h" + +#include <utility> + +#include "base/functional/bind.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/web_contents.h" +#include "content/public/common/content_switches.h" +#include "net/base/filename_util.h" +#include "net/http/http_status_code.h" +#include "services/network/public/cpp/cors/cors.h" +#include "services/network/public/cpp/resource_request.h" +#include "services/network/public/mojom/early_hints.mojom.h" +#include "third_party/blink/public/mojom/loader/resource_load_info.mojom-shared.h" +#include "url/url_util.h" +#include "url/url_util_qt.h" + +#include "api/qwebengineurlrequestinfo_p.h" +#include "type_conversion.h" +#include "web_contents_adapter.h" +#include "web_contents_adapter_client.h" +#include "web_contents_view_qt.h" +#include "net/resource_request_body_qt.h" + +// originally based on aw_proxying_url_loader_factory.cc: +// Copyright 2018 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. + +namespace { + network::mojom::URLResponseHeadPtr createResponse(const network::ResourceRequest &request) { + const bool disable_web_security = base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableWebSecurity); + network::mojom::URLResponseHeadPtr response = network::mojom::URLResponseHead::New(); + response->response_type = network::cors::CalculateResponseType( + request.mode, disable_web_security || ( + request.request_initiator && request.request_initiator->IsSameOriginWith(url::Origin::Create(request.url)))); + + return response; + } +} + +namespace QtWebEngineCore { + +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeMainFrame, blink::mojom::ResourceType::kMainFrame) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeSubFrame, blink::mojom::ResourceType::kSubFrame) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeStylesheet, blink::mojom::ResourceType::kStylesheet) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeScript, blink::mojom::ResourceType::kScript) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeImage, blink::mojom::ResourceType::kImage) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeFontResource, blink::mojom::ResourceType::kFontResource) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeSubResource, blink::mojom::ResourceType::kSubResource) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeObject, blink::mojom::ResourceType::kObject) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeMedia, blink::mojom::ResourceType::kMedia) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeWorker, blink::mojom::ResourceType::kWorker) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeSharedWorker, blink::mojom::ResourceType::kSharedWorker) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypePrefetch, blink::mojom::ResourceType::kPrefetch) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeFavicon, blink::mojom::ResourceType::kFavicon) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeXhr, blink::mojom::ResourceType::kXhr) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypePing, blink::mojom::ResourceType::kPing) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker, blink::mojom::ResourceType::kServiceWorker) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeCspReport, blink::mojom::ResourceType::kCspReport) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypePluginResource, blink::mojom::ResourceType::kPluginResource) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadMainFrame, blink::mojom::ResourceType::kNavigationPreloadMainFrame) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeNavigationPreloadSubFrame, blink::mojom::ResourceType::kNavigationPreloadSubFrame) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeJson, blink::mojom::ResourceType::kJson) +ASSERT_ENUMS_MATCH(QWebEngineUrlRequestInfo::ResourceTypeLast, blink::mojom::ResourceType::kMaxValue) + +extern WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition); + +static QWebEngineUrlRequestInfo::ResourceType toQt(blink::mojom::ResourceType resourceType) +{ + if (resourceType >= blink::mojom::ResourceType::kMinValue && resourceType <= blink::mojom::ResourceType::kMaxValue) + return static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType); + return QWebEngineUrlRequestInfo::ResourceTypeUnknown; +} + +static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) +{ + return static_cast<QWebEngineUrlRequestInfo::NavigationType>(navigationType); +} + +static QHash<QByteArray, QByteArray> toQt(const net::HttpRequestHeaders &headers) +{ + const auto vector = headers.GetHeaderVector(); + QHash<QByteArray, QByteArray> hash; + + for (const auto &header : vector) { + hash.insert(QByteArray::fromStdString(header.key), QByteArray::fromStdString(header.value)); + } + + return hash; +} + +// Handles intercepted, in-progress requests/responses, so that they can be +// controlled and modified accordingly. +class InterceptedRequest : public network::mojom::URLLoader + , public network::mojom::URLLoaderClient +{ +public: + InterceptedRequest(ProfileAdapter *profile_adapter, + int frame_tree_node_id, int32_t request_id, uint32_t options, + const network::ResourceRequest &request, + const net::MutableNetworkTrafficAnnotationTag &traffic_annotation, + mojo::PendingReceiver<network::mojom::URLLoader> loader, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory); + ~InterceptedRequest() override; + + void Restart(); + + // network::mojom::URLLoaderClient + void OnReceiveResponse(network::mojom::URLResponseHeadPtr head, mojo::ScopedDataPipeConsumerHandle, absl::optional<mojo_base::BigBuffer>) override; + void OnReceiveRedirect(const net::RedirectInfo &redirect_info, network::mojom::URLResponseHeadPtr head) override; + void OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback) override; + void OnTransferSizeUpdated(int32_t transfer_size_diff) override; + void OnComplete(const network::URLLoaderCompletionStatus &status) override; + void OnReceiveEarlyHints(network::mojom::EarlyHintsPtr) override {} + + // network::mojom::URLLoader + void FollowRedirect(const std::vector<std::string> &removed_headers, + const net::HttpRequestHeaders &modified_headers, + const net::HttpRequestHeaders &modified_cors_exempt_headers, + const absl::optional<GURL> &new_url) override; + void SetPriority(net::RequestPriority priority, int32_t intra_priority_value) override; + void PauseReadingBodyFromNet() override; + void ResumeReadingBodyFromNet() override; + +private: + void InterceptOnUIThread(); + void ContinueAfterIntercept(); + + // This is called when the original URLLoaderClient has a connection error. + void OnURLLoaderClientError(); + + // This is called when the original URLLoader has a connection error. + void OnURLLoaderError(uint32_t custom_reason, const std::string &description); + + // Call OnComplete on |target_client_|. If |wait_for_loader_error| is true + // then this object will wait for |proxied_loader_binding_| to have a + // connection error before destructing. + void CallOnComplete(const network::URLLoaderCompletionStatus &status, bool wait_for_loader_error); + + void SendErrorAndCompleteImmediately(int error_code); + + content::WebContents* webContents(); + QWebEngineUrlRequestInterceptor* getProfileInterceptor(); + QWebEngineUrlRequestInterceptor* getPageInterceptor(); + + QPointer<ProfileAdapter> profile_adapter_; + const int frame_tree_node_id_; + const int32_t request_id_; + const uint32_t options_; + bool allow_local_ = false; + bool allow_remote_ = true; + bool local_access_ = false; + bool remote_access_ = true; + + bool loader_error_seen_ = false; + + // If the |target_loader_| called OnComplete with an error this stores it. + // That way the destructor can send it to OnReceivedError if safe browsing + // error didn't occur. + int error_status_ = net::OK; + network::ResourceRequest request_; + ResourceRequestBody request_body_; + network::mojom::URLResponseHeadPtr current_response_; + + const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; + + struct RequestInfoDeleter + { + void operator()(QWebEngineUrlRequestInfo *ptr) const + { delete ptr; } + }; + + std::unique_ptr<QWebEngineUrlRequestInfo, RequestInfoDeleter> request_info_; + + mojo::Receiver<network::mojom::URLLoader> proxied_loader_receiver_; + mojo::Remote<network::mojom::URLLoaderClient> target_client_; + mojo::Receiver<network::mojom::URLLoaderClient> proxied_client_receiver_{this}; + mojo::Remote<network::mojom::URLLoader> target_loader_; + mojo::Remote<network::mojom::URLLoaderFactory> target_factory_; + + base::WeakPtrFactory<InterceptedRequest> weak_factory_; +}; + +InterceptedRequest::InterceptedRequest(ProfileAdapter *profile_adapter, + int frame_tree_node_id, int32_t request_id, uint32_t options, + const network::ResourceRequest &request, + const net::MutableNetworkTrafficAnnotationTag &traffic_annotation, + mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory) + : profile_adapter_(profile_adapter) + , frame_tree_node_id_(frame_tree_node_id) + , request_id_(request_id) + , options_(options) + , request_(request) + , request_body_(ResourceRequestBody(request_.request_body.get())) + , traffic_annotation_(traffic_annotation) + , proxied_loader_receiver_(this, std::move(loader_receiver)) + , target_client_(std::move(client)) + , target_factory_(std::move(target_factory)) + , weak_factory_(this) +{ + const bool disable_web_security = base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kDisableWebSecurity); + current_response_ = createResponse(request_); + // If there is a client error, clean up the request. + target_client_.set_disconnect_handler( + base::BindOnce(&InterceptedRequest::OnURLLoaderClientError, base::Unretained(this))); + proxied_loader_receiver_.set_disconnect_with_reason_handler( + base::BindOnce(&InterceptedRequest::OnURLLoaderError, base::Unretained(this))); + if (!disable_web_security && request_.request_initiator) { + const std::vector<std::string> &localSchemes = url::GetLocalSchemes(); + const std::string fromScheme = request_.request_initiator->GetTupleOrPrecursorTupleIfOpaque().scheme(); + const std::string toScheme = request_.url.scheme(); + const bool fromLocal = base::Contains(localSchemes, fromScheme); + const bool toLocal = base::Contains(localSchemes, toScheme); + bool hasLocalAccess = false; + local_access_ = toLocal; + remote_access_ = !toLocal && (toScheme != "data") && (toScheme != "qrc"); + if (const url::CustomScheme *cs = url::CustomScheme::FindScheme(fromScheme)) + hasLocalAccess = cs->flags & url::CustomScheme::LocalAccessAllowed; + if (fromLocal || toLocal) { + content::WebContents *wc = webContents(); + // local schemes must have universal access, or be accessing something local and have local access. + allow_local_ = hasLocalAccess || (fromLocal && wc && wc->GetOrCreateWebPreferences().allow_file_access_from_file_urls); + allow_remote_ = !fromLocal || (wc && wc->GetOrCreateWebPreferences().allow_remote_access_from_local_urls); + } + } +} + +InterceptedRequest::~InterceptedRequest() +{ + weak_factory_.InvalidateWeakPtrs(); +} + +content::WebContents* InterceptedRequest::webContents() +{ + if (frame_tree_node_id_ == content::RenderFrameHost::kNoFrameTreeNodeId) + return nullptr; + return content::WebContents::FromFrameTreeNodeId(frame_tree_node_id_); +} + +QWebEngineUrlRequestInterceptor* InterceptedRequest::getProfileInterceptor() +{ + return profile_adapter_ ? profile_adapter_->requestInterceptor() : nullptr; +} + +QWebEngineUrlRequestInterceptor* InterceptedRequest::getPageInterceptor() +{ + if (auto wc = webContents()) { + auto view = static_cast<content::WebContentsImpl *>(wc)->GetView(); + if (WebContentsAdapterClient *client = WebContentsViewQt::from(view)->client()) + return client->webContentsAdapter()->requestInterceptor(); + } + return nullptr; +} + +void InterceptedRequest::Restart() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + bool granted_special_access = false; + auto navigationType = toQt(pageTransitionToNavigationType(ui::PageTransition(request_.transition_type))); + switch (navigationType) { + case QWebEngineUrlRequestInfo::NavigationTypeLink: + case QWebEngineUrlRequestInfo::NavigationTypeTyped: + if (blink::mojom::ResourceType(request_.resource_type) == blink::mojom::ResourceType::kMainFrame && request_.has_user_gesture) + granted_special_access = true; // allow normal explicit navigation + break; + case QWebEngineUrlRequestInfo::NavigationTypeBackForward: + case QWebEngineUrlRequestInfo::NavigationTypeReload: + if (blink::mojom::ResourceType(request_.resource_type) == blink::mojom::ResourceType::kMainFrame) + granted_special_access = true; + break; + default: + break; + } + + // Check if non-local access is allowed + if (!allow_remote_ && remote_access_) { + if (!granted_special_access) { + target_client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_NETWORK_ACCESS_DENIED)); + delete this; + return; + } + } + + // Check if local access is allowed + if (!allow_local_ && local_access_) { + // Check for specifically granted file access: + if (auto *frame_tree = content::FrameTreeNode::GloballyFindByID(frame_tree_node_id_)) { + const int renderer_id = frame_tree->current_frame_host()->GetProcess()->GetID(); + base::FilePath file_path; + if (net::FileURLToFilePath(request_.url, &file_path)) { + if (content::ChildProcessSecurityPolicy::GetInstance()->CanReadFile(renderer_id, file_path)) + granted_special_access = true; + } + } + if (!granted_special_access) { + target_client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_ACCESS_DENIED)); + delete this; + return; + } + } + + // MEMO since all codepatch leading to Restart scheduled and executed as asynchronous tasks in main thread, + // interceptors may change in meantime and also during intercept call, so they should be resolved anew. + // Set here only profile's interceptor since it runs first without going to user code. + auto profileInterceptor = getProfileInterceptor(); + if (!profileInterceptor && !getPageInterceptor()) { + ContinueAfterIntercept(); + return; + } + + auto resourceType = toQt(blink::mojom::ResourceType(request_.resource_type)); + const QUrl originalUrl = toQt(request_.url); + const QUrl initiator = request_.request_initiator.has_value() ? toQt(request_.request_initiator->GetURL()) : QUrl(); + + auto wc = webContents(); + GURL top_document_url = wc ? wc->GetVisibleURL() : GURL(); + QUrl firstPartyUrl; + if (!top_document_url.is_empty()) + firstPartyUrl = toQt(top_document_url); + else + firstPartyUrl = toQt(request_.site_for_cookies.first_party_url()); // m_topDocumentUrl can be empty for the main-frame. + + QHash<QByteArray, QByteArray> headers = toQt(request_.headers); + + if (!request_.referrer.is_empty()) + headers.insert("Referer", toQt(request_.referrer).toEncoded()); + + auto info = new QWebEngineUrlRequestInfoPrivate( + resourceType, navigationType, originalUrl, firstPartyUrl, initiator, + QByteArray::fromStdString(request_.method), &request_body_, headers); + Q_ASSERT(!request_info_); + request_info_.reset(new QWebEngineUrlRequestInfo(info)); + + InterceptOnUIThread(); + ContinueAfterIntercept(); +} + +void InterceptedRequest::InterceptOnUIThread() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (auto interceptor = getProfileInterceptor()) + interceptor->interceptRequest(*request_info_); + + if (!request_info_->changed()) { + if (auto interceptor = getPageInterceptor()) + interceptor->interceptRequest(*request_info_); + } +} + +void InterceptedRequest::ContinueAfterIntercept() +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + + if (request_info_) { + // cleanup in scope because of delete this and it's not needed else where after + const auto scoped_request_info = std::move(request_info_); + QWebEngineUrlRequestInfoPrivate &info = *scoped_request_info->d_ptr; + + for (auto header = info.extraHeaders.constBegin(); header != info.extraHeaders.constEnd(); ++header) { + std::string h = header.key().toStdString(); + if (base::EqualsCaseInsensitiveASCII(h, "referer")) + request_.referrer = GURL(header.value().toStdString()); + else + request_.headers.SetHeader(h, header.value().toStdString()); + } + + if (info.changed) { + if (info.shouldBlockRequest) + return SendErrorAndCompleteImmediately(net::ERR_BLOCKED_BY_CLIENT); + + if (info.shouldRedirectRequest) { + net::RedirectInfo::FirstPartyURLPolicy first_party_url_policy = + request_.update_first_party_url_on_redirect ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT + : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL; + net::RedirectInfo redirectInfo = net::RedirectInfo::ComputeRedirectInfo( + request_.method, request_.url, request_.site_for_cookies, + first_party_url_policy, request_.referrer_policy, request_.referrer.spec(), + net::HTTP_TEMPORARY_REDIRECT, toGurl(info.url), absl::nullopt, + false /*insecure_scheme_was_upgraded*/); + request_.method = redirectInfo.new_method; + request_.url = redirectInfo.new_url; + request_.site_for_cookies = redirectInfo.new_site_for_cookies; + request_.referrer = GURL(redirectInfo.new_referrer); + request_.referrer_policy = redirectInfo.new_referrer_policy; + if (request_.method == net::HttpRequestHeaders::kGetMethod) + request_.request_body = nullptr; + // In case of multiple sequential rediredts, current_response_ has previously been moved to target_client_ + // so we create a new one using the redirect url. + if (!current_response_) + current_response_ = createResponse(request_); + current_response_->encoded_data_length = 0; + target_client_->OnReceiveRedirect(redirectInfo, std::move(current_response_)); + return; + } + } + } + + if (!target_loader_ && target_factory_) { + loader_error_seen_ = false; + target_factory_->CreateLoaderAndStart(target_loader_.BindNewPipeAndPassReceiver(), request_id_, + options_, request_, proxied_client_receiver_.BindNewPipeAndPassRemote(), + traffic_annotation_); + } +} + +// URLLoaderClient methods. + +void InterceptedRequest::OnReceiveResponse(network::mojom::URLResponseHeadPtr head, mojo::ScopedDataPipeConsumerHandle handle, absl::optional<mojo_base::BigBuffer> buffer) +{ + current_response_ = head.Clone(); + + target_client_->OnReceiveResponse(std::move(head), std::move(handle), std::move(buffer)); +} + +void InterceptedRequest::OnReceiveRedirect(const net::RedirectInfo &redirect_info, network::mojom::URLResponseHeadPtr head) +{ + // TODO(timvolodine): handle redirect override. + current_response_ = head.Clone(); + target_client_->OnReceiveRedirect(redirect_info, std::move(head)); + request_.url = redirect_info.new_url; + request_.method = redirect_info.new_method; + request_.site_for_cookies = redirect_info.new_site_for_cookies; + request_.referrer = GURL(redirect_info.new_referrer); + request_.referrer_policy = redirect_info.new_referrer_policy; +} + +void InterceptedRequest::OnUploadProgress(int64_t current_position, int64_t total_size, OnUploadProgressCallback callback) +{ + target_client_->OnUploadProgress(current_position, total_size, std::move(callback)); +} + +void InterceptedRequest::OnTransferSizeUpdated(int32_t transfer_size_diff) +{ + target_client_->OnTransferSizeUpdated(transfer_size_diff); +} + +void InterceptedRequest::OnComplete(const network::URLLoaderCompletionStatus &status) +{ + // Only wait for the original loader to possibly have a custom error if the + // target loader succeeded. If the target loader failed, then it was a race as + // to whether that error or the safe browsing error would be reported. + CallOnComplete(status, status.error_code == net::OK); +} + +// URLLoader methods. + +void InterceptedRequest::FollowRedirect(const std::vector<std::string> &removed_headers, + const net::HttpRequestHeaders &modified_headers, + const net::HttpRequestHeaders &modified_cors_exempt_headers, + const absl::optional<GURL> &new_url) +{ + if (target_loader_) + target_loader_->FollowRedirect(removed_headers, modified_headers, modified_cors_exempt_headers, new_url); + + // If |OnURLLoaderClientError| was called then we're just waiting for the + // connection error handler of |proxied_loader_binding_|. Don't restart the + // job since that'll create another URLLoader + if (!target_client_) + return; + + Restart(); +} + +void InterceptedRequest::SetPriority(net::RequestPriority priority, int32_t intra_priority_value) +{ + if (target_loader_) + target_loader_->SetPriority(priority, intra_priority_value); +} + +void InterceptedRequest::PauseReadingBodyFromNet() +{ + if (target_loader_) + target_loader_->PauseReadingBodyFromNet(); +} + +void InterceptedRequest::ResumeReadingBodyFromNet() +{ + if (target_loader_) + target_loader_->ResumeReadingBodyFromNet(); +} + +void InterceptedRequest::OnURLLoaderClientError() +{ + // We set |wait_for_loader_error| to true because if the loader did have a + // custom_reason error then the client would be reset as well and it would be + // a race as to which connection error we saw first. + CallOnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED), true /* wait_for_loader_error */); +} + +void InterceptedRequest::OnURLLoaderError(uint32_t custom_reason, const std::string &description) +{ + // If CallOnComplete was already called, then this object is ready to be deleted. + if (!target_client_) + delete this; + else + loader_error_seen_ = true; +} + +void InterceptedRequest::CallOnComplete(const network::URLLoaderCompletionStatus &status, bool wait_for_loader_error) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + // Save an error status so that we call onReceiveError at destruction if there + // was no safe browsing error. + if (status.error_code != net::OK) + error_status_ = status.error_code; + + if (target_client_) + target_client_->OnComplete(status); + + if (proxied_loader_receiver_.is_bound() && wait_for_loader_error && !loader_error_seen_) { + // Since the original client is gone no need to continue loading the + // request. + proxied_client_receiver_.reset(); + target_loader_.reset(); + + // Don't delete |this| yet, in case the |proxied_loader_receiver_|'s + // error_handler is called with a reason to indicate an error which we want + // to send to the client bridge. Also reset |target_client_| so we don't + // get its error_handler called and then delete |this|. + target_client_.reset(); + + // In case there are pending checks as to whether this request should be + // intercepted, we don't want that causing |target_client_| to be used + // later. + weak_factory_.InvalidateWeakPtrs(); + } else { + delete this; + } +} + +void InterceptedRequest::SendErrorAndCompleteImmediately(int error_code) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + auto status = network::URLLoaderCompletionStatus(error_code); + target_client_->OnComplete(status); + delete this; +} + +ProxyingURLLoaderFactoryQt::ProxyingURLLoaderFactoryQt(ProfileAdapter *adapter, int frame_tree_node_id, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver, + mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_info) + : m_profileAdapter(adapter), m_frameTreeNodeId(frame_tree_node_id), m_weakFactory(this) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (target_factory_info) { + m_targetFactory.Bind(std::move(target_factory_info)); + m_targetFactory.set_disconnect_handler( + base::BindOnce(&ProxyingURLLoaderFactoryQt::OnTargetFactoryError, m_weakFactory.GetWeakPtr())); + } + m_proxyReceivers.Add(this, std::move(loader_receiver)); + m_proxyReceivers.set_disconnect_handler( + base::BindRepeating(&ProxyingURLLoaderFactoryQt::OnProxyBindingError, m_weakFactory.GetWeakPtr())); +} + +ProxyingURLLoaderFactoryQt::~ProxyingURLLoaderFactoryQt() +{ + m_weakFactory.InvalidateWeakPtrs(); +} + +void ProxyingURLLoaderFactoryQt::CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader, int32_t request_id, + uint32_t options, const network::ResourceRequest &request, + mojo::PendingRemote<network::mojom::URLLoaderClient> url_loader_client, + const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_clone; + if (m_targetFactory) + m_targetFactory->Clone(target_factory_clone.InitWithNewPipeAndPassReceiver()); + + // Will manage its own lifetime + InterceptedRequest *req = new InterceptedRequest(m_profileAdapter, m_frameTreeNodeId, request_id, options, + request, traffic_annotation, std::move(loader), + std::move(url_loader_client), std::move(target_factory_clone)); + req->Restart(); +} + +void ProxyingURLLoaderFactoryQt::OnTargetFactoryError() +{ + delete this; +} + +void ProxyingURLLoaderFactoryQt::OnProxyBindingError() +{ + if (m_proxyReceivers.empty()) + delete this; +} + +void ProxyingURLLoaderFactoryQt::Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) +{ + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + m_proxyReceivers.Add(this, std::move(receiver)); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/proxying_url_loader_factory_qt.h b/src/core/net/proxying_url_loader_factory_qt.h new file mode 100644 index 000000000..904a40c2d --- /dev/null +++ b/src/core/net/proxying_url_loader_factory_qt.h @@ -0,0 +1,57 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef PROXYING_URL_LOADER_FACTORY_QT_H_ +#define PROXYING_URL_LOADER_FACTORY_QT_H_ + +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/receiver_set.h" +#include "net/traffic_annotation/network_traffic_annotation.h" +#include "services/network/public/mojom/url_loader.mojom.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" + +#include <QPointer> +// based on aw_proxying_url_loader_factory.h: +// Copyright 2018 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. + +namespace network { +struct ResourceRequest; +} + +namespace QtWebEngineCore { + +class ProfileAdapter; + +class ProxyingURLLoaderFactoryQt : public network::mojom::URLLoaderFactory +{ +public: + ProxyingURLLoaderFactoryQt(ProfileAdapter *adapter, int frameTreeNodeId, + mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver, + mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_target_factory_remote); + + ~ProxyingURLLoaderFactoryQt() override; + + void CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> loader, + int32_t request_id, + uint32_t options, const network::ResourceRequest &request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override; + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) override; + +private: + void OnTargetFactoryError(); + void OnProxyBindingError(); + + QPointer<ProfileAdapter> m_profileAdapter; + int m_frameTreeNodeId; + mojo::ReceiverSet<network::mojom::URLLoaderFactory> m_proxyReceivers; + mojo::Remote<network::mojom::URLLoaderFactory> m_targetFactory; + base::WeakPtrFactory<ProxyingURLLoaderFactoryQt> m_weakFactory; +}; + +} // namespace QtWebEngineCore + +#endif // PROXYING_URL_LOADER_FACTORY_QT_H_ diff --git a/src/core/net/qrc_url_scheme_handler.cpp b/src/core/net/qrc_url_scheme_handler.cpp index 73bf24f1d..a8b4e4388 100644 --- a/src/core/net/qrc_url_scheme_handler.cpp +++ b/src/core/net/qrc_url_scheme_handler.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qrc_url_scheme_handler.h" @@ -46,6 +10,8 @@ #include <QMimeDatabase> #include <QMimeType> +#include <memory> + namespace QtWebEngineCore { void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) @@ -58,7 +24,7 @@ void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) QUrl requestUrl = job->requestUrl(); QString requestPath = requestUrl.path(); - QScopedPointer<QFile> file(new QFile(':' + requestPath, job)); + auto file = std::make_unique<QFile>(':' + requestPath, job); if (!file->exists() || file->size() == 0) { qWarning("QResource '%s' not found or is empty", qUtf8Printable(requestPath)); job->fail(QWebEngineUrlRequestJob::UrlNotFound); @@ -67,7 +33,10 @@ void QrcUrlSchemeHandler::requestStarted(QWebEngineUrlRequestJob *job) QFileInfo fileInfo(*file); QMimeDatabase mimeDatabase; QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo); - job->reply(mimeType.name().toUtf8(), file.take()); + if (mimeType.name() == QStringLiteral("application/x-extension-html")) + job->reply("text/html", file.release()); + else + job->reply(mimeType.name().toUtf8(), file.release()); } } // namespace QtWebEngineCore diff --git a/src/core/net/qrc_url_scheme_handler.h b/src/core/net/qrc_url_scheme_handler.h index f6ca92879..96155b05b 100644 --- a/src/core/net/qrc_url_scheme_handler.h +++ b/src/core/net/qrc_url_scheme_handler.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QRC_URL_SCHEME_HANDLER_H #define QRC_URL_SCHEME_HANDLER_H @@ -45,7 +9,8 @@ namespace QtWebEngineCore { -class QrcUrlSchemeHandler final : public QWebEngineUrlSchemeHandler { +class QrcUrlSchemeHandler final : public QWebEngineUrlSchemeHandler +{ public: void requestStarted(QWebEngineUrlRequestJob *) override; }; diff --git a/src/core/net/resource_request_body_qt.cpp b/src/core/net/resource_request_body_qt.cpp new file mode 100644 index 000000000..d0d54784d --- /dev/null +++ b/src/core/net/resource_request_body_qt.cpp @@ -0,0 +1,181 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "resource_request_body_qt.h" +#include "type_conversion.h" + +#include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/mojom/data_pipe_getter.mojom.h" +#include "services/network/public/mojom/url_request.mojom-shared.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace QtWebEngineCore { + +ResourceRequestBody::ResourceRequestBody(network::ResourceRequestBody *requestBody, QObject *parent) + : QIODevice(parent) + , m_requestBody(requestBody) + , m_dataElementsIdx(0) + , m_dataElementBytesIdx(0) + , m_dataElementFileIdx(0) +{}; + +ResourceRequestBody::~ResourceRequestBody(){}; + +qint64 ResourceRequestBody::readData(char *data, qint64 maxSize) +{ + if (!m_requestBody) + return -1; + + const std::size_t dataElementsSize = m_requestBody->elements()->size(); + if (m_dataElementsIdx == dataElementsSize) + return -1; + + qint64 bytesRead = 0; + const std::vector<network::DataElement> *elements = m_requestBody->elements(); + while (bytesRead < maxSize && m_dataElementsIdx < dataElementsSize) { + const network::DataElement ¤tDataElement = elements->at(m_dataElementsIdx); + + switch (currentDataElement.type()) { + case network::mojom::DataElementDataView::Tag::kBytes: { + readDataElementBytes(currentDataElement.As<network::DataElementBytes>().bytes(), + bytesRead, maxSize, &data); + break; + } + case network::mojom::DataElementDataView::Tag::kFile: { + const network::DataElementFile file = currentDataElement.As<network::DataElementFile>(); + const qint64 offset = file.offset(); + const qint64 length = file.length(); + readDataElementFile(file.path(), offset, length, bytesRead, maxSize, &data); + break; + } + case network::mojom::DataElementDataView::Tag::kDataPipe: { + mojo::Remote<network::mojom::DataPipeGetter> pipeGetter; + pipeGetter.Bind( + currentDataElement.As<network::DataElementDataPipe>().CloneDataPipeGetter()); + const mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> consumerHandle = + getConsumerHandleFromPipeGetter(pipeGetter); + readDataElementPipe(consumerHandle, bytesRead, maxSize, &data); + break; + } + case network::mojom::DataElementDataView::Tag::kChunkedDataPipe: { + setErrorString(QStringLiteral("Chunked data pipe is used in request body upload, which " + "is currently not supported")); + // Nothing should come before or after DataElementChunkedDataPipe + return -1; + } + } + + if (bytesRead == maxSize || m_dataElementsIdx == dataElementsSize) + break; + } + + return bytesRead; +} + +// We don't want to write, ever +qint64 ResourceRequestBody::writeData(const char *data, qint64 maxSize) +{ + return -1; +} + +bool ResourceRequestBody::isSequential() const +{ + return true; +} + +void ResourceRequestBody::readDataElementBytes(const std::vector<uint8_t> &dataElement, + qint64 &bytesRead, const qint64 &maxSize, + char **data) +{ + const std::size_t dataElementSize = dataElement.size(); + const std::size_t bytesToRead = std::min(dataElementSize, static_cast<std::size_t>(maxSize)); + + std::memcpy(*data, dataElement.data(), bytesToRead); + *data += bytesToRead; + m_dataElementBytesIdx += bytesToRead; + bytesRead += bytesToRead; + + if (m_dataElementBytesIdx == dataElementSize) { + m_dataElementsIdx++; + m_dataElementBytesIdx = 0; + } +} + +void ResourceRequestBody::readDataElementFile(const base::FilePath &filePath, const qint64 &offset, + const qint64 &length, qint64 &bytesRead, + const qint64 &maxSize, char **data) +{ + QFile file(toQt(filePath.value())); + const qint64 realOffset = offset + m_dataElementFileIdx; + const std::size_t fileSize = std::min(file.size(), length) - realOffset; + const std::size_t bytesToRead = std::min(fileSize, static_cast<std::size_t>(maxSize)); + + file.open(QFile::ReadOnly); + file.seek(realOffset); + + std::memcpy(*data, file.read(bytesToRead).data(), bytesToRead); + *data += bytesToRead; + m_dataElementFileIdx += bytesToRead; + bytesRead += bytesToRead; + + file.close(); + + if (m_dataElementFileIdx == fileSize) { + m_dataElementsIdx++; + m_dataElementFileIdx = 0; + } +} + +mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> +ResourceRequestBody::getConsumerHandleFromPipeGetter( + mojo::Remote<network::mojom::DataPipeGetter> &pipeGetter) +{ + mojo::ScopedHandleBase<mojo::DataPipeProducerHandle> producerHandle; + mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> consumerHandle; + mojo::CreateDataPipe(nullptr, producerHandle, consumerHandle); + base::WeakPtrFactory<ResourceRequestBody> weakPtrFactory{ this }; + pipeGetter->Read(std::move(producerHandle), + base::BindOnce(&ResourceRequestBody::pipeGetterOnReadComplete, + weakPtrFactory.GetWeakPtr())); + + return consumerHandle; +} + +void ResourceRequestBody::readDataElementPipe( + const mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> &consumerHandle, + qint64 &bytesRead, const qint64 &maxSize, char **data) +{ + MojoResult result; + do { + uint32_t bytesToRead = 1; + result = consumerHandle->ReadData(*data, &bytesToRead, MOJO_READ_DATA_FLAG_NONE); + + if (result == MOJO_RESULT_OK) { + *data += bytesToRead; + bytesRead += bytesToRead; + } else if (result != MOJO_RESULT_SHOULD_WAIT && result != MOJO_RESULT_FAILED_PRECONDITION) { + setErrorString(QString::fromLatin1("Error while reading from data pipe, skipping" + "remaining content of data pipe. Mojo error code: ") + + QString::number(result)); + } + } while ((result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_OK) + && bytesRead < maxSize); + + m_dataElementsIdx++; +} + +void ResourceRequestBody::pipeGetterOnReadComplete(int32_t status, uint64_t size) { } + +void ResourceRequestBody::appendFilesForTest(const QString &path) +{ + if (!m_requestBody) + return; + + base::FilePath filePath = toFilePath(path); + m_requestBody->elements_mutable()->push_back(static_cast<network::DataElement>( + network::DataElementFile(filePath, 0, 23, base::Time()))); + m_requestBody->elements_mutable()->push_back(static_cast<network::DataElement>( + network::DataElementFile(filePath, 10, 23, base::Time()))); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/resource_request_body_qt.h b/src/core/net/resource_request_body_qt.h new file mode 100644 index 000000000..717885d7d --- /dev/null +++ b/src/core/net/resource_request_body_qt.h @@ -0,0 +1,70 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef RESOURCEREQUESTBODY_QT_H +#define RESOURCEREQUESTBODY_QT_H + +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#include <QtCore/QIODevice> +#include <QtCore/QFile> +#include <QtCore/QUrl> + +namespace network { +class ResourceRequestBody; +namespace mojom { +class DataPipeGetter; +class ChunkedDataPipeGetter; +} +} + +namespace base { +class FilePath; +} + +namespace mojo { +template<typename T> +class Remote; +template<typename T> +class ScopedHandleBase; +class DataPipeConsumerHandle; +} + +namespace QtWebEngineCore { + +class Q_WEBENGINECORE_EXPORT ResourceRequestBody : public QIODevice +{ + Q_OBJECT +public: + explicit ResourceRequestBody(network::ResourceRequestBody *requestBody, + QObject *parent = nullptr); + ~ResourceRequestBody(); + + qint64 readData(char *data, qint64 maxSize) override; + qint64 writeData(const char *data, qint64 maxSize) override; + bool isSequential() const override; + + void appendFilesForTest(const QString &path); + +private: + network::ResourceRequestBody *const m_requestBody; + + std::size_t m_dataElementsIdx; + std::size_t m_dataElementBytesIdx; + std::size_t m_dataElementFileIdx; + + void readDataElementBytes(const std::vector<uint8_t> &dataElement, qint64 &bytesRead, + const qint64 &maxSize, char **data); + void readDataElementFile(const base::FilePath &filePath, const qint64 &offset, + const qint64 &length, qint64 &bytesRead, const qint64 &maxSize, + char **data); + mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> + getConsumerHandleFromPipeGetter(mojo::Remote<network::mojom::DataPipeGetter> &pipeGetter); + void + readDataElementPipe(const mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> &consumerHandle, + qint64 &bytesRead, const qint64 &maxSize, char **data); + void pipeGetterOnReadComplete(int32_t status, uint64_t size); +}; + +} // namespace QtWebEngineCore + +#endif // RESOURCEREQUESTBODY_QT_H diff --git a/src/core/net/ssl_host_state_delegate_qt.cpp b/src/core/net/ssl_host_state_delegate_qt.cpp index ecc3c681e..809a95c8d 100644 --- a/src/core/net/ssl_host_state_delegate_qt.cpp +++ b/src/core/net/ssl_host_state_delegate_qt.cpp @@ -1,47 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "base/callback.h" +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "ssl_host_state_delegate_qt.h" -#include "type_conversion.h" +#include "base/functional/callback.h" namespace QtWebEngineCore { @@ -67,27 +29,23 @@ bool CertPolicy::Check(const net::X509Certificate &cert, int error) const return false; } -void CertPolicy::Allow(const net::X509Certificate& cert, int error) +void CertPolicy::Allow(const net::X509Certificate &cert, int error) { net::SHA256HashValue fingerprint = cert.CalculateChainFingerprint256(); m_allowed[fingerprint] |= error; } -SSLHostStateDelegateQt::SSLHostStateDelegateQt() -{ -} +SSLHostStateDelegateQt::SSLHostStateDelegateQt() {} -SSLHostStateDelegateQt::~SSLHostStateDelegateQt() -{ -} +SSLHostStateDelegateQt::~SSLHostStateDelegateQt() {} -void SSLHostStateDelegateQt::AllowCert(const std::string &host, const net::X509Certificate &cert, int error) +void SSLHostStateDelegateQt::AllowCert(const std::string &host, const net::X509Certificate &cert, int error, content::StoragePartition *) { m_certPolicyforHost[host].Allow(cert, error); } // Clear all allow preferences. -void SSLHostStateDelegateQt::Clear(const base::Callback<bool(const std::string&)>& host_filter) +void SSLHostStateDelegateQt::Clear(base::RepeatingCallback<bool(const std::string&)> host_filter) { if (host_filter.is_null()) { m_certPolicyforHost.clear(); @@ -107,9 +65,9 @@ void SSLHostStateDelegateQt::Clear(const base::Callback<bool(const std::string&) // Queries whether |cert| is allowed for |host| and |error|. Returns true in // |expired_previous_decision| if a previous user decision expired immediately // prior to this query, otherwise false. -content::SSLHostStateDelegate::CertJudgment SSLHostStateDelegateQt::QueryPolicy( - const std::string &host, const net::X509Certificate &cert, - int error, bool */*expired_previous_decision*/) +content::SSLHostStateDelegate::CertJudgment SSLHostStateDelegateQt::QueryPolicy(const std::string &host, + const net::X509Certificate &cert, + int error, content::StoragePartition *) { return m_certPolicyforHost[host].Check(cert, error) ? SSLHostStateDelegate::ALLOWED : SSLHostStateDelegate::DENIED; } @@ -120,7 +78,17 @@ void SSLHostStateDelegateQt::HostRanInsecureContent(const std::string &host, int } // Returns whether the specified host ran insecure content. -bool SSLHostStateDelegateQt::DidHostRunInsecureContent(const std::string &host, int pid, InsecureContentType content_type) const +bool SSLHostStateDelegateQt::DidHostRunInsecureContent(const std::string &host, int pid, InsecureContentType content_type) +{ + return false; +} + +void SSLHostStateDelegateQt::AllowHttpForHost(const std::string &host, content::StoragePartition *web_contents) +{ + // Intentional no-op see aw_ssl_host_state_delegate +} + +bool SSLHostStateDelegateQt::IsHttpAllowedForHost(const std::string &host, content::StoragePartition *web_contents) { return false; } @@ -136,12 +104,33 @@ void SSLHostStateDelegateQt::RevokeUserAllowExceptions(const std::string &host) // |host|. This does not mean that *all* certificate errors are allowed, just // that there exists an exception. To see if a particular certificate and // error combination exception is allowed, use QueryPolicy(). -bool SSLHostStateDelegateQt::HasAllowException(const std::string &host) const +bool SSLHostStateDelegateQt::HasAllowException(const std::string &host, content::StoragePartition *) { auto policy_iterator = m_certPolicyforHost.find(host); return policy_iterator != m_certPolicyforHost.end() && policy_iterator->second.HasAllowException(); } +bool SSLHostStateDelegateQt::HasAllowExceptionForAnyHost(content::StoragePartition *storage_partition) +{ + for (auto const &it : m_certPolicyforHost) { + if (it.second.HasAllowException()) { + return true; + } + } + return false; +} + +void SSLHostStateDelegateQt::SetHttpsEnforcementForHost(const std::string &host, bool enforce, + content::StoragePartition *storage_partition) +{ + // Intentional no-op see aw_ssl_host_state_delegate +} + +bool SSLHostStateDelegateQt::IsHttpsEnforcedForUrl(const GURL &url, + content::StoragePartition *storage_partition) +{ + return false; +} } // namespace QtWebEngineCore diff --git a/src/core/net/ssl_host_state_delegate_qt.h b/src/core/net/ssl_host_state_delegate_qt.h index b1b49bcf3..f210f028a 100644 --- a/src/core/net/ssl_host_state_delegate_qt.h +++ b/src/core/net/ssl_host_state_delegate_qt.h @@ -1,77 +1,51 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef SSL_HOST_STATE_DELEGATE_QT_H #define SSL_HOST_STATE_DELEGATE_QT_H #include "content/public/browser/ssl_host_state_delegate.h" -#include "profile_adapter.h" + +#include <map> +#include <string> namespace QtWebEngineCore { -class CertPolicy { +class CertPolicy +{ public: CertPolicy(); ~CertPolicy(); - bool Check(const net::X509Certificate& cert, int error) const; - void Allow(const net::X509Certificate& cert, int error); + bool Check(const net::X509Certificate &cert, int error) const; + void Allow(const net::X509Certificate &cert, int error); bool HasAllowException() const { return m_allowed.size() > 0; } private: std::map<net::SHA256HashValue, int> m_allowed; }; -class SSLHostStateDelegateQt : public content::SSLHostStateDelegate { +class SSLHostStateDelegateQt : public content::SSLHostStateDelegate +{ public: SSLHostStateDelegateQt(); ~SSLHostStateDelegateQt(); // content::SSLHostStateDelegate implementation: - void AllowCert(const std::string &, const net::X509Certificate &cert, int error) override; - void Clear(const base::Callback<bool(const std::string&)>& host_filter) override; - CertJudgment QueryPolicy(const std::string &host, const net::X509Certificate &cert, - int error, bool *expired_previous_decision) override; - void HostRanInsecureContent(const std::string& host, int child_id, InsecureContentType content_type) override; - bool DidHostRunInsecureContent(const std::string& host, int child_id, InsecureContentType content_type) const override; + void AllowCert(const std::string &, const net::X509Certificate &cert, int error, content::StoragePartition *storage_partition) override; + void Clear(base::RepeatingCallback<bool(const std::string&)> host_filter) override; + CertJudgment QueryPolicy(const std::string &host, const net::X509Certificate &cert, int error, content::StoragePartition *web_contents) override; + void HostRanInsecureContent(const std::string &host, int child_id, InsecureContentType content_type) override; + bool DidHostRunInsecureContent(const std::string &host, int child_id, InsecureContentType content_type) override; + void AllowHttpForHost(const std::string &host, content::StoragePartition *web_contents) override; + bool IsHttpAllowedForHost(const std::string &host, content::StoragePartition *web_contents) override; + void SetHttpsEnforcementForHost(const std::string &host, bool enforce, + content::StoragePartition *storage_partition) override; void RevokeUserAllowExceptions(const std::string &host) override; - bool HasAllowException(const std::string &host) const override; + bool HasAllowException(const std::string &host, content::StoragePartition *web_contents) override; + bool HasAllowExceptionForAnyHost(content::StoragePartition *storage_partition) override; + bool IsHttpsEnforcedForUrl(const GURL &url, + content::StoragePartition *storage_partition) override; private: std::map<std::string, CertPolicy> m_certPolicyforHost; diff --git a/src/core/net/system_network_context_manager.cpp b/src/core/net/system_network_context_manager.cpp new file mode 100644 index 000000000..78098529d --- /dev/null +++ b/src/core/net/system_network_context_manager.cpp @@ -0,0 +1,356 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// based on chrome/browser/net/system_network_context_manager.cc: +// Copyright 2017 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 "net/system_network_context_manager.h" + +#include "base/command_line.h" +#include "base/functional/bind.h" +#include "base/strings/string_split.h" +#include "chrome/browser/net/chrome_mojo_proxy_resolver_factory.h" +#include "chrome/common/chrome_switches.h" +#include "components/certificate_transparency/ct_known_logs.h" +#include "components/network_session_configurator/common/network_switches.h" +#include "content/public/browser/network_service_instance.h" +#include "content/public/common/content_switches.h" +#include "crypto/sha2.h" +#include "mojo/public/cpp/bindings/pending_remote.h" +#include "net/base/port_util.h" +#include "net/net_buildflags.h" +#include "services/cert_verifier/public/mojom/cert_verifier_service_factory.mojom.h" +#include "services/network/network_service.h" +#include "services/network/public/cpp/cross_thread_pending_shared_url_loader_factory.h" +#include "services/network/public/cpp/features.h" +#include "services/network/public/cpp/shared_url_loader_factory.h" +#include "services/network/public/mojom/cert_verifier_service.mojom.h" +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/proxy_resolver/public/mojom/proxy_resolver.mojom.h" +#include "api/qwebengineglobalsettings.h" +#include "api/qwebengineglobalsettings_p.h" + +#if BUILDFLAG(IS_WIN) +#include "chrome/browser/net/chrome_mojo_proxy_resolver_win.h" +#include "components/os_crypt/sync/os_crypt.h" +#include "content/public/browser/network_service_util.h" +#endif + +ASSERT_ENUMS_MATCH(net::SecureDnsMode::kSecure, QWebEngineGlobalSettings::SecureDnsMode::SecureOnly) +ASSERT_ENUMS_MATCH(net::SecureDnsMode::kAutomatic, + QWebEngineGlobalSettings::SecureDnsMode::SecureWithFallback) +ASSERT_ENUMS_MATCH(net::SecureDnsMode::kOff, QWebEngineGlobalSettings::SecureDnsMode::SystemOnly) + +namespace { + +network::mojom::HttpAuthStaticParamsPtr CreateHttpAuthStaticParams() +{ + network::mojom::HttpAuthStaticParamsPtr auth_static_params = + network::mojom::HttpAuthStaticParams::New(); + + return auth_static_params; +} + +network::mojom::HttpAuthDynamicParamsPtr CreateHttpAuthDynamicParams() +{ + network::mojom::HttpAuthDynamicParamsPtr auth_dynamic_params = network::mojom::HttpAuthDynamicParams::New(); + + auth_dynamic_params->allowed_schemes = { "basic", "digest", "ntlm", "negotiate" }; + + auto *command_line = base::CommandLine::ForCurrentProcess(); + auth_dynamic_params->server_allowlist = command_line->GetSwitchValueASCII(switches::kAuthServerAllowlist); +// auth_dynamic_params->delegate_allowlist = command_line->GetSwitchValueASCII(switches::kAuthNegotiateDelegateWhitelist); +// auth_dynamic_params->enable_negotiate_port = command_line->HasSwitch(switches::kEnableAuthNegotiatePort); + + return auth_dynamic_params; +} + +} // namespace + +namespace QtWebEngineCore { + +// The global instance of the SystemNetworkContextmanager. +SystemNetworkContextManager *g_system_network_context_manager = nullptr; + +// SharedURLLoaderFactory backed by a SystemNetworkContextManager and its +// network context. Transparently handles crashes. +class SystemNetworkContextManager::URLLoaderFactoryForSystem : public network::SharedURLLoaderFactory +{ +public: + explicit URLLoaderFactoryForSystem(SystemNetworkContextManager *manager) : manager_(manager) + { + DETACH_FROM_SEQUENCE(sequence_checker_); + } + + // mojom::URLLoaderFactory implementation: + + void CreateLoaderAndStart(mojo::PendingReceiver<network::mojom::URLLoader> receiver, + int32_t request_id, + uint32_t options, + const network::ResourceRequest &url_request, + mojo::PendingRemote<network::mojom::URLLoaderClient> client, + const net::MutableNetworkTrafficAnnotationTag &traffic_annotation) override + { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + if (!manager_) + return; + manager_->GetURLLoaderFactory()->CreateLoaderAndStart( + std::move(receiver), request_id, options, url_request, + std::move(client), traffic_annotation); + } + + void Clone(mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver) override + { + if (!manager_) + return; + manager_->GetURLLoaderFactory()->Clone(std::move(receiver)); + } + + // SharedURLLoaderFactory implementation: + std::unique_ptr<network::PendingSharedURLLoaderFactory> Clone() override + { + DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); + return std::make_unique<network::CrossThreadPendingSharedURLLoaderFactory>(this); + } + + void Shutdown() { manager_ = nullptr; } + +private: + friend class base::RefCounted<URLLoaderFactoryForSystem>; + ~URLLoaderFactoryForSystem() override {} + + SEQUENCE_CHECKER(sequence_checker_); + SystemNetworkContextManager *manager_; +}; + +network::mojom::NetworkContext *SystemNetworkContextManager::GetContext() +{ + if (!network_service_network_context_ || + !network_service_network_context_.is_connected()) { + // This should call into OnNetworkServiceCreated(), which will re-create + // the network service, if needed. There's a chance that it won't be + // invoked, if the NetworkContext has encountered an error but the + // NetworkService has not yet noticed its pipe was closed. In that case, + // trying to create a new NetworkContext would fail, anyways, and hopefully + // a new NetworkContext will be created on the next GetContext() call. + content::GetNetworkService(); + DCHECK(network_service_network_context_); + } + return network_service_network_context_.get(); +} + +network::mojom::URLLoaderFactory *SystemNetworkContextManager::GetURLLoaderFactory() +{ + // Create the URLLoaderFactory as needed. + if (url_loader_factory_ && url_loader_factory_.is_connected()) { + return url_loader_factory_.get(); + } + + network::mojom::URLLoaderFactoryParamsPtr params = network::mojom::URLLoaderFactoryParams::New(); + params->process_id = network::mojom::kBrowserProcessId; + params->is_corb_enabled = false; + GetContext()->CreateURLLoaderFactory(url_loader_factory_.BindNewPipeAndPassReceiver(), std::move(params)); + return url_loader_factory_.get(); +} + +scoped_refptr<network::SharedURLLoaderFactory> SystemNetworkContextManager::GetSharedURLLoaderFactory() +{ + return shared_url_loader_factory_; +} + +// static +SystemNetworkContextManager *SystemNetworkContextManager::CreateInstance() +{ + DCHECK(!g_system_network_context_manager); + g_system_network_context_manager = new SystemNetworkContextManager(); + return g_system_network_context_manager; +} + +// static +SystemNetworkContextManager *SystemNetworkContextManager::GetInstance() +{ + return g_system_network_context_manager; +} + +// static +void SystemNetworkContextManager::DeleteInstance() +{ + DCHECK(g_system_network_context_manager); + delete g_system_network_context_manager; +} + +SystemNetworkContextManager::SystemNetworkContextManager() +{ + shared_url_loader_factory_ = new URLLoaderFactoryForSystem(this); +} + +SystemNetworkContextManager::~SystemNetworkContextManager() +{ + shared_url_loader_factory_->Shutdown(); +} + +void SystemNetworkContextManager::OnNetworkServiceCreated(network::mojom::NetworkService *network_service) +{ + bool is_quic_force_enabled = base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableQuic); + // Disable QUIC globally + if (!is_quic_force_enabled) + network_service->DisableQuic(); + + network_service->SetUpHttpAuth(CreateHttpAuthStaticParams()); + network_service->ConfigureHttpAuthPrefs(CreateHttpAuthDynamicParams()); + +#if BUILDFLAG(IS_WIN) + if (content::IsOutOfProcessNetworkService()) + network_service->SetEncryptionKey(OSCrypt::GetRawEncryptionKey()); +#endif + + // Configure the Certificate Transparency logs. + std::vector<std::pair<std::string, base::Time>> disqualified_logs = + certificate_transparency::GetDisqualifiedLogs(); + std::vector<network::mojom::CTLogInfoPtr> log_list_mojo; + for (const auto &ct_log : certificate_transparency::GetKnownLogs()) { + network::mojom::CTLogInfoPtr log_info = network::mojom::CTLogInfo::New(); + log_info->public_key = std::string(ct_log.log_key, ct_log.log_key_length); + log_info->id = crypto::SHA256HashString(log_info->public_key); + log_info->name = ct_log.log_name; + log_info->current_operator = ct_log.current_operator; + + auto it = std::lower_bound( + std::begin(disqualified_logs), std::end(disqualified_logs), log_info->id, + [](const auto& disqualified_log, const std::string& log_id) { + return disqualified_log.first < log_id; + }); + if (it != std::end(disqualified_logs) && it->first == log_info->id) + log_info->disqualified_at = it->second; + + for (size_t i = 0; i < ct_log.previous_operators_length; i++) { + const auto& op = ct_log.previous_operators[i]; + network::mojom::PreviousOperatorEntryPtr previous_operator = + network::mojom::PreviousOperatorEntry::New(); + previous_operator->name = op.name; + previous_operator->end_time = op.end_time; + log_info->previous_operators.push_back(std::move(previous_operator)); + } + + log_list_mojo.push_back(std::move(log_info)); + } + network_service->UpdateCtLogList(std::move(log_list_mojo), base::DoNothing()); + + // The system NetworkContext is created first + network_service_network_context_.reset(); + network_service->CreateNetworkContext( + network_service_network_context_.BindNewPipeAndPassReceiver(), + CreateNetworkContextParams()); + + // Handle --explicitly-allowed-ports + if (base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kExplicitlyAllowedPorts)) { + std::vector<uint16_t> explicitly_allowed_network_ports; + std::string switch_value = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(switches::kExplicitlyAllowedPorts); + const auto split = base::SplitStringPiece(switch_value, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + for (const auto &piece : split) { + int port; + if (!base::StringToInt(piece, &port)) + continue; + if (!net::IsPortValid(port)) + continue; + explicitly_allowed_network_ports.push_back(static_cast<uint16_t>(port)); + } + + network_service->SetExplicitlyAllowedPorts(explicitly_allowed_network_ports); + } + + // The network service is a singleton that can be reinstantiated for different reasons, + // e.g., when the network service crashes. Therefore, we configure the stub host + // resolver of the network service here, each time it is instantiated, with our global + // DNS-Over-HTTPS settings. This ensures that the global settings don't get lost + // on reinstantiation and are in effect upon initial instantiation. + QWebEngineGlobalSettingsPrivate::instance()->configureStubHostResolver(); +} + +void SystemNetworkContextManager::AddSSLConfigToNetworkContextParams(network::mojom::NetworkContextParams *network_context_params) +{ + network_context_params->initial_ssl_config = network::mojom::SSLConfig::New(); + network_context_params->initial_ssl_config->symantec_enforcement_disabled = true; +} + +void SystemNetworkContextManager::ConfigureDefaultNetworkContextParams(network::mojom::NetworkContextParams *network_context_params, + cert_verifier::mojom::CertVerifierCreationParams *cert_verifier_creation_params) +{ + network_context_params->enable_brotli = true; + + // Disable referrers by default. Any consumer that enables referrers should + // respect prefs::kEnableReferrers from the appropriate pref store. + network_context_params->enable_referrers = false; + + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + + if (!command_line.HasSwitch(switches::kWinHttpProxyResolver)) { + if (command_line.HasSwitch(switches::kSingleProcess)) { + LOG(ERROR) << "Cannot use V8 Proxy resolver in single process mode."; + } else { + network_context_params->proxy_resolver_factory = + ChromeMojoProxyResolverFactory::CreateWithSelfOwnedReceiver(); + } + } +#if BUILDFLAG(IS_WIN) + if (command_line.HasSwitch(switches::kUseSystemProxyResolver)) { + network_context_params->windows_system_proxy_resolver = + ChromeMojoProxyResolverWin::CreateWithSelfOwnedReceiver(); + } +#endif + // Use the SystemNetworkContextManager to populate and update SSL + // configuration. The SystemNetworkContextManager is owned by the + // BrowserProcess itself, so will only be destroyed on shutdown, at which + // point, all NetworkContexts will be destroyed as well. + AddSSLConfigToNetworkContextParams(network_context_params); +} + +network::mojom::NetworkContextParamsPtr SystemNetworkContextManager::CreateNetworkContextParams() +{ + // TODO(mmenke): Set up parameters here (in memory cookie store, etc). + network::mojom::NetworkContextParamsPtr network_context_params = network::mojom::NetworkContextParams::New(); + cert_verifier::mojom::CertVerifierCreationParamsPtr + cert_verifier_creation_params = cert_verifier::mojom::CertVerifierCreationParams::New(); + ConfigureDefaultNetworkContextParams(network_context_params.get(), cert_verifier_creation_params.get()); + + network_context_params->enable_referrers = false; + + network_context_params->http_cache_enabled = false; + + proxy_config_monitor_.AddToNetworkContextParams(network_context_params.get()); + + network_context_params->cert_verifier_params = + content::GetCertVerifierParams(std::move(cert_verifier_creation_params)); + return network_context_params; +} + +bool isValidTemplates(std::string templates) +{ + absl::optional<net::DnsOverHttpsConfig> dnsOverHttpsConfig = + net::DnsOverHttpsConfig::FromString(templates); + return dnsOverHttpsConfig.has_value(); +} + + +void configureStubHostResolver(QWebEngineGlobalSettings::SecureDnsMode dnsMode, + std::string dnsOverHttpsTemplates, bool insecureDnsClientEnabled, + bool additionalInsecureDnsTypesEnabled) +{ + if (content::IsNetworkServiceCreated()) { + network::mojom::NetworkService *networkService = content::GetNetworkService(); + if (networkService) { + absl::optional<net::DnsOverHttpsConfig> dohConfig = dnsOverHttpsTemplates.empty() + ? net::DnsOverHttpsConfig() + : net::DnsOverHttpsConfig::FromString(dnsOverHttpsTemplates); + networkService->ConfigureStubHostResolver(insecureDnsClientEnabled, + net::SecureDnsMode(dnsMode), *dohConfig, + additionalInsecureDnsTypesEnabled); + } + } +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/system_network_context_manager.h b/src/core/net/system_network_context_manager.h new file mode 100644 index 000000000..d56bdab78 --- /dev/null +++ b/src/core/net/system_network_context_manager.h @@ -0,0 +1,121 @@ +// Copyright (C) 2019 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// based on chrome/browser/net/system_network_context_manager.h: +// Copyright 2017 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. + +#ifndef SYSTEM_NETWORK_CONTEXT_MANAGER_H_ +#define SYSTEM_NETWORK_CONTEXT_MANAGER_H_ + +#include <memory> + +#include "services/network/public/mojom/network_context.mojom.h" +#include "services/network/public/mojom/network_service.mojom-forward.h" +#include "services/network/public/mojom/url_loader_factory.mojom.h" +#include "net/proxy_config_monitor.h" + +namespace cert_verifier { +namespace mojom { +class CertVerifierCreationParams; +}} + +namespace network { +namespace mojom { +class URLLoaderFactory; +} +class SharedURLLoaderFactory; +} // namespace network + +namespace QtWebEngineCore { + +// Responsible for creating and managing access to the system NetworkContext. +// Lives on the UI thread. The NetworkContext this owns is intended for requests +// not associated with a profile. It stores no data on disk, and has no HTTP +// cache, but it does have ephemeral cookie and channel ID stores. It also does +// not have access to HTTP proxy auth information the user has entered or that +// comes from extensions, and similarly, has no extension-provided per-profile +// proxy configuration information. +// +// This class is also responsible for configuring global NetworkService state. +// +// The "system" NetworkContext will either share a URLRequestContext with +// IOThread's SystemURLRequestContext and be part of IOThread's NetworkService +// (If the network service is disabled) or be an independent NetworkContext +// using the actual network service. +// +// This class is intended to eventually replace IOThread. Handling the two cases +// differently allows this to be used in production without breaking anything or +// requiring two separate paths, while IOThread consumers slowly transition over +// to being compatible with the network service. +class SystemNetworkContextManager +{ +public: + ~SystemNetworkContextManager(); + + // Creates the global instance of SystemNetworkContextManager. If an + // instance already exists, this will cause a DCHECK failure. + static SystemNetworkContextManager *CreateInstance(); + + // Gets the global SystemNetworkContextManager instance. + static SystemNetworkContextManager *GetInstance(); + + // Destroys the global SystemNetworkContextManager instance. + static void DeleteInstance(); + + // Returns the System NetworkContext. May only be called after SetUp(). Does + // any initialization of the NetworkService that may be needed when first + // called. + network::mojom::NetworkContext *GetContext(); + + // Returns a URLLoaderFactory owned by the SystemNetworkContextManager that is + // backed by the SystemNetworkContext. Allows sharing of the URLLoaderFactory. + // Prefer this to creating a new one. Call Clone() on the value returned by + // this method to get a URLLoaderFactory that can be used on other threads. + network::mojom::URLLoaderFactory *GetURLLoaderFactory(); + + // Returns a SharedURLLoaderFactory owned by the SystemNetworkContextManager + // that is backed by the SystemNetworkContext. + scoped_refptr<network::SharedURLLoaderFactory> GetSharedURLLoaderFactory(); + + // Called when content creates a NetworkService. Creates the + // SystemNetworkContext, if the network service is enabled. + void OnNetworkServiceCreated(network::mojom::NetworkService *network_service); + + // Populates |initial_ssl_config| and |ssl_config_client_request| members of + // |network_context_params|. As long as the SystemNetworkContextManager + // exists, any NetworkContext created with the params will continue to get + // SSL configuration updates. + void AddSSLConfigToNetworkContextParams(network::mojom::NetworkContextParams *network_context_params); + + // Configures the default set of parameters for the network context. + void ConfigureDefaultNetworkContextParams(network::mojom::NetworkContextParams *, + cert_verifier::mojom::CertVerifierCreationParams *); + +private: + class URLLoaderFactoryForSystem; + + explicit SystemNetworkContextManager(); + + // Creates parameters for the NetworkContext. May only be called once, since + // it initializes some class members. + network::mojom::NetworkContextParamsPtr CreateNetworkContextParams(); + + // ProxyConfigMonitor proxy_config_monitor_; + + // NetworkContext using the network service, if the network service is + // enabled. nullptr, otherwise. + mojo::Remote<network::mojom::NetworkContext> network_service_network_context_; + + // URLLoaderFactory backed by the NetworkContext returned by GetContext(), so + // consumers don't all need to create their own factory. + scoped_refptr<URLLoaderFactoryForSystem> shared_url_loader_factory_; + mojo::Remote<network::mojom::URLLoaderFactory> url_loader_factory_; + + ProxyConfigMonitor proxy_config_monitor_; +}; + +} // namespace QtWebEngineCore + +#endif // SYSTEM_NETWORK_CONTEXT_MANAGER_H_ diff --git a/src/core/net/url_request_context_getter_qt.cpp b/src/core/net/url_request_context_getter_qt.cpp deleted file mode 100644 index 6081a5e9f..000000000 --- a/src/core/net/url_request_context_getter_qt.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "url_request_context_getter_qt.h" -#include "profile_io_data_qt.h" - -#include "base/task/post_task.h" -#include "content/public/browser/browser_task_traits.h" -#include "content/public/browser/browser_thread.h" - -namespace QtWebEngineCore { - -URLRequestContextGetterQt::URLRequestContextGetterQt(ProfileIODataQt *data) - : m_profileIOData(data) -{ -} - -URLRequestContextGetterQt::~URLRequestContextGetterQt() -{ -} - -net::URLRequestContext *URLRequestContextGetterQt::GetURLRequestContext() -{ - Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); - return m_profileIOData->urlRequestContext(); -} - -scoped_refptr<base::SingleThreadTaskRunner> URLRequestContextGetterQt::GetNetworkTaskRunner() const -{ - return base::CreateSingleThreadTaskRunnerWithTraits({content::BrowserThread::IO}); -} - -} // namespace QtWebEngineCore diff --git a/src/core/net/url_request_context_getter_qt.h b/src/core/net/url_request_context_getter_qt.h deleted file mode 100644 index b6135cb16..000000000 --- a/src/core/net/url_request_context_getter_qt.h +++ /dev/null @@ -1,61 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef URL_REQUEST_CONTEXT_GETTER_QT_H -#define URL_REQUEST_CONTEXT_GETTER_QT_H - -#include "net/url_request/url_request_context_getter.h" - -namespace QtWebEngineCore { - -class ProfileIODataQt; - -class URLRequestContextGetterQt : public net::URLRequestContextGetter { -public: - URLRequestContextGetterQt(ProfileIODataQt *data); - net::URLRequestContext *GetURLRequestContext() override; - scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() const override; -private: - virtual ~URLRequestContextGetterQt(); - ProfileIODataQt *m_profileIOData; -}; - -} // namespace QtWebEngineCore - -#endif // URL_REQUEST_CONTEXT_GETTER_QT_H diff --git a/src/core/net/url_request_custom_job.cpp b/src/core/net/url_request_custom_job.cpp deleted file mode 100644 index dd213d4f8..000000000 --- a/src/core/net/url_request_custom_job.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "url_request_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> - -using namespace net; - -namespace QtWebEngineCore { - -URLRequestCustomJob::URLRequestCustomJob(URLRequest *request, - NetworkDelegate *networkDelegate, - const std::string &scheme, - QPointer<ProfileAdapter> profileAdapter) - : URLRequestJob(request, networkDelegate) - , m_proxy(new URLRequestCustomJobProxy(this, scheme, profileAdapter)) - , m_device(nullptr) - , m_error(0) - , m_pendingReadSize(0) - , m_pendingReadPos(0) - , m_pendingReadBuffer(nullptr) - , m_corsEnabled(QWebEngineUrlScheme::schemeByName(QByteArray::fromStdString(scheme)) - .flags().testFlag(QWebEngineUrlScheme::CorsEnabled)) - , m_httpStatusCode(500) -{ -} - -URLRequestCustomJob::~URLRequestCustomJob() -{ - m_proxy->m_job = nullptr; - if (m_device && m_device->isOpen()) - m_device->close(); - m_device = nullptr; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&URLRequestCustomJobProxy::release, m_proxy)); -} - -void URLRequestCustomJob::Start() -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - HttpRequestHeaders requestHeaders = request()->extra_request_headers(); - std::map<std::string, std::string> headers; - net::HttpRequestHeaders::Iterator it(requestHeaders); - while (it.GetNext()) - headers.emplace(it.name(), it.value()); - if (!request()->referrer().empty()) - headers.emplace("Referer", request()->referrer()); - - // TODO: handle UploadDataStream, for instance using a QIODevice wrapper. - - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&URLRequestCustomJobProxy::initialize, - m_proxy, - request()->url(), - request()->method(), - request()->initiator(), - std::move(headers))); -} - -void URLRequestCustomJob::Kill() -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - m_proxy->m_job = nullptr; - if (m_device && m_device->isOpen()) - m_device->close(); - if (m_pendingReadBuffer) { - m_pendingReadBuffer->Release(); - m_pendingReadBuffer = nullptr; - m_pendingReadSize = 0; - m_pendingReadPos = 0; - } - m_device = nullptr; - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::UI}, - base::BindOnce(&URLRequestCustomJobProxy::release, - m_proxy)); - URLRequestJob::Kill(); -} - -bool URLRequestCustomJob::GetMimeType(std::string *mimeType) const -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (m_mimeType.size() > 0) { - *mimeType = m_mimeType; - return true; - } - return false; -} - -bool URLRequestCustomJob::GetCharset(std::string* charset) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (m_charset.size() > 0) { - *charset = m_charset; - return true; - } - 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); - if (m_redirect.is_valid()) { - *location = m_redirect; - *http_status_code = 303; - return true; - } - return false; -} - -int URLRequestCustomJob::ReadRawData(IOBuffer *buf, int bufSize) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (m_error) - return m_error; - qint64 rv = m_device ? m_device->read(buf->data(), bufSize) : -1; - if (rv > 0) { - return static_cast<int>(rv); - } else if (rv == 0) { - // Returning zero is interpreted as EOF by Chromium, so only - // return zero if we are the end of the file. - if (m_device->atEnd()) - return 0; - // Otherwise return IO_PENDING and call ReadRawDataComplete when we have data - // for them. - buf->AddRef(); - m_pendingReadPos = 0; - m_pendingReadSize = bufSize; - m_pendingReadBuffer = buf; - return ERR_IO_PENDING; - } else { - // QIODevice::read might have called fail on us. - if (m_error) - return m_error; - if (m_device && m_device->atEnd()) - return 0; - return ERR_FAILED; - } -} - -void URLRequestCustomJob::notifyReadyRead() -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!m_device) - return; - if (!m_pendingReadSize) - return; - Q_ASSERT(m_pendingReadBuffer); - if (!m_pendingReadBuffer) - return; - - qint64 rv = m_device->read(m_pendingReadBuffer->data() + m_pendingReadPos, m_pendingReadSize - m_pendingReadPos); - if (rv == 0) - return; - if (rv < 0) { - if (m_error) - rv = m_error; - else if (m_device->atEnd()) - rv = 0; - else - rv = ERR_FAILED; - } else { - m_pendingReadPos += rv; - if (m_pendingReadPos < m_pendingReadSize && !m_device->atEnd()) - return; - rv = m_pendingReadPos; - } - // killJob may be called from ReadRawDataComplete - net::IOBuffer *buf = m_pendingReadBuffer; - m_pendingReadBuffer = nullptr; - m_pendingReadSize = 0; - m_pendingReadPos = 0; - ReadRawDataComplete(rv); - buf->Release(); -} - -} // namespace diff --git a/src/core/net/url_request_custom_job.h b/src/core/net/url_request_custom_job.h deleted file mode 100644 index e1e8e9fba..000000000 --- a/src/core/net/url_request_custom_job.h +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef URL_REQUEST_CUSTOM_JOB_H_ -#define URL_REQUEST_CUSTOM_JOB_H_ - -#include "net/url_request/url_request_job.h" -#include "url/gurl.h" -#include <QtCore/QPointer> - -QT_FORWARD_DECLARE_CLASS(QIODevice) - -namespace QtWebEngineCore { - -class ProfileAdapter; -class URLRequestCustomJobDelegate; -class URLRequestCustomJobProxy; - -// A request job that handles reading custom URL schemes -class URLRequestCustomJob : public net::URLRequestJob { -public: - URLRequestCustomJob(net::URLRequest *request, - net::NetworkDelegate *networkDelegate, - const std::string &scheme, - QPointer<ProfileAdapter> profileAdapter); - void Start() override; - void Kill() override; - 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: - virtual ~URLRequestCustomJob(); - -private: - void notifyReadyRead(); - scoped_refptr<URLRequestCustomJobProxy> m_proxy; - std::string m_mimeType; - std::string m_charset; - GURL m_redirect; - QIODevice *m_device; - int m_error; - int m_pendingReadSize; - int m_pendingReadPos; - net::IOBuffer *m_pendingReadBuffer; - const bool m_corsEnabled; - int m_httpStatusCode; - - friend class URLRequestCustomJobProxy; - - DISALLOW_COPY_AND_ASSIGN(URLRequestCustomJob); -}; -} // namespace QtWebEngineCore - -#endif // URL_REQUEST_CUSTOM_JOB_H_ diff --git a/src/core/net/url_request_custom_job_delegate.cpp b/src/core/net/url_request_custom_job_delegate.cpp index b5a7a55a7..fb6b605a4 100644 --- a/src/core/net/url_request_custom_job_delegate.cpp +++ b/src/core/net/url_request_custom_job_delegate.cpp @@ -1,46 +1,9 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "url_request_custom_job_delegate.h" #include "url_request_custom_job_proxy.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/net_errors.h" @@ -51,16 +14,16 @@ namespace QtWebEngineCore { -URLRequestCustomJobDelegate::URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, - const QUrl &url, - const QByteArray &method, - const QUrl &initiatorOrigin, - const QMap<QByteArray, QByteArray> &headers) - : m_proxy(proxy), - m_request(url), - m_method(method), - m_initiatorOrigin(initiatorOrigin), - m_requestHeaders(headers) +URLRequestCustomJobDelegate::URLRequestCustomJobDelegate( + URLRequestCustomJobProxy *proxy, const QUrl &url, const QByteArray &method, + const QUrl &initiatorOrigin, const QMap<QByteArray, QByteArray> &headers, + network::ResourceRequestBody *requestBody) + : m_proxy(proxy) + , m_request(url) + , m_method(method) + , m_initiatorOrigin(initiatorOrigin) + , m_requestHeaders(headers) + , m_resourceRequestBody(ResourceRequestBody(requestBody)) { } @@ -88,36 +51,52 @@ QMap<QByteArray, QByteArray> URLRequestCustomJobDelegate::requestHeaders() const return m_requestHeaders; } +QIODevice *URLRequestCustomJobDelegate::requestBody() +{ + return &m_resourceRequestBody; +} + +void URLRequestCustomJobDelegate::setAdditionalResponseHeaders( + const QMultiMap<QByteArray, QByteArray> &additionalResponseHeaders) +{ + m_additionalResponseHeaders = additionalResponseHeaders; +} + void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice *device) { - if (device) + if (!device) + m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::succeed, m_proxy)); + else { QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead); - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&URLRequestCustomJobProxy::reply, - m_proxy,contentType.toStdString(),device)); + m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::reply, m_proxy, + contentType.toStdString(), device, + std::move(m_additionalResponseHeaders))); + } } void URLRequestCustomJobDelegate::slotReadyRead() { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&URLRequestCustomJobProxy::readyRead, m_proxy)); + m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::readyRead, m_proxy)); } void URLRequestCustomJobDelegate::abort() { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&URLRequestCustomJobProxy::abort, m_proxy)); + m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::abort, m_proxy)); } void URLRequestCustomJobDelegate::redirect(const QUrl &url) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&URLRequestCustomJobProxy::redirect, m_proxy, toGurl(url))); + m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::redirect, m_proxy, toGurl(url))); } void URLRequestCustomJobDelegate::fail(Error error) { - int net_error = 0; + int net_error = 0; switch (error) { case NoError: break; @@ -138,8 +117,8 @@ void URLRequestCustomJobDelegate::fail(Error error) break; } if (net_error) { - base::PostTaskWithTraits(FROM_HERE, {content::BrowserThread::IO}, - base::BindOnce(&URLRequestCustomJobProxy::fail, m_proxy, net_error)); + m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, + base::BindOnce(&URLRequestCustomJobProxy::fail, m_proxy, net_error)); } } diff --git a/src/core/net/url_request_custom_job_delegate.h b/src/core/net/url_request_custom_job_delegate.h index 0ab1a82c7..63db46464 100644 --- a/src/core/net/url_request_custom_job_delegate.h +++ b/src/core/net/url_request_custom_job_delegate.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtWebEngine module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // // W A R N I N G @@ -53,6 +17,7 @@ #include "base/memory/ref_counted.h" #include "qtwebenginecoreglobal_p.h" +#include "resource_request_body_qt.h" #include <QMap> #include <QObject> @@ -60,11 +25,16 @@ QT_FORWARD_DECLARE_CLASS(QIODevice) +namespace network { +class ResourceRequestBody; +} + namespace QtWebEngineCore { class URLRequestCustomJobProxy; -class Q_WEBENGINECORE_PRIVATE_EXPORT URLRequestCustomJobDelegate : public QObject { +class Q_WEBENGINECORE_EXPORT URLRequestCustomJobDelegate : public QObject +{ Q_OBJECT public: ~URLRequestCustomJobDelegate(); @@ -82,9 +52,12 @@ public: QByteArray method() const; QUrl initiator() const; QMap<QByteArray, QByteArray> requestHeaders() const; + QIODevice *requestBody(); + void + setAdditionalResponseHeaders(const QMultiMap<QByteArray, QByteArray> &additionalResponseHeaders); void reply(const QByteArray &contentType, QIODevice *device); - void redirect(const QUrl& url); + void redirect(const QUrl &url); void abort(); void fail(Error); @@ -92,11 +65,10 @@ private Q_SLOTS: void slotReadyRead(); private: - URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, - const QUrl &url, - const QByteArray &method, - const QUrl &initiatorOrigin, - const QMap<QByteArray, QByteArray> &requestHeaders); + URLRequestCustomJobDelegate(URLRequestCustomJobProxy *proxy, const QUrl &url, + const QByteArray &method, const QUrl &initiatorOrigin, + const QMap<QByteArray, QByteArray> &requestHeaders, + network::ResourceRequestBody *requestBody); friend class URLRequestCustomJobProxy; scoped_refptr<URLRequestCustomJobProxy> m_proxy; @@ -104,6 +76,8 @@ private: QByteArray m_method; QUrl m_initiatorOrigin; const QMap<QByteArray, QByteArray> m_requestHeaders; + QMultiMap<QByteArray, QByteArray> m_additionalResponseHeaders; + ResourceRequestBody m_resourceRequestBody; }; } // namespace diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index b9ccf7ea4..54faddc62 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -1,65 +1,31 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "url_request_custom_job_proxy.h" -#include "url_request_custom_job.h" #include "url_request_custom_job_delegate.h" + +#include "content/public/browser/browser_thread.h" +#include "net/base/net_errors.h" +#include "services/network/public/cpp/resource_request_body.h" + #include "api/qwebengineurlrequestjob.h" #include "profile_adapter.h" #include "type_conversion.h" -#include "content/public/browser/browser_thread.h" #include "web_engine_context.h" -using namespace net; - namespace QtWebEngineCore { -URLRequestCustomJobProxy::URLRequestCustomJobProxy(URLRequestCustomJob *job, +URLRequestCustomJobProxy::URLRequestCustomJobProxy(URLRequestCustomJobProxy::Client *client, const std::string &scheme, QPointer<ProfileAdapter> profileAdapter) - : m_job(job) + : m_client(client) , m_started(false) , m_scheme(scheme) , m_delegate(nullptr) , m_profileAdapter(profileAdapter) + , m_ioTaskRunner(m_client->taskRunner()) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); + DCHECK(m_ioTaskRunner && m_ioTaskRunner->RunsTasksInCurrentSequence()); } URLRequestCustomJobProxy::~URLRequestCustomJobProxy() @@ -75,89 +41,99 @@ void URLRequestCustomJobProxy::release() } } -// Fix me: this is never used -/* -void URLRequestCustomJobProxy::setReplyCharset(const std::string &charset) -{ - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!m_job) - return; - m_job->m_charset = charset; -} -*/ -void URLRequestCustomJobProxy::reply(std::string mimeType, QIODevice *device) +void URLRequestCustomJobProxy::reply(std::string contentType, QIODevice *device, + QMultiMap<QByteArray, QByteArray> additionalResponseHeaders) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!m_job) + if (!m_client) return; - m_job->m_mimeType = mimeType; - m_job->m_device = device; - if (m_job->m_device && !m_job->m_device->isReadable()) - m_job->m_device->open(QIODevice::ReadOnly); - - qint64 size = m_job->m_device ? m_job->m_device->size() : -1; - if (size > 0) - m_job->set_expected_content_size(size); - if (m_job->m_device && m_job->m_device->isReadable()) { + DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence()); + QByteArray qcontentType = QByteArray::fromStdString(contentType).toLower(); + const int sidx = qcontentType.indexOf(';'); + if (sidx > 0) { + const int cidx = qcontentType.indexOf("charset=", sidx); + if (cidx > 0) { + m_client->m_charset = qcontentType.mid(cidx + 8).trimmed().toStdString(); + qcontentType = qcontentType.first(sidx); + } else { + qWarning() << "QWebEngineUrlRequestJob::reply(): Unrecognized content-type format with ';'" << qcontentType; + } + } + m_client->m_mimeType = qcontentType.trimmed().toStdString(); + m_client->m_device = device; + m_client->m_additionalResponseHeaders = std::move(additionalResponseHeaders); + if (m_client->m_device && !m_client->m_device->isReadable()) + m_client->m_device->open(QIODevice::ReadOnly); + + if (m_client->m_device && m_client->m_firstBytePosition > 0) + m_client->m_device->seek(m_client->m_firstBytePosition); + + qint64 deviceSize = m_client->m_device ? m_client->m_device->size() : -1; + if (deviceSize > 0) + m_client->notifyExpectedContentSize(deviceSize); + + if (m_client->m_device && m_client->m_device->isReadable()) { m_started = true; - m_job->m_httpStatusCode = 200; - m_job->NotifyHeadersComplete(); + m_client->notifyHeadersComplete(); } else { - fail(ERR_INVALID_URL); + fail(net::ERR_INVALID_URL); } } void URLRequestCustomJobProxy::redirect(GURL url) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!m_job) + if (!m_client) return; - if (m_job->m_device || m_job->m_error) + DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence()); + if (m_client->m_device || m_client->m_error) return; - m_job->m_redirect = url; - m_job->m_httpStatusCode = 303; + m_client->m_redirect = url; m_started = true; - m_job->NotifyHeadersComplete(); + m_client->notifyHeadersComplete(); } void URLRequestCustomJobProxy::abort() { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!m_job) + if (!m_client) return; - if (m_job->m_device && m_job->m_device->isOpen()) - m_job->m_device->close(); - m_job->m_device = nullptr; + DCHECK (!m_ioTaskRunner || m_ioTaskRunner->RunsTasksInCurrentSequence()); + if (m_client->m_device && m_client->m_device->isOpen()) + m_client->m_device->close(); + m_client->m_device = nullptr; if (m_started) - m_job->NotifyCanceled(); + m_client->notifyCanceled(); else - m_job->NotifyStartError(URLRequestStatus(URLRequestStatus::CANCELED, ERR_ABORTED)); + m_client->notifyAborted(); } void URLRequestCustomJobProxy::fail(int error) { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (!m_job) + if (!m_client) return; - m_job->m_error = error; - m_job->m_httpStatusCode = 500; - if (m_job->m_device) - m_job->m_device->close(); + DCHECK (m_ioTaskRunner->RunsTasksInCurrentSequence()); + m_client->m_error = error; + if (m_client->m_device) + m_client->m_device->close(); if (!m_started) - m_job->NotifyStartError(URLRequestStatus::FromError(error)); + m_client->notifyStartFailure(error); // else we fail on the next read, or the read that might already be in progress } +void URLRequestCustomJobProxy::succeed() +{ + m_client->notifySuccess(); +} + void URLRequestCustomJobProxy::readyRead() { - DCHECK_CURRENTLY_ON(content::BrowserThread::IO); - if (m_job) - m_job->notifyReadyRead(); + DCHECK (m_ioTaskRunner->RunsTasksInCurrentSequence()); + if (m_client) + m_client->notifyReadyRead(); } void URLRequestCustomJobProxy::initialize(GURL url, std::string method, - base::Optional<url::Origin> initiator, - std::map<std::string, std::string> headers) + absl::optional<url::Origin> initiator, + std::map<std::string, std::string> headers, + scoped_refptr<network::ResourceRequestBody> requestBody) { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); Q_ASSERT(!m_delegate); @@ -175,10 +151,9 @@ void URLRequestCustomJobProxy::initialize(GURL url, std::string method, qHeaders.insert(toQByteArray(it->first), toQByteArray(it->second)); if (schemeHandler) { - m_delegate = new URLRequestCustomJobDelegate(this, toQt(url), - QByteArray::fromStdString(method), - initiatorOrigin, - qHeaders); + m_delegate = + new URLRequestCustomJobDelegate(this, toQt(url), QByteArray::fromStdString(method), + initiatorOrigin, qHeaders, requestBody.get()); QWebEngineUrlRequestJob *requestJob = new QWebEngineUrlRequestJob(m_delegate); schemeHandler->requestStarted(requestJob); } diff --git a/src/core/net/url_request_custom_job_proxy.h b/src/core/net/url_request_custom_job_proxy.h index aa55db07c..c03992411 100644 --- a/src/core/net/url_request_custom_job_proxy.h +++ b/src/core/net/url_request_custom_job_proxy.h @@ -1,53 +1,23 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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) 2017 Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef URL_REQUEST_CUSTOM_JOB_PROXY_H_ #define URL_REQUEST_CUSTOM_JOB_PROXY_H_ -#include "base/memory/weak_ptr.h" -#include "base/optional.h" +#include "base/task/sequenced_task_runner.h" +#include "third_party/abseil-cpp/absl/types/optional.h" #include "url/gurl.h" #include "url/origin.h" #include <QtCore/QPointer> +#include <QMap> +#include <QByteArray> QT_FORWARD_DECLARE_CLASS(QIODevice) +namespace network { +class ResourceRequestBody; +} + namespace QtWebEngineCore { class URLRequestCustomJob; @@ -56,33 +26,57 @@ class ProfileAdapter; // Used to comunicate between URLRequestCustomJob living on the IO thread // and URLRequestCustomJobDelegate living on the UI thread. -class URLRequestCustomJobProxy - : public base::RefCountedThreadSafe<URLRequestCustomJobProxy> { +class URLRequestCustomJobProxy : public base::RefCountedThreadSafe<URLRequestCustomJobProxy> +{ public: - URLRequestCustomJobProxy(URLRequestCustomJob *job, + class Client { + public: + std::string m_mimeType; + std::string m_charset; + QMultiMap<QByteArray, QByteArray> m_additionalResponseHeaders; + GURL m_redirect; + QIODevice *m_device; + int64_t m_firstBytePosition; + int m_error; + virtual void notifyExpectedContentSize(qint64 size) = 0; + virtual void notifyHeadersComplete() = 0; + virtual void notifyCanceled() = 0; + virtual void notifyAborted() = 0; + virtual void notifyStartFailure(int) = 0; + virtual void notifySuccess() = 0; + virtual void notifyReadyRead() = 0; + virtual base::SequencedTaskRunner *taskRunner() = 0; + }; + + URLRequestCustomJobProxy(Client *client, const std::string &scheme, QPointer<ProfileAdapter> profileAdapter); ~URLRequestCustomJobProxy(); // Called from URLRequestCustomJobDelegate via post: //void setReplyCharset(const std::string &); - void reply(std::string mimeType, QIODevice *device); + void reply(std::string mimeType, QIODevice *device, + QMultiMap<QByteArray, QByteArray> additionalResponseHeaders); void redirect(GURL url); void abort(); void fail(int error); + void succeed(); void release(); - void initialize(GURL url, std::string method, base::Optional<url::Origin> initiatorOrigin, std::map<std::string, std::string> headers); + void initialize(GURL url, std::string method, absl::optional<url::Origin> initiatorOrigin, + std::map<std::string, std::string> headers, + scoped_refptr<network::ResourceRequestBody> requestBody); void readyRead(); // IO thread owned: - URLRequestCustomJob *m_job; + Client *m_client; bool m_started; // UI thread owned: std::string m_scheme; URLRequestCustomJobDelegate *m_delegate; QPointer<ProfileAdapter> m_profileAdapter; + scoped_refptr<base::SequencedTaskRunner> m_ioTaskRunner; }; } // namespace QtWebEngineCore diff --git a/src/core/net/url_request_notification.cpp b/src/core/net/url_request_notification.cpp deleted file mode 100644 index e37ad35bc..000000000 --- a/src/core/net/url_request_notification.cpp +++ /dev/null @@ -1,194 +0,0 @@ -/**************************************************************************** -** -** 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(); - if (!interceptor->property("deprecated").toBool()) - 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) { - std::string h = header.key().toStdString(); - if (base::LowerCaseEqualsASCII(h, "referer")) { - m_request->SetReferrer(header.value().toStdString()); - } else { - m_request->SetExtraRequestHeaderByName(h, 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 deleted file mode 100644 index 1d9acf12f..000000000 --- a/src/core/net/url_request_notification.h +++ /dev/null @@ -1,85 +0,0 @@ -/**************************************************************************** -** -** 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/net/version_ui_qt.cpp b/src/core/net/version_ui_qt.cpp new file mode 100644 index 000000000..61a89596a --- /dev/null +++ b/src/core/net/version_ui_qt.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "version_ui_qt.h" +#include "api/qtwebenginecoreglobal.h" +#include "build/build_config.h" +#include "base/command_line.h" +#include "chrome/common/url_constants.h" +#include "chrome/browser/profiles/profile.h" +#include "qtwebengine/grit/qt_webengine_resources.h" +#include "services/network/public/cpp/content_security_policy/content_security_policy.h" + +namespace { +const char kQtWebEngineVersion[] = "qtwebengine_version"; +const char kQtWebEngineChromiumVersion[] = "qtwebengine_chromium_version"; +const char kQtWebEngineChromiumSecurityPatchVersion[] = + "qtwebengine_chromium_security_patch_version"; +const char kCommandLine[] = "command_line"; +const char kQtVersionCSS[] = "qt_version.css"; +const char kQtLogo[] = "images/qt.png"; +const char kQtWebEngineLogo[] = "images/qtwebengine.png"; +} + +VersionUIQt::VersionUIQt(content::WebUI *web_ui) : content::WebUIController(web_ui) +{ + + Profile *profile = Profile::FromWebUI(web_ui); + content::WebUIDataSource *html_source = + content::WebUIDataSource::CreateAndAdd(profile, chrome::kChromeUIVersionQtHost); + html_source->OverrideContentSecurityPolicy( + network::mojom::CSPDirectiveName::ScriptSrc, + "script-src chrome://resources 'self' 'unsafe-inline';"); + html_source->SetDefaultResource(IDR_VERSION_UI_QT_HTML); + html_source->AddResourcePath(kQtVersionCSS, IDR_VERSION_UI_QT_CSS); + html_source->AddResourcePath(kQtLogo, IDR_QT_LOGO); + html_source->AddResourcePath(kQtWebEngineLogo, IDR_QTWEBENGINE_LOGO); + + html_source->AddString(kQtWebEngineVersion, qWebEngineVersion()); + html_source->AddString(kQtWebEngineChromiumVersion, qWebEngineChromiumVersion()); + html_source->AddString(kQtWebEngineChromiumSecurityPatchVersion, + qWebEngineChromiumSecurityPatchVersion()); +#if BUILDFLAG(IS_WIN) + html_source->AddString( + kCommandLine, + base::AsString16(base::CommandLine::ForCurrentProcess()->GetCommandLineString())); +#else + std::string command_line; + typedef std::vector<std::string> ArgvList; + const ArgvList &argv = base::CommandLine::ForCurrentProcess()->argv(); + for (auto iter = argv.begin(); iter != argv.end(); iter++) + command_line += " " + *iter; + html_source->AddString(kCommandLine, command_line); +#endif +} + +VersionUIQt::~VersionUIQt() { } diff --git a/src/core/net/version_ui_qt.h b/src/core/net/version_ui_qt.h new file mode 100644 index 000000000..1fe8ef9e0 --- /dev/null +++ b/src/core/net/version_ui_qt.h @@ -0,0 +1,32 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef VERSION_UI_QT_H_ +#define VERSION_UI_QT_H_ + +#include "build/build_config.h" +#include "content/public/browser/web_ui_controller.h" +#include "content/public/browser/web_ui_data_source.h" + +class VersionUIQt : public content::WebUIController +{ +public: + explicit VersionUIQt(content::WebUI *web_ui); + ~VersionUIQt() override; + + VersionUIQt(const VersionUIQt &) = delete; + VersionUIQt &operator=(const VersionUIQt &) = delete; +}; + +#endif // VERSION_UI_QT_H diff --git a/src/core/net/webui_controller_factory_qt.cpp b/src/core/net/webui_controller_factory_qt.cpp index 92bb8854f..2acd05cae 100644 --- a/src/core/net/webui_controller_factory_qt.cpp +++ b/src/core/net/webui_controller_factory_qt.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // Based on chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc: // Copyright (c) 2012 The Chromium Authors. All rights reserved. @@ -44,39 +8,37 @@ #include "webui_controller_factory_qt.h" -#include "base/bind.h" -#include "base/location.h" -#include "base/threading/thread_task_runner_handle.h" +#include "build_config_qt.h" +#include "devtools_frontend_qt.h" +#include "base/functional/bind.h" #include "build/build_config.h" #include "chrome/browser/accessibility/accessibility_ui.h" #include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/webui/devtools_ui.h" -#include "chrome/browser/ui/webui/quota_internals/quota_internals_ui.h" +#include "chrome/browser/ui/webui/device_log/device_log_ui.h" +#include "chrome/browser/ui/webui/devtools/devtools_ui.h" +#include "chrome/browser/ui/webui/net_internals/net_internals_ui.h" +#include "chrome/browser/ui/webui/user_actions/user_actions_ui.h" #include "chrome/common/url_constants.h" -#include "content/public/browser/web_contents.h" #include "content/public/browser/web_ui.h" -#include "content/public/common/content_client.h" #include "content/public/common/url_utils.h" #include "extensions/buildflags/buildflags.h" #include "media/media_buildflags.h" -#include "ppapi/buildflags/buildflags.h" #include "printing/buildflags/buildflags.h" -#include "ui/web_dialogs/web_dialog_ui.h" #include "url/gurl.h" +#include "version_ui_qt.h" -#if defined(OS_LINUX) || defined(OS_ANDROID) -#include "chrome/browser/ui/webui/sandbox_internals_ui.h" +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) +#include "chrome/browser/ui/webui/sandbox/sandbox_internals_ui.h" +#endif + +#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions) +#include "chrome/browser/ui/webui/media/webrtc_logs_ui.h" #endif // The Following WebUIs are disabled because they currently doesn't build // or doesn't work, but would be interesting for us if they did: // #include "chrome/browser/ui/webui/inspect_ui.h" -// #include "chrome/browser/ui/webui/user_actions/user_actions_ui.h" - -// #if BUILDFLAG(ENABLE_WEBRTC) -// #include "chrome/browser/ui/webui/media/webrtc_logs_ui.h" -// #endif // #if BUILDFLAG(ENABLE_PRINT_PREVIEW) // #include "chrome/browser/ui/webui/print_preview/print_preview_ui.h" @@ -109,7 +71,7 @@ typedef std::unique_ptr<WebUIController> (*WebUIFactoryFunction)(WebUI *web_ui, // Template for defining WebUIFactoryFunction. template<class T> -std::unique_ptr<WebUIController> NewWebUI(WebUI *web_ui, const GURL &/*url*/) +std::unique_ptr<WebUIController> NewWebUI(WebUI *web_ui, const GURL & /*url*/) { return std::unique_ptr<WebUIController>(new T(web_ui)); } @@ -119,26 +81,35 @@ std::unique_ptr<WebUIController> NewWebUI(WebUI *web_ui, const GURL &/*url*/) // with it. WebUIFactoryFunction GetWebUIFactoryFunction(WebUI *web_ui, Profile *profile, const GURL &url) { + Q_UNUSED(web_ui); + Q_UNUSED(profile); // This will get called a lot to check all URLs, so do a quick check of other // schemes to filter out most URLs. if (!content::HasWebUIScheme(url)) - return NULL; + return nullptr; // We must compare hosts only since some of the Web UIs append extra stuff // after the host name. - if (url.host() == chrome::kChromeUIQuotaInternalsHost) - return &NewWebUI<QuotaInternalsUI>; + if (url.host_piece() == chrome::kChromeUINetInternalsHost) + return &NewWebUI<NetInternalsUI>; if (url.SchemeIs(content::kChromeDevToolsScheme)) { -// if (!DevToolsUIBindings::IsValidFrontendURL(url)) -// return nullptr; + if (!QtWebEngineCore::DevToolsFrontendQt::IsValidFrontendURL(url)) + return nullptr; return &NewWebUI<DevToolsUI>; } - if (url.host() == chrome::kChromeUIAccessibilityHost) + if (url.host_piece() == chrome::kChromeUIAccessibilityHost) return &NewWebUI<AccessibilityUI>; -// if (url.host_piece() == chrome::kChromeUIUserActionsHost) -// return &NewWebUI<UserActionsUI>; + if (url.host_piece() == chrome::kChromeUIUserActionsHost) + return &NewWebUI<UserActionsUI>; + + if (url.host_piece() == chrome::kChromeUIDeviceLogHost) + return &NewWebUI<chromeos::DeviceLogUI>; + + if (url.host_piece() == chrome::kChromeUIVersionQtHost) + return &NewWebUI<VersionUIQt>; + // if (url.host_piece() == chrome::kChromeUIInspectHost) // return &NewWebUI<InspectUI>; // @@ -150,21 +121,17 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI *web_ui, Profile *profile, co // if (url.host_piece() == chrome::kChromeUIExtensionsFrameHost) // return &NewWebUI<extensions::ExtensionsUI>; //#endif -//#if BUILDFLAG(ENABLE_PLUGINS) -// if (url.host_piece() == chrome::kChromeUIFlashHost) -// return &NewWebUI<FlashUI>; -//#endif //#if BUILDFLAG(ENABLE_PRINT_PREVIEW) // if (url.host_piece() == chrome::kChromeUIPrintHost && // !profile->GetPrefs()->GetBoolean(prefs::kPrintPreviewDisabled)) { // return &NewWebUI<PrintPreviewUI>; // } //#endif -//#if BUILDFLAG(ENABLE_WEBRTC) -// if (url.host_piece() == chrome::kChromeUIWebRtcLogsHost) -// return &NewWebUI<WebRtcLogsUI>; -//#endif -#if defined(OS_LINUX) || defined(OS_ANDROID) +#if QT_CONFIG(webengine_webrtc) && QT_CONFIG(webengine_extensions) + if (url.host_piece() == chrome::kChromeUIWebRtcLogsHost) + return &NewWebUI<WebRtcLogsUI>; +#endif +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) if (url.host_piece() == chrome::kChromeUISandboxHost) return &NewWebUI<SandboxInternalsUI>; #endif @@ -175,24 +142,19 @@ WebUIFactoryFunction GetWebUIFactoryFunction(WebUI *web_ui, Profile *profile, co namespace QtWebEngineCore { -WebUI::TypeID WebUIControllerFactoryQt::GetWebUIType(content::BrowserContext *browser_context, const GURL &url) const +WebUI::TypeID WebUIControllerFactoryQt::GetWebUIType(content::BrowserContext *browser_context, const GURL &url) { Profile *profile = Profile::FromBrowserContext(browser_context); WebUIFactoryFunction function = GetWebUIFactoryFunction(nullptr, profile, url); return function ? reinterpret_cast<WebUI::TypeID>(function) : WebUI::kNoWebUI; } -bool WebUIControllerFactoryQt::UseWebUIForURL(content::BrowserContext *browser_context, const GURL &url) const +bool WebUIControllerFactoryQt::UseWebUIForURL(content::BrowserContext *browser_context, const GURL &url) { return GetWebUIType(browser_context, url) != WebUI::kNoWebUI; } -bool WebUIControllerFactoryQt::UseWebUIBindingsForURL(content::BrowserContext *browser_context, const GURL &url) const -{ - return UseWebUIForURL(browser_context, url); -} - -std::unique_ptr<WebUIController> WebUIControllerFactoryQt::CreateWebUIControllerForURL(WebUI *web_ui, const GURL &url) const +std::unique_ptr<WebUIController> WebUIControllerFactoryQt::CreateWebUIControllerForURL(WebUI *web_ui, const GURL &url) { Profile *profile = Profile::FromWebUI(web_ui); WebUIFactoryFunction function = GetWebUIFactoryFunction(web_ui, profile, url); diff --git a/src/core/net/webui_controller_factory_qt.h b/src/core/net/webui_controller_factory_qt.h index 4038e6538..22219dd5a 100644 --- a/src/core/net/webui_controller_factory_qt.h +++ b/src/core/net/webui_controller_factory_qt.h @@ -1,66 +1,21 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef WEB_UI_CONTROLLER_FACTORY_QT_H_ #define WEB_UI_CONTROLLER_FACTORY_QT_H_ -#include "base/macros.h" #include "base/memory/singleton.h" -#include "components/favicon_base/favicon_callback.h" #include "content/public/browser/web_ui.h" #include "content/public/browser/web_ui_controller_factory.h" -#include "ui/base/layout.h" - -class Profile; - -namespace base { -class RefCountedMemory; -} namespace QtWebEngineCore { -class WebUIControllerFactoryQt : public content::WebUIControllerFactory { +class WebUIControllerFactoryQt : public content::WebUIControllerFactory +{ public: - content::WebUI::TypeID GetWebUIType(content::BrowserContext *browserContext, const GURL &url) const override; - bool UseWebUIForURL(content::BrowserContext *browserContext, const GURL &url) const override; - bool UseWebUIBindingsForURL(content::BrowserContext *browserContext, const GURL &url) const override; - std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(content::WebUI *webUi, const GURL &url) const override; + content::WebUI::TypeID GetWebUIType(content::BrowserContext *browserContext, const GURL &url) override; + bool UseWebUIForURL(content::BrowserContext *browserContext, const GURL &url) override; + std::unique_ptr<content::WebUIController> CreateWebUIControllerForURL(content::WebUI *webUi, const GURL &url) override; static WebUIControllerFactoryQt *GetInstance(); @@ -70,10 +25,8 @@ protected: private: friend struct base::DefaultSingletonTraits<WebUIControllerFactoryQt>; - - DISALLOW_COPY_AND_ASSIGN(WebUIControllerFactoryQt); }; } // namespace QtWebEngineCore -#endif // WEB_UI_CONTROLLER_FACTORY_QT_H_ +#endif // WEB_UI_CONTROLLER_FACTORY_QT_H_ |