blob: bccdf1ada778ca929e4fda3f28e1953bb45a571f [file] [log] [blame]
/****************************************************************************
**
** 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 "profile_adapter.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/browsing_data_remover.h"
#include "content/public/browser/download_manager.h"
#include "api/qwebengineurlscheme.h"
#include "content_browser_client_qt.h"
#include "download_manager_delegate_qt.h"
#include "net/url_request_context_getter_qt.h"
#include "permission_manager_qt.h"
#include "profile_adapter_client.h"
#include "profile_qt.h"
#include "renderer_host/user_resource_controller_host.h"
#include "type_conversion.h"
#include "visited_links_manager_qt.h"
#include "web_engine_context.h"
#include "web_contents_adapter_client.h"
#include "base/files/file_util.h"
#include "base/time/time_to_iso8601.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h"
#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/browser/extension_system.h"
#endif
#include <QCoreApplication>
#include <QDir>
#include <QString>
#include <QStandardPaths>
namespace {
inline QString buildLocationFromStandardPath(const QString &standardPath, const QString &name) {
QString location = standardPath;
if (location.isEmpty())
location = QDir::homePath() % QLatin1String("/.") % QCoreApplication::applicationName();
location.append(QLatin1String("/QtWebEngine/") % name);
return location;
}
}
namespace QtWebEngineCore {
ProfileAdapter::ProfileAdapter(const QString &storageName):
m_name(storageName)
, m_offTheRecord(storageName.isEmpty())
, m_downloadPath(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))
, m_httpCacheType(DiskHttpCache)
, m_persistentCookiesPolicy(AllowPersistentCookies)
, m_visitedLinksPolicy(TrackVisitedLinksOnDisk)
, m_httpCacheMaxSize(0)
, m_pageRequestInterceptors(0)
{
WebEngineContext::current()->addProfileAdapter(this);
// creation of profile requires webengine context
m_profile.reset(new ProfileQt(this));
content::BrowserContext::Initialize(m_profile.data(), toFilePath(dataPath()));
// fixme: this should not be here
m_profile->m_profileIOData->initializeOnUIThread();
m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler);
#if BUILDFLAG(ENABLE_EXTENSIONS)
if (!storageName.isEmpty())
extensions::ExtensionSystem::Get(m_profile.data())->InitForRegularProfile(true);
#endif
}
ProfileAdapter::~ProfileAdapter()
{
while (!m_webContentsAdapterClients.isEmpty()) {
m_webContentsAdapterClients.first()->releaseProfile();
}
WebEngineContext::current()->removeProfileAdapter(this);
if (m_downloadManagerDelegate) {
m_profile->GetDownloadManager(m_profile.data())->Shutdown();
m_downloadManagerDelegate.reset();
}
#if QT_CONFIG(ssl)
delete m_clientCertificateStore;
#endif
Q_ASSERT(m_pageRequestInterceptors == 0);
}
void ProfileAdapter::setStorageName(const QString &storageName)
{
if (storageName == m_name)
return;
m_name = storageName;
if (!m_offTheRecord) {
m_profile->setupPrefService();
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateStorageSettings();
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
}
void ProfileAdapter::setOffTheRecord(bool offTheRecord)
{
if (offTheRecord == m_offTheRecord)
return;
m_offTheRecord = offTheRecord;
m_profile->setupPrefService();
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateStorageSettings();
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
ProfileQt *ProfileAdapter::profile()
{
return m_profile.data();
}
VisitedLinksManagerQt *ProfileAdapter::visitedLinksManager()
{
if (!m_visitedLinksManager)
resetVisitedLinksManager();
return m_visitedLinksManager.data();
}
DownloadManagerDelegateQt *ProfileAdapter::downloadManagerDelegate()
{
if (!m_downloadManagerDelegate)
m_downloadManagerDelegate.reset(new DownloadManagerDelegateQt(this));
return m_downloadManagerDelegate.data();
}
QWebEngineCookieStore *ProfileAdapter::cookieStore()
{
if (!m_cookieStore)
m_cookieStore.reset(new QWebEngineCookieStore);
return m_cookieStore.data();
}
QWebEngineUrlRequestInterceptor *ProfileAdapter::requestInterceptor()
{
return m_requestInterceptor.data();
}
void ProfileAdapter::setRequestInterceptor(QWebEngineUrlRequestInterceptor *interceptor)
{
if (m_requestInterceptor == interceptor)
return;
if (m_requestInterceptor)
disconnect(m_requestInterceptor, &QObject::destroyed, this, nullptr);
m_requestInterceptor = interceptor;
if (m_requestInterceptor)
connect(m_requestInterceptor, &QObject::destroyed, this, [this] () {
m_profile->m_profileIOData->updateRequestInterceptor();
Q_ASSERT(!m_profile->m_profileIOData->requestInterceptor());
});
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateRequestInterceptor();
}
void ProfileAdapter::addClient(ProfileAdapterClient *adapterClient)
{
m_clients.append(adapterClient);
}
void ProfileAdapter::removeClient(ProfileAdapterClient *adapterClient)
{
m_clients.removeOne(adapterClient);
}
void ProfileAdapter::addPageRequestInterceptor()
{
++m_pageRequestInterceptors;
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateRequestInterceptor();
}
void ProfileAdapter::removePageRequestInterceptor()
{
Q_ASSERT(m_pageRequestInterceptors > 0);
--m_pageRequestInterceptors;
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateRequestInterceptor();
}
void ProfileAdapter::cancelDownload(quint32 downloadId)
{
downloadManagerDelegate()->cancelDownload(downloadId);
}
void ProfileAdapter::pauseDownload(quint32 downloadId)
{
downloadManagerDelegate()->pauseDownload(downloadId);
}
void ProfileAdapter::resumeDownload(quint32 downloadId)
{
downloadManagerDelegate()->resumeDownload(downloadId);
}
void ProfileAdapter::removeDownload(quint32 downloadId)
{
downloadManagerDelegate()->removeDownload(downloadId);
}
ProfileAdapter *ProfileAdapter::createDefaultProfileAdapter()
{
return WebEngineContext::current()->createDefaultProfileAdapter();
}
ProfileAdapter *ProfileAdapter::defaultProfileAdapter()
{
WebEngineContext *context = WebEngineContext::current();
return context ? context->defaultProfileAdapter() : nullptr;
}
QObject* ProfileAdapter::globalQObjectRoot()
{
return WebEngineContext::current()->globalQObject();
}
QString ProfileAdapter::dataPath() const
{
if (m_offTheRecord)
return QString();
if (!m_dataPath.isEmpty())
return m_dataPath;
if (!m_name.isNull())
return buildLocationFromStandardPath(QStandardPaths::writableLocation(QStandardPaths::DataLocation), m_name);
return QString();
}
void ProfileAdapter::setDataPath(const QString &path)
{
if (m_dataPath == path)
return;
m_dataPath = path;
if (!m_offTheRecord) {
m_profile->setupPrefService();
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateStorageSettings();
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
}
void ProfileAdapter::setDownloadPath(const QString &path)
{
m_downloadPath = path.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) : path;
}
QString ProfileAdapter::cachePath() const
{
if (m_offTheRecord)
return QString();
if (!m_cachePath.isEmpty())
return m_cachePath;
if (!m_name.isNull())
return buildLocationFromStandardPath(QStandardPaths::writableLocation(QStandardPaths::CacheLocation), m_name);
return QString();
}
void ProfileAdapter::setCachePath(const QString &path)
{
if (m_cachePath == path)
return;
m_cachePath = path;
if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateHttpCache();
}
QString ProfileAdapter::cookiesPath() const
{
if (m_offTheRecord)
return QString();
QString basePath = dataPath();
if (!basePath.isEmpty()) {
// This is a typo fix. We still need the old path in order to avoid breaking migration.
QDir coookiesFolder(basePath % QLatin1String("/Coookies"));
if (coookiesFolder.exists())
return coookiesFolder.path();
return basePath % QLatin1String("/Cookies");
}
return QString();
}
QString ProfileAdapter::httpCachePath() const
{
if (m_offTheRecord)
return QString();
QString basePath = cachePath();
if (!basePath.isEmpty())
return basePath % QLatin1String("/Cache");
return QString();
}
QString ProfileAdapter::httpUserAgent() const
{
if (m_httpUserAgent.isNull())
return QString::fromStdString(ContentBrowserClientQt::getUserAgent());
return m_httpUserAgent;
}
void ProfileAdapter::setHttpUserAgent(const QString &userAgent)
{
if (m_httpUserAgent == userAgent)
return;
m_httpUserAgent = userAgent.simplified();
std::vector<content::WebContentsImpl *> list = content::WebContentsImpl::GetAllWebContents();
for (content::WebContentsImpl *web_contents : list)
if (web_contents->GetBrowserContext() == m_profile.data())
web_contents->SetUserAgentOverride(m_httpUserAgent.toStdString(), true);
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateUserAgent();
}
ProfileAdapter::HttpCacheType ProfileAdapter::httpCacheType() const
{
if (m_httpCacheType == NoCache)
return NoCache;
if (isOffTheRecord() || httpCachePath().isEmpty())
return MemoryHttpCache;
return m_httpCacheType;
}
void ProfileAdapter::setHttpCacheType(ProfileAdapter::HttpCacheType newhttpCacheType)
{
ProfileAdapter::HttpCacheType oldCacheType = httpCacheType();
m_httpCacheType = newhttpCacheType;
if (oldCacheType == httpCacheType())
return;
if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateHttpCache();
}
ProfileAdapter::PersistentCookiesPolicy ProfileAdapter::persistentCookiesPolicy() const
{
if (isOffTheRecord() || cookiesPath().isEmpty())
return NoPersistentCookies;
return m_persistentCookiesPolicy;
}
void ProfileAdapter::setPersistentCookiesPolicy(ProfileAdapter::PersistentCookiesPolicy newPersistentCookiesPolicy)
{
ProfileAdapter::PersistentCookiesPolicy oldPolicy = persistentCookiesPolicy();
m_persistentCookiesPolicy = newPersistentCookiesPolicy;
if (oldPolicy == persistentCookiesPolicy())
return;
if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateCookieStore();
}
ProfileAdapter::VisitedLinksPolicy ProfileAdapter::visitedLinksPolicy() const
{
if (isOffTheRecord() || m_visitedLinksPolicy == DoNotTrackVisitedLinks)
return DoNotTrackVisitedLinks;
if (dataPath().isEmpty())
return TrackVisitedLinksInMemory;
return m_visitedLinksPolicy;
}
bool ProfileAdapter::trackVisitedLinks() const
{
switch (visitedLinksPolicy()) {
case DoNotTrackVisitedLinks:
return false;
default:
break;
}
return true;
}
bool ProfileAdapter::persistVisitedLinks() const
{
switch (visitedLinksPolicy()) {
case DoNotTrackVisitedLinks:
case TrackVisitedLinksInMemory:
return false;
default:
break;
}
return true;
}
void ProfileAdapter::setVisitedLinksPolicy(ProfileAdapter::VisitedLinksPolicy visitedLinksPolicy)
{
if (m_visitedLinksPolicy == visitedLinksPolicy)
return;
m_visitedLinksPolicy = visitedLinksPolicy;
if (m_visitedLinksManager)
resetVisitedLinksManager();
}
int ProfileAdapter::httpCacheMaxSize() const
{
return m_httpCacheMaxSize;
}
void ProfileAdapter::setHttpCacheMaxSize(int maxSize)
{
if (m_httpCacheMaxSize == maxSize)
return;
m_httpCacheMaxSize = maxSize;
if (!m_offTheRecord && m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateHttpCache();
}
enum class SchemeType { Protected, Overridable, Custom, Unknown };
static SchemeType schemeType(const QByteArray &canonicalScheme)
{
static const QSet<QByteArray> blacklist{
QByteArrayLiteral("about"),
QByteArrayLiteral("blob"),
QByteArrayLiteral("data"),
QByteArrayLiteral("javascript"),
QByteArrayLiteral("qrc"),
// See also kStandardURLSchemes in url/url_util.cc (through url::IsStandard below)
};
static const QSet<QByteArray> whitelist{
QByteArrayLiteral("gopher"),
};
bool standardSyntax = url::IsStandard(canonicalScheme.data(), url::Component(0, canonicalScheme.size()));
bool customScheme = QWebEngineUrlScheme::schemeByName(canonicalScheme) != QWebEngineUrlScheme();
bool blacklisted = blacklist.contains(canonicalScheme);
bool whitelisted = whitelist.contains(canonicalScheme);
if (whitelisted)
return SchemeType::Overridable;
if (blacklisted || (standardSyntax && !customScheme))
return SchemeType::Protected;
if (customScheme)
return SchemeType::Custom;
return SchemeType::Unknown;
}
QWebEngineUrlSchemeHandler *ProfileAdapter::urlSchemeHandler(const QByteArray &scheme)
{
return m_customUrlSchemeHandlers.value(scheme.toLower()).data();
}
const QList<QByteArray> ProfileAdapter::customUrlSchemes() const
{
return m_customUrlSchemeHandlers.keys();
}
void ProfileAdapter::updateCustomUrlSchemeHandlers()
{
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateJobFactory();
}
void ProfileAdapter::removeUrlSchemeHandler(QWebEngineUrlSchemeHandler *handler)
{
Q_ASSERT(handler);
bool removedOneOrMore = false;
auto it = m_customUrlSchemeHandlers.begin();
while (it != m_customUrlSchemeHandlers.end()) {
if (it.value() == handler) {
if (schemeType(it.key()) == SchemeType::Protected) {
qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", it.key().constData());
continue;
}
it = m_customUrlSchemeHandlers.erase(it);
removedOneOrMore = true;
continue;
}
++it;
}
if (removedOneOrMore)
updateCustomUrlSchemeHandlers();
}
void ProfileAdapter::removeUrlScheme(const QByteArray &scheme)
{
QByteArray canonicalScheme = scheme.toLower();
if (schemeType(canonicalScheme) == SchemeType::Protected) {
qWarning("Cannot remove the URL scheme handler for an internal scheme: %s", scheme.constData());
return;
}
if (m_customUrlSchemeHandlers.remove(canonicalScheme))
updateCustomUrlSchemeHandlers();
}
void ProfileAdapter::installUrlSchemeHandler(const QByteArray &scheme, QWebEngineUrlSchemeHandler *handler)
{
Q_ASSERT(handler);
QByteArray canonicalScheme = scheme.toLower();
SchemeType type = schemeType(canonicalScheme);
if (type == SchemeType::Protected) {
qWarning("Cannot install a URL scheme handler overriding internal scheme: %s", scheme.constData());
return;
}
if (m_customUrlSchemeHandlers.value(canonicalScheme, handler) != handler) {
qWarning("URL scheme handler already installed for the scheme: %s", scheme.constData());
return;
}
if (type == SchemeType::Unknown)
qWarning("Please register the custom scheme '%s' via QWebEngineUrlScheme::registerScheme() "
"before installing the custom scheme handler.", scheme.constData());
m_customUrlSchemeHandlers.insert(canonicalScheme, handler);
updateCustomUrlSchemeHandlers();
}
void ProfileAdapter::removeAllUrlSchemeHandlers()
{
m_customUrlSchemeHandlers.clear();
m_customUrlSchemeHandlers.insert(QByteArrayLiteral("qrc"), &m_qrcHandler);
updateCustomUrlSchemeHandlers();
}
UserResourceControllerHost *ProfileAdapter::userResourceController()
{
if (!m_userResourceController)
m_userResourceController.reset(new UserResourceControllerHost);
return m_userResourceController.data();
}
void ProfileAdapter::permissionRequestReply(const QUrl &origin, PermissionType type, bool reply)
{
static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->permissionRequestReply(origin, type, reply);
}
bool ProfileAdapter::checkPermission(const QUrl &origin, PermissionType type)
{
return static_cast<PermissionManagerQt*>(profile()->GetPermissionControllerDelegate())->checkPermission(origin, type);
}
QString ProfileAdapter::httpAcceptLanguageWithoutQualities() const
{
const QStringList list = m_httpAcceptLanguage.split(QLatin1Char(','));
QString out;
for (const QString &str : list) {
if (!out.isEmpty())
out.append(QLatin1Char(','));
out.append(str.split(QLatin1Char(';')).first());
}
return out;
}
QString ProfileAdapter::httpAcceptLanguage() const
{
return m_httpAcceptLanguage;
}
void ProfileAdapter::setHttpAcceptLanguage(const QString &httpAcceptLanguage)
{
if (m_httpAcceptLanguage == httpAcceptLanguage)
return;
m_httpAcceptLanguage = httpAcceptLanguage;
std::vector<content::WebContentsImpl *> list = content::WebContentsImpl::GetAllWebContents();
for (content::WebContentsImpl *web_contents : list) {
if (web_contents->GetBrowserContext() == m_profile.data()) {
blink::mojom::RendererPreferences *rendererPrefs = web_contents->GetMutableRendererPrefs();
rendererPrefs->accept_languages = httpAcceptLanguageWithoutQualities().toStdString();
web_contents->GetRenderViewHost()->SyncRendererPrefs();
}
}
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateUserAgent();
}
void ProfileAdapter::clearHttpCache()
{
content::BrowsingDataRemover *remover = content::BrowserContext::GetBrowsingDataRemover(m_profile.data());
remover->Remove(base::Time(), base::Time::Max(),
content::BrowsingDataRemover::DATA_TYPE_CACHE,
content::BrowsingDataRemover::ORIGIN_TYPE_UNPROTECTED_WEB | content::BrowsingDataRemover::ORIGIN_TYPE_PROTECTED_WEB);
}
void ProfileAdapter::setSpellCheckLanguages(const QStringList &languages)
{
#if QT_CONFIG(webengine_spellchecker)
m_profile->prefServiceAdapter().setSpellCheckLanguages(languages);
#endif
}
QStringList ProfileAdapter::spellCheckLanguages() const
{
#if QT_CONFIG(webengine_spellchecker)
return m_profile->prefServiceAdapter().spellCheckLanguages();
#else
return QStringList();
#endif
}
void ProfileAdapter::setSpellCheckEnabled(bool enabled)
{
#if QT_CONFIG(webengine_spellchecker)
m_profile->prefServiceAdapter().setSpellCheckEnabled(enabled);
#endif
}
bool ProfileAdapter::isSpellCheckEnabled() const
{
#if QT_CONFIG(webengine_spellchecker)
return m_profile->prefServiceAdapter().isSpellCheckEnabled();
#else
return false;
#endif
}
void ProfileAdapter::addWebContentsAdapterClient(WebContentsAdapterClient *client)
{
m_webContentsAdapterClients.append(client);
}
void ProfileAdapter::removeWebContentsAdapterClient(WebContentsAdapterClient *client)
{
m_webContentsAdapterClients.removeAll(client);
}
void ProfileAdapter::resetVisitedLinksManager()
{
m_visitedLinksManager.reset(new VisitedLinksManagerQt(m_profile.data(), persistVisitedLinks()));
}
void ProfileAdapter::setUseForGlobalCertificateVerification(bool enable)
{
if (m_usedForGlobalCertificateVerification == enable)
return;
static QPointer<ProfileAdapter> profileForglobalCertificateVerification;
m_usedForGlobalCertificateVerification = enable;
if (enable) {
if (profileForglobalCertificateVerification) {
profileForglobalCertificateVerification->m_usedForGlobalCertificateVerification = false;
for (auto *client : qAsConst(profileForglobalCertificateVerification->m_clients))
client->useForGlobalCertificateVerificationChanged();
}
profileForglobalCertificateVerification = this;
} else {
Q_ASSERT(profileForglobalCertificateVerification);
Q_ASSERT(profileForglobalCertificateVerification == this);
profileForglobalCertificateVerification = nullptr;
}
if (m_profile->m_urlRequestContextGetter.get())
m_profile->m_profileIOData->updateUsedForGlobalCertificateVerification();
}
bool ProfileAdapter::isUsedForGlobalCertificateVerification() const
{
return m_usedForGlobalCertificateVerification;
}
QString ProfileAdapter::determineDownloadPath(const QString &downloadDirectory, const QString &suggestedFilename, const time_t &startTime)
{
QFileInfo suggestedFile(QDir(downloadDirectory).absoluteFilePath(suggestedFilename));
QString suggestedFilePath = suggestedFile.absoluteFilePath();
base::FilePath tmpFilePath(toFilePath(suggestedFilePath).NormalizePathSeparatorsTo('/'));
int uniquifier = base::GetUniquePathNumber(tmpFilePath, base::FilePath::StringType());
if (uniquifier > 0)
suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(base::StringPrintf(" (%d)", uniquifier)).AsUTF8Unsafe());
else if (uniquifier == -1) {
base::Time::Exploded exploded;
base::Time::FromTimeT(startTime).LocalExplode(&exploded);
std::string suffix = base::StringPrintf(
" - %04d-%02d-%02dT%02d%02d%02d.%03d", exploded.year, exploded.month,
exploded.day_of_month, exploded.hour, exploded.minute,
exploded.second, exploded.millisecond);
suggestedFilePath = toQt(tmpFilePath.InsertBeforeExtensionASCII(suffix).AsUTF8Unsafe());
}
return suggestedFilePath;
}
#if QT_CONFIG(ssl)
QWebEngineClientCertificateStore *ProfileAdapter::clientCertificateStore()
{
if (!m_clientCertificateStore)
m_clientCertificateStore = new QWebEngineClientCertificateStore(m_profile->m_profileIOData->clientCertificateStoreData());
return m_clientCertificateStore;
}
#endif
} // namespace QtWebEngineCore