summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill Burtsev <kirill.burtsev@qt.io>2020-11-03 19:36:18 +0100
committerKirill Burtsev <kirill.burtsev@qt.io>2021-02-08 00:09:29 +0100
commit0185f4114579c818fd50374ff1e7d8c054c01e24 (patch)
tree5fcee73fcfd93c2979c2abbea5afc7ee5565a661
parenta4fce092cbd877560dd75b38a2e495aaa75f60f3 (diff)
Resolve installed interceptors right before interception point
Ammends a05bb73747. Since request processing is asynchronous, resolve interceptors right before actual interception point to accommodate cases where interceptor on profile or page was replaced or uninstalled before all instantiated InterceptedRequest's are processed. Effectively, this doesn't send url requests to uninstalled interceptors. Fixes: QTBUG-86286 Change-Id: Iaf55e5ef99d62b55f7304ee68a0c89a1469fd86f Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--src/core/content_browser_client_qt.cpp34
-rw-r--r--src/core/net/proxying_url_loader_factory_qt.cpp202
-rw-r--r--src/core/net/proxying_url_loader_factory_qt.h10
-rw-r--r--tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp127
-rw-r--r--tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp6
5 files changed, 261 insertions, 118 deletions
diff --git a/src/core/content_browser_client_qt.cpp b/src/core/content_browser_client_qt.cpp
index ec2d1156a..5a30a4388 100644
--- a/src/core/content_browser_client_qt.cpp
+++ b/src/core/content_browser_client_qt.cpp
@@ -1270,32 +1270,14 @@ bool ContentBrowserClientQt::WillCreateURLLoaderFactory(
bool *disable_secure_dns,
network::mojom::URLLoaderFactoryOverridePtr *factory_override)
{
- auto *web_contents = content::WebContents::FromRenderFrameHost(frame);
- ProfileQt *profile = static_cast<ProfileQt *>(browser_context);
-
- QWebEngineUrlRequestInterceptor *profile_interceptor = profile->profileAdapter()->requestInterceptor();
- QWebEngineUrlRequestInterceptor *page_interceptor = nullptr;
-
- if (web_contents) {
- WebContentsAdapterClient *client =
- WebContentsViewQt::from(static_cast<content::WebContentsImpl *>(web_contents)->GetView())->client();
- if (!client)
- return false;
-
- page_interceptor = client->webContentsAdapter()->requestInterceptor();
- }
-
- if (profile_interceptor || page_interceptor) {
- int process_id = type == URLLoaderFactoryType::kNavigation ? 0 : render_process_id;
- auto proxied_receiver = std::move(*factory_receiver);
- mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_url_loader_factory;
- *factory_receiver = pending_url_loader_factory.InitWithNewPipeAndPassReceiver();
- // Will manage its own lifetime
- new ProxyingURLLoaderFactoryQt(process_id, profile_interceptor, page_interceptor, std::move(proxied_receiver),
- std::move(pending_url_loader_factory));
- return true;
- }
- return false;
+ auto adapter = static_cast<ProfileQt *>(browser_context)->profileAdapter();
+ int process_id = type == URLLoaderFactoryType::kNavigation ? 0 : render_process_id;
+ auto proxied_receiver = std::move(*factory_receiver);
+ mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_url_loader_factory;
+ *factory_receiver = pending_url_loader_factory.InitWithNewPipeAndPassReceiver();
+ // Will manage its own lifetime
+ new ProxyingURLLoaderFactoryQt(adapter, process_id, std::move(proxied_receiver), std::move(pending_url_loader_factory));
+ return true;
}
void ContentBrowserClientQt::SiteInstanceGotProcess(content::SiteInstance *site_instance)
diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp
index 6488310c4..c541c17b6 100644
--- a/src/core/net/proxying_url_loader_factory_qt.cpp
+++ b/src/core/net/proxying_url_loader_factory_qt.cpp
@@ -43,6 +43,7 @@
#include "base/bind.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/web_contents.h"
@@ -51,7 +52,9 @@
#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 <QVariant>
@@ -104,11 +107,10 @@ class InterceptedRequest : public network::mojom::URLLoader
, public network::mojom::URLLoaderClient
{
public:
- InterceptedRequest(int process_id, uint64_t request_id, int32_t routing_id, uint32_t options,
+ InterceptedRequest(ProfileAdapter *profile_adapter,
+ int process_id, uint64_t request_id, int32_t routing_id, uint32_t options,
const network::ResourceRequest &request,
const net::MutableNetworkTrafficAnnotationTag &traffic_annotation,
- QWebEngineUrlRequestInterceptor *profile_request_interceptor,
- QWebEngineUrlRequestInterceptor *page_request_interceptor,
mojo::PendingReceiver<network::mojom::URLLoader> loader,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory);
@@ -134,9 +136,8 @@ public:
void PauseReadingBodyFromNet() override;
void ResumeReadingBodyFromNet() override;
-private:
- void InterceptOnUIThread();
- void InterceptOnIOThread(base::WaitableEvent *event);
+ void InterceptOnUIThread(QWebEngineUrlRequestInterceptor *profileInterceptor);
+ void InterceptOnIOThread(base::WaitableEvent *event, QWebEngineUrlRequestInterceptor *profileInterceptor);
void ContinueAfterIntercept();
// This is called when the original URLLoaderClient has a connection error.
@@ -152,12 +153,15 @@ private:
void SendErrorAndCompleteImmediately(int error_code);
+ content::WebContents* webContents();
+ QWebEngineUrlRequestInterceptor* getProfileInterceptor();
+ QWebEngineUrlRequestInterceptor* getPageInterceptor();
+
+ QPointer<ProfileAdapter> profile_adapter_;
const int process_id_;
const uint64_t request_id_;
const int32_t routing_id_;
const uint32_t options_;
- bool input_stream_previously_failed_ = false;
- bool request_was_redirected_ = 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
@@ -168,9 +172,9 @@ private:
const net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
- QWebEngineUrlRequestInfo request_info_;
- QPointer<QWebEngineUrlRequestInterceptor> profile_request_interceptor_;
- QPointer<QWebEngineUrlRequestInterceptor> page_request_interceptor_;
+ static inline void cleanup(QWebEngineUrlRequestInfo *info) { delete info; }
+ QScopedPointer<QWebEngineUrlRequestInfo, InterceptedRequest> 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};
@@ -181,22 +185,20 @@ private:
DISALLOW_COPY_AND_ASSIGN(InterceptedRequest);
};
-InterceptedRequest::InterceptedRequest(int process_id, uint64_t request_id, int32_t routing_id, uint32_t options,
+InterceptedRequest::InterceptedRequest(ProfileAdapter *profile_adapter,
+ int process_id, uint64_t request_id, int32_t routing_id, uint32_t options,
const network::ResourceRequest &request,
const net::MutableNetworkTrafficAnnotationTag &traffic_annotation,
- QWebEngineUrlRequestInterceptor *profile_request_interceptor,
- QWebEngineUrlRequestInterceptor *page_request_interceptor,
mojo::PendingReceiver<network::mojom::URLLoader> loader_receiver,
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory)
- : process_id_(process_id)
+ : profile_adapter_(profile_adapter)
+ , process_id_(process_id)
, request_id_(request_id)
, routing_id_(routing_id)
, options_(options)
, request_(request)
, traffic_annotation_(traffic_annotation)
- , profile_request_interceptor_(profile_request_interceptor)
- , page_request_interceptor_(page_request_interceptor)
, proxied_loader_receiver_(this, std::move(loader_receiver))
, target_client_(std::move(client))
, target_factory_(std::move(target_factory))
@@ -215,109 +217,144 @@ InterceptedRequest::~InterceptedRequest()
weak_factory_.InvalidateWeakPtrs();
}
+content::WebContents* InterceptedRequest::webContents()
+{
+ if (process_id_) {
+ content::RenderFrameHost *frameHost = content::RenderFrameHost::FromID(process_id_, request_.render_frame_id);
+ return content::WebContents::FromRenderFrameHost(frameHost);
+ }
+
+ return content::WebContents::FromFrameTreeNodeId(request_.render_frame_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();
+ WebContentsAdapterClient *client = WebContentsViewQt::from(view)->client();
+ return client->webContentsAdapter()->requestInterceptor();
+ }
+ return nullptr;
+}
+
void InterceptedRequest::Restart()
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- blink::mojom::ResourceType resourceType = blink::mojom::ResourceType(request_.resource_type);
- WebContentsAdapterClient::NavigationType navigationType =
- pageTransitionToNavigationType(ui::PageTransition(request_.transition_type));
+ // 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));
+ auto navigationType = toQt(pageTransitionToNavigationType(ui::PageTransition(request_.transition_type)));
const QUrl originalUrl = toQt(request_.url);
const QUrl initiator = request_.request_initiator.has_value() ? toQt(request_.request_initiator->GetURL()) : QUrl();
- content::WebContents *webContents = nullptr;
- if (process_id_) {
- content::RenderFrameHost *frameHost = content::RenderFrameHost::FromID(process_id_, request_.render_frame_id);
- webContents = content::WebContents::FromRenderFrameHost(frameHost);
- } else {
- webContents = content::WebContents::FromFrameTreeNodeId(request_.render_frame_id);
- }
-
- GURL top_document_url = webContents ? webContents->GetVisibleURL() : GURL();
+ 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.RepresentativeUrl()); // m_topDocumentUrl can be empty for the main-frame.
- QWebEngineUrlRequestInfoPrivate *infoPrivate =
- new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), toQt(navigationType), originalUrl, firstPartyUrl,
- initiator, QByteArray::fromStdString(request_.method));
- request_info_ = QWebEngineUrlRequestInfo(infoPrivate);
+ auto info = new QWebEngineUrlRequestInfoPrivate(resourceType, navigationType, originalUrl, firstPartyUrl,
+ initiator, QByteArray::fromStdString(request_.method));
+ Q_ASSERT(!request_info_);
+ request_info_.reset(new QWebEngineUrlRequestInfo(info));
// TODO: remove for Qt6
- if (profile_request_interceptor_ && profile_request_interceptor_->property("deprecated").toBool()) {
+ bool isDeprecatedProfileInterceptor = profileInterceptor == nullptr;
+ if (profileInterceptor && profileInterceptor->property("deprecated").toBool()) {
+ isDeprecatedProfileInterceptor = true;
// sync call supports depracated call of an interceptor on io thread
base::WaitableEvent event;
base::PostTask(FROM_HERE, { content::BrowserThread::IO },
- base::BindOnce(&InterceptedRequest::InterceptOnIOThread, base::Unretained(this), &event));
+ base::BindOnce(&InterceptedRequest::InterceptOnIOThread,
+ base::Unretained(this), &event, profileInterceptor));
event.Wait();
- if (request_info_.changed()) {
+ if (request_info_->changed()) {
ContinueAfterIntercept();
return;
}
}
- InterceptOnUIThread();
+ InterceptOnUIThread(isDeprecatedProfileInterceptor ? nullptr : profileInterceptor);
ContinueAfterIntercept();
}
-void InterceptedRequest::InterceptOnIOThread(base::WaitableEvent *event)
+void InterceptedRequest::InterceptOnIOThread(base::WaitableEvent *event, QWebEngineUrlRequestInterceptor *interceptor)
{
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
- if (profile_request_interceptor_)
- profile_request_interceptor_->interceptRequest(request_info_);
+ interceptor->interceptRequest(*request_info_);
event->Signal();
}
-void InterceptedRequest::InterceptOnUIThread()
+void InterceptedRequest::InterceptOnUIThread(QWebEngineUrlRequestInterceptor *profileInterceptor)
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (profile_request_interceptor_ && !profile_request_interceptor_->property("deprecated").toBool())
- profile_request_interceptor_->interceptRequest(request_info_);
+ if (profileInterceptor)
+ profileInterceptor->interceptRequest(*request_info_);
- if (!request_info_.changed() && page_request_interceptor_)
- page_request_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_.changed()) {
- QWebEngineUrlRequestInfoPrivate &info = *request_info_.d_ptr;
- if (info.shouldBlockRequest)
- return SendErrorAndCompleteImmediately(net::ERR_BLOCKED_BY_CLIENT);
-
- for (auto header = info.extraHeaders.constBegin(); header != info.extraHeaders.constEnd(); ++header) {
- std::string h = header.key().toStdString();
- if (base::LowerCaseEqualsASCII(h, "referer")) {
- request_.referrer = GURL(header.value().toStdString());
- } else {
- request_.headers.SetHeader(h, header.value().toStdString());
+ if (request_info_) {
+ // cleanup in scope because of delete this and it's not needed else where after
+ decltype(request_info_) scoped_request_info(request_info_.take());
+ QWebEngineUrlRequestInfoPrivate &info = *scoped_request_info->d_ptr;
+
+ if (info.changed) {
+ if (info.shouldBlockRequest)
+ return SendErrorAndCompleteImmediately(net::ERR_BLOCKED_BY_CLIENT);
+
+ for (auto header = info.extraHeaders.constBegin(); header != info.extraHeaders.constEnd(); ++header) {
+ std::string h = header.key().toStdString();
+ if (base::LowerCaseEqualsASCII(h, "referer")) {
+ request_.referrer = GURL(header.value().toStdString());
+ } else {
+ request_.headers.SetHeader(h, header.value().toStdString());
+ }
}
- }
- 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(request_info_.requestUrl()), base::nullopt,
- false /*insecure_scheme_was_upgraded*/);
-
- // FIXME: Should probably create a new header.
- current_response_->encoded_data_length = 0;
- 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;
- target_client_->OnReceiveRedirect(redirectInfo, std::move(current_response_));
- return;
+ 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), base::nullopt,
+ false /*insecure_scheme_was_upgraded*/);
+
+ // FIXME: Should probably create a new header.
+ current_response_->encoded_data_length = 0;
+ 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;
+ target_client_->OnReceiveRedirect(redirectInfo, std::move(current_response_));
+ return;
+ }
}
}
@@ -340,7 +377,6 @@ void InterceptedRequest::OnReceiveResponse(network::mojom::URLResponseHeadPtr he
void InterceptedRequest::OnReceiveRedirect(const net::RedirectInfo &redirect_info, network::mojom::URLResponseHeadPtr head)
{
// TODO(timvolodine): handle redirect override.
- request_was_redirected_ = true;
current_response_ = head.Clone();
target_client_->OnReceiveRedirect(redirect_info, std::move(head));
request_.url = redirect_info.new_url;
@@ -470,10 +506,10 @@ void InterceptedRequest::SendErrorAndCompleteImmediately(int error_code)
delete this;
}
-ProxyingURLLoaderFactoryQt::ProxyingURLLoaderFactoryQt(int process_id, QWebEngineUrlRequestInterceptor *profile, QWebEngineUrlRequestInterceptor *page,
+ProxyingURLLoaderFactoryQt::ProxyingURLLoaderFactoryQt(ProfileAdapter *adapter, int process_id,
mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
mojo::PendingRemote<network::mojom::URLLoaderFactory> target_factory_info)
- : m_processId(process_id), m_profileRequestInterceptor(profile), m_pageRequestInterceptor(page), m_weakFactory(this)
+ : m_profileAdapter(adapter), m_processId(process_id), m_weakFactory(this)
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (target_factory_info) {
@@ -502,8 +538,8 @@ void ProxyingURLLoaderFactoryQt::CreateLoaderAndStart(mojo::PendingReceiver<netw
m_targetFactory->Clone(target_factory_clone.InitWithNewPipeAndPassReceiver());
// Will manage its own lifetime
- InterceptedRequest *req = new InterceptedRequest(m_processId, request_id, routing_id, options, request, traffic_annotation,
- m_profileRequestInterceptor, m_pageRequestInterceptor, std::move(loader),
+ InterceptedRequest *req = new InterceptedRequest(m_profileAdapter, m_processId, request_id, routing_id, options,
+ request, traffic_annotation, std::move(loader),
std::move(url_loader_client), std::move(target_factory_clone));
req->Restart();
}
diff --git a/src/core/net/proxying_url_loader_factory_qt.h b/src/core/net/proxying_url_loader_factory_qt.h
index 50caa69e9..5345e3220 100644
--- a/src/core/net/proxying_url_loader_factory_qt.h
+++ b/src/core/net/proxying_url_loader_factory_qt.h
@@ -55,15 +55,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-QT_FORWARD_DECLARE_CLASS(QWebEngineUrlRequestInterceptor)
-
namespace QtWebEngineCore {
+class ProfileAdapter;
+
class ProxyingURLLoaderFactoryQt : public network::mojom::URLLoaderFactory
{
public:
- ProxyingURLLoaderFactoryQt(int processId, QWebEngineUrlRequestInterceptor *profile,
- QWebEngineUrlRequestInterceptor *page,
+ ProxyingURLLoaderFactoryQt(ProfileAdapter *adapter, int processId,
mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader_receiver,
mojo::PendingRemote<network::mojom::URLLoaderFactory> pending_target_factory_remote);
@@ -81,11 +80,10 @@ private:
void OnTargetFactoryError();
void OnProxyBindingError();
+ QPointer<ProfileAdapter> m_profileAdapter;
int m_processId;
mojo::ReceiverSet<network::mojom::URLLoaderFactory> m_proxyReceivers;
mojo::Remote<network::mojom::URLLoaderFactory> m_targetFactory;
- QPointer<QWebEngineUrlRequestInterceptor> m_profileRequestInterceptor;
- QPointer<QWebEngineUrlRequestInterceptor> m_pageRequestInterceptor;
base::WeakPtrFactory<ProxyingURLLoaderFactoryQt> m_weakFactory;
DISALLOW_COPY_AND_ASSIGN(ProxyingURLLoaderFactoryQt);
diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
index bf4acec14..4750c8d0e 100644
--- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
+++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp
@@ -76,6 +76,9 @@ private Q_SLOTS:
void initiator();
void jsServiceWorker_data();
void jsServiceWorker();
+ void replaceInterceptor_data();
+ void replaceInterceptor();
+ void replaceOnIntercept();
};
tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor()
@@ -128,6 +131,7 @@ public:
QUrl redirectUrl;
QMap<QUrl, QSet<QUrl>> requestInitiatorUrls;
QMap<QByteArray, QByteArray> headers;
+ std::function<bool (QWebEngineUrlRequestInfo &)> onIntercept;
void interceptRequest(QWebEngineUrlRequestInfo &info) override
{
@@ -139,6 +143,9 @@ public:
if (info.requestUrl().scheme() == QLatin1String("blob"))
return;
+ if (onIntercept && !onIntercept(info))
+ return;
+
bool block = info.requestMethod() != QByteArrayLiteral("GET");
bool redirect = shouldRedirect && info.requestUrl() != redirectUrl;
@@ -800,5 +807,125 @@ void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker()
QVERIFY(server.stop());
}
+void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor_data()
+{
+ QTest::addColumn<bool>("firstInterceptIsInPage");
+ QTest::addColumn<bool>("keepInterceptionPoint");
+ QTest::newRow("page") << true << true;
+ QTest::newRow("page-profile") << true << false;
+ QTest::newRow("profile") << false << true;
+ QTest::newRow("profile-page") << false << false;
+}
+
+void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor()
+{
+ QFETCH(bool, firstInterceptIsInPage);
+ QFETCH(bool, keepInterceptionPoint);
+
+ HttpServer server;
+ server.setResourceDirs({ ":/resources" });
+ QVERIFY(server.start());
+
+ QWebEngineProfile profile;
+ profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
+ QWebEnginePage page(&profile);
+ QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
+ bool fetchFinished = false;
+
+ auto setInterceptor = [&] (QWebEngineUrlRequestInterceptor *interceptor, bool interceptInPage) {
+ interceptInPage ? page.setUrlRequestInterceptor(interceptor) : profile.setUrlRequestInterceptor(interceptor);
+ };
+
+ std::vector<TestRequestInterceptor> interceptors(3);
+ std::vector<int> requestsOnReplace;
+ setInterceptor(&interceptors.front(), firstInterceptIsInPage);
+
+ auto sc = connect(&page, &QWebEnginePage::loadFinished, [&] () {
+ auto currentInterceptorIndex = requestsOnReplace.size();
+ requestsOnReplace.push_back(interceptors[currentInterceptorIndex].requestInfos.size());
+
+ bool isFirstReinstall = currentInterceptorIndex == 0;
+ bool interceptInPage = keepInterceptionPoint ? firstInterceptIsInPage : (isFirstReinstall ^ firstInterceptIsInPage);
+ setInterceptor(&interceptors[++currentInterceptorIndex], interceptInPage);
+ if (!keepInterceptionPoint)
+ setInterceptor(nullptr, !interceptInPage);
+
+ if (isFirstReinstall) {
+ page.triggerAction(QWebEnginePage::Reload);
+ } else {
+ page.runJavaScript("fetch('http://nonexistent.invalid').catch(() => {})", [&, interceptInPage] (const QVariant &) {
+ requestsOnReplace.push_back(interceptors.back().requestInfos.size());
+ setInterceptor(nullptr, interceptInPage);
+ fetchFinished = true;
+ });
+ }
+ });
+
+ page.setUrl(server.url("/favicon.html"));
+ QTRY_COMPARE(spy.count(), 2);
+ QTRY_VERIFY(fetchFinished);
+
+ QString s; QDebug d(&s);
+ for (auto i = 0u; i < interceptors.size(); ++i) {
+ auto &&interceptor = interceptors[i];
+ auto &&requests = interceptor.requestInfos;
+ d << "\nInterceptor [" << i << "] with" << requestsOnReplace[i] << "requests on replace and" << requests.size() << "in the end:";
+ for (int j = 0; j < requests.size(); ++j) {
+ auto &&r = requests[j];
+ d << "\n\t" << j << "| url:" << r.requestUrl << "firstPartyUrl:" << r.firstPartyUrl;
+ }
+ QVERIFY2(!requests.isEmpty(), qPrintable(s));
+ QVERIFY2(requests.size() == requestsOnReplace[i], qPrintable(s));
+ }
+}
+
+void tst_QWebEngineUrlRequestInterceptor::replaceOnIntercept()
+{
+ HttpServer server;
+ server.setResourceDirs({ ":/resources" });
+ QVERIFY(server.start());
+
+ QWebEngineProfile profile;
+ profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
+ QWebEnginePage page(&profile);
+ QSignalSpy spy(&page, SIGNAL(loadFinished(bool)));
+
+ struct Interceptor : QWebEngineUrlRequestInterceptor {
+ Interceptor(const std::function<void ()> &a) : action(a) { }
+ void interceptRequest(QWebEngineUrlRequestInfo &) override { action(); }
+ std::function<void ()> action;
+ int interceptRequestReceived = 0;
+ };
+
+ TestRequestInterceptor profileInterceptor, pageInterceptor1, pageInterceptor2;
+ page.setUrlRequestInterceptor(&pageInterceptor1);
+ profile.setUrlRequestInterceptor(&profileInterceptor);
+ profileInterceptor.onIntercept = [&] (QWebEngineUrlRequestInfo &) {
+ page.setUrlRequestInterceptor(&pageInterceptor2);
+ return true;
+ };
+
+ page.setUrl(server.url("/favicon.html"));
+ QTRY_COMPARE(spy.count(), 1);
+ QTRY_COMPARE(profileInterceptor.requestInfos.size(), 2);
+
+ // if interceptor for page was replaced on intercept call in profile then, since request first
+ // comes to profile, forward to page's interceptor should land to second one
+ QCOMPARE(pageInterceptor1.requestInfos.size(), 0);
+ QCOMPARE(profileInterceptor.requestInfos.size(), pageInterceptor2.requestInfos.size());
+
+ page.setUrlRequestInterceptor(&pageInterceptor1);
+ bool fetchFinished = false;
+ page.runJavaScript("fetch('http://nonexistent.invalid').catch(() => {})", [&] (const QVariant &) {
+ page.setUrlRequestInterceptor(&pageInterceptor2);
+ fetchFinished = true;
+ });
+
+ QTRY_VERIFY(fetchFinished);
+ QCOMPARE(profileInterceptor.requestInfos.size(), 3);
+ QCOMPARE(pageInterceptor1.requestInfos.size(), 0);
+ QCOMPARE(profileInterceptor.requestInfos.size(), pageInterceptor2.requestInfos.size());
+}
+
QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor)
#include "tst_qwebengineurlrequestinterceptor.moc"
diff --git a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
index f4c0484eb..1b0f95064 100644
--- a/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
+++ b/tests/auto/widgets/qwebengineprofile/tst_qwebengineprofile.cpp
@@ -514,7 +514,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerStreaming()
view.setPage(new QWebEnginePage(&profile, &view));
view.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
view.load(QUrl(QStringLiteral("stream://whatever")));
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000);
QByteArray result;
result.append(1000, 'c');
QCOMPARE(toPlainTextSync(view.page()), QString::fromLatin1(result));
@@ -575,7 +575,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerRequestHeaders()
QWebEnginePage page(&profile);
QSignalSpy loadFinishedSpy(&page, SIGNAL(loadFinished(bool)));
page.load(QUrl(QStringLiteral("myscheme://whatever")));
- QVERIFY(loadFinishedSpy.wait());
+ QTRY_COMPARE_WITH_TIMEOUT(loadFinishedSpy.count(), 1, 30000);
}
void tst_QWebEngineProfile::urlSchemeHandlerInstallation()
@@ -707,7 +707,7 @@ void tst_QWebEngineProfile::urlSchemeHandlerXhrStatus()
profile.installUrlSchemeHandler("aviancarrier", &handler);
page.setWebChannel(&channel);
page.load(QUrl("aviancarrier:/"));
- QTRY_VERIFY(host.isReady());
+ QTRY_VERIFY_WITH_TIMEOUT(host.isReady(), 30000);
host.load(QUrl("aviancarrier:/ok"));
host.load(QUrl("aviancarrier:/redirect"));
host.load(QUrl("aviancarrier:/fail"));