/**************************************************************************** ** ** 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" namespace QtWebEngineCore { WebContentsAdapterClient::NavigationType pageTransitionToNavigationType(ui::PageTransition transition) { 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 >= 0 && resourceType < content::ResourceType(QWebEngineUrlRequestInfo::ResourceTypeLast)) return static_cast(resourceType); return QWebEngineUrlRequestInfo::ResourceTypeUnknown; } static content::ResourceType fromQt(QWebEngineUrlRequestInfo::ResourceType resourceType) { return static_cast(resourceType); } static QWebEngineUrlRequestInfo::NavigationType toQt(WebContentsAdapterClient::NavigationType navigationType) { return static_cast(navigationType); } // Notifies WebContentsAdapterClient of a new URLRequest. class URLRequestNotification { public: URLRequestNotification(net::URLRequest *request, bool isMainFrameRequest, GURL *newUrl, QWebEngineUrlRequestInfo &&requestInfo, content::ResourceRequestInfo::WebContentsGetter webContentsGetter, net::CompletionOnceCallback callback) : m_request(request) , m_isMainFrameRequest(isMainFrameRequest) , m_newUrl(newUrl) , m_originalUrl(requestInfo.requestUrl()) , m_requestInfo(std::move(requestInfo)) , m_webContentsGetter(webContentsGetter) , m_callback(std::move(callback)) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); m_request->SetUserData(UserData::key, std::make_unique(this)); base::PostTaskWithTraits( FROM_HERE, {content::BrowserThread::UI}, base::BindOnce(&URLRequestNotification::notify, base::Unretained(this))); } private: // Calls cancel() when the URLRequest is destroyed. class UserData : public base::SupportsUserData::Data { public: UserData(URLRequestNotification *ptr) : m_ptr(ptr) {} ~UserData() { m_ptr->cancel(); } static const char key[]; private: URLRequestNotification *m_ptr; }; void cancel() { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); // May run concurrently with notify() but we only touch m_request here. m_request = nullptr; } void notify() { DCHECK_CURRENTLY_ON(content::BrowserThread::UI); // May run concurrently with cancel() so no peeking at m_request here. int error = net::OK; content::WebContents *webContents = m_webContentsGetter.Run(); if (webContents) { WebContentsAdapterClient *client = WebContentsViewQt::from(static_cast(webContents)->GetView())->client(); client->interceptRequest(m_requestInfo); if (m_requestInfo.changed()) { error = m_requestInfo.d_ptr->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; // We handle the rest of the changes later when we are back in I/O thread } // Only do navigationRequested on MAIN_FRAME and SUB_FRAME resources if (error == net::OK && content::IsResourceTypeFrame(fromQt(m_requestInfo.resourceType()))) { int navigationRequestAction = WebContentsAdapterClient::AcceptRequest; client->navigationRequested(m_requestInfo.navigationType(), m_requestInfo.requestUrl(), navigationRequestAction, m_isMainFrameRequest); error = net::ERR_FAILED; switch (static_cast(navigationRequestAction)) { case WebContentsAdapterClient::AcceptRequest: error = net::OK; break; case WebContentsAdapterClient::IgnoreRequest: error = net::ERR_ABORTED; break; } DCHECK(error != net::ERR_FAILED); } } // Run the callback on the IO thread. base::PostTaskWithTraits( FROM_HERE, {content::BrowserThread::IO}, base::BindOnce(&URLRequestNotification::complete, base::Unretained(this), error)); } void complete(int error) { DCHECK_CURRENTLY_ON(content::BrowserThread::IO); if (m_request) { if (m_requestInfo.changed()) { if (m_originalUrl != m_requestInfo.d_ptr->url) *m_newUrl = toGurl(m_requestInfo.d_ptr->url); if (!m_requestInfo.d_ptr->extraHeaders.isEmpty()) { auto end = m_requestInfo.d_ptr->extraHeaders.constEnd(); for (auto header = m_requestInfo.d_ptr->extraHeaders.constBegin(); header != end; ++header) m_request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); } } if (m_request->status().status() != net::URLRequestStatus::CANCELED) std::move(m_callback).Run(error); m_request->RemoveUserData(UserData::key); } delete this; } ~URLRequestNotification() {} net::URLRequest *m_request; bool m_isMainFrameRequest; GURL *m_newUrl; const QUrl m_originalUrl; QWebEngineUrlRequestInfo m_requestInfo; content::ResourceRequestInfo::WebContentsGetter m_webContentsGetter; net::CompletionOnceCallback m_callback; }; const char URLRequestNotification::UserData::key[] = "QtWebEngineCore::URLRequestNotification"; NetworkDelegateQt::NetworkDelegateQt(ProfileIODataQt *data) : m_profileIOData(data) { } int NetworkDelegateQt::OnBeforeURLRequest(net::URLRequest *request, net::CompletionOnceCallback callback, GURL *newUrl) { Q_ASSERT(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); Q_ASSERT(m_profileIOData); const content::ResourceRequestInfo *resourceInfo = content::ResourceRequestInfo::ForRequest(request); content::ResourceType resourceType = content::RESOURCE_TYPE_LAST_TYPE; 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::RESOURCE_TYPE_SUB_FRAME) firstPartyUrl = toQt(request->first_party_url()); else firstPartyUrl = toQt(request->site_for_cookies()); QWebEngineUrlRequestInfoPrivate *infoPrivate = new QWebEngineUrlRequestInfoPrivate(toQt(resourceType), toQt(navigationType), qUrl, firstPartyUrl, QByteArray::fromStdString(request->method())); QWebEngineUrlRequestInfo requestInfo(infoPrivate); if (QWebEngineUrlRequestInterceptor* interceptor = m_profileIOData->acquireInterceptor()) { interceptor->interceptRequest(requestInfo); m_profileIOData->releaseInterceptor(); if (requestInfo.changed()) { int result = infoPrivate->shouldBlockRequest ? net::ERR_BLOCKED_BY_CLIENT : net::OK; if (qUrl != infoPrivate->url) *newUrl = toGurl(infoPrivate->url); if (!infoPrivate->extraHeaders.isEmpty()) { auto end = infoPrivate->extraHeaders.constEnd(); for (auto header = infoPrivate->extraHeaders.constBegin(); header != end; ++header) request->SetExtraRequestHeaderByName(header.key().toStdString(), header.value().toStdString(), /* overwrite */ true); } if (result != net::OK) return result; requestInfo.resetChanged(); } } else m_profileIOData->releaseInterceptor(); if (!resourceInfo) return net::OK; if (!m_profileIOData->hasPageInterceptors() && !content::IsResourceTypeFrame(resourceType)) return net::OK; auto webContentsGetter = resourceInfo->GetWebContentsGetterForRequest(); new URLRequestNotification( request, resourceInfo->IsMainFrame(), newUrl, std::move(requestInfo), webContentsGetter, std::move(callback) ); // 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::OnCanEnablePrivacyMode(const GURL &url, const GURL &site_for_cookies) const { 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*, 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::OnAreExperimentalCookieFeaturesEnabled() const { return false; } 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 origins, base::OnceCallback)> result_callback) const { std::move(result_callback).Run(std::set()); } 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