1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtWebEngine module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "profile_adapter.h"
41 
42 #include "content/browser/web_contents/web_contents_impl.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/browsing_data_remover.h"
45 #include "content/public/browser/download_manager.h"
46 #include "content/public/browser/shared_cors_origin_access_list.h"
47 #include "content/public/browser/storage_partition.h"
48 #include "services/network/public/cpp/cors/origin_access_list.h"
49 #include "url/url_util.h"
50 
51 #include "api/qwebengineurlscheme.h"
52 #include "content_browser_client_qt.h"
53 #include "download_manager_delegate_qt.h"
54 #include "permission_manager_qt.h"
55 #include "profile_adapter_client.h"
56 #include "profile_io_data_qt.h"
57 #include "profile_qt.h"
58 #include "renderer_host/user_resource_controller_host.h"
59 #include "type_conversion.h"
60 #include "visited_links_manager_qt.h"
61 #include "web_engine_context.h"
62 #include "web_contents_adapter_client.h"
63 
64 #include "base/files/file_util.h"
65 #include "base/time/time_to_iso8601.h"
66 #include "components/keyed_service/content/browser_context_dependency_manager.h"
67 
68 #if BUILDFLAG(ENABLE_EXTENSIONS)
69 #include "extensions/browser/extension_system.h"
70 #endif
71 
72 #include <QCoreApplication>
73 #include <QDir>
74 #include <QString>
75 #include <QStandardPaths>
76 
77 namespace {
buildLocationFromStandardPath(const QString & standardPath,const QString & name)78 inline QString buildLocationFromStandardPath(const QString &standardPath, const QString &name) {
79     QString location = standardPath;
80     if (location.isEmpty())
81         location = QDir::homePath() % QLatin1String("/.") % QCoreApplication::applicationName();
82 
83     location.append(QLatin1String("/QtWebEngine/") % name);
84     return location;
85 }
86 }
87 
88 namespace QtWebEngineCore {
89 
ProfileAdapter(const QString & storageName)90 ProfileAdapter::ProfileAdapter(const QString &storageName):
91       m_name(storageName)
92     , m_offTheRecord(storageName.isEmpty())
93     , m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))
94     , m_httpCacheType(DiskHttpCache)
95     , m_persistentCookiesPolicy(AllowPersistentCookies)
96     , m_visitedLinksPolicy(TrackVisitedLinksOnDisk)
97     , m_httpCacheMaxSize(0)
98 {
99     WebEngineContext::current()->addProfileAdapter(this);
100     // creation of profile requires webengine context
101     m_profile.reset(new ProfileQt(this));
102     content::BrowserContext::Initialize(m_profile.data(), toFilePath(dataPath()));
103     // fixme: this should not be here
104     m_profile->m_profileIOData->initializeOnUIThread();
105     m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler);
106 #if BUILDFLAG(ENABLE_EXTENSIONS)
107     if (!storageName.isEmpty())
108         extensions::ExtensionSystem::Get(m_profile.data())->InitForRegularProfile(true);
109 #endif
110 
111     // Allow XMLHttpRequests from qrc to file.
112     // ### consider removing for Qt6
113     url::Origin qrc = url::Origin::Create(GURL("qrc://"));
114     auto pattern = network::mojom::CorsOriginPattern::New("file", "", 0,
115                                                           network::mojom::CorsDomainMatchMode::kAllowSubdomains,
116                                                           network::mojom::CorsPortMatchMode::kAllowAnyPort,
117                                                           network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
118     std::vector<network::mojom::CorsOriginPatternPtr> list;
119     list.push_back(std::move(pattern));
120     m_profile->GetSharedCorsOriginAccessList()->SetForOrigin(qrc, std::move(list), {}, base::BindOnce([]{}));
121 }
122 
~ProfileAdapter()123 ProfileAdapter::~ProfileAdapter()
124 {
125     while (!m_webContentsAdapterClients.isEmpty()) {
126        m_webContentsAdapterClients.first()->releaseProfile();
127     }
128     WebEngineContext::current()->removeProfileAdapter(this);
129     if (m_downloadManagerDelegate) {
130         m_profile->GetDownloadManager(m_profile.data())->Shutdown();
131         m_downloadManagerDelegate.reset();
132     }
133 #if QT_CONFIG(ssl)
134     delete m_clientCertificateStore;
135 #endif
136 }
137 
setStorageName(const QString & storageName)138 void ProfileAdapter::setStorageName(const QString &storageName)
139 {
140     if (storageName == m_name)
141         return;
142     m_name = storageName;
143     if (!m_offTheRecord) {
144         m_profile->setupPrefService();
145         if (!m_profile->m_profileIOData->isClearHttpCacheInProgress())
146             m_profile->m_profileIOData->resetNetworkContext();
147         if (m_visitedLinksManager)
148             resetVisitedLinksManager();
149     }
150 }
151 
setOffTheRecord(bool offTheRecord)152 void ProfileAdapter::setOffTheRecord(bool offTheRecord)
153 {
154     if (offTheRecord == m_offTheRecord)
155         return;
156     m_offTheRecord = offTheRecord;
157     m_profile->setupPrefService();
158     if (!m_profile->m_profileIOData->isClearHttpCacheInProgress())
159         m_profile->m_profileIOData->resetNetworkContext();
160     if (m_visitedLinksManager)
161         resetVisitedLinksManager();
162 }
163 
profile()164 ProfileQt *ProfileAdapter::profile()
165 {
166     return m_profile.data();
167 }
168 
visitedLinksManager()169 VisitedLinksManagerQt *ProfileAdapter::visitedLinksManager()
170 {
171     if (!m_visitedLinksManager)
172         resetVisitedLinksManager();
173     return m_visitedLinksManager.data();
174 }
175 
downloadManagerDelegate()176 DownloadManagerDelegateQt *ProfileAdapter::downloadManagerDelegate()
177 {
178     if (!m_downloadManagerDelegate)
179         m_downloadManagerDelegate.reset(new DownloadManagerDelegateQt(this));
180     return m_downloadManagerDelegate.data();
181 }
182 
cookieStore()183 QWebEngineCookieStore *ProfileAdapter::cookieStore()
184 {
185     if (!m_cookieStore)
186         m_cookieStore.reset(new QWebEngineCookieStore);
187     return m_cookieStore.data();
188 }
189 
requestInterceptor()190 QWebEngineUrlRequestInterceptor *ProfileAdapter::requestInterceptor()
191 {
192     return m_requestInterceptor.data();
193 }
194 
setRequestInterceptor(QWebEngineUrlRequestInterceptor * interceptor)195 void ProfileAdapter::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor)
196 {
197     m_requestInterceptor = interceptor;
198 }
199 
addClient(ProfileAdapterClient * adapterClient)200 void ProfileAdapter::addClient(ProfileAdapterClient *adapterClient)
201 {
202     m_clients.append(adapterClient);
203 }
204 
removeClient(ProfileAdapterClient * adapterClient)205 void ProfileAdapter::removeClient(ProfileAdapterClient *adapterClient)
206 {
207     m_clients.removeOne(adapterClient);
208 }
209 
cancelDownload(quint32 downloadId)210 void ProfileAdapter::cancelDownload(quint32 downloadId)
211 {
212     downloadManagerDelegate()->cancelDownload(downloadId);
213 }
214 
pauseDownload(quint32 downloadId)215 void ProfileAdapter::pauseDownload(quint32 downloadId)
216 {
217     downloadManagerDelegate()->pauseDownload(downloadId);
218 }
219 
resumeDownload(quint32 downloadId)220 void ProfileAdapter::resumeDownload(quint32 downloadId)
221 {
222     downloadManagerDelegate()->resumeDownload(downloadId);
223 }
224 
removeDownload(quint32 downloadId)225 void ProfileAdapter::removeDownload(quint32 downloadId)
226 {
227     downloadManagerDelegate()->removeDownload(downloadId);
228 }
229 
createDefaultProfileAdapter()230 ProfileAdapter *ProfileAdapter::createDefaultProfileAdapter()
231 {
232     return WebEngineContext::current()->createDefaultProfileAdapter();
233 }
234 
defaultProfileAdapter()235 ProfileAdapter *ProfileAdapter::defaultProfileAdapter()
236 {
237     WebEngineContext *context = WebEngineContext::current();
238     return context ? context->defaultProfileAdapter() : nullptr;
239 }
240 
globalQObjectRoot()241 QObject* ProfileAdapter::globalQObjectRoot()
242 {
243     return WebEngineContext::current()->globalQObject();
244 }
245 
dataPath() const246 QString ProfileAdapter::dataPath() const
247 {
248     if (!m_dataPath.isEmpty())
249         return m_dataPath;
250     // And off-the-record or memory-only profile should not write to disk
251     // but Chromium often creates temporary directories anyway, so given them
252     // a location to do so.
253     QString name = m_name;
254     if (m_offTheRecord)
255         name = QStringLiteral("OffTheRecord");
256     else if (m_name.isEmpty())
257         name = QStringLiteral("UnknownProfile");
258     return buildLocationFromStandardPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation), name);
259 }
260 
setDataPath(const QString & path)261 void ProfileAdapter::setDataPath(const QString &path)
262 {
263     if (m_dataPath == path)
264         return;
265     m_dataPath = path;
266     m_profile->setupPrefService();
267     if (!m_profile->m_profileIOData->isClearHttpCacheInProgress())
268         m_profile->m_profileIOData->resetNetworkContext();
269     if (!m_offTheRecord && m_visitedLinksManager)
270         resetVisitedLinksManager();
271 }
272 
setDownloadPath(const QString & path)273 void ProfileAdapter::setDownloadPath(const QString &path)
274 {
275     m_downloadPath = path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) : path;
276 }
277 
cachePath() const278 QString ProfileAdapter::cachePath() const
279 {
280     if (m_offTheRecord)
281         return QString();
282     if (!m_cachePath.isEmpty())
283         return m_cachePath;
284     if (!m_name.isNull())
285         return buildLocationFromStandardPath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), m_name);
286     return QString();
287 }
288 
setCachePath(const QString & path)289 void ProfileAdapter::setCachePath(const QString &path)
290 {
291     if (m_cachePath == path)
292         return;
293     m_cachePath = path;
294     if (!m_offTheRecord && !m_profile->m_profileIOData->isClearHttpCacheInProgress())
295         m_profile->m_profileIOData->resetNetworkContext();
296 }
297 
httpCachePath() const298 QString ProfileAdapter::httpCachePath() const
299 {
300     if (m_offTheRecord)
301         return QString();
302     QString basePath = cachePath();
303     if (!basePath.isEmpty())
304         return basePath % QLatin1String("/Cache");
305     return QString();
306 }
307 
httpUserAgent() const308 QString ProfileAdapter::httpUserAgent() const
309 {
310     if (m_httpUserAgent.isNull())
311         return QString::fromStdString(ContentBrowserClientQt::getUserAgent());
312     return m_httpUserAgent;
313 }
314 
setHttpUserAgent(const QString & userAgent)315 void ProfileAdapter::setHttpUserAgent(const QString &userAgent)
316 {
317     const QString httpUserAgent = userAgent.simplified();
318     if (m_httpUserAgent == httpUserAgent)
319         return;
320     m_httpUserAgent = httpUserAgent;
321     const std::string stdUserAgent = httpUserAgent.toStdString();
322 
323     std::vector<content::WebContentsImpl *> list = content::WebContentsImpl::GetAllWebContents();
324     for (content::WebContentsImpl *web_contents : list)
325         if (web_contents->GetBrowserContext() == m_profile.data())
326             web_contents->SetUserAgentOverride(blink::UserAgentOverride::UserAgentOnly(stdUserAgent), true);
327 
328     content::BrowserContext::ForEachStoragePartition(
329         m_profile.get(), base::BindRepeating([](const std::string &user_agent, content::StoragePartition *storage_partition) {
330                                                  storage_partition->GetNetworkContext()->SetUserAgent(user_agent);
331                                              }, stdUserAgent));
332 }
333 
httpCacheType() const334 ProfileAdapter::HttpCacheType ProfileAdapter::httpCacheType() const
335 {
336     if (m_httpCacheType == NoCache)
337         return NoCache;
338     if (isOffTheRecord() || httpCachePath().isEmpty())
339         return MemoryHttpCache;
340     return m_httpCacheType;
341 }
342 
setHttpCacheType(ProfileAdapter::HttpCacheType newhttpCacheType)343 void ProfileAdapter::setHttpCacheType(ProfileAdapter::HttpCacheType newhttpCacheType)
344 {
345     ProfileAdapter::HttpCacheType oldCacheType = httpCacheType();
346     m_httpCacheType = newhttpCacheType;
347     if (oldCacheType == httpCacheType())
348         return;
349     if (!m_offTheRecord && !m_profile->m_profileIOData->isClearHttpCacheInProgress()) {
350         m_profile->m_profileIOData->resetNetworkContext();
351         if (m_httpCacheType == NoCache)
352             clearHttpCache();
353     }
354 }
355 
persistentCookiesPolicy() const356 ProfileAdapter::PersistentCookiesPolicy ProfileAdapter::persistentCookiesPolicy() const
357 {
358     if (isOffTheRecord() || m_name.isEmpty())
359         return NoPersistentCookies;
360     return m_persistentCookiesPolicy;
361 }
362 
setPersistentCookiesPolicy(ProfileAdapter::PersistentCookiesPolicy newPersistentCookiesPolicy)363 void ProfileAdapter::setPersistentCookiesPolicy(ProfileAdapter::PersistentCookiesPolicy newPersistentCookiesPolicy)
364 {
365     ProfileAdapter::PersistentCookiesPolicy oldPolicy = persistentCookiesPolicy();
366     m_persistentCookiesPolicy = newPersistentCookiesPolicy;
367     if (oldPolicy == persistentCookiesPolicy())
368         return;
369     if (!m_offTheRecord && !m_profile->m_profileIOData->isClearHttpCacheInProgress())
370         m_profile->m_profileIOData->resetNetworkContext();
371 }
372 
visitedLinksPolicy() const373 ProfileAdapter::VisitedLinksPolicy ProfileAdapter::visitedLinksPolicy() const
374 {
375     if (isOffTheRecord() || m_visitedLinksPolicy == DoNotTrackVisitedLinks)
376         return DoNotTrackVisitedLinks;
377     if (m_name.isEmpty())
378         return TrackVisitedLinksInMemory;
379     return m_visitedLinksPolicy;
380 }
381 
trackVisitedLinks() const382 bool ProfileAdapter::trackVisitedLinks() const
383 {
384     switch (visitedLinksPolicy()) {
385     case DoNotTrackVisitedLinks:
386         return false;
387     default:
388         break;
389     }
390     return true;
391 }
392 
persistVisitedLinks() const393 bool ProfileAdapter::persistVisitedLinks() const
394 {
395     switch (visitedLinksPolicy()) {
396     case DoNotTrackVisitedLinks:
397     case TrackVisitedLinksInMemory:
398         return false;
399     default:
400         break;
401     }
402     return true;
403 }
404 
setVisitedLinksPolicy(ProfileAdapter::VisitedLinksPolicy visitedLinksPolicy)405 void ProfileAdapter::setVisitedLinksPolicy(ProfileAdapter::VisitedLinksPolicy visitedLinksPolicy)
406 {
407     if (m_visitedLinksPolicy == visitedLinksPolicy)
408         return;
409     m_visitedLinksPolicy = visitedLinksPolicy;
410     if (m_visitedLinksManager)
411         resetVisitedLinksManager();
412 }
413 
httpCacheMaxSize() const414 int ProfileAdapter::httpCacheMaxSize() const
415 {
416     return m_httpCacheMaxSize;
417 }
418 
setHttpCacheMaxSize(int maxSize)419 void ProfileAdapter::setHttpCacheMaxSize(int maxSize)
420 {
421     if (m_httpCacheMaxSize == maxSize)
422         return;
423     m_httpCacheMaxSize = maxSize;
424     if (!m_offTheRecord && !m_profile->m_profileIOData->isClearHttpCacheInProgress())
425         m_profile->m_profileIOData->resetNetworkContext();
426 }
427 
428 enum class SchemeType { Protected, Overridable, Custom, Unknown };
schemeType(const QByteArray & canonicalScheme)429 static SchemeType schemeType(const QByteArray &canonicalScheme)
430 {
431     static const QSet<QByteArray> blacklist{
432         QByteArrayLiteral("about"),
433         QByteArrayLiteral("blob"),
434         QByteArrayLiteral("data"),
435         QByteArrayLiteral("javascript"),
436         QByteArrayLiteral("qrc"),
437         // See also kStandardURLSchemes in url/url_util.cc (through url::IsStandard below)
438     };
439 
440     static const QSet<QByteArray> whitelist{
441         QByteArrayLiteral("gopher"),
442     };
443 
444     bool standardSyntax = url::IsStandard(canonicalScheme.data(), url::Component(0, canonicalScheme.size()));
445     bool customScheme = QWebEngineUrlScheme::schemeByName(canonicalScheme) != QWebEngineUrlScheme();
446     bool blacklisted = blacklist.contains(canonicalScheme);
447     bool whitelisted = whitelist.contains(canonicalScheme);
448 
449     if (whitelisted)
450         return SchemeType::Overridable;
451     if (blacklisted || (standardSyntax && !customScheme))
452         return SchemeType::Protected;
453     if (customScheme)
454         return SchemeType::Custom;
455     return SchemeType::Unknown;
456 }
457 
urlSchemeHandler(const QByteArray & scheme)458 QWebEngineUrlSchemeHandler *ProfileAdapter::urlSchemeHandler(const QByteArray &scheme)
459 {
460     return m_customUrlSchemeHandlers.value(scheme.toLower()).data();
461 }
462 
customUrlSchemes() const463 const QList<QByteArray> ProfileAdapter::customUrlSchemes() const
464 {
465     return m_customUrlSchemeHandlers.keys();
466 }
467 
updateCustomUrlSchemeHandlers()468 void ProfileAdapter::updateCustomUrlSchemeHandlers()
469 {
470     content::BrowserContext::ForEachStoragePartition(
471         m_profile.get(), base::BindRepeating([](content::StoragePartition *storage_partition) {
472                                                  storage_partition->ResetURLLoaderFactories();
473                                              }));
474 }
475 
removeUrlSchemeHandler(QWebEngineUrlSchemeHandler * handler)476 void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler)
477 {
478     Q_ASSERT(handler);
479     bool removedOneOrMore = false;
480     auto it = m_customUrlSchemeHandlers.begin();
481     while (it != m_customUrlSchemeHandlers.end()) {
482         if (it.value() == handler) {
483             if (schemeType(it.key()) == SchemeType::Protected) {
484                 qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", it.key().constData());
485                 continue;
486             }
487             it = m_customUrlSchemeHandlers.erase(it);
488             removedOneOrMore = true;
489             continue;
490         }
491         ++it;
492     }
493     if (removedOneOrMore)
494         updateCustomUrlSchemeHandlers();
495 }
496 
removeUrlScheme(const QByteArray & scheme)497 void ProfileAdapter::removeUrlScheme(const QByteArray &scheme)
498 {
499     QByteArray canonicalScheme = scheme.toLower();
500     if (schemeType(canonicalScheme) == SchemeType::Protected) {
501         qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", scheme.constData());
502         return;
503     }
504     if (m_customUrlSchemeHandlers.remove(canonicalScheme))
505         updateCustomUrlSchemeHandlers();
506 }
507 
installUrlSchemeHandler(const QByteArray & scheme,QWebEngineUrlSchemeHandler * handler)508 void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
509 {
510     Q_ASSERT(handler);
511     QByteArray canonicalScheme = scheme.toLower();
512     SchemeType type = schemeType(canonicalScheme);
513 
514     if (type == SchemeType::Protected) {
515         qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData());
516         return;
517     }
518 
519     if (m_customUrlSchemeHandlers.value(canonicalScheme, handler) != handler) {
520         qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData());
521         return;
522     }
523 
524     if (type == SchemeType::Unknown)
525         qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() "
526                  "before installing the custom scheme handler.", scheme.constData());
527 
528     m_customUrlSchemeHandlers.insert(canonicalScheme, handler);
529     updateCustomUrlSchemeHandlers();
530 }
531 
removeAllUrlSchemeHandlers()532 void ProfileAdapter::removeAllUrlSchemeHandlers()
533 {
534     if (m_customUrlSchemeHandlers.size() > 1) {
535         m_customUrlSchemeHandlers.clear();
536         m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler);
537         updateCustomUrlSchemeHandlers();
538     }
539 }
540 
userResourceController()541 UserResourceControllerHost *ProfileAdapter::userResourceController()
542 {
543     if (!m_userResourceController)
544         m_userResourceController.reset(new UserResourceControllerHost);
545     return m_userResourceController.data();
546 }
547 
permissionRequestReply(const QUrl & origin,PermissionType type,PermissionState reply)548 void ProfileAdapter::permissionRequestReply(const QUrl &origin, PermissionType type, PermissionState reply)
549 {
550     static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->permissionRequestReply(origin, type, reply);
551 }
552 
checkPermission(const QUrl & origin,PermissionType type)553 bool ProfileAdapter::checkPermission(const QUrl &origin, PermissionType type)
554 {
555     return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->checkPermission(origin, type);
556 }
557 
httpAcceptLanguageWithoutQualities() const558 QString ProfileAdapter::httpAcceptLanguageWithoutQualities() const
559 {
560     const QStringList list = m_httpAcceptLanguage.split(QLatin1Char(','));
561     QString out;
562     for (const QString &str : list) {
563         if (!out.isEmpty())
564             out.append(QLatin1Char(','));
565         out.append(str.split(QLatin1Char(';')).first());
566     }
567     return out;
568 }
569 
httpAcceptLanguage() const570 QString ProfileAdapter::httpAcceptLanguage() const
571 {
572     return m_httpAcceptLanguage;
573 }
574 
setHttpAcceptLanguage(const QString & httpAcceptLanguage)575 void ProfileAdapter::setHttpAcceptLanguage(const QString &httpAcceptLanguage)
576 {
577     if (m_httpAcceptLanguage == httpAcceptLanguage)
578         return;
579     m_httpAcceptLanguage = httpAcceptLanguage;
580 
581     std::string http_accept_language = httpAcceptLanguageWithoutQualities().toStdString();
582 
583     std::vector<content::WebContentsImpl *> list = content::WebContentsImpl::GetAllWebContents();
584     for (content::WebContentsImpl *web_contents : list) {
585         if (web_contents->GetBrowserContext() == m_profile.data()) {
586             blink::mojom::RendererPreferences *rendererPrefs = web_contents->GetMutableRendererPrefs();
587             rendererPrefs->accept_languages = http_accept_language;
588             web_contents->SyncRendererPrefs();
589         }
590     }
591 
592     content::BrowserContext::ForEachStoragePartition(
593         m_profile.get(), base::BindRepeating([](std::string accept_language, content::StoragePartition *storage_partition) {
594                                                  storage_partition->GetNetworkContext()->SetAcceptLanguage(accept_language);
595                                              }, http_accept_language));
596 }
597 
clearHttpCache()598 void ProfileAdapter::clearHttpCache()
599 {
600     m_profile->m_profileIOData->clearHttpCache();
601 }
602 
setSpellCheckLanguages(const QStringList & languages)603 void ProfileAdapter::setSpellCheckLanguages(const QStringList &languages)
604 {
605 #if QT_CONFIG(webengine_spellchecker)
606     m_profile->prefServiceAdapter().setSpellCheckLanguages(languages);
607 #endif
608 }
609 
spellCheckLanguages() const610 QStringList ProfileAdapter::spellCheckLanguages() const
611 {
612 #if QT_CONFIG(webengine_spellchecker)
613     return m_profile->prefServiceAdapter().spellCheckLanguages();
614 #else
615     return QStringList();
616 #endif
617 }
618 
setSpellCheckEnabled(bool enabled)619 void ProfileAdapter::setSpellCheckEnabled(bool enabled)
620 {
621 #if QT_CONFIG(webengine_spellchecker)
622     m_profile->prefServiceAdapter().setSpellCheckEnabled(enabled);
623 #endif
624 }
625 
isSpellCheckEnabled() const626 bool ProfileAdapter::isSpellCheckEnabled() const
627 {
628 #if QT_CONFIG(webengine_spellchecker)
629     return m_profile->prefServiceAdapter().isSpellCheckEnabled();
630 #else
631     return false;
632 #endif
633 }
634 
addWebContentsAdapterClient(WebContentsAdapterClient * client)635 void ProfileAdapter::addWebContentsAdapterClient(WebContentsAdapterClient *client)
636 {
637     m_webContentsAdapterClients.append(client);
638 }
639 
removeWebContentsAdapterClient(WebContentsAdapterClient * client)640 void ProfileAdapter::removeWebContentsAdapterClient(WebContentsAdapterClient *client)
641 {
642     m_webContentsAdapterClients.removeAll(client);
643 }
644 
resetVisitedLinksManager()645 void ProfileAdapter::resetVisitedLinksManager()
646 {
647     m_visitedLinksManager.reset(new VisitedLinksManagerQt(m_profile.data(), persistVisitedLinks()));
648 }
649 
setUseForGlobalCertificateVerification(bool enable)650 void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable)
651 {
652     if (m_usedForGlobalCertificateVerification == enable)
653         return;
654 
655     static QPointer<ProfileAdapter> profileForglobalCertificateVerification;
656 
657     m_usedForGlobalCertificateVerification = enable;
658     if (enable) {
659         if (profileForglobalCertificateVerification) {
660             profileForglobalCertificateVerification->m_usedForGlobalCertificateVerification = false;
661             if (!m_profile->m_profileIOData->isClearHttpCacheInProgress())
662                 profileForglobalCertificateVerification->m_profile->m_profileIOData->resetNetworkContext();
663             for (auto *client : qAsConst(profileForglobalCertificateVerification->m_clients))
664                 client->useForGlobalCertificateVerificationChanged();
665         }
666         profileForglobalCertificateVerification = this;
667     } else {
668         Q_ASSERT(profileForglobalCertificateVerification);
669         Q_ASSERT(profileForglobalCertificateVerification == this);
670         profileForglobalCertificateVerification = nullptr;
671     }
672 
673     if (!m_profile->m_profileIOData->isClearHttpCacheInProgress())
674         m_profile->m_profileIOData->resetNetworkContext();
675 }
676 
isUsedForGlobalCertificateVerification() const677 bool ProfileAdapter::isUsedForGlobalCertificateVerification() const
678 {
679     return m_usedForGlobalCertificateVerification;
680 }
681 
determineDownloadPath(const QString & downloadDirectory,const QString & suggestedFilename,const time_t & startTime)682 QString ProfileAdapter::determineDownloadPath(const QString &downloadDirectory, const QString &suggestedFilename, const time_t &startTime)
683 {
684     QFileInfo suggestedFile(QDir(downloadDirectory).absoluteFilePath(suggestedFilename));
685     QString suggestedFilePath = suggestedFile.absoluteFilePath();
686     base::FilePath tmpFilePath(toFilePath(suggestedFilePath).NormalizePathSeparatorsTo('/'));
687 
688     int uniquifier = base::GetUniquePathNumber(tmpFilePath);
689     if (uniquifier > 0)
690         suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier)).AsUTF8Unsafe());
691     else if (uniquifier == -1) {
692         base::Time::Exploded exploded;
693         base::Time::FromTimeT(startTime).LocalExplode(&exploded);
694         std::string suffix = base::StringPrintf(
695                     " - %04d-%02d-%02dT%02d%02d%02d.%03d", exploded.year, exploded.month,
696                     exploded.day_of_month, exploded.hour, exploded.minute,
697                     exploded.second, exploded.millisecond);
698         suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(suffix).AsUTF8Unsafe());
699     }
700     return suggestedFilePath;
701 }
702 
703 #if QT_CONFIG(ssl)
clientCertificateStore()704 QWebEngineClientCertificateStore *ProfileAdapter::clientCertificateStore()
705 {
706     if (!m_clientCertificateStore)
707         m_clientCertificateStore = new QWebEngineClientCertificateStore(m_profile->m_profileIOData->clientCertificateStoreData());
708     return m_clientCertificateStore;
709 }
710 #endif
711 
712 } // namespace QtWebEngineCore
713