WebKitWebContext.cpp [plain text]
#include "config.h"
#include "WebKitWebContext.h"
#include "APIAutomationClient.h"
#include "APICustomProtocolManagerClient.h"
#include "APIDownloadClient.h"
#include "APIInjectedBundleClient.h"
#include "APIPageConfiguration.h"
#include "APIProcessPoolConfiguration.h"
#include "APIString.h"
#include "TextChecker.h"
#include "TextCheckerState.h"
#include "WebAutomationSession.h"
#include "WebCertificateInfo.h"
#include "WebGeolocationManagerProxy.h"
#include "WebKitAutomationSessionPrivate.h"
#include "WebKitCustomProtocolManagerClient.h"
#include "WebKitDownloadClient.h"
#include "WebKitDownloadPrivate.h"
#include "WebKitFaviconDatabasePrivate.h"
#include "WebKitGeolocationProvider.h"
#include "WebKitInjectedBundleClient.h"
#include "WebKitNetworkProxySettingsPrivate.h"
#include "WebKitNotificationProvider.h"
#include "WebKitPluginPrivate.h"
#include "WebKitPrivate.h"
#include "WebKitSecurityManagerPrivate.h"
#include "WebKitSecurityOriginPrivate.h"
#include "WebKitSettingsPrivate.h"
#include "WebKitURISchemeRequestPrivate.h"
#include "WebKitUserContentManagerPrivate.h"
#include "WebKitWebContextPrivate.h"
#include "WebKitWebViewPrivate.h"
#include "WebKitWebsiteDataManagerPrivate.h"
#include "WebNotificationManagerProxy.h"
#include "WebsiteDataType.h"
#include <JavaScriptCore/RemoteInspector.h>
#include <WebCore/FileSystem.h>
#include <glib/gi18n-lib.h>
#include <libintl.h>
#include <memory>
#include <wtf/HashMap.h>
#include <wtf/Language.h>
#include <wtf/NeverDestroyed.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
#include <wtf/glib/GRefPtr.h>
#include <wtf/glib/GUniquePtr.h>
#include <wtf/glib/WTFGType.h>
#include <wtf/text/CString.h>
#if PLATFORM(GTK)
#include "WebKitRemoteInspectorProtocolHandler.h"
#endif
using namespace WebKit;
enum {
PROP_0,
#if PLATFORM(GTK)
PROP_LOCAL_STORAGE_DIRECTORY,
#endif
PROP_WEBSITE_DATA_MANAGER
};
enum {
DOWNLOAD_STARTED,
INITIALIZE_WEB_EXTENSIONS,
INITIALIZE_NOTIFICATION_PERMISSIONS,
AUTOMATION_STARTED,
LAST_SIGNAL
};
class WebKitURISchemeHandler: public RefCounted<WebKitURISchemeHandler> {
public:
WebKitURISchemeHandler(WebKitURISchemeRequestCallback callback, void* userData, GDestroyNotify destroyNotify)
: m_callback(callback)
, m_userData(userData)
, m_destroyNotify(destroyNotify)
{
}
~WebKitURISchemeHandler()
{
if (m_destroyNotify)
m_destroyNotify(m_userData);
}
bool hasCallback()
{
return m_callback;
}
void performCallback(WebKitURISchemeRequest* request)
{
ASSERT(m_callback);
m_callback(request, m_userData);
}
private:
WebKitURISchemeRequestCallback m_callback { nullptr };
void* m_userData { nullptr };
GDestroyNotify m_destroyNotify { nullptr };
};
typedef HashMap<String, RefPtr<WebKitURISchemeHandler> > URISchemeHandlerMap;
typedef HashMap<uint64_t, GRefPtr<WebKitURISchemeRequest> > URISchemeRequestMap;
class WebKitAutomationClient;
struct _WebKitWebContextPrivate {
RefPtr<WebProcessPool> processPool;
bool clientsDetached;
GRefPtr<WebKitFaviconDatabase> faviconDatabase;
GRefPtr<WebKitSecurityManager> securityManager;
URISchemeHandlerMap uriSchemeHandlers;
URISchemeRequestMap uriSchemeRequests;
#if ENABLE(GEOLOCATION)
std::unique_ptr<WebKitGeolocationProvider> geolocationProvider;
#endif
std::unique_ptr<WebKitNotificationProvider> notificationProvider;
GRefPtr<WebKitWebsiteDataManager> websiteDataManager;
CString faviconDatabaseDirectory;
WebKitTLSErrorsPolicy tlsErrorsPolicy;
WebKitProcessModel processModel;
unsigned processCountLimit;
HashMap<uint64_t, WebKitWebView*> webViews;
unsigned ephemeralPageCount;
CString webExtensionsDirectory;
GRefPtr<GVariant> webExtensionsInitializationUserData;
CString localStorageDirectory;
#if ENABLE(REMOTE_INSPECTOR)
#if PLATFORM(GTK)
std::unique_ptr<RemoteInspectorProtocolHandler> remoteInspectorProtocolHandler;
#endif
std::unique_ptr<WebKitAutomationClient> automationClient;
GRefPtr<WebKitAutomationSession> automationSession;
#endif
};
static guint signals[LAST_SIGNAL] = { 0, };
#if ENABLE(REMOTE_INSPECTOR)
class WebKitAutomationClient final : Inspector::RemoteInspector::Client {
public:
explicit WebKitAutomationClient(WebKitWebContext* context)
: m_webContext(context)
{
Inspector::RemoteInspector::singleton().setClient(this);
}
~WebKitAutomationClient()
{
Inspector::RemoteInspector::singleton().setClient(nullptr);
}
private:
bool remoteAutomationAllowed() const override { return true; }
String browserName() const override
{
if (!m_webContext->priv->automationSession)
return { };
return webkitAutomationSessionGetBrowserName(m_webContext->priv->automationSession.get());
}
String browserVersion() const override
{
if (!m_webContext->priv->automationSession)
return { };
return webkitAutomationSessionGetBrowserVersion(m_webContext->priv->automationSession.get());
}
void requestAutomationSession(const String& sessionIdentifier, const Inspector::RemoteInspector::Client::SessionCapabilities& capabilities) override
{
ASSERT(!m_webContext->priv->automationSession);
m_webContext->priv->automationSession = adoptGRef(webkitAutomationSessionCreate(m_webContext, sessionIdentifier.utf8().data(), capabilities));
g_signal_emit(m_webContext, signals[AUTOMATION_STARTED], 0, m_webContext->priv->automationSession.get());
m_webContext->priv->processPool->setAutomationSession(&webkitAutomationSessionGetSession(m_webContext->priv->automationSession.get()));
}
WebKitWebContext* m_webContext;
};
void webkitWebContextWillCloseAutomationSession(WebKitWebContext* webContext)
{
webContext->priv->processPool->setAutomationSession(nullptr);
webContext->priv->automationSession = nullptr;
}
#endif // ENABLE(REMOTE_INSPECTOR)
WEBKIT_DEFINE_TYPE(WebKitWebContext, webkit_web_context, G_TYPE_OBJECT)
#if PLATFORM(GTK)
#define INJECTED_BUNDLE_FILENAME "libwebkit2gtkinjectedbundle.so"
#elif PLATFORM(WPE)
#define INJECTED_BUNDLE_FILENAME "libWPEInjectedBundle.so"
#endif
static const char* injectedBundleDirectory()
{
#if ENABLE(DEVELOPER_MODE)
const char* bundleDirectory = g_getenv("WEBKIT_INJECTED_BUNDLE_PATH");
if (bundleDirectory && g_file_test(bundleDirectory, G_FILE_TEST_IS_DIR))
return bundleDirectory;
#endif
#if PLATFORM(GTK)
static const char* injectedBundlePath = LIBDIR G_DIR_SEPARATOR_S "webkit2gtk-" WEBKITGTK_API_VERSION_STRING
G_DIR_SEPARATOR_S "injected-bundle" G_DIR_SEPARATOR_S;
return injectedBundlePath;
#elif PLATFORM(WPE)
static const char* injectedBundlePath = PKGLIBDIR G_DIR_SEPARATOR_S "injected-bundle" G_DIR_SEPARATOR_S;
return injectedBundlePath;
#endif
}
static void webkitWebContextGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
{
WebKitWebContext* context = WEBKIT_WEB_CONTEXT(object);
switch (propID) {
#if PLATFORM(GTK)
case PROP_LOCAL_STORAGE_DIRECTORY:
g_value_set_string(value, context->priv->localStorageDirectory.data());
break;
#endif
case PROP_WEBSITE_DATA_MANAGER:
g_value_set_object(value, webkit_web_context_get_website_data_manager(context));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
}
}
static void webkitWebContextSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
{
WebKitWebContext* context = WEBKIT_WEB_CONTEXT(object);
switch (propID) {
#if PLATFORM(GTK)
case PROP_LOCAL_STORAGE_DIRECTORY:
context->priv->localStorageDirectory = g_value_get_string(value);
break;
#endif
case PROP_WEBSITE_DATA_MANAGER: {
gpointer manager = g_value_get_object(value);
context->priv->websiteDataManager = manager ? WEBKIT_WEBSITE_DATA_MANAGER(manager) : nullptr;
break;
}
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
}
}
static inline WebsiteDataStore::Configuration websiteDataStoreConfigurationForWebProcessPoolConfiguration(const API::ProcessPoolConfiguration& processPoolconfigurarion)
{
WebsiteDataStore::Configuration configuration;
configuration.applicationCacheDirectory = processPoolconfigurarion.applicationCacheDirectory();
configuration.networkCacheDirectory = processPoolconfigurarion.diskCacheDirectory();
configuration.webSQLDatabaseDirectory = processPoolconfigurarion.webSQLDatabaseDirectory();
configuration.localStorageDirectory = processPoolconfigurarion.localStorageDirectory();
configuration.mediaKeysStorageDirectory = processPoolconfigurarion.mediaKeysStorageDirectory();
return configuration;
}
static void webkitWebContextConstructed(GObject* object)
{
G_OBJECT_CLASS(webkit_web_context_parent_class)->constructed(object);
GUniquePtr<char> bundleFilename(g_build_filename(injectedBundleDirectory(), INJECTED_BUNDLE_FILENAME, nullptr));
API::ProcessPoolConfiguration configuration;
configuration.setInjectedBundlePath(WebCore::FileSystem::stringFromFileSystemRepresentation(bundleFilename.get()));
configuration.setMaximumProcessCount(1);
configuration.setDiskCacheSpeculativeValidationEnabled(true);
WebKitWebContext* webContext = WEBKIT_WEB_CONTEXT(object);
WebKitWebContextPrivate* priv = webContext->priv;
if (priv->websiteDataManager && !webkit_website_data_manager_is_ephemeral(priv->websiteDataManager.get())) {
configuration.setLocalStorageDirectory(WebCore::FileSystem::stringFromFileSystemRepresentation(webkit_website_data_manager_get_local_storage_directory(priv->websiteDataManager.get())));
configuration.setDiskCacheDirectory(WebCore::FileSystem::pathByAppendingComponent(WebCore::FileSystem::stringFromFileSystemRepresentation(webkit_website_data_manager_get_disk_cache_directory(priv->websiteDataManager.get())), networkCacheSubdirectory));
configuration.setApplicationCacheDirectory(WebCore::FileSystem::stringFromFileSystemRepresentation(webkit_website_data_manager_get_offline_application_cache_directory(priv->websiteDataManager.get())));
configuration.setIndexedDBDatabaseDirectory(WebCore::FileSystem::stringFromFileSystemRepresentation(webkit_website_data_manager_get_indexeddb_directory(priv->websiteDataManager.get())));
configuration.setWebSQLDatabaseDirectory(WebCore::FileSystem::stringFromFileSystemRepresentation(webkit_website_data_manager_get_websql_directory(priv->websiteDataManager.get())));
} else if (!priv->localStorageDirectory.isNull())
configuration.setLocalStorageDirectory(WebCore::FileSystem::stringFromFileSystemRepresentation(priv->localStorageDirectory.data()));
priv->processPool = WebProcessPool::create(configuration);
if (!priv->websiteDataManager)
priv->websiteDataManager = adoptGRef(webkitWebsiteDataManagerCreate(websiteDataStoreConfigurationForWebProcessPoolConfiguration(configuration)));
priv->processPool->setPrimaryDataStore(webkitWebsiteDataManagerGetDataStore(priv->websiteDataManager.get()));
webkitWebsiteDataManagerAddProcessPool(priv->websiteDataManager.get(), *priv->processPool);
priv->tlsErrorsPolicy = WEBKIT_TLS_ERRORS_POLICY_FAIL;
priv->processPool->setIgnoreTLSErrors(false);
#if ENABLE(MEMORY_SAMPLER)
if (getenv("WEBKIT_SAMPLE_MEMORY"))
priv->processPool->startMemorySampler(0);
#endif
attachInjectedBundleClientToContext(webContext);
attachDownloadClientToContext(webContext);
attachCustomProtocolManagerClientToContext(webContext);
#if ENABLE(GEOLOCATION)
priv->geolocationProvider = std::make_unique<WebKitGeolocationProvider>(priv->processPool->supplement<WebGeolocationManagerProxy>());
#endif
priv->notificationProvider = std::make_unique<WebKitNotificationProvider>(priv->processPool->supplement<WebNotificationManagerProxy>(), webContext);
#if PLATFORM(GTK) && ENABLE(REMOTE_INSPECTOR)
priv->remoteInspectorProtocolHandler = std::make_unique<RemoteInspectorProtocolHandler>(webContext);
#endif
}
static void webkitWebContextDispose(GObject* object)
{
WebKitWebContextPrivate* priv = WEBKIT_WEB_CONTEXT(object)->priv;
if (!priv->clientsDetached) {
priv->clientsDetached = true;
priv->processPool->setInjectedBundleClient(nullptr);
priv->processPool->setDownloadClient(nullptr);
priv->processPool->setLegacyCustomProtocolManagerClient(nullptr);
}
if (priv->websiteDataManager) {
webkitWebsiteDataManagerRemoveProcessPool(priv->websiteDataManager.get(), *priv->processPool);
priv->websiteDataManager = nullptr;
}
G_OBJECT_CLASS(webkit_web_context_parent_class)->dispose(object);
}
static void webkit_web_context_class_init(WebKitWebContextClass* webContextClass)
{
GObjectClass* gObjectClass = G_OBJECT_CLASS(webContextClass);
bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
gObjectClass->get_property = webkitWebContextGetProperty;
gObjectClass->set_property = webkitWebContextSetProperty;
gObjectClass->constructed = webkitWebContextConstructed;
gObjectClass->dispose = webkitWebContextDispose;
#if PLATFORM(GTK)
g_object_class_install_property(
gObjectClass,
PROP_LOCAL_STORAGE_DIRECTORY,
g_param_spec_string(
"local-storage-directory",
_("Local Storage Directory"),
_("The directory where local storage data will be saved"),
nullptr,
static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
#endif
g_object_class_install_property(
gObjectClass,
PROP_WEBSITE_DATA_MANAGER,
g_param_spec_object(
"website-data-manager",
_("Website Data Manager"),
_("The WebKitWebsiteDataManager associated with this context"),
WEBKIT_TYPE_WEBSITE_DATA_MANAGER,
static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
signals[DOWNLOAD_STARTED] =
g_signal_new("download-started",
G_TYPE_FROM_CLASS(gObjectClass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(WebKitWebContextClass, download_started),
nullptr, nullptr,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
WEBKIT_TYPE_DOWNLOAD);
signals[INITIALIZE_WEB_EXTENSIONS] =
g_signal_new("initialize-web-extensions",
G_TYPE_FROM_CLASS(gObjectClass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(WebKitWebContextClass, initialize_web_extensions),
nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[INITIALIZE_NOTIFICATION_PERMISSIONS] =
g_signal_new("initialize-notification-permissions",
G_TYPE_FROM_CLASS(gObjectClass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(WebKitWebContextClass, initialize_notification_permissions),
nullptr, nullptr,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[AUTOMATION_STARTED] =
g_signal_new("automation-started",
G_TYPE_FROM_CLASS(gObjectClass),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET(WebKitWebContextClass, automation_started),
nullptr, nullptr,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
WEBKIT_TYPE_AUTOMATION_SESSION);
}
static gpointer createDefaultWebContext(gpointer)
{
static GRefPtr<WebKitWebContext> webContext = adoptGRef(WEBKIT_WEB_CONTEXT(g_object_new(WEBKIT_TYPE_WEB_CONTEXT, nullptr)));
return webContext.get();
}
WebKitWebContext* webkit_web_context_get_default(void)
{
static GOnce onceInit = G_ONCE_INIT;
return WEBKIT_WEB_CONTEXT(g_once(&onceInit, createDefaultWebContext, 0));
}
WebKitWebContext* webkit_web_context_new(void)
{
return WEBKIT_WEB_CONTEXT(g_object_new(WEBKIT_TYPE_WEB_CONTEXT, nullptr));
}
WebKitWebContext* webkit_web_context_new_ephemeral()
{
GRefPtr<WebKitWebsiteDataManager> manager = adoptGRef(webkit_website_data_manager_new_ephemeral());
return WEBKIT_WEB_CONTEXT(g_object_new(WEBKIT_TYPE_WEB_CONTEXT, "website-data-manager", manager.get(), nullptr));
}
WebKitWebContext* webkit_web_context_new_with_website_data_manager(WebKitWebsiteDataManager* manager)
{
g_return_val_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager), nullptr);
return WEBKIT_WEB_CONTEXT(g_object_new(WEBKIT_TYPE_WEB_CONTEXT, "website-data-manager", manager, nullptr));
}
WebKitWebsiteDataManager* webkit_web_context_get_website_data_manager(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), nullptr);
return context->priv->websiteDataManager.get();
}
gboolean webkit_web_context_is_ephemeral(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE);
return webkit_website_data_manager_is_ephemeral(context->priv->websiteDataManager.get());
}
gboolean webkit_web_context_is_automation_allowed(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE);
#if ENABLE(REMOTE_INSPECTOR)
return !!context->priv->automationClient;
#else
return FALSE;
#endif
}
void webkit_web_context_set_automation_allowed(WebKitWebContext* context, gboolean allowed)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
if (webkit_web_context_is_automation_allowed(context) == allowed)
return;
#if ENABLE(REMOTE_INSPECTOR)
if (allowed) {
if (Inspector::RemoteInspector::singleton().client()) {
g_warning("Not enabling automation on WebKitWebContext because there's another context with automation enabled, only one is allowed");
return;
}
context->priv->automationClient = std::make_unique<WebKitAutomationClient>(context);
} else
context->priv->automationClient = nullptr;
#endif
}
void webkit_web_context_set_cache_model(WebKitWebContext* context, WebKitCacheModel model)
{
CacheModel cacheModel;
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
switch (model) {
case WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER:
cacheModel = CacheModelDocumentViewer;
break;
case WEBKIT_CACHE_MODEL_WEB_BROWSER:
cacheModel = CacheModelPrimaryWebBrowser;
break;
case WEBKIT_CACHE_MODEL_DOCUMENT_BROWSER:
cacheModel = CacheModelDocumentBrowser;
break;
default:
g_assert_not_reached();
}
if (cacheModel != context->priv->processPool->cacheModel())
context->priv->processPool->setCacheModel(cacheModel);
}
WebKitCacheModel webkit_web_context_get_cache_model(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), WEBKIT_CACHE_MODEL_WEB_BROWSER);
switch (context->priv->processPool->cacheModel()) {
case CacheModelDocumentViewer:
return WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER;
case CacheModelPrimaryWebBrowser:
return WEBKIT_CACHE_MODEL_WEB_BROWSER;
case CacheModelDocumentBrowser:
return WEBKIT_CACHE_MODEL_DOCUMENT_BROWSER;
default:
g_assert_not_reached();
}
return WEBKIT_CACHE_MODEL_WEB_BROWSER;
}
void webkit_web_context_clear_cache(WebKitWebContext* context)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
OptionSet<WebsiteDataType> websiteDataTypes;
websiteDataTypes |= WebsiteDataType::MemoryCache;
websiteDataTypes |= WebsiteDataType::DiskCache;
auto& websiteDataStore = webkitWebsiteDataManagerGetDataStore(context->priv->websiteDataManager.get()).websiteDataStore();
websiteDataStore.removeData(websiteDataTypes, -WallTime::infinity(), [] { });
}
void webkit_web_context_set_network_proxy_settings(WebKitWebContext* context, WebKitNetworkProxyMode proxyMode, WebKitNetworkProxySettings* proxySettings)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail((proxyMode != WEBKIT_NETWORK_PROXY_MODE_CUSTOM && !proxySettings) || (proxyMode == WEBKIT_NETWORK_PROXY_MODE_CUSTOM && proxySettings));
WebKitWebContextPrivate* priv = context->priv;
switch (proxyMode) {
case WEBKIT_NETWORK_PROXY_MODE_DEFAULT:
priv->processPool->setNetworkProxySettings({ });
break;
case WEBKIT_NETWORK_PROXY_MODE_NO_PROXY:
priv->processPool->setNetworkProxySettings(WebCore::SoupNetworkProxySettings(WebCore::SoupNetworkProxySettings::Mode::NoProxy));
break;
case WEBKIT_NETWORK_PROXY_MODE_CUSTOM:
const auto& settings = webkitNetworkProxySettingsGetNetworkProxySettings(proxySettings);
if (settings.isEmpty()) {
g_warning("Invalid attempt to set custom network proxy settings with an empty WebKitNetworkProxySettings. Use "
"WEBKIT_NETWORK_PROXY_MODE_NO_PROXY to not use any proxy or WEBKIT_NETWORK_PROXY_MODE_DEFAULT to use the default system settings");
} else
priv->processPool->setNetworkProxySettings(settings);
break;
}
}
typedef HashMap<DownloadProxy*, GRefPtr<WebKitDownload> > DownloadsMap;
static DownloadsMap& downloadsMap()
{
static NeverDestroyed<DownloadsMap> downloads;
return downloads;
}
WebKitDownload* webkit_web_context_download_uri(WebKitWebContext* context, const gchar* uri)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), nullptr);
g_return_val_if_fail(uri, nullptr);
GRefPtr<WebKitDownload> download = webkitWebContextStartDownload(context, uri, nullptr);
return download.leakRef();
}
WebKitCookieManager* webkit_web_context_get_cookie_manager(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), nullptr);
return webkit_website_data_manager_get_cookie_manager(context->priv->websiteDataManager.get());
}
static void ensureFaviconDatabase(WebKitWebContext* context)
{
WebKitWebContextPrivate* priv = context->priv;
if (priv->faviconDatabase)
return;
priv->faviconDatabase = adoptGRef(webkitFaviconDatabaseCreate());
}
static void webkitWebContextEnableIconDatabasePrivateBrowsingIfNeeded(WebKitWebContext* context, WebKitWebView* webView)
{
if (webkit_web_context_is_ephemeral(context))
return;
if (!webkit_web_view_is_ephemeral(webView))
return;
if (!context->priv->ephemeralPageCount && context->priv->faviconDatabase)
webkitFaviconDatabaseSetPrivateBrowsingEnabled(context->priv->faviconDatabase.get(), true);
context->priv->ephemeralPageCount++;
}
static void webkitWebContextDisableIconDatabasePrivateBrowsingIfNeeded(WebKitWebContext* context, WebKitWebView* webView)
{
if (webkit_web_context_is_ephemeral(context))
return;
if (!webkit_web_view_is_ephemeral(webView))
return;
ASSERT(context->priv->ephemeralPageCount);
context->priv->ephemeralPageCount--;
if (!context->priv->ephemeralPageCount && context->priv->faviconDatabase)
webkitFaviconDatabaseSetPrivateBrowsingEnabled(context->priv->faviconDatabase.get(), false);
}
void webkit_web_context_set_favicon_database_directory(WebKitWebContext* context, const gchar* path)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
WebKitWebContextPrivate* priv = context->priv;
ensureFaviconDatabase(context);
String directoryPath = WebCore::FileSystem::stringFromFileSystemRepresentation(path);
if (directoryPath.isEmpty()) {
#if PLATFORM(GTK)
const char* portDirectory = "webkitgtk";
#elif PLATFORM(WPE)
const char* portDirectory = "wpe";
#endif
GUniquePtr<gchar> databaseDirectory(g_build_filename(g_get_user_cache_dir(), portDirectory, "icondatabase", nullptr));
directoryPath = WebCore::FileSystem::stringFromFileSystemRepresentation(databaseDirectory.get());
}
priv->faviconDatabaseDirectory = directoryPath.utf8();
GUniquePtr<gchar> faviconDatabasePath(g_build_filename(priv->faviconDatabaseDirectory.data(),
"WebpageIcons.db", nullptr));
webkitFaviconDatabaseOpen(priv->faviconDatabase.get(), WebCore::FileSystem::stringFromFileSystemRepresentation(faviconDatabasePath.get()));
if (webkit_web_context_is_ephemeral(context))
webkitFaviconDatabaseSetPrivateBrowsingEnabled(priv->faviconDatabase.get(), true);
}
const gchar* webkit_web_context_get_favicon_database_directory(WebKitWebContext *context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
WebKitWebContextPrivate* priv = context->priv;
if (priv->faviconDatabaseDirectory.isNull())
return 0;
return priv->faviconDatabaseDirectory.data();
}
WebKitFaviconDatabase* webkit_web_context_get_favicon_database(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
ensureFaviconDatabase(context);
return context->priv->faviconDatabase.get();
}
WebKitSecurityManager* webkit_web_context_get_security_manager(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
WebKitWebContextPrivate* priv = context->priv;
if (!priv->securityManager)
priv->securityManager = adoptGRef(webkitSecurityManagerCreate(context));
return priv->securityManager.get();
}
void webkit_web_context_set_additional_plugins_directory(WebKitWebContext* context, const char* directory)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(directory);
#if ENABLE(NETSCAPE_PLUGIN_API)
context->priv->processPool->setAdditionalPluginsDirectory(WebCore::FileSystem::stringFromFileSystemRepresentation(directory));
#endif
}
static void destroyPluginList(GList* plugins)
{
g_list_free_full(plugins, g_object_unref);
}
static void webkitWebContextGetPluginThread(GTask* task, gpointer object, gpointer , GCancellable*)
{
GList* returnValue = 0;
#if ENABLE(NETSCAPE_PLUGIN_API)
Vector<PluginModuleInfo> plugins = WEBKIT_WEB_CONTEXT(object)->priv->processPool->pluginInfoStore().plugins();
for (size_t i = 0; i < plugins.size(); ++i)
returnValue = g_list_prepend(returnValue, webkitPluginCreate(plugins[i]));
#endif
g_task_return_pointer(task, returnValue, reinterpret_cast<GDestroyNotify>(destroyPluginList));
}
void webkit_web_context_get_plugins(WebKitWebContext* context, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
GRefPtr<GTask> task = adoptGRef(g_task_new(context, cancellable, callback, userData));
g_task_run_in_thread(task.get(), webkitWebContextGetPluginThread);
}
GList* webkit_web_context_get_plugins_finish(WebKitWebContext* context, GAsyncResult* result, GError** error)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
g_return_val_if_fail(g_task_is_valid(result, context), 0);
return static_cast<GList*>(g_task_propagate_pointer(G_TASK(result), error));
}
void webkit_web_context_register_uri_scheme(WebKitWebContext* context, const char* scheme, WebKitURISchemeRequestCallback callback, gpointer userData, GDestroyNotify destroyNotify)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(scheme);
g_return_if_fail(callback);
RefPtr<WebKitURISchemeHandler> handler = adoptRef(new WebKitURISchemeHandler(callback, userData, destroyNotify));
auto addResult = context->priv->uriSchemeHandlers.set(String::fromUTF8(scheme), handler.get());
if (addResult.isNewEntry)
context->priv->processPool->registerSchemeForCustomProtocol(String::fromUTF8(scheme));
}
gboolean webkit_web_context_get_spell_checking_enabled(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE);
#if ENABLE(SPELLCHECK)
return TextChecker::state().isContinuousSpellCheckingEnabled;
#else
return false;
#endif
}
void webkit_web_context_set_spell_checking_enabled(WebKitWebContext* context, gboolean enabled)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
#if ENABLE(SPELLCHECK)
TextChecker::setContinuousSpellCheckingEnabled(enabled);
#endif
}
const gchar* const* webkit_web_context_get_spell_checking_languages(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), nullptr);
#if ENABLE(SPELLCHECK)
Vector<String> spellCheckingLanguages = TextChecker::loadedSpellCheckingLanguages();
if (spellCheckingLanguages.isEmpty())
return nullptr;
static GRefPtr<GPtrArray> languagesToReturn;
languagesToReturn = adoptGRef(g_ptr_array_new_with_free_func(g_free));
for (const auto& language : spellCheckingLanguages)
g_ptr_array_add(languagesToReturn.get(), g_strdup(language.utf8().data()));
g_ptr_array_add(languagesToReturn.get(), nullptr);
return reinterpret_cast<char**>(languagesToReturn->pdata);
#else
return 0;
#endif
}
void webkit_web_context_set_spell_checking_languages(WebKitWebContext* context, const gchar* const* languages)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(languages);
#if ENABLE(SPELLCHECK)
Vector<String> spellCheckingLanguages;
for (size_t i = 0; languages[i]; ++i)
spellCheckingLanguages.append(String::fromUTF8(languages[i]));
TextChecker::setSpellCheckingLanguages(spellCheckingLanguages);
#endif
}
void webkit_web_context_set_preferred_languages(WebKitWebContext* context, const gchar* const* languageList)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
if (!languageList || !g_strv_length(const_cast<char**>(languageList)))
return;
Vector<String> languages;
for (size_t i = 0; languageList[i]; ++i) {
if (!g_ascii_strcasecmp(languageList[i], "C") || !g_ascii_strcasecmp(languageList[i], "POSIX"))
languages.append("en-us"_s);
else
languages.append(String::fromUTF8(languageList[i]).convertToASCIILowercase().replace("_", "-"));
}
overrideUserPreferredLanguages(languages);
}
void webkit_web_context_set_tls_errors_policy(WebKitWebContext* context, WebKitTLSErrorsPolicy policy)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
if (context->priv->tlsErrorsPolicy == policy)
return;
context->priv->tlsErrorsPolicy = policy;
bool ignoreTLSErrors = policy == WEBKIT_TLS_ERRORS_POLICY_IGNORE;
if (context->priv->processPool->ignoreTLSErrors() != ignoreTLSErrors)
context->priv->processPool->setIgnoreTLSErrors(ignoreTLSErrors);
}
WebKitTLSErrorsPolicy webkit_web_context_get_tls_errors_policy(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), WEBKIT_TLS_ERRORS_POLICY_IGNORE);
return context->priv->tlsErrorsPolicy;
}
void webkit_web_context_set_web_extensions_directory(WebKitWebContext* context, const char* directory)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(directory);
context->priv->webExtensionsDirectory = directory;
}
void webkit_web_context_set_web_extensions_initialization_user_data(WebKitWebContext* context, GVariant* userData)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(userData);
context->priv->webExtensionsInitializationUserData = userData;
}
#if PLATFORM(GTK)
void webkit_web_context_set_disk_cache_directory(WebKitWebContext* context, const char* directory)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(directory);
context->priv->processPool->configuration().setDiskCacheDirectory(WebCore::FileSystem::pathByAppendingComponent(WebCore::FileSystem::stringFromFileSystemRepresentation(directory), networkCacheSubdirectory));
}
#endif
void webkit_web_context_prefetch_dns(WebKitWebContext* context, const char* hostname)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(hostname);
API::Dictionary::MapType message;
message.set(String::fromUTF8("Hostname"), API::String::create(String::fromUTF8(hostname)));
context->priv->processPool->postMessageToInjectedBundle(String::fromUTF8("PrefetchDNS"), API::Dictionary::create(WTFMove(message)).ptr());
}
void webkit_web_context_allow_tls_certificate_for_host(WebKitWebContext* context, GTlsCertificate* certificate, const gchar* host)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
g_return_if_fail(G_IS_TLS_CERTIFICATE(certificate));
g_return_if_fail(host);
RefPtr<WebCertificateInfo> webCertificateInfo = WebCertificateInfo::create(WebCore::CertificateInfo(certificate, static_cast<GTlsCertificateFlags>(0)));
context->priv->processPool->allowSpecificHTTPSCertificateForHost(webCertificateInfo.get(), String::fromUTF8(host));
}
void webkit_web_context_set_process_model(WebKitWebContext* context, WebKitProcessModel processModel)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
if (processModel == context->priv->processModel)
return;
context->priv->processModel = processModel;
switch (context->priv->processModel) {
case WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS:
context->priv->processPool->setMaximumNumberOfProcesses(1);
break;
case WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES:
context->priv->processPool->setMaximumNumberOfProcesses(context->priv->processCountLimit);
break;
}
}
WebKitProcessModel webkit_web_context_get_process_model(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS);
return context->priv->processModel;
}
void webkit_web_context_set_web_process_count_limit(WebKitWebContext* context, guint limit)
{
g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
if (context->priv->processCountLimit == limit)
return;
context->priv->processCountLimit = limit;
if (context->priv->processModel != WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS)
context->priv->processPool->setMaximumNumberOfProcesses(limit);
}
guint webkit_web_context_get_web_process_count_limit(WebKitWebContext* context)
{
g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
return context->priv->processCountLimit;
}
static void addOriginToMap(WebKitSecurityOrigin* origin, HashMap<String, bool>* map, bool allowed)
{
String string = webkitSecurityOriginGetSecurityOrigin(origin).toString();
if (string != "null")
map->set(string, allowed);
}
void webkit_web_context_initialize_notification_permissions(WebKitWebContext* context, GList* allowedOrigins, GList* disallowedOrigins)
{
HashMap<String, bool> map;
g_list_foreach(allowedOrigins, [](gpointer data, gpointer userData) {
addOriginToMap(static_cast<WebKitSecurityOrigin*>(data), static_cast<HashMap<String, bool>*>(userData), true);
}, &map);
g_list_foreach(disallowedOrigins, [](gpointer data, gpointer userData) {
addOriginToMap(static_cast<WebKitSecurityOrigin*>(data), static_cast<HashMap<String, bool>*>(userData), false);
}, &map);
context->priv->notificationProvider->setNotificationPermissions(WTFMove(map));
}
void webkitWebContextInitializeNotificationPermissions(WebKitWebContext* context)
{
g_signal_emit(context, signals[INITIALIZE_NOTIFICATION_PERMISSIONS], 0);
}
WebKitDownload* webkitWebContextGetOrCreateDownload(DownloadProxy* downloadProxy)
{
GRefPtr<WebKitDownload> download = downloadsMap().get(downloadProxy);
if (download)
return download.get();
download = adoptGRef(webkitDownloadCreate(downloadProxy));
downloadsMap().set(downloadProxy, download.get());
return download.get();
}
WebKitDownload* webkitWebContextStartDownload(WebKitWebContext* context, const char* uri, WebPageProxy* initiatingPage)
{
WebCore::ResourceRequest request(String::fromUTF8(uri));
return webkitWebContextGetOrCreateDownload(context->priv->processPool->download(initiatingPage, request));
}
void webkitWebContextRemoveDownload(DownloadProxy* downloadProxy)
{
downloadsMap().remove(downloadProxy);
}
void webkitWebContextDownloadStarted(WebKitWebContext* context, WebKitDownload* download)
{
g_signal_emit(context, signals[DOWNLOAD_STARTED], 0, download);
}
GVariant* webkitWebContextInitializeWebExtensions(WebKitWebContext* context)
{
g_signal_emit(context, signals[INITIALIZE_WEB_EXTENSIONS], 0);
return g_variant_new("(msmv)",
context->priv->webExtensionsDirectory.data(),
context->priv->webExtensionsInitializationUserData.get());
}
WebProcessPool& webkitWebContextGetProcessPool(WebKitWebContext* context)
{
g_assert(WEBKIT_IS_WEB_CONTEXT(context));
return *context->priv->processPool;
}
void webkitWebContextStartLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID, const WebCore::ResourceRequest& resourceRequest, LegacyCustomProtocolManagerProxy& manager)
{
GRefPtr<WebKitURISchemeRequest> request = adoptGRef(webkitURISchemeRequestCreate(customProtocolID, context, resourceRequest, manager));
String scheme(String::fromUTF8(webkit_uri_scheme_request_get_scheme(request.get())));
RefPtr<WebKitURISchemeHandler> handler = context->priv->uriSchemeHandlers.get(scheme);
ASSERT(handler.get());
if (!handler->hasCallback())
return;
context->priv->uriSchemeRequests.set(customProtocolID, request.get());
handler->performCallback(request.get());
}
void webkitWebContextStopLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID)
{
GRefPtr<WebKitURISchemeRequest> request = context->priv->uriSchemeRequests.get(customProtocolID);
if (!request.get())
return;
webkitURISchemeRequestCancel(request.get());
}
void webkitWebContextInvalidateCustomProtocolRequests(WebKitWebContext* context, LegacyCustomProtocolManagerProxy& manager)
{
for (auto& request : copyToVector(context->priv->uriSchemeRequests.values())) {
if (webkitURISchemeRequestGetManager(request.get()) == &manager)
webkitURISchemeRequestInvalidate(request.get());
}
}
void webkitWebContextDidFinishLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID)
{
context->priv->uriSchemeRequests.remove(customProtocolID);
}
bool webkitWebContextIsLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID)
{
return context->priv->uriSchemeRequests.get(customProtocolID);
}
void webkitWebContextCreatePageForWebView(WebKitWebContext* context, WebKitWebView* webView, WebKitUserContentManager* userContentManager, WebKitWebView* relatedView)
{
webkitWebContextEnableIconDatabasePrivateBrowsingIfNeeded(context, webView);
auto pageConfiguration = API::PageConfiguration::create();
pageConfiguration->setProcessPool(context->priv->processPool.get());
pageConfiguration->setPreferences(webkitSettingsGetPreferences(webkit_web_view_get_settings(webView)));
pageConfiguration->setRelatedPage(relatedView ? &webkitWebViewGetPage(relatedView) : nullptr);
pageConfiguration->setUserContentController(userContentManager ? webkitUserContentManagerGetUserContentControllerProxy(userContentManager) : nullptr);
pageConfiguration->setControlledByAutomation(webkit_web_view_is_controlled_by_automation(webView));
WebKitWebsiteDataManager* manager = webkitWebViewGetWebsiteDataManager(webView);
if (!manager)
manager = context->priv->websiteDataManager.get();
pageConfiguration->setWebsiteDataStore(&webkitWebsiteDataManagerGetDataStore(manager));
pageConfiguration->setSessionID(pageConfiguration->websiteDataStore()->websiteDataStore().sessionID());
webkitWebViewCreatePage(webView, WTFMove(pageConfiguration));
context->priv->webViews.set(webkit_web_view_get_page_id(webView), webView);
}
void webkitWebContextWebViewDestroyed(WebKitWebContext* context, WebKitWebView* webView)
{
webkitWebContextDisableIconDatabasePrivateBrowsingIfNeeded(context, webView);
context->priv->webViews.remove(webkit_web_view_get_page_id(webView));
}
WebKitWebView* webkitWebContextGetWebViewForPage(WebKitWebContext* context, WebPageProxy* page)
{
return page ? context->priv->webViews.get(page->pageID()) : 0;
}