1/*
2 * Copyright (C) 2011 Igalia S.L.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20#include "config.h"
21#include "WebKitWebContext.h"
22
23#include "APIString.h"
24#include "WebBatteryManagerProxy.h"
25#include "WebCertificateInfo.h"
26#include "WebCookieManagerProxy.h"
27#include "WebGeolocationManagerProxy.h"
28#include "WebKitBatteryProvider.h"
29#include "WebKitCertificateInfoPrivate.h"
30#include "WebKitCookieManagerPrivate.h"
31#include "WebKitDownloadClient.h"
32#include "WebKitDownloadPrivate.h"
33#include "WebKitFaviconDatabasePrivate.h"
34#include "WebKitGeolocationProvider.h"
35#include "WebKitInjectedBundleClient.h"
36#include "WebKitPluginPrivate.h"
37#include "WebKitPrivate.h"
38#include "WebKitRequestManagerClient.h"
39#include "WebKitSecurityManagerPrivate.h"
40#include "WebKitTextChecker.h"
41#include "WebKitURISchemeRequestPrivate.h"
42#include "WebKitUserContentManagerPrivate.h"
43#include "WebKitWebContextPrivate.h"
44#include "WebKitWebViewBasePrivate.h"
45#include "WebKitWebViewGroupPrivate.h"
46#include "WebResourceCacheManagerProxy.h"
47#include <WebCore/FileSystem.h>
48#include <WebCore/IconDatabase.h>
49#include <WebCore/Language.h>
50#include <memory>
51#include <wtf/HashMap.h>
52#include <wtf/NeverDestroyed.h>
53#include <wtf/PassRefPtr.h>
54#include <wtf/RefCounted.h>
55#include <wtf/gobject/GRefPtr.h>
56#include <wtf/gobject/GUniquePtr.h>
57#include <wtf/text/CString.h>
58
59using namespace WebKit;
60
61/**
62 * SECTION: WebKitWebContext
63 * @Short_description: Manages aspects common to all #WebKitWebView<!-- -->s
64 * @Title: WebKitWebContext
65 *
66 * The #WebKitWebContext manages all aspects common to all
67 * #WebKitWebView<!-- -->s.
68 *
69 * You can define the #WebKitCacheModel and #WebKitProcessModel with
70 * webkit_web_context_set_cache_model() and
71 * webkit_web_context_set_process_model(), depending on the needs of
72 * your application. You can access the #WebKitCookieManager or the
73 * #WebKitSecurityManager to specify the behaviour of your application
74 * regarding cookies and security, using
75 * webkit_web_context_get_cookie_manager() and
76 * webkit_web_context_get_security_manager() for that.
77 *
78 * It is also possible to change your preferred language or enable
79 * spell checking, using webkit_web_context_set_preferred_languages(),
80 * webkit_web_context_set_spell_checking_languages() and
81 * webkit_web_context_set_spell_checking_enabled().
82 *
83 * You can use webkit_web_context_register_uri_scheme() to register
84 * custom URI schemes, and manage several other settings.
85 *
86 */
87
88enum {
89    DOWNLOAD_STARTED,
90    INITIALIZE_WEB_EXTENSIONS,
91
92    LAST_SIGNAL
93};
94
95class WebKitURISchemeHandler: public RefCounted<WebKitURISchemeHandler> {
96public:
97    WebKitURISchemeHandler()
98        : m_callback(0)
99        , m_userData(0)
100        , m_destroyNotify(0)
101    {
102    }
103    WebKitURISchemeHandler(WebKitURISchemeRequestCallback callback, void* userData, GDestroyNotify destroyNotify)
104        : m_callback(callback)
105        , m_userData(userData)
106        , m_destroyNotify(destroyNotify)
107    {
108    }
109
110    ~WebKitURISchemeHandler()
111    {
112        if (m_destroyNotify)
113            m_destroyNotify(m_userData);
114    }
115
116    bool hasCallback()
117    {
118        return m_callback;
119    }
120
121    void performCallback(WebKitURISchemeRequest* request)
122    {
123        ASSERT(m_callback);
124
125        m_callback(request, m_userData);
126    }
127
128private:
129    WebKitURISchemeRequestCallback m_callback;
130    void* m_userData;
131    GDestroyNotify m_destroyNotify;
132};
133
134typedef HashMap<String, RefPtr<WebKitURISchemeHandler> > URISchemeHandlerMap;
135typedef HashMap<uint64_t, GRefPtr<WebKitURISchemeRequest> > URISchemeRequestMap;
136
137struct _WebKitWebContextPrivate {
138    RefPtr<WebContext> context;
139
140    GRefPtr<WebKitCookieManager> cookieManager;
141    GRefPtr<WebKitFaviconDatabase> faviconDatabase;
142    GRefPtr<WebKitSecurityManager> securityManager;
143    RefPtr<WebSoupCustomProtocolRequestManager> requestManager;
144    URISchemeHandlerMap uriSchemeHandlers;
145    URISchemeRequestMap uriSchemeRequests;
146#if ENABLE(GEOLOCATION)
147    RefPtr<WebKitGeolocationProvider> geolocationProvider;
148#endif
149#if ENABLE(BATTERY_STATUS)
150    RefPtr<WebKitBatteryProvider> batteryProvider;
151#endif
152#if ENABLE(SPELLCHECK)
153    std::unique_ptr<WebKitTextChecker> textChecker;
154#endif
155    CString faviconDatabaseDirectory;
156    WebKitTLSErrorsPolicy tlsErrorsPolicy;
157
158    HashMap<uint64_t, WebKitWebView*> webViews;
159    GRefPtr<WebKitWebViewGroup> defaultWebViewGroup;
160
161    CString webExtensionsDirectory;
162    GRefPtr<GVariant> webExtensionsInitializationUserData;
163};
164
165static guint signals[LAST_SIGNAL] = { 0, };
166
167WEBKIT_DEFINE_TYPE(WebKitWebContext, webkit_web_context, G_TYPE_OBJECT)
168
169static inline WebKit::ProcessModel toProcessModel(WebKitProcessModel webKitProcessModel)
170{
171    switch (webKitProcessModel) {
172    case WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS:
173        return ProcessModelSharedSecondaryProcess;
174    case WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES:
175        return ProcessModelMultipleSecondaryProcesses;
176    default:
177        ASSERT_NOT_REACHED();
178        return ProcessModelSharedSecondaryProcess;
179    }
180}
181
182static inline WebKitProcessModel toWebKitProcessModel(WebKit::ProcessModel processModel)
183{
184    switch (processModel) {
185    case ProcessModelSharedSecondaryProcess:
186        return WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS;
187    case ProcessModelMultipleSecondaryProcesses:
188        return WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES;
189    default:
190        ASSERT_NOT_REACHED();
191        return WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS;
192    }
193}
194
195static void webkit_web_context_class_init(WebKitWebContextClass* webContextClass)
196{
197    GObjectClass* gObjectClass = G_OBJECT_CLASS(webContextClass);
198
199    /**
200     * WebKitWebContext::download-started:
201     * @context: the #WebKitWebContext
202     * @download: the #WebKitDownload associated with this event
203     *
204     * This signal is emitted when a new download request is made.
205     */
206    signals[DOWNLOAD_STARTED] =
207        g_signal_new("download-started",
208                     G_TYPE_FROM_CLASS(gObjectClass),
209                     G_SIGNAL_RUN_LAST,
210                     0, 0, 0,
211                     g_cclosure_marshal_VOID__OBJECT,
212                     G_TYPE_NONE, 1,
213                     WEBKIT_TYPE_DOWNLOAD);
214
215    /**
216     * WebKitWebContext::initialize-web-extensions:
217     * @context: the #WebKitWebContext
218     *
219     * This signal is emitted when a new web process is about to be
220     * launched. It signals the most appropriate moment to use
221     * webkit_web_context_set_web_extensions_initialization_user_data()
222     * and webkit_web_context_set_web_extensions_directory().
223     *
224     * Since: 2.4
225     */
226    signals[INITIALIZE_WEB_EXTENSIONS] =
227        g_signal_new("initialize-web-extensions",
228            G_TYPE_FROM_CLASS(gObjectClass),
229            G_SIGNAL_RUN_LAST,
230            0, nullptr, nullptr,
231            g_cclosure_marshal_VOID__VOID,
232            G_TYPE_NONE, 0);
233}
234
235static CString injectedBundleDirectory()
236{
237    const char* bundleDirectory = g_getenv("WEBKIT_INJECTED_BUNDLE_PATH");
238    if (bundleDirectory && g_file_test(bundleDirectory, G_FILE_TEST_IS_DIR))
239        return bundleDirectory;
240
241    static const char* injectedBundlePath = LIBDIR G_DIR_SEPARATOR_S "webkit2gtk-" WEBKITGTK_API_VERSION_STRING
242        G_DIR_SEPARATOR_S "injected-bundle" G_DIR_SEPARATOR_S;
243    return injectedBundlePath;
244}
245
246static CString injectedBundleFilename()
247{
248    GUniquePtr<char> bundleFilename(g_build_filename(injectedBundleDirectory().data(), "libwebkit2gtkinjectedbundle.so", NULL));
249    return bundleFilename.get();
250}
251
252static gpointer createDefaultWebContext(gpointer)
253{
254    static GRefPtr<WebKitWebContext> webContext = adoptGRef(WEBKIT_WEB_CONTEXT(g_object_new(WEBKIT_TYPE_WEB_CONTEXT, NULL)));
255    WebKitWebContextPrivate* priv = webContext->priv;
256
257    WebContextConfiguration webContextConfiguration;
258    webContextConfiguration.injectedBundlePath = WebCore::filenameToString(injectedBundleFilename().data());
259    WebContext::applyPlatformSpecificConfigurationDefaults(webContextConfiguration);
260    priv->context = WebContext::create(WTF::move(webContextConfiguration));
261
262    priv->requestManager = webContext->priv->context->supplement<WebSoupCustomProtocolRequestManager>();
263    priv->context->setCacheModel(CacheModelPrimaryWebBrowser);
264    priv->tlsErrorsPolicy = WEBKIT_TLS_ERRORS_POLICY_IGNORE;
265
266    attachInjectedBundleClientToContext(webContext.get());
267    attachDownloadClientToContext(webContext.get());
268    attachRequestManagerClientToContext(webContext.get());
269
270#if ENABLE(GEOLOCATION)
271    priv->geolocationProvider = WebKitGeolocationProvider::create(priv->context->supplement<WebGeolocationManagerProxy>());
272#endif
273#if ENABLE(BATTERY_STATUS)
274    priv->batteryProvider = WebKitBatteryProvider::create(priv->context->supplement<WebBatteryManagerProxy>());
275#endif
276#if ENABLE(SPELLCHECK)
277    priv->textChecker = WebKitTextChecker::create();
278#endif
279    return webContext.get();
280}
281
282/**
283 * webkit_web_context_get_default:
284 *
285 * Gets the default web context
286 *
287 * Returns: (transfer none): a #WebKitWebContext
288 */
289WebKitWebContext* webkit_web_context_get_default(void)
290{
291    static GOnce onceInit = G_ONCE_INIT;
292    return WEBKIT_WEB_CONTEXT(g_once(&onceInit, createDefaultWebContext, 0));
293}
294
295/**
296 * webkit_web_context_set_cache_model:
297 * @context: the #WebKitWebContext
298 * @cache_model: a #WebKitCacheModel
299 *
300 * Specifies a usage model for WebViews, which WebKit will use to
301 * determine its caching behavior. All web views follow the cache
302 * model. This cache model determines the RAM and disk space to use
303 * for caching previously viewed content .
304 *
305 * Research indicates that users tend to browse within clusters of
306 * documents that hold resources in common, and to revisit previously
307 * visited documents. WebKit and the frameworks below it include
308 * built-in caches that take advantage of these patterns,
309 * substantially improving document load speed in browsing
310 * situations. The WebKit cache model controls the behaviors of all of
311 * these caches, including various WebCore caches.
312 *
313 * Browsers can improve document load speed substantially by
314 * specifying %WEBKIT_CACHE_MODEL_WEB_BROWSER. Applications without a
315 * browsing interface can reduce memory usage substantially by
316 * specifying %WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER. The default value is
317 * %WEBKIT_CACHE_MODEL_WEB_BROWSER.
318 */
319void webkit_web_context_set_cache_model(WebKitWebContext* context, WebKitCacheModel model)
320{
321    CacheModel cacheModel;
322
323    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
324
325    switch (model) {
326    case WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER:
327        cacheModel = CacheModelDocumentViewer;
328        break;
329    case WEBKIT_CACHE_MODEL_WEB_BROWSER:
330        cacheModel = CacheModelPrimaryWebBrowser;
331        break;
332    case WEBKIT_CACHE_MODEL_DOCUMENT_BROWSER:
333        cacheModel = CacheModelDocumentBrowser;
334        break;
335    default:
336        g_assert_not_reached();
337    }
338
339    if (cacheModel != context->priv->context->cacheModel())
340        context->priv->context->setCacheModel(cacheModel);
341}
342
343/**
344 * webkit_web_context_get_cache_model:
345 * @context: the #WebKitWebContext
346 *
347 * Returns the current cache model. For more information about this
348 * value check the documentation of the function
349 * webkit_web_context_set_cache_model().
350 *
351 * Returns: the current #WebKitCacheModel
352 */
353WebKitCacheModel webkit_web_context_get_cache_model(WebKitWebContext* context)
354{
355    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), WEBKIT_CACHE_MODEL_WEB_BROWSER);
356
357    switch (context->priv->context->cacheModel()) {
358    case CacheModelDocumentViewer:
359        return WEBKIT_CACHE_MODEL_DOCUMENT_VIEWER;
360    case CacheModelPrimaryWebBrowser:
361        return WEBKIT_CACHE_MODEL_WEB_BROWSER;
362    case CacheModelDocumentBrowser:
363        return WEBKIT_CACHE_MODEL_DOCUMENT_BROWSER;
364    default:
365        g_assert_not_reached();
366    }
367
368    return WEBKIT_CACHE_MODEL_WEB_BROWSER;
369}
370
371/**
372 * webkit_web_context_clear_cache:
373 * @context: a #WebKitWebContext
374 *
375 * Clears all resources currently cached.
376 * See also webkit_web_context_set_cache_model().
377 */
378void webkit_web_context_clear_cache(WebKitWebContext* context)
379{
380    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
381
382    context->priv->context->supplement<WebResourceCacheManagerProxy>()->clearCacheForAllOrigins(AllResourceCaches);
383}
384
385typedef HashMap<DownloadProxy*, GRefPtr<WebKitDownload> > DownloadsMap;
386
387static DownloadsMap& downloadsMap()
388{
389    static NeverDestroyed<DownloadsMap> downloads;
390    return downloads;
391}
392
393/**
394 * webkit_web_context_download_uri:
395 * @context: a #WebKitWebContext
396 * @uri: the URI to download
397 *
398 * Requests downloading of the specified URI string. The download operation
399 * will not be associated to any #WebKitWebView, if you are interested in
400 * starting a download from a particular #WebKitWebView use
401 * webkit_web_view_download_uri() instead.
402 *
403 * Returns: (transfer full): a new #WebKitDownload representing the
404 *    the download operation.
405 */
406WebKitDownload* webkit_web_context_download_uri(WebKitWebContext* context, const gchar* uri)
407{
408    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
409    g_return_val_if_fail(uri, 0);
410
411    return webkitWebContextStartDownload(context, uri, 0);
412}
413
414/**
415 * webkit_web_context_get_cookie_manager:
416 * @context: a #WebKitWebContext
417 *
418 * Get the #WebKitCookieManager of @context.
419 *
420 * Returns: (transfer none): the #WebKitCookieManager of @context.
421 */
422WebKitCookieManager* webkit_web_context_get_cookie_manager(WebKitWebContext* context)
423{
424    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
425
426    WebKitWebContextPrivate* priv = context->priv;
427    if (!priv->cookieManager)
428        priv->cookieManager = adoptGRef(webkitCookieManagerCreate(priv->context->supplement<WebCookieManagerProxy>()));
429
430    return priv->cookieManager.get();
431}
432
433static void ensureFaviconDatabase(WebKitWebContext* context)
434{
435    WebKitWebContextPrivate* priv = context->priv;
436    if (priv->faviconDatabase)
437        return;
438
439    priv->faviconDatabase = adoptGRef(webkitFaviconDatabaseCreate(priv->context->iconDatabase()));
440}
441
442/**
443 * webkit_web_context_set_favicon_database_directory:
444 * @context: a #WebKitWebContext
445 * @path: (allow-none): an absolute path to the icon database
446 * directory or %NULL to use the defaults
447 *
448 * Set the directory path to be used to store the favicons database
449 * for @context on disk. Passing %NULL as @path means using the
450 * default directory for the platform (see g_get_user_data_dir()).
451 *
452 * Calling this method also means enabling the favicons database for
453 * its use from the applications, so that's why it's expected to be
454 * called only once. Further calls for the same instance of
455 * #WebKitWebContext won't cause any effect.
456 */
457void webkit_web_context_set_favicon_database_directory(WebKitWebContext* context, const gchar* path)
458{
459    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
460
461    WebKitWebContextPrivate* priv = context->priv;
462    WebIconDatabase* iconDatabase = priv->context->iconDatabase();
463    if (iconDatabase->isOpen())
464        return;
465
466    ensureFaviconDatabase(context);
467
468    // Use default if 0 is passed as parameter.
469    String directoryPath = WebCore::filenameToString(path);
470    priv->faviconDatabaseDirectory = directoryPath.isEmpty()
471        ? priv->context->iconDatabasePath().utf8()
472        : directoryPath.utf8();
473
474    // Build the full path to the icon database file on disk.
475    GUniquePtr<gchar> faviconDatabasePath(g_build_filename(priv->faviconDatabaseDirectory.data(),
476        WebCore::IconDatabase::defaultDatabaseFilename().utf8().data(), nullptr));
477
478    // Setting the path will cause the icon database to be opened.
479    priv->context->setIconDatabasePath(WebCore::filenameToString(faviconDatabasePath.get()));
480}
481
482/**
483 * webkit_web_context_get_favicon_database_directory:
484 * @context: a #WebKitWebContext
485 *
486 * Get the directory path being used to store the favicons database
487 * for @context, or %NULL if
488 * webkit_web_context_set_favicon_database_directory() hasn't been
489 * called yet.
490 *
491 * This function will always return the same path after having called
492 * webkit_web_context_set_favicon_database_directory() for the first
493 * time.
494 *
495 * Returns: (transfer none): the path of the directory of the favicons
496 * database associated with @context, or %NULL.
497 */
498const gchar* webkit_web_context_get_favicon_database_directory(WebKitWebContext *context)
499{
500    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
501
502    WebKitWebContextPrivate* priv = context->priv;
503    if (priv->faviconDatabaseDirectory.isNull())
504        return 0;
505
506    return priv->faviconDatabaseDirectory.data();
507}
508
509/**
510 * webkit_web_context_get_favicon_database:
511 * @context: a #WebKitWebContext
512 *
513 * Get the #WebKitFaviconDatabase associated with @context.
514 *
515 * To initialize the database you need to call
516 * webkit_web_context_set_favicon_database_directory().
517 *
518 * Returns: (transfer none): the #WebKitFaviconDatabase of @context.
519 */
520WebKitFaviconDatabase* webkit_web_context_get_favicon_database(WebKitWebContext* context)
521{
522    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
523
524    ensureFaviconDatabase(context);
525    return context->priv->faviconDatabase.get();
526}
527
528/**
529 * webkit_web_context_get_security_manager:
530 * @context: a #WebKitWebContext
531 *
532 * Get the #WebKitSecurityManager of @context.
533 *
534 * Returns: (transfer none): the #WebKitSecurityManager of @context.
535 */
536WebKitSecurityManager* webkit_web_context_get_security_manager(WebKitWebContext* context)
537{
538    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
539
540    WebKitWebContextPrivate* priv = context->priv;
541    if (!priv->securityManager)
542        priv->securityManager = adoptGRef(webkitSecurityManagerCreate(context));
543
544    return priv->securityManager.get();
545}
546
547/**
548 * webkit_web_context_set_additional_plugins_directory:
549 * @context: a #WebKitWebContext
550 * @directory: the directory to add
551 *
552 * Set an additional directory where WebKit will look for plugins.
553 */
554void webkit_web_context_set_additional_plugins_directory(WebKitWebContext* context, const char* directory)
555{
556    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
557    g_return_if_fail(directory);
558
559    context->priv->context->setAdditionalPluginsDirectory(WebCore::filenameToString(directory));
560}
561
562static void destroyPluginList(GList* plugins)
563{
564    g_list_free_full(plugins, g_object_unref);
565}
566
567static void webkitWebContextGetPluginThread(GTask* task, gpointer object, gpointer /* taskData */, GCancellable*)
568{
569    Vector<PluginModuleInfo> plugins = WEBKIT_WEB_CONTEXT(object)->priv->context->pluginInfoStore().plugins();
570    GList* returnValue = 0;
571    for (size_t i = 0; i < plugins.size(); ++i)
572        returnValue = g_list_prepend(returnValue, webkitPluginCreate(plugins[i]));
573    g_task_return_pointer(task, returnValue, reinterpret_cast<GDestroyNotify>(destroyPluginList));
574}
575
576/**
577 * webkit_web_context_get_plugins:
578 * @context: a #WebKitWebContext
579 * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
580 * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
581 * @user_data: (closure): the data to pass to callback function
582 *
583 * Asynchronously get the list of installed plugins.
584 *
585 * When the operation is finished, @callback will be called. You can then call
586 * webkit_web_context_get_plugins_finish() to get the result of the operation.
587 */
588void webkit_web_context_get_plugins(WebKitWebContext* context, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
589{
590    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
591
592    GRefPtr<GTask> task = adoptGRef(g_task_new(context, cancellable, callback, userData));
593    g_task_run_in_thread(task.get(), webkitWebContextGetPluginThread);
594}
595
596/**
597 * webkit_web_context_get_plugins_finish:
598 * @context: a #WebKitWebContext
599 * @result: a #GAsyncResult
600 * @error: return location for error or %NULL to ignore
601 *
602 * Finish an asynchronous operation started with webkit_web_context_get_plugins.
603 *
604 * Returns: (element-type WebKitPlugin) (transfer full): a #GList of #WebKitPlugin. You must free the #GList with
605 *    g_list_free() and unref the #WebKitPlugin<!-- -->s with g_object_unref() when you're done with them.
606 */
607GList* webkit_web_context_get_plugins_finish(WebKitWebContext* context, GAsyncResult* result, GError** error)
608{
609    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
610    g_return_val_if_fail(g_task_is_valid(result, context), 0);
611
612    return static_cast<GList*>(g_task_propagate_pointer(G_TASK(result), error));
613}
614
615/**
616 * webkit_web_context_register_uri_scheme:
617 * @context: a #WebKitWebContext
618 * @scheme: the network scheme to register
619 * @callback: (scope async): a #WebKitURISchemeRequestCallback
620 * @user_data: data to pass to callback function
621 * @user_data_destroy_func: destroy notify for @user_data
622 *
623 * Register @scheme in @context, so that when an URI request with @scheme is made in the
624 * #WebKitWebContext, the #WebKitURISchemeRequestCallback registered will be called with a
625 * #WebKitURISchemeRequest.
626 * It is possible to handle URI scheme requests asynchronously, by calling g_object_ref() on the
627 * #WebKitURISchemeRequest and calling webkit_uri_scheme_request_finish() later
628 * when the data of the request is available or
629 * webkit_uri_scheme_request_finish_error() in case of error.
630 *
631 * <informalexample><programlisting>
632 * static void
633 * about_uri_scheme_request_cb (WebKitURISchemeRequest *request,
634 *                              gpointer                user_data)
635 * {
636 *     GInputStream *stream;
637 *     gsize         stream_length;
638 *     const gchar  *path;
639 *
640 *     path = webkit_uri_scheme_request_get_path (request);
641 *     if (!g_strcmp0 (path, "plugins")) {
642 *         /<!-- -->* Create a GInputStream with the contents of plugins about page, and set its length to stream_length *<!-- -->/
643 *     } else if (!g_strcmp0 (path, "memory")) {
644 *         /<!-- -->* Create a GInputStream with the contents of memory about page, and set its length to stream_length *<!-- -->/
645 *     } else if (!g_strcmp0 (path, "applications")) {
646 *         /<!-- -->* Create a GInputStream with the contents of applications about page, and set its length to stream_length *<!-- -->/
647 *     } else if (!g_strcmp0 (path, "example")) {
648 *         gchar *contents;
649 *
650 *         contents = g_strdup_printf ("&lt;html&gt;&lt;body&gt;&lt;p&gt;Example about page&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;");
651 *         stream_length = strlen (contents);
652 *         stream = g_memory_input_stream_new_from_data (contents, stream_length, g_free);
653 *     } else {
654 *         GError *error;
655 *
656 *         error = g_error_new (ABOUT_HANDLER_ERROR, ABOUT_HANDLER_ERROR_INVALID, "Invalid about:%s page.", path);
657 *         webkit_uri_scheme_request_finish_error (request, error);
658 *         g_error_free (error);
659 *         return;
660 *     }
661 *     webkit_uri_scheme_request_finish (request, stream, stream_length, "text/html");
662 *     g_object_unref (stream);
663 * }
664 * </programlisting></informalexample>
665 */
666void webkit_web_context_register_uri_scheme(WebKitWebContext* context, const char* scheme, WebKitURISchemeRequestCallback callback, gpointer userData, GDestroyNotify destroyNotify)
667{
668    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
669    g_return_if_fail(scheme);
670    g_return_if_fail(callback);
671
672    RefPtr<WebKitURISchemeHandler> handler = adoptRef(new WebKitURISchemeHandler(callback, userData, destroyNotify));
673    context->priv->uriSchemeHandlers.set(String::fromUTF8(scheme), handler.get());
674    context->priv->requestManager->registerSchemeForCustomProtocol(String::fromUTF8(scheme));
675}
676
677/**
678 * webkit_web_context_get_spell_checking_enabled:
679 * @context: a #WebKitWebContext
680 *
681 * Get whether spell checking feature is currently enabled.
682 *
683 * Returns: %TRUE If spell checking is enabled, or %FALSE otherwise.
684 */
685gboolean webkit_web_context_get_spell_checking_enabled(WebKitWebContext* context)
686{
687    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), FALSE);
688
689#if ENABLE(SPELLCHECK)
690    return context->priv->textChecker->isSpellCheckingEnabled();
691#else
692    return false;
693#endif
694}
695
696/**
697 * webkit_web_context_set_spell_checking_enabled:
698 * @context: a #WebKitWebContext
699 * @enabled: Value to be set
700 *
701 * Enable or disable the spell checking feature.
702 */
703void webkit_web_context_set_spell_checking_enabled(WebKitWebContext* context, gboolean enabled)
704{
705    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
706
707#if ENABLE(SPELLCHECK)
708    context->priv->textChecker->setSpellCheckingEnabled(enabled);
709#endif
710}
711
712/**
713 * webkit_web_context_get_spell_checking_languages:
714 * @context: a #WebKitWebContext
715 *
716 * Get the the list of spell checking languages associated with
717 * @context, or %NULL if no languages have been previously set.
718 *
719 * See webkit_web_context_set_spell_checking_languages() for more
720 * details on the format of the languages in the list.
721 *
722 * Returns: (array zero-terminated=1) (element-type utf8) (transfer none): A %NULL-terminated
723 *    array of languages if available, or %NULL otherwise.
724 */
725const gchar* const* webkit_web_context_get_spell_checking_languages(WebKitWebContext* context)
726{
727    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), 0);
728
729#if ENABLE(SPELLCHECK)
730    return context->priv->textChecker->getSpellCheckingLanguages();
731#else
732    return 0;
733#endif
734}
735
736/**
737 * webkit_web_context_set_spell_checking_languages:
738 * @context: a #WebKitWebContext
739 * @languages: (array zero-terminated=1) (transfer none): a %NULL-terminated list of spell checking languages
740 *
741 * Set the list of spell checking languages to be used for spell
742 * checking.
743 *
744 * The locale string typically is in the form lang_COUNTRY, where lang
745 * is an ISO-639 language code, and COUNTRY is an ISO-3166 country code.
746 * For instance, sv_FI for Swedish as written in Finland or pt_BR
747 * for Portuguese as written in Brazil.
748 *
749 * You need to call this function with a valid list of languages at
750 * least once in order to properly enable the spell checking feature
751 * in WebKit.
752 */
753void webkit_web_context_set_spell_checking_languages(WebKitWebContext* context, const gchar* const* languages)
754{
755    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
756    g_return_if_fail(languages);
757
758#if ENABLE(SPELLCHECK)
759    context->priv->textChecker->setSpellCheckingLanguages(languages);
760#endif
761}
762
763/**
764 * webkit_web_context_set_preferred_languages:
765 * @context: a #WebKitWebContext
766 * @languages: (allow-none) (array zero-terminated=1) (element-type utf8) (transfer none): a %NULL-terminated list of language identifiers
767 *
768 * Set the list of preferred languages, sorted from most desirable
769 * to least desirable. The list will be used to build the "Accept-Language"
770 * header that will be included in the network requests started by
771 * the #WebKitWebContext.
772 */
773void webkit_web_context_set_preferred_languages(WebKitWebContext* context, const gchar* const* languageList)
774{
775    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
776
777    if (!languageList || !g_strv_length(const_cast<char**>(languageList)))
778        return;
779
780    Vector<String> languages;
781    for (size_t i = 0; languageList[i]; ++i)
782        languages.append(String::fromUTF8(languageList[i]).lower().replace("_", "-"));
783
784    WebCore::overrideUserPreferredLanguages(languages);
785    WebCore::languageDidChange();
786}
787
788/**
789 * webkit_web_context_set_tls_errors_policy:
790 * @context: a #WebKitWebContext
791 * @policy: a #WebKitTLSErrorsPolicy
792 *
793 * Set the TLS errors policy of @context as @policy
794 */
795void webkit_web_context_set_tls_errors_policy(WebKitWebContext* context, WebKitTLSErrorsPolicy policy)
796{
797    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
798
799    if (context->priv->tlsErrorsPolicy == policy)
800        return;
801
802    context->priv->tlsErrorsPolicy = policy;
803    bool ignoreTLSErrors = policy == WEBKIT_TLS_ERRORS_POLICY_IGNORE;
804    if (context->priv->context->ignoreTLSErrors() != ignoreTLSErrors)
805        context->priv->context->setIgnoreTLSErrors(ignoreTLSErrors);
806}
807
808/**
809 * webkit_web_context_get_tls_errors_policy:
810 * @context: a #WebKitWebContext
811 *
812 * Get the TLS errors policy of @context
813 *
814 * Returns: a #WebKitTLSErrorsPolicy
815 */
816WebKitTLSErrorsPolicy webkit_web_context_get_tls_errors_policy(WebKitWebContext* context)
817{
818    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), WEBKIT_TLS_ERRORS_POLICY_IGNORE);
819
820    return context->priv->tlsErrorsPolicy;
821}
822
823/**
824 * webkit_web_context_set_web_extensions_directory:
825 * @context: a #WebKitWebContext
826 * @directory: the directory to add
827 *
828 * Set the directory where WebKit will look for Web Extensions.
829 * This method must be called before loading anything in this context,
830 * otherwise it will not have any effect. You can connect to
831 * #WebKitWebContext::initialize-web-extensions to call this method
832 * before anything is loaded.
833 */
834void webkit_web_context_set_web_extensions_directory(WebKitWebContext* context, const char* directory)
835{
836    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
837    g_return_if_fail(directory);
838
839    context->priv->webExtensionsDirectory = directory;
840}
841
842/**
843 * webkit_web_context_set_web_extensions_initialization_user_data:
844 * @context: a #WebKitWebContext
845 * @user_data: a #GVariant
846 *
847 * Set user data to be passed to Web Extensions on initialization.
848 * The data will be passed to the
849 * #WebKitWebExtensionInitializeWithUserDataFunction.
850 * This method must be called before loading anything in this context,
851 * otherwise it will not have any effect. You can connect to
852 * #WebKitWebContext::initialize-web-extensions to call this method
853 * before anything is loaded.
854 *
855 * Since: 2.4
856 */
857void webkit_web_context_set_web_extensions_initialization_user_data(WebKitWebContext* context, GVariant* userData)
858{
859    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
860    g_return_if_fail(userData);
861
862    context->priv->webExtensionsInitializationUserData = userData;
863}
864
865/**
866 * webkit_web_context_set_disk_cache_directory:
867 * @context: a #WebKitWebContext
868 * @directory: the directory to set
869 *
870 * Set the directory where disk cache files will be stored
871 * This method must be called before loading anything in this context, otherwise
872 * it will not have any effect.
873 */
874void webkit_web_context_set_disk_cache_directory(WebKitWebContext* context, const char* directory)
875{
876    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
877    g_return_if_fail(directory);
878
879    context->priv->context->setDiskCacheDirectory(WebCore::filenameToString(directory));
880}
881
882/**
883 * webkit_web_context_prefetch_dns:
884 * @context: a #WebKitWebContext
885 * @hostname: a hostname to be resolved
886 *
887 * Resolve the domain name of the given @hostname in advance, so that if a URI
888 * of @hostname is requested the load will be performed more quickly.
889 */
890void webkit_web_context_prefetch_dns(WebKitWebContext* context, const char* hostname)
891{
892    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
893    g_return_if_fail(hostname);
894
895    ImmutableDictionary::MapType message;
896    message.set(String::fromUTF8("Hostname"), API::String::create(String::fromUTF8(hostname)));
897    context->priv->context->postMessageToInjectedBundle(String::fromUTF8("PrefetchDNS"), ImmutableDictionary::create(WTF::move(message)).get());
898}
899
900/**
901 * webkit_web_context_allow_tls_certificate_for_host:
902 * @context: a #WebKitWebContext
903 * @info: a #WebKitCertificateInfo
904 * @host: the host for which a certificate is to be allowed
905 *
906 * Ignore further TLS errors on the @host for the certificate present in @info.
907 *
908 * Since: 2.4
909 */
910void webkit_web_context_allow_tls_certificate_for_host(WebKitWebContext* context, WebKitCertificateInfo* info, const gchar* host)
911{
912    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
913    g_return_if_fail(info);
914    g_return_if_fail(host);
915
916    RefPtr<WebCertificateInfo> webCertificateInfo = WebCertificateInfo::create(webkitCertificateInfoGetCertificateInfo(info));
917    context->priv->context->allowSpecificHTTPSCertificateForHost(webCertificateInfo.get(), String::fromUTF8(host));
918}
919
920/**
921 * webkit_web_context_set_process_model:
922 * @context: the #WebKitWebContext
923 * @process_model: a #WebKitProcessModel
924 *
925 * Specifies a process model for WebViews, which WebKit will use to
926 * determine how auxiliary processes are handled. The default setting
927 * (%WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS) is suitable for most
928 * applications which embed a small amount of WebViews, or are used to
929 * display documents which are considered safe — like local files.
930 *
931 * Applications which may potentially use a large amount of WebViews
932 * —for example a multi-tabbed web browser— may want to use
933 * %WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES, which will use
934 * one process per view most of the time, while still allowing for web
935 * views to share a process when needed (for example when different
936 * views interact with each other). Using this model, when a process
937 * hangs or crashes, only the WebViews using it stop working, while
938 * the rest of the WebViews in the application will still function
939 * normally.
940 *
941 * This method **must be called before any other functions**,
942 * as early as possible in your application. Calling it later will make
943 * your application crash.
944 *
945 * Since: 2.4
946 */
947void webkit_web_context_set_process_model(WebKitWebContext* context, WebKitProcessModel processModel)
948{
949    g_return_if_fail(WEBKIT_IS_WEB_CONTEXT(context));
950
951    ProcessModel newProcessModel(toProcessModel(processModel));
952
953    if (newProcessModel == context->priv->context->processModel())
954        return;
955
956    context->priv->context->setUsesNetworkProcess(newProcessModel == ProcessModelMultipleSecondaryProcesses);
957    context->priv->context->setProcessModel(newProcessModel);
958}
959
960/**
961 * webkit_web_context_get_process_model:
962 * @context: the #WebKitWebContext
963 *
964 * Returns the current process model. For more information about this value
965 * see webkit_web_context_set_process_model().
966 *
967 * Returns: the current #WebKitProcessModel
968 *
969 * Since: 2.4
970 */
971WebKitProcessModel webkit_web_context_get_process_model(WebKitWebContext* context)
972{
973    g_return_val_if_fail(WEBKIT_IS_WEB_CONTEXT(context), WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS);
974
975    return toWebKitProcessModel(context->priv->context->processModel());
976}
977
978WebKitDownload* webkitWebContextGetOrCreateDownload(DownloadProxy* downloadProxy)
979{
980    GRefPtr<WebKitDownload> download = downloadsMap().get(downloadProxy);
981    if (download)
982        return download.get();
983
984    download = adoptGRef(webkitDownloadCreate(downloadProxy));
985    downloadsMap().set(downloadProxy, download.get());
986    return download.get();
987}
988
989WebKitDownload* webkitWebContextStartDownload(WebKitWebContext* context, const char* uri, WebPageProxy* initiatingPage)
990{
991    WebCore::ResourceRequest request(String::fromUTF8(uri));
992    DownloadProxy* downloadProxy = context->priv->context->download(initiatingPage, request);
993    WebKitDownload* download = webkitDownloadCreateForRequest(downloadProxy, request);
994    downloadsMap().set(downloadProxy, download);
995    return download;
996}
997
998void webkitWebContextRemoveDownload(DownloadProxy* downloadProxy)
999{
1000    downloadsMap().remove(downloadProxy);
1001}
1002
1003void webkitWebContextDownloadStarted(WebKitWebContext* context, WebKitDownload* download)
1004{
1005    g_signal_emit(context, signals[DOWNLOAD_STARTED], 0, download);
1006}
1007
1008GVariant* webkitWebContextInitializeWebExtensions(WebKitWebContext* context)
1009{
1010    g_signal_emit(context, signals[INITIALIZE_WEB_EXTENSIONS], 0);
1011    return g_variant_new("(msmv)",
1012        context->priv->webExtensionsDirectory.data(),
1013        context->priv->webExtensionsInitializationUserData.get());
1014}
1015
1016WebContext* webkitWebContextGetContext(WebKitWebContext* context)
1017{
1018    g_assert(WEBKIT_IS_WEB_CONTEXT(context));
1019
1020    return context->priv->context.get();
1021}
1022
1023WebSoupCustomProtocolRequestManager* webkitWebContextGetRequestManager(WebKitWebContext* context)
1024{
1025    return context->priv->requestManager.get();
1026}
1027
1028void webkitWebContextStartLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID, API::URLRequest* urlRequest)
1029{
1030    GRefPtr<WebKitURISchemeRequest> request = adoptGRef(webkitURISchemeRequestCreate(customProtocolID, context, urlRequest));
1031    String scheme(String::fromUTF8(webkit_uri_scheme_request_get_scheme(request.get())));
1032    RefPtr<WebKitURISchemeHandler> handler = context->priv->uriSchemeHandlers.get(scheme);
1033    ASSERT(handler.get());
1034    if (!handler->hasCallback())
1035        return;
1036
1037    context->priv->uriSchemeRequests.set(customProtocolID, request.get());
1038    handler->performCallback(request.get());
1039}
1040
1041void webkitWebContextStopLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID)
1042{
1043    GRefPtr<WebKitURISchemeRequest> request = context->priv->uriSchemeRequests.get(customProtocolID);
1044    if (!request.get())
1045        return;
1046    webkitURISchemeRequestCancel(request.get());
1047}
1048
1049void webkitWebContextDidFinishLoadingCustomProtocol(WebKitWebContext* context, uint64_t customProtocolID)
1050{
1051    context->priv->uriSchemeRequests.remove(customProtocolID);
1052}
1053
1054void webkitWebContextCreatePageForWebView(WebKitWebContext* context, WebKitWebView* webView, WebKitWebViewGroup* webViewGroup, WebKitUserContentManager* userContentManager, WebKitWebView* relatedView)
1055{
1056    WebKitWebViewBase* webViewBase = WEBKIT_WEB_VIEW_BASE(webView);
1057    WebPageGroup* pageGroup = webViewGroup ? webkitWebViewGroupGetPageGroup(webViewGroup) : 0;
1058    WebPageProxy* relatedPage = relatedView ? webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(relatedView)) : nullptr;
1059    WebUserContentControllerProxy* userContentControllerProxy = userContentManager ? webkitUserContentManagerGetUserContentControllerProxy(userContentManager) : nullptr;
1060    webkitWebViewBaseCreateWebPage(webViewBase, context->priv->context.get(), pageGroup, userContentControllerProxy, relatedPage);
1061
1062    WebPageProxy* page = webkitWebViewBaseGetPage(webViewBase);
1063    context->priv->webViews.set(page->pageID(), webView);
1064
1065    if (!pageGroup && !context->priv->defaultWebViewGroup)
1066        context->priv->defaultWebViewGroup = adoptGRef(webkitWebViewGroupCreate(&page->pageGroup()));
1067}
1068
1069void webkitWebContextWebViewDestroyed(WebKitWebContext* context, WebKitWebView* webView)
1070{
1071    WebPageProxy* page = webkitWebViewBaseGetPage(WEBKIT_WEB_VIEW_BASE(webView));
1072    context->priv->webViews.remove(page->pageID());
1073}
1074
1075WebKitWebView* webkitWebContextGetWebViewForPage(WebKitWebContext* context, WebPageProxy* page)
1076{
1077    return page ? context->priv->webViews.get(page->pageID()) : 0;
1078}
1079
1080WebKitWebViewGroup* webkitWebContextGetDefaultWebViewGroup(WebKitWebContext* context)
1081{
1082    return context->priv->defaultWebViewGroup.get();
1083}
1084