[GTK] Add API to WebKitWebsiteDataManager to handle website data
authorcarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jan 2017 07:48:40 +0000 (07:48 +0000)
committercarlosgc@webkit.org <carlosgc@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 24 Jan 2017 07:48:40 +0000 (07:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=146589

Reviewed by Michael Catanzaro.

Source/WebKit2:

Add WebKitWebsiteData boxed type to wrap a WebsiteDataRecord and new methods to WebKitWebsiteDataManager to
fetch, remove and clear website data.

* PlatformGTK.cmake: Add new files to compilation.
* UIProcess/API/gtk/WebKitWebsiteData.cpp: Added.
(_WebKitWebsiteData::_WebKitWebsiteData):
(recordContainsSupportedDataTypes): Helper function to check if the WebsiteDataRecord contains any type exposed
by our API.
(toWebKitWebsiteDataTypes): Convert from WebKit::WebsiteDataType to the GTK+ public API types.
(webkitWebsiteDataCreate): Create a new WebKitWebsiteData for the given WebsiteDataRecord if it contains any
supported type.
(webkitWebsiteDataGetRecord): Returns the wrapped WebsiteDataRecord.
(webkit_website_data_ref):
(webkit_website_data_unref):
(webkit_website_data_get_name): Return the display name.
(webkit_website_data_get_types): Returns the mask of types.
(webkit_website_data_get_size): Returns the size for the given types.
* UIProcess/API/gtk/WebKitWebsiteData.h: Added.
* UIProcess/API/gtk/WebKitWebsiteDataManager.cpp:
(toWebsiteDataTypes): Convert from GTK+ public API types to WebKit::WebsiteDataType.
(webkit_website_data_manager_fetch): Fetch website data of the given types.
(webkit_website_data_manager_fetch_finish):
(webkit_website_data_manager_remove): Remove the website data of the given types for the given WebKitWebsiteData list.
(webkit_website_data_manager_remove_finish):
(webkit_website_data_manager_clear): Clear all website data of the given types modified since the given time span.
(webkit_website_data_manager_clear_finish):
* UIProcess/API/gtk/WebKitWebsiteDataManager.h:
* UIProcess/API/gtk/WebKitWebsiteDataPrivate.h: Added.
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt: Add new symbols.
* UIProcess/API/gtk/docs/webkit2gtk-docs.sgml: Add new section.
* UIProcess/API/gtk/webkit2.h: Inlcude WebKitWebsiteData.h

Tools:

Add about:data handler to MiniBrowser to manually test the website data API. Also add unit tests to test all the
new API, moving the WebKitWebsiteDataManager configuration API to the new file too.

* MiniBrowser/gtk/BrowserWindow.c:
(webViewDecidePolicy):
(newTabCallback):
* MiniBrowser/gtk/main.c:
(createBrowserTab):
(aboutDataRequestFree):
(aboutDataRequestNew):
(aboutDataRequestForView):
(websiteDataRemovedCallback):
(websiteDataClearedCallback):
(aboutDataScriptMessageReceivedCallback):
(domainListFree):
(aboutDataFillTable):
(gotWebsiteDataCallback):
(aboutDataHandleRequest):
(aboutURISchemeRequestCallback):
(main):
* TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt:
* TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp:
(serverCallback):
(beforeAll):
* TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp: Added.
(serverCallback):
(WebsiteDataTest::WebsiteDataTest):
(WebsiteDataTest::~WebsiteDataTest):
(WebsiteDataTest::fetch):
(WebsiteDataTest::remove):
(WebsiteDataTest::clear):
(testWebsiteDataConfiguration):
(testWebsiteDataCache):
(testWebsiteDataStorage):
(testWebsiteDataDatabases):
(testWebsiteDataAppcache):
(beforeAll):
(afterAll):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@211079 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
Source/WebCore/platform/gtk/po/POTFILES.in
Source/WebKit2/ChangeLog
Source/WebKit2/PlatformGTK.cmake
Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteData.cpp [new file with mode: 0644]
Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteData.h [new file with mode: 0644]
Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteDataManager.cpp
Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteDataManager.h
Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteDataPrivate.h [new file with mode: 0644]
Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
Source/WebKit2/UIProcess/API/gtk/docs/webkit2gtk-docs.sgml
Source/WebKit2/UIProcess/API/gtk/webkit2.h
Tools/ChangeLog
Tools/MiniBrowser/gtk/BrowserWindow.c
Tools/MiniBrowser/gtk/main.c
Tools/TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt
Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp
Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp [new file with mode: 0644]

index b65fb8d..8122112 100644 (file)
@@ -15,6 +15,7 @@ LocalizedStringsGtk.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitURIResponse.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitWebInspector.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitWebResource.cpp
+../../../WebKit2/UIProcess/API/gtk/WebKitWebsiteData.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitWebViewBase.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitWebView.cpp
 ../../../WebKit2/UIProcess/API/gtk/WebKitWindowProperties.cpp
index 6ae032e..62c3b0f 100644 (file)
@@ -1,3 +1,42 @@
+2017-01-23  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Add API to WebKitWebsiteDataManager to handle website data
+        https://bugs.webkit.org/show_bug.cgi?id=146589
+
+        Reviewed by Michael Catanzaro.
+
+        Add WebKitWebsiteData boxed type to wrap a WebsiteDataRecord and new methods to WebKitWebsiteDataManager to
+        fetch, remove and clear website data.
+
+        * PlatformGTK.cmake: Add new files to compilation.
+        * UIProcess/API/gtk/WebKitWebsiteData.cpp: Added.
+        (_WebKitWebsiteData::_WebKitWebsiteData):
+        (recordContainsSupportedDataTypes): Helper function to check if the WebsiteDataRecord contains any type exposed
+        by our API.
+        (toWebKitWebsiteDataTypes): Convert from WebKit::WebsiteDataType to the GTK+ public API types.
+        (webkitWebsiteDataCreate): Create a new WebKitWebsiteData for the given WebsiteDataRecord if it contains any
+        supported type.
+        (webkitWebsiteDataGetRecord): Returns the wrapped WebsiteDataRecord.
+        (webkit_website_data_ref):
+        (webkit_website_data_unref):
+        (webkit_website_data_get_name): Return the display name.
+        (webkit_website_data_get_types): Returns the mask of types.
+        (webkit_website_data_get_size): Returns the size for the given types.
+        * UIProcess/API/gtk/WebKitWebsiteData.h: Added.
+        * UIProcess/API/gtk/WebKitWebsiteDataManager.cpp:
+        (toWebsiteDataTypes): Convert from GTK+ public API types to WebKit::WebsiteDataType.
+        (webkit_website_data_manager_fetch): Fetch website data of the given types.
+        (webkit_website_data_manager_fetch_finish):
+        (webkit_website_data_manager_remove): Remove the website data of the given types for the given WebKitWebsiteData list.
+        (webkit_website_data_manager_remove_finish):
+        (webkit_website_data_manager_clear): Clear all website data of the given types modified since the given time span.
+        (webkit_website_data_manager_clear_finish):
+        * UIProcess/API/gtk/WebKitWebsiteDataManager.h:
+        * UIProcess/API/gtk/WebKitWebsiteDataPrivate.h: Added.
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt: Add new symbols.
+        * UIProcess/API/gtk/docs/webkit2gtk-docs.sgml: Add new section.
+        * UIProcess/API/gtk/webkit2.h: Inlcude WebKitWebsiteData.h
+
 2017-01-23  Antti Koivisto  <antti@apple.com>
 
         Maintain ordering when doing speculative loads
index 5154117..755c2ba 100644 (file)
@@ -270,6 +270,9 @@ list(APPEND WebKit2_SOURCES
     UIProcess/API/gtk/WebKitWebViewSessionState.cpp
     UIProcess/API/gtk/WebKitWebViewSessionState.h
     UIProcess/API/gtk/WebKitWebViewSessionStatePrivate.h
+    UIProcess/API/gtk/WebKitWebsiteData.cpp
+    UIProcess/API/gtk/WebKitWebsiteData.h
+    UIProcess/API/gtk/WebKitWebsiteDataPrivate.h
     UIProcess/API/gtk/WebKitWebsiteDataManager.cpp
     UIProcess/API/gtk/WebKitWebsiteDataManager.h
     UIProcess/API/gtk/WebKitWebsiteDataManagerPrivate.h
@@ -554,6 +557,7 @@ set(WebKit2GTK_INSTALLED_HEADERS
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitWebView.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitWebViewBase.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitWebViewSessionState.h
+    ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitWebsiteData.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitWebsiteDataManager.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/WebKitWindowProperties.h
     ${WEBKIT2_DIR}/UIProcess/API/gtk/webkit2.h
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteData.cpp b/Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteData.cpp
new file mode 100644 (file)
index 0000000..4f40b67
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "WebKitWebsiteData.h"
+
+#include "WebKitSecurityOriginPrivate.h"
+#include "WebKitWebsiteDataPrivate.h"
+#include <glib/gi18n.h>
+#include <wtf/HashTable.h>
+#include <wtf/Vector.h>
+
+using namespace WebKit;
+
+/**
+ * SECTION: WebKitWebsiteData
+ * @Short_description: Website data
+ * @Title: WebKitWebsiteData
+ * @See_also: #WebKitWebsiteDataManager
+ *
+ * WebKitWebsiteData represents data stored in the client by a particular website.
+ * A website is normally a set of URLs grouped by domain name. You can get the website name,
+ * which is usually the domain, with webkit_website_data_get_name().
+ * Documents loaded from the file system, like file:// URIs, are all grouped in the same WebKitWebsiteData
+ * with the name "Local files".
+ *
+ * A website can store different types of data in the client side. #WebKitWebsiteDataTypes is an enum containing
+ * all the possible data types; use webkit_website_data_get_types() to get the bitmask of data types.
+ * It's also possible to know the size of the data stored for some of the #WebKitWebsiteDataTypes by using
+ * webkit_website_data_get_size().
+ *
+ * A list of WebKitWebsiteData can be retrieved with webkit_website_data_manager_fetch(). See #WebKitWebsiteDataManager
+ * for more information.
+ *
+ * Since: 2.16
+ */
+struct _WebKitWebsiteData {
+    explicit _WebKitWebsiteData(WebsiteDataRecord&& websiteDataDecord)
+        : record(WTFMove(websiteDataDecord))
+    {
+    }
+
+    WebsiteDataRecord record;
+    CString displayName;
+    int referenceCount { 1 };
+};
+
+G_DEFINE_BOXED_TYPE(WebKitWebsiteData, webkit_website_data, webkit_website_data_ref, webkit_website_data_unref)
+
+static bool recordContainsSupportedDataTypes(const WebsiteDataRecord& record)
+{
+    static const OptionSet<WebsiteDataType> typesSupported = {
+        WebsiteDataType::MemoryCache,
+        WebsiteDataType::DiskCache,
+        WebsiteDataType::OfflineWebApplicationCache,
+        WebsiteDataType::SessionStorage,
+        WebsiteDataType::LocalStorage,
+        WebsiteDataType::WebSQLDatabases,
+        WebsiteDataType::IndexedDBDatabases,
+        WebsiteDataType::PlugInData
+    };
+    return record.types.contains(typesSupported);
+}
+
+static WebKitWebsiteDataTypes toWebKitWebsiteDataTypes(OptionSet<WebsiteDataType> types)
+{
+    uint32_t returnValue = 0;
+    if (types.contains(WebsiteDataType::MemoryCache))
+        returnValue |= WEBKIT_WEBSITE_DATA_MEMORY_CACHE;
+    if (types.contains(WebsiteDataType::DiskCache))
+        returnValue |= WEBKIT_WEBSITE_DATA_DISK_CACHE;
+    if (types.contains(WebsiteDataType::OfflineWebApplicationCache))
+        returnValue |= WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE;
+    if (types.contains(WebsiteDataType::SessionStorage))
+        returnValue |= WEBKIT_WEBSITE_DATA_SESSION_STORAGE;
+    if (types.contains(WebsiteDataType::LocalStorage))
+        returnValue |= WEBKIT_WEBSITE_DATA_LOCAL_STORAGE;
+    if (types.contains(WebsiteDataType::WebSQLDatabases))
+        returnValue |= WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES;
+    if (types.contains(WebsiteDataType::IndexedDBDatabases))
+        returnValue |= WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES;
+    if (types.contains(WebsiteDataType::PlugInData))
+        returnValue |= WEBKIT_WEBSITE_DATA_PLUGIN_DATA;
+    return static_cast<WebKitWebsiteDataTypes>(returnValue);
+}
+
+WebKitWebsiteData* webkitWebsiteDataCreate(WebsiteDataRecord&& record)
+{
+    if (!recordContainsSupportedDataTypes(record))
+        return nullptr;
+
+    WebKitWebsiteData* websiteData = static_cast<WebKitWebsiteData*>(fastMalloc(sizeof(WebKitWebsiteData)));
+    new (websiteData) WebKitWebsiteData(WTFMove(record));
+    return websiteData;
+}
+
+const WebKit::WebsiteDataRecord& webkitWebsiteDataGetRecord(WebKitWebsiteData* websiteData)
+{
+    ASSERT(websiteData);
+    return websiteData->record;
+}
+
+/**
+ * webkit_website_data_ref:
+ * @website_data: a #WebKitWebsiteData
+ *
+ * Atomically increments the reference count of @website_data by one.
+ * This function is MT-safe and may be called from any thread.
+ *
+ * Returns: The passed #WebKitWebsiteData
+ *
+ * Since: 2.16
+ */
+WebKitWebsiteData* webkit_website_data_ref(WebKitWebsiteData* websiteData)
+{
+    g_return_val_if_fail(websiteData, nullptr);
+
+    g_atomic_int_inc(&websiteData->referenceCount);
+    return websiteData;
+}
+
+/**
+ * webkit_website_data_unref:
+ * @website_data: A #WebKitWebsiteData
+ *
+ * Atomically decrements the reference count of @website_data by one.
+ * If the reference count drops to 0, all memory allocated by
+ * #WebKitWebsiteData is released. This function is MT-safe and may be
+ * called from any thread.
+ *
+ * Since: 2.16
+ */
+void webkit_website_data_unref(WebKitWebsiteData* websiteData)
+{
+    g_return_if_fail(websiteData);
+
+    if (g_atomic_int_dec_and_test(&websiteData->referenceCount)) {
+        websiteData->~WebKitWebsiteData();
+        fastFree(websiteData);
+    }
+}
+
+/**
+ * webkit_website_data_get_name:
+ * @website_data: a #WebKitWebsiteData
+ *
+ * Gets the name of #WebKitWebsiteData. This is the website name, normally represented by
+ * a domain or host name. All local documents are grouped in the same #WebKitWebsiteData using
+ * the name "Local files".
+ *
+ * Returns: the website name of @website_data.
+ *
+ * Since: 2.16
+ */
+const char* webkit_website_data_get_name(WebKitWebsiteData* websiteData)
+{
+    g_return_val_if_fail(websiteData, nullptr);
+
+    if (websiteData->displayName.isNull()) {
+        if (websiteData->record.displayName == "Local documents on your computer")
+            websiteData->displayName = _("Local files");
+        else
+            websiteData->displayName = websiteData->record.displayName.utf8();
+    }
+    return websiteData->displayName.data();
+}
+
+/**
+ * webkit_website_data_get_types:
+ * @website_data: a #WebKitWebsiteData
+ *
+ * Gets the types of data stored in the client for a #WebKitWebsiteData. These are the
+ * types actually present, not the types queried with webkit_website_data_manager_fetch().
+ *
+ * Returns: a bitmask of #WebKitWebsiteDataTypes in @website_data
+ *
+ * Since: 2.16
+ */
+WebKitWebsiteDataTypes webkit_website_data_get_types(WebKitWebsiteData* websiteData)
+{
+    g_return_val_if_fail(websiteData, static_cast<WebKitWebsiteDataTypes>(0));
+
+    return toWebKitWebsiteDataTypes(websiteData->record.types);
+}
+
+/**
+ * webkit_website_data_get_size:
+ * @website_data: a #WebKitWebsiteData
+ * @types: a bitmask  of #WebKitWebsiteDataTypes
+ *
+ * Gets the size of the data of types @types in a #WebKitWebsiteData.
+ * Note that currently the data size is only known for %WEBKIT_WEBSITE_DATA_DISK_CACHE data type
+ * so for all other types 0 will be returned.
+ *
+ * Returns: the size of @website_data for the given @types.
+ *
+ * Since: 2.16
+ */
+guint64 webkit_website_data_get_size(WebKitWebsiteData* websiteData, WebKitWebsiteDataTypes types)
+{
+    g_return_val_if_fail(websiteData, 0);
+
+    if (!types || !websiteData->record.size)
+        return 0;
+
+    guint64 totalSize = 0;
+    for (auto type : websiteData->record.size->typeSizes.keys()) {
+        if (type & types)
+            totalSize += websiteData->record.size->typeSizes.get(type);
+    }
+
+    return totalSize;
+}
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteData.h b/Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteData.h
new file mode 100644 (file)
index 0000000..5e8b6c8
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#if !defined(__WEBKIT2_H_INSIDE__) && !defined(WEBKIT2_COMPILATION)
+#error "Only <webkit2/webkit2.h> can be included directly."
+#endif
+
+#ifndef WebKitWebsiteData_h
+#define WebKitWebsiteData_h
+
+#include <glib-object.h>
+#include <webkit2/WebKitDefines.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_WEBSITE_DATA (webkit_website_data_get_type())
+
+typedef struct _WebKitWebsiteData WebKitWebsiteData;
+
+/**
+ * WebKitWebsiteDataTypes:
+ * @WEBKIT_WEBSITE_DATA_MEMORY_CACHE: Memory cache.
+ * @WEBKIT_WEBSITE_DATA_DISK_CACHE: HTTP disk cache.
+ * @WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE: Offline web application cache.
+ * @WEBKIT_WEBSITE_DATA_SESSION_STORAGE: Session storage data.
+ * @WEBKIT_WEBSITE_DATA_LOCAL_STORAGE: Local storage data.
+ * @WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES: WebSQL databases.
+ * @WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES: IndexedDB databases.
+ * @WEBKIT_WEBSITE_DATA_PLUGIN_DATA: Plugins data.
+ * @WEBKIT_WEBSITE_DATA_ALL: All types.
+ *
+ * Enum values with flags representing types of Website data.
+ *
+ * Since: 2.16
+ */
+typedef enum {
+    WEBKIT_WEBSITE_DATA_MEMORY_CACHE              = 1 << 0,
+    WEBKIT_WEBSITE_DATA_DISK_CACHE                = 1 << 1,
+    WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE = 1 << 2,
+    WEBKIT_WEBSITE_DATA_SESSION_STORAGE           = 1 << 3,
+    WEBKIT_WEBSITE_DATA_LOCAL_STORAGE             = 1 << 4,
+    WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES          = 1 << 5,
+    WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES       = 1 << 6,
+    WEBKIT_WEBSITE_DATA_PLUGIN_DATA               = 1 << 7,
+    WEBKIT_WEBSITE_DATA_ALL                       = (1 << 8) - 1
+} WebKitWebsiteDataTypes;
+
+WEBKIT_API GType
+webkit_website_data_get_type      (void);
+
+WEBKIT_API WebKitWebsiteData *
+webkit_website_data_ref           (WebKitWebsiteData     *website_data);
+
+WEBKIT_API void
+webkit_website_data_unref         (WebKitWebsiteData     *website_data);
+
+WEBKIT_API const char *
+webkit_website_data_get_name      (WebKitWebsiteData     *website_data);
+
+WEBKIT_API WebKitWebsiteDataTypes
+webkit_website_data_get_types     (WebKitWebsiteData     *website_data);
+
+WEBKIT_API guint64
+webkit_website_data_get_size      (WebKitWebsiteData     *website_data,
+                                   WebKitWebsiteDataTypes types);
+
+G_END_DECLS
+
+#endif /* WebKitWebsiteData_h */
index 1c35524..af75c87 100644 (file)
@@ -22,6 +22,8 @@
 
 #include "APIWebsiteDataStore.h"
 #include "WebKitWebsiteDataManagerPrivate.h"
+#include "WebKitWebsiteDataPrivate.h"
+#include "WebsiteDataFetchOption.h"
 #include <WebCore/FileSystem.h>
 #include <glib/gi18n-lib.h>
 #include <wtf/glib/GUniquePtr.h>
@@ -32,7 +34,7 @@ using namespace WebKit;
  * SECTION: WebKitWebsiteDataManager
  * @Short_description: Website data manager
  * @Title: WebKitWebsiteDataManager
- * @See_also: #WebKitWebContext
+ * @See_also: #WebKitWebContext, #WebKitWebsiteData
  *
  * WebKitWebsiteDataManager allows you to manage the data that websites
  * can store in the client file system like databases or caches.
@@ -54,6 +56,10 @@ using namespace WebKit;
  * with the default configuration. To get the WebKitWebsiteDataManager of a #WebKitWebContext
  * you can use webkit_web_context_get_website_data_manager().
  *
+ * WebKitWebsiteDataManager can also be used to fetch websites data, remove data
+ * stored by particular websites, or clear data for all websites modified since a given
+ * period of time.
+ *
  * Since: 2.10
  */
 
@@ -483,3 +489,188 @@ const gchar* webkit_website_data_manager_get_websql_directory(WebKitWebsiteDataM
         priv->webSQLDirectory.reset(g_strdup(API::WebsiteDataStore::defaultWebSQLDatabaseDirectory().utf8().data()));
     return priv->webSQLDirectory.get();
 }
+
+static OptionSet<WebsiteDataType> toWebsiteDataTypes(WebKitWebsiteDataTypes types)
+{
+    OptionSet<WebsiteDataType> returnValue;
+    if (types & WEBKIT_WEBSITE_DATA_MEMORY_CACHE)
+        returnValue |= WebsiteDataType::MemoryCache;
+    if (types & WEBKIT_WEBSITE_DATA_DISK_CACHE)
+        returnValue |= WebsiteDataType::DiskCache;
+    if (types & WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE)
+        returnValue |= WebsiteDataType::OfflineWebApplicationCache;
+    if (types & WEBKIT_WEBSITE_DATA_SESSION_STORAGE)
+        returnValue |= WebsiteDataType::SessionStorage;
+    if (types & WEBKIT_WEBSITE_DATA_LOCAL_STORAGE)
+        returnValue |= WebsiteDataType::LocalStorage;
+    if (types & WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES)
+        returnValue |= WebsiteDataType::WebSQLDatabases;
+    if (types & WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES)
+        returnValue |= WebsiteDataType::IndexedDBDatabases;
+    if (types & WEBKIT_WEBSITE_DATA_PLUGIN_DATA)
+        returnValue |= WebsiteDataType::PlugInData;
+    return returnValue;
+}
+
+/**
+ * webkit_website_data_manager_fetch:
+ * @manager: a #WebKitWebsiteDataManager
+ * @types: #WebKitWebsiteDataTypes
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously get the list of #WebKitWebsiteData for the given @types.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_website_data_manager_fetch_finish() to get the result of the operation.
+ *
+ * Since: 2.16
+ */
+void webkit_website_data_manager_fetch(WebKitWebsiteDataManager* manager, WebKitWebsiteDataTypes types, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(manager, cancellable, callback, userData));
+    manager->priv->websiteDataStore->websiteDataStore().fetchData(toWebsiteDataTypes(types), WebsiteDataFetchOption::ComputeSizes, [task = WTFMove(task)] (Vector<WebsiteDataRecord> records) {
+        GList* dataList = nullptr;
+        while (!records.isEmpty()) {
+            if (auto* data = webkitWebsiteDataCreate(records.takeLast()))
+                dataList = g_list_prepend(dataList, data);
+        }
+
+        g_task_return_pointer(task.get(), dataList, [](gpointer data) {
+            g_list_free_full(static_cast<GList*>(data), reinterpret_cast<GDestroyNotify>(webkit_website_data_unref));
+        });
+    });
+}
+
+/**
+ * webkit_website_data_manager_fetch_finish:
+ * @manager: a #WebKitWebsiteDataManager
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finish an asynchronous operation started with webkit_website_data_manager_fetch().
+ *
+ * Returns: (element-type WebKitWebsiteData) (transfer full): a #GList of #WebKitWebsiteData. You must free the #GList with
+ *    g_list_free() and unref the #WebKitWebsiteData<!-- -->s with webkit_website_data_unref() when you're done with them.
+ *
+ * Since: 2.16
+ */
+GList* webkit_website_data_manager_fetch_finish(WebKitWebsiteDataManager* manager, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager), nullptr);
+    g_return_val_if_fail(g_task_is_valid(result, manager), nullptr);
+
+    return static_cast<GList*>(g_task_propagate_pointer(G_TASK(result), error));
+}
+
+/**
+ * webkit_website_data_manager_remove:
+ * @manager: a #WebKitWebsiteDataManager
+ * @types: #WebKitWebsiteDataTypes
+ * @website_data: (element-type WebKitWebsiteData): a #GList of #WebKitWebsiteData
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously removes the website data of the for the given @types for websites in the given @website_data list.
+ * Use webkit_website_data_manager_clear() if you want to remove the website data for all sites.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_website_data_manager_remove_finish() to get the result of the operation.
+ *
+ * Since: 2.16
+ */
+void webkit_website_data_manager_remove(WebKitWebsiteDataManager* manager, WebKitWebsiteDataTypes types, GList* websiteData, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
+    g_return_if_fail(websiteData);
+
+    Vector<WebsiteDataRecord> records;
+    for (GList* item = websiteData; item; item = g_list_next(item)) {
+        WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(item->data);
+
+        if (webkit_website_data_get_types(data) & types)
+            records.append(webkitWebsiteDataGetRecord(data));
+    }
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(manager, cancellable, callback, userData));
+    if (records.isEmpty()) {
+        g_task_return_boolean(task.get(), TRUE);
+        return;
+    }
+
+    manager->priv->websiteDataStore->websiteDataStore().removeData(toWebsiteDataTypes(types), records, [task = WTFMove(task)] {
+        g_task_return_boolean(task.get(), TRUE);
+    });
+}
+
+/**
+ * webkit_website_data_manager_remove_finish:
+ * @manager: a #WebKitWebsiteDataManager
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finish an asynchronous operation started with webkit_website_data_manager_remove().
+ *
+ * Returns: %TRUE if website data resources were succesfully removed, or %FALSE otherwise.
+ *
+ * Since: 2.16
+ */
+gboolean webkit_website_data_manager_remove_finish(WebKitWebsiteDataManager* manager, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager), FALSE);
+    g_return_val_if_fail(g_task_is_valid(result, manager), FALSE);
+
+    return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+/**
+ * webkit_website_data_manager_clear:
+ * @manager: a #WebKitWebsiteDataManager
+ * @types: #WebKitWebsiteDataTypes
+ * @timespan: a #GTimeSpan
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: (closure): the data to pass to callback function
+ *
+ * Asynchronously clear the website data of the given @types modified in the past @timespan.
+ * If @timespan is 0 all website data will be removed.
+ *
+ * When the operation is finished, @callback will be called. You can then call
+ * webkit_website_data_manager_clear_finish() to get the result of the operation.
+ *
+ * Since: 2.16
+ */
+void webkit_website_data_manager_clear(WebKitWebsiteDataManager* manager, WebKitWebsiteDataTypes types, GTimeSpan timeSpan, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
+
+    std::chrono::system_clock::time_point timePoint = timeSpan ? std::chrono::system_clock::now() - std::chrono::microseconds(timeSpan) : std::chrono::system_clock::from_time_t(0);
+    GRefPtr<GTask> task = adoptGRef(g_task_new(manager, cancellable, callback, userData));
+    manager->priv->websiteDataStore->websiteDataStore().removeData(toWebsiteDataTypes(types), timePoint, [task = WTFMove(task)] {
+        g_task_return_boolean(task.get(), TRUE);
+    });
+}
+
+/**
+ * webkit_website_data_manager_clear_finish:
+ * @manager: a #WebKitWebsiteDataManager
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finish an asynchronous operation started with webkit_website_data_manager_clear()
+ *
+ * Returns: %TRUE if website data was succesfully cleared, or %FALSE otherwise.
+ *
+ * Since: 2.16
+ */
+gboolean webkit_website_data_manager_clear_finish(WebKitWebsiteDataManager* manager, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager), FALSE);
+    g_return_val_if_fail(g_task_is_valid(result, manager), FALSE);
+
+    return g_task_propagate_boolean(G_TASK(result), error);
+}
index e9d8591..21624aa 100644 (file)
@@ -24,8 +24,9 @@
 #ifndef WebKitWebsiteDataManager_h
 #define WebKitWebsiteDataManager_h
 
-#include <glib-object.h>
+#include <gio/gio.h>
 #include <webkit2/WebKitDefines.h>
+#include <webkit2/WebKitWebsiteData.h>
 
 G_BEGIN_DECLS
 
@@ -56,31 +57,67 @@ struct _WebKitWebsiteDataManagerClass {
 };
 
 WEBKIT_API GType
-webkit_website_data_manager_get_type                                (void);
+webkit_website_data_manager_get_type                                  (void);
 
 WEBKIT_API WebKitWebsiteDataManager *
-webkit_website_data_manager_new                                     (const gchar              *first_option_name,
-                                                                     ...);
+webkit_website_data_manager_new                                       (const gchar              *first_option_name,
+                                                                       ...);
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_base_data_directory                 (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_base_data_directory                   (WebKitWebsiteDataManager *manager);
 
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_base_cache_directory                 (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_base_cache_directory                  (WebKitWebsiteDataManager *manager);
 
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_local_storage_directory             (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_local_storage_directory               (WebKitWebsiteDataManager *manager);
 
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_disk_cache_directory                (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_disk_cache_directory                  (WebKitWebsiteDataManager *manager);
 
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_offline_application_cache_directory (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_offline_application_cache_directory   (WebKitWebsiteDataManager *manager);
 
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_indexeddb_directory                 (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_indexeddb_directory                   (WebKitWebsiteDataManager *manager);
 
 WEBKIT_API const gchar *
-webkit_website_data_manager_get_websql_directory                    (WebKitWebsiteDataManager *manager);
+webkit_website_data_manager_get_websql_directory                      (WebKitWebsiteDataManager *manager);
+
+WEBKIT_API void
+webkit_website_data_manager_fetch                                     (WebKitWebsiteDataManager *manager,
+                                                                       WebKitWebsiteDataTypes    types,
+                                                                       GCancellable             *cancellable,
+                                                                       GAsyncReadyCallback       callback,
+                                                                       gpointer                  user_data);
+
+WEBKIT_API GList *
+webkit_website_data_manager_fetch_finish                              (WebKitWebsiteDataManager *manager,
+                                                                       GAsyncResult             *result,
+                                                                       GError                  **error);
+WEBKIT_API void
+webkit_website_data_manager_remove                                    (WebKitWebsiteDataManager *manager,
+                                                                       WebKitWebsiteDataTypes    types,
+                                                                       GList                    *website_data,
+                                                                       GCancellable             *cancellable,
+                                                                       GAsyncReadyCallback       callback,
+                                                                       gpointer                  user_data);
+WEBKIT_API gboolean
+webkit_website_data_manager_remove_finish                             (WebKitWebsiteDataManager *manager,
+                                                                       GAsyncResult             *result,
+                                                                       GError                  **error);
+
+WEBKIT_API void
+webkit_website_data_manager_clear                                      (WebKitWebsiteDataManager *manager,
+                                                                        WebKitWebsiteDataTypes    types,
+                                                                        GTimeSpan                 timespan,
+                                                                        GCancellable             *cancellable,
+                                                                        GAsyncReadyCallback       callback,
+                                                                        gpointer                  user_data);
+
+WEBKIT_API gboolean
+webkit_website_data_manager_clear_finish                               (WebKitWebsiteDataManager *manager,
+                                                                        GAsyncResult             *result,
+                                                                        GError                  **error);
 
 G_END_DECLS
 
diff --git a/Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteDataPrivate.h b/Source/WebKit2/UIProcess/API/gtk/WebKitWebsiteDataPrivate.h
new file mode 100644 (file)
index 0000000..509591f
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include "WebKitPrivate.h"
+#include "WebKitWebsiteData.h"
+#include "WebsiteDataRecord.h"
+
+WebKitWebsiteData* webkitWebsiteDataCreate(WebKit::WebsiteDataRecord&&);
+const WebKit::WebsiteDataRecord& webkitWebsiteDataGetRecord(WebKitWebsiteData*);
index a374c63..0535454 100644 (file)
@@ -1244,6 +1244,12 @@ webkit_website_data_manager_get_disk_cache_directory
 webkit_website_data_manager_get_offline_application_cache_directory
 webkit_website_data_manager_get_indexeddb_directory
 webkit_website_data_manager_get_websql_directory
+webkit_website_data_manager_fetch
+webkit_website_data_manager_fetch_finish
+webkit_website_data_manager_remove
+webkit_website_data_manager_remove_finish
+webkit_website_data_manager_clear
+webkit_website_data_manager_clear_finish
 
 <SUBSECTION Standard>
 WebKitWebsiteDataManagerClass
@@ -1260,6 +1266,23 @@ webkit_website_data_manager_get_type
 </SECTION>
 
 <SECTION>
+<FILE>WebKitWebsiteData</FILE>
+WebKitWebsiteData
+WebKitWebsiteDataTypes
+webkit_website_data_ref
+webkit_website_data_unref
+webkit_website_data_get_name
+webkit_website_data_get_types
+webkit_website_data_get_size
+
+<SUBSECTION Standard>
+WEBKIT_TYPE_WEBSITE_DATA
+
+<SUBSECTION Private>
+webkit_website_data_get_type
+</SECTION>
+
+<SECTION>
 <FILE>WebKitNetworkProxySettings</FILE>
 WebKitNetworkProxySettings
 webkit_network_proxy_settings_new
index 8dc3e61..2150e57 100644 (file)
@@ -53,6 +53,7 @@
     <xi:include href="xml/WebKitNotificationPermissionRequest.xml"/>
     <xi:include href="xml/WebKitSecurityOrigin.xml"/>
     <xi:include href="xml/WebKitWebsiteDataManager.xml"/>
+    <xi:include href="xml/WebKitWebsiteData.xml"/>
     <xi:include href="xml/WebKitNetworkProxySettings.xml"/>
   </chapter>
 
     <xi:include href="xml/api-index-2.12.xml"><xi:fallback /></xi:include>
   </index>
 
+  <index id="api-index-2-14" role="2.14">
+    <title>Index of new symbols in 2.14</title>
+    <xi:include href="xml/api-index-2.14.xml"><xi:fallback /></xi:include>
+  </index>
+
+  <index id="api-index-2-16" role="2.16">
+    <title>Index of new symbols in 2.16</title>
+    <xi:include href="xml/api-index-2.16.xml"><xi:fallback /></xi:include>
+  </index>
+
   <xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
 </book>
index 98fe129..34b12a8 100644 (file)
@@ -77,6 +77,7 @@
 #include <webkit2/WebKitWebView.h>
 #include <webkit2/WebKitWebViewBase.h>
 #include <webkit2/WebKitWebViewSessionState.h>
+#include <webkit2/WebKitWebsiteData.h>
 #include <webkit2/WebKitWebsiteDataManager.h>
 #include <webkit2/WebKitWindowProperties.h>
 
index b24d891..ada105e 100644 (file)
@@ -1,3 +1,49 @@
+2017-01-23  Carlos Garcia Campos  <cgarcia@igalia.com>
+
+        [GTK] Add API to WebKitWebsiteDataManager to handle website data
+        https://bugs.webkit.org/show_bug.cgi?id=146589
+
+        Reviewed by Michael Catanzaro.
+
+        Add about:data handler to MiniBrowser to manually test the website data API. Also add unit tests to test all the
+        new API, moving the WebKitWebsiteDataManager configuration API to the new file too.
+
+        * MiniBrowser/gtk/BrowserWindow.c:
+        (webViewDecidePolicy):
+        (newTabCallback):
+        * MiniBrowser/gtk/main.c:
+        (createBrowserTab):
+        (aboutDataRequestFree):
+        (aboutDataRequestNew):
+        (aboutDataRequestForView):
+        (websiteDataRemovedCallback):
+        (websiteDataClearedCallback):
+        (aboutDataScriptMessageReceivedCallback):
+        (domainListFree):
+        (aboutDataFillTable):
+        (gotWebsiteDataCallback):
+        (aboutDataHandleRequest):
+        (aboutURISchemeRequestCallback):
+        (main):
+        * TestWebKitAPI/Tests/WebKit2Gtk/CMakeLists.txt:
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestWebKitWebContext.cpp:
+        (serverCallback):
+        (beforeAll):
+        * TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp: Added.
+        (serverCallback):
+        (WebsiteDataTest::WebsiteDataTest):
+        (WebsiteDataTest::~WebsiteDataTest):
+        (WebsiteDataTest::fetch):
+        (WebsiteDataTest::remove):
+        (WebsiteDataTest::clear):
+        (testWebsiteDataConfiguration):
+        (testWebsiteDataCache):
+        (testWebsiteDataStorage):
+        (testWebsiteDataDatabases):
+        (testWebsiteDataAppcache):
+        (beforeAll):
+        (afterAll):
+
 2017-01-23  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r211062.
index bbb5ab5..f4326dd 100644 (file)
@@ -330,7 +330,11 @@ static gboolean webViewDecidePolicy(WebKitWebView *webView, WebKitPolicyDecision
         return FALSE;
 
     /* Opening a new tab if link clicked with the middle button. */
-    WebKitWebView *newWebView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_context(webkit_web_view_get_context(webView)));
+    WebKitWebView *newWebView = WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+        "web-context", webkit_web_view_get_context(webView),
+        "settings", webkit_web_view_get_settings(webView),
+        "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+        NULL));
     browser_window_append_view(window, newWebView);
     webkit_web_view_load_request(newWebView, webkit_navigation_action_get_request(navigationAction));
 
@@ -466,8 +470,12 @@ static void newTabCallback(BrowserWindow *window)
     WebKitWebView *webView = browser_tab_get_web_view(window->activeTab);
     if (webkit_web_view_is_editable(webView))
         return;
-    WebKitSettings *settings = webkit_web_view_get_settings(webView);
-    browser_window_append_view(window, WEBKIT_WEB_VIEW(webkit_web_view_new_with_settings(settings)));
+
+    browser_window_append_view(window, WEBKIT_WEB_VIEW(g_object_new(WEBKIT_TYPE_WEB_VIEW,
+        "web-context", webkit_web_view_get_context(webView),
+        "settings", webkit_web_view_get_settings(webView),
+        "user-content-manager", webkit_web_view_get_user_content_manager(webView),
+        NULL)));
     gtk_notebook_set_current_page(GTK_NOTEBOOK(window->notebook), -1);
 }
 
index 34b9a85..8bb87b6 100644 (file)
@@ -28,6 +28,7 @@
 #include "cmakeconfig.h"
 
 #include "BrowserWindow.h"
+#include <JavaScriptCore/JavaScript.h>
 #include <errno.h>
 #include <gtk/gtk.h>
 #include <string.h>
@@ -59,9 +60,9 @@ static gchar *argumentToURL(const char *filename)
     return fileURL;
 }
 
-static WebKitWebView *createBrowserTab(BrowserWindow *window, WebKitSettings *webkitSettings)
+static WebKitWebView *createBrowserTab(BrowserWindow *window, WebKitSettings *webkitSettings, WebKitUserContentManager *userContentManager)
 {
-    WebKitWebView *webView = WEBKIT_WEB_VIEW(webkit_web_view_new());
+    WebKitWebView *webView = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(userContentManager));
     if (webkitSettings)
         webkit_web_view_set_settings(webView, webkitSettings);
     if (editorMode)
@@ -225,6 +226,194 @@ static gboolean addSettingsGroupToContext(GOptionContext *context, WebKitSetting
     return TRUE;
 }
 
+typedef struct {
+    WebKitURISchemeRequest *request;
+    GList *dataList;
+    GHashTable *dataMap;
+} AboutDataRequest;
+
+static GHashTable *aboutDataRequestMap;
+
+static void aboutDataRequestFree(AboutDataRequest *request)
+{
+    if (!request)
+        return;
+
+    g_list_free_full(request->dataList, g_object_unref);
+
+    if (request->request)
+        g_object_unref(request->request);
+    if (request->dataMap)
+        g_hash_table_destroy(request->dataMap);
+
+    g_slice_free(AboutDataRequest, request);
+}
+
+static AboutDataRequest* aboutDataRequestNew(WebKitURISchemeRequest *uriRequest)
+{
+    if (!aboutDataRequestMap)
+        aboutDataRequestMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)aboutDataRequestFree);
+
+    AboutDataRequest *request = g_slice_new0(AboutDataRequest);
+    request->request = g_object_ref(uriRequest);
+    g_hash_table_insert(aboutDataRequestMap, GUINT_TO_POINTER(webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(request->request))), request);
+
+    return request;
+}
+
+static AboutDataRequest *aboutDataRequestForView(guint64 pageID)
+{
+    return aboutDataRequestMap ? g_hash_table_lookup(aboutDataRequestMap, GUINT_TO_POINTER(pageID)) : NULL;
+}
+
+static void websiteDataRemovedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
+{
+    if (webkit_website_data_manager_remove_finish(manager, result, NULL))
+        webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+}
+
+static void websiteDataClearedCallback(WebKitWebsiteDataManager *manager, GAsyncResult *result, AboutDataRequest *dataRequest)
+{
+    if (webkit_website_data_manager_clear_finish(manager, result, NULL))
+        webkit_web_view_reload(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+}
+
+static void aboutDataScriptMessageReceivedCallback(WebKitUserContentManager *userContentManager, WebKitJavascriptResult *message)
+{
+    JSValueRef jsValue = webkit_javascript_result_get_value(message);
+    JSStringRef jsString = JSValueToStringCopy(webkit_javascript_result_get_global_context(message), jsValue, NULL);
+    size_t maxSize = JSStringGetMaximumUTF8CStringSize(jsString);
+    if (!maxSize) {
+        JSStringRelease(jsString);
+        return;
+    }
+    char *messageString = g_malloc(maxSize);
+    JSStringGetUTF8CString(jsString, messageString, maxSize);
+    JSStringRelease(jsString);
+
+    char **tokens = g_strsplit(messageString, ":", 3);
+    g_free(messageString);
+
+    unsigned tokenCount = g_strv_length(tokens);
+    if (!tokens || tokenCount < 2) {
+        g_strfreev(tokens);
+        return;
+    }
+
+    guint64 pageID = g_ascii_strtoull(tokens[0], NULL, 10);
+    AboutDataRequest *dataRequest = aboutDataRequestForView(pageID);
+    if (!dataRequest) {
+        g_strfreev(tokens);
+        return;
+    }
+
+    WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default());
+    guint64 types = g_ascii_strtoull(tokens[1], NULL, 10);
+    if (tokenCount == 2)
+        webkit_website_data_manager_clear(manager, types, 0, NULL, (GAsyncReadyCallback)websiteDataClearedCallback, dataRequest);
+    else {
+        guint64 domainID = g_ascii_strtoull(tokens[2], NULL, 10);
+        GList *dataList = g_hash_table_lookup(dataRequest->dataMap, GUINT_TO_POINTER(types));
+        WebKitWebsiteData *data = g_list_nth_data(dataList, domainID);
+        if (data) {
+            GList dataList = { data, NULL, NULL };
+            webkit_website_data_manager_remove(manager, types, &dataList, NULL, (GAsyncReadyCallback)websiteDataRemovedCallback, dataRequest);
+        }
+    }
+    g_strfreev(tokens);
+}
+
+static void domainListFree(GList *domains)
+{
+    g_list_free_full(domains, (GDestroyNotify)webkit_website_data_unref);
+}
+
+static void aboutDataFillTable(GString *result, AboutDataRequest *dataRequest, GList* dataList, const char *title, WebKitWebsiteDataTypes types, const char *dataPath, guint64 pageID)
+{
+    guint64 totalDataSize = 0;
+    GList *domains = NULL;
+    GList *l;
+    for (l = dataList; l; l = g_list_next(l)) {
+        WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
+
+        if (webkit_website_data_get_types(data) & types) {
+            domains = g_list_prepend(domains, webkit_website_data_ref(data));
+            totalDataSize += webkit_website_data_get_size(data, types);
+        }
+    }
+    if (!domains)
+        return;
+
+    if (!dataRequest->dataMap)
+        dataRequest->dataMap = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)domainListFree);
+    g_hash_table_insert(dataRequest->dataMap, GUINT_TO_POINTER(types), domains);
+
+    if (totalDataSize) {
+        char *totalDataSizeStr = g_format_size(totalDataSize);
+        g_string_append_printf(result, "<h1>%s (%s)</h1>\n<table>\n", title, totalDataSizeStr);
+        g_free(totalDataSizeStr);
+    } else
+        g_string_append_printf(result, "<h1>%s</h1>\n<table>\n", title);
+    if (dataPath)
+        g_string_append_printf(result, "<tr><td colspan=\"2\">Path: %s</td></tr>\n", dataPath);
+
+    unsigned index;
+    for (l = domains, index = 0; l; l = g_list_next(l), index++) {
+        WebKitWebsiteData *data = (WebKitWebsiteData *)l->data;
+        const char *displayName = webkit_website_data_get_name(data);
+        guint64 dataSize = webkit_website_data_get_size(data, types);
+        if (dataSize) {
+            char *dataSizeStr = g_format_size(dataSize);
+            g_string_append_printf(result, "<tr><td>%s (%s)</td>", displayName, dataSizeStr);
+            g_free(dataSizeStr);
+        } else
+            g_string_append_printf(result, "<tr><td>%s</td>", displayName);
+        g_string_append_printf(result, "<td><input type=\"button\" value=\"Remove\" onclick=\"removeData('%"G_GUINT64_FORMAT":%u:%u');\"></td></tr>\n",
+            pageID, types, index);
+    }
+    g_string_append_printf(result, "<tr><td><input type=\"button\" value=\"Clear all\" onclick=\"clearData('%"G_GUINT64_FORMAT":%u');\"></td></tr></table>\n",
+        pageID, types);
+}
+
+static void gotWebsiteDataCallback(WebKitWebsiteDataManager *manager, GAsyncResult *asyncResult, AboutDataRequest *dataRequest)
+{
+    GList *dataList = webkit_website_data_manager_fetch_finish(manager, asyncResult, NULL);
+
+    GString *result = g_string_new(
+        "<html><head>"
+        "<script>"
+        "  function removeData(domain) {"
+        "    window.webkit.messageHandlers.aboutData.postMessage(domain);"
+        "  }"
+        "  function clearData(dataType) {"
+        "    window.webkit.messageHandlers.aboutData.postMessage(dataType);"
+        "  }"
+        "</script></head><body>\n");
+
+    guint64 pageID = webkit_web_view_get_page_id(webkit_uri_scheme_request_get_web_view(dataRequest->request));
+    aboutDataFillTable(result, dataRequest, dataList, "Memory Cache", WEBKIT_WEBSITE_DATA_MEMORY_CACHE, NULL, pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "Disk Cache", WEBKIT_WEBSITE_DATA_DISK_CACHE, webkit_website_data_manager_get_disk_cache_directory(manager), pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "Session Storage", WEBKIT_WEBSITE_DATA_SESSION_STORAGE, NULL, pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "Local Storage", WEBKIT_WEBSITE_DATA_LOCAL_STORAGE, webkit_website_data_manager_get_local_storage_directory(manager), pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "WebSQL Databases", WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES, webkit_website_data_manager_get_websql_directory(manager), pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "IndexedDB Databases", WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES, webkit_website_data_manager_get_indexeddb_directory(manager), pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "Plugins Data", WEBKIT_WEBSITE_DATA_PLUGIN_DATA, NULL, pageID);
+    aboutDataFillTable(result, dataRequest, dataList, "Offline Web Applications Cache", WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE, webkit_website_data_manager_get_offline_application_cache_directory(manager), pageID);
+
+    result = g_string_append(result, "</body></html>");
+    gsize streamLength = result->len;
+    GInputStream *stream = g_memory_input_stream_new_from_data(g_string_free(result, FALSE), streamLength, g_free);
+    webkit_uri_scheme_request_finish(dataRequest->request, stream, streamLength, "text/html");
+    g_list_free_full(dataList, (GDestroyNotify)webkit_website_data_unref);
+}
+
+static void aboutDataHandleRequest(WebKitURISchemeRequest *request)
+{
+    AboutDataRequest *dataRequest = aboutDataRequestNew(request);
+    WebKitWebsiteDataManager *manager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default());
+    webkit_website_data_manager_fetch(manager, WEBKIT_WEBSITE_DATA_ALL, NULL, (GAsyncReadyCallback)gotWebsiteDataCallback, dataRequest);
+}
+
 static void aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpointer userData)
 {
     GInputStream *stream;
@@ -244,7 +433,9 @@ static void aboutURISchemeRequestCallback(WebKitURISchemeRequest *request, gpoin
 
         webkit_uri_scheme_request_finish(request, stream, streamLength, "text/html");
         g_object_unref(stream);
-    } else {
+    } else if (!g_strcmp0(path, "data"))
+        aboutDataHandleRequest(request);
+    else {
         error = g_error_new(MINI_BROWSER_ERROR, MINI_BROWSER_ERROR_INVALID_ABOUT_PATH, "Invalid about:%s page.", path);
         webkit_uri_scheme_request_finish_error(request, error);
         g_error_free(error);
@@ -288,6 +479,10 @@ int main(int argc, char *argv[])
 
     webkit_web_context_register_uri_scheme(webkit_web_context_get_default(), BROWSER_ABOUT_SCHEME, aboutURISchemeRequestCallback, NULL, NULL);
 
+    WebKitUserContentManager *userContentManager = webkit_user_content_manager_new();
+    webkit_user_content_manager_register_script_message_handler(userContentManager, "aboutData");
+    g_signal_connect(userContentManager, "script-message-received::aboutData", G_CALLBACK(aboutDataScriptMessageReceivedCallback), NULL);
+
     BrowserWindow *mainWindow = BROWSER_WINDOW(browser_window_new(NULL));
     if (geometry)
         gtk_window_parse_geometry(GTK_WINDOW(mainWindow), geometry);
@@ -297,7 +492,7 @@ int main(int argc, char *argv[])
         int i;
 
         for (i = 0; uriArguments[i]; i++) {
-            WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings);
+            WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
             if (!i)
                 firstTab = GTK_WIDGET(webView);
             gchar *url = argumentToURL(uriArguments[i]);
@@ -305,7 +500,7 @@ int main(int argc, char *argv[])
             g_free(url);
         }
     } else {
-        WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings);
+        WebKitWebView *webView = createBrowserTab(mainWindow, webkitSettings, userContentManager);
         firstTab = GTK_WIDGET(webView);
 
         if (backgroundColor)
@@ -323,6 +518,7 @@ int main(int argc, char *argv[])
     gtk_widget_show(GTK_WIDGET(mainWindow));
 
     g_clear_object(&webkitSettings);
+    g_clear_object(&userContentManager);
 
     gtk_main();
 
index 2584df2..64640f5 100644 (file)
@@ -125,6 +125,7 @@ ADD_WK2_TEST(TestWebViewEditor TestWebViewEditor.cpp)
 ADD_WK2_TEST(TestWebKitWebContext TestWebKitWebContext.cpp)
 ADD_WK2_TEST(TestWebKitWebView TestWebKitWebView.cpp)
 ADD_WK2_TEST(TestWebKitUserContentManager TestWebKitUserContentManager.cpp)
+ADD_WK2_TEST(TestWebsiteData TestWebsiteData.cpp)
 ADD_WK2_TEST(TestEditor TestEditor.cpp)
 ADD_WK2_TEST(TestConsoleMessage TestConsoleMessage.cpp)
 
index 314e780..197b777 100644 (file)
@@ -40,88 +40,6 @@ static void testWebContextDefault(Test* test, gconstpointer)
     g_assert(webkit_web_context_get_default() != test->m_webContext.get());
 }
 
-static void testWebContextConfiguration(WebViewTest* test, gconstpointer)
-{
-    WebKitWebsiteDataManager* manager = webkit_web_context_get_website_data_manager(test->m_webContext.get());
-    g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(manager));
-    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(manager));
-
-    // Base directories are not used by TestMain.
-    g_assert(!webkit_website_data_manager_get_base_data_directory(manager));
-    g_assert(!webkit_website_data_manager_get_base_cache_directory(manager));
-
-    GUniquePtr<char> localStorageDirectory(g_build_filename(Test::dataDirectory(), "local-storage", nullptr));
-    g_assert_cmpstr(localStorageDirectory.get(), ==, webkit_website_data_manager_get_local_storage_directory(manager));
-    g_assert(g_file_test(localStorageDirectory.get(), G_FILE_TEST_IS_DIR));
-
-    test->loadURI(kServer->getURIForPath("/empty").data());
-    test->waitUntilLoadFinished();
-    test->runJavaScriptAndWaitUntilFinished("window.indexedDB.open('TestDatabase');", nullptr);
-    GUniquePtr<char> indexedDBDirectory(g_build_filename(Test::dataDirectory(), "indexeddb", nullptr));
-    g_assert_cmpstr(indexedDBDirectory.get(), ==, webkit_website_data_manager_get_indexeddb_directory(manager));
-    g_assert(g_file_test(indexedDBDirectory.get(), G_FILE_TEST_IS_DIR));
-
-    test->loadURI(kServer->getURIForPath("/appcache").data());
-    test->waitUntilLoadFinished();
-    GUniquePtr<char> applicationCacheDirectory(g_build_filename(Test::dataDirectory(), "appcache", nullptr));
-    g_assert_cmpstr(applicationCacheDirectory.get(), ==, webkit_website_data_manager_get_offline_application_cache_directory(manager));
-    GUniquePtr<char> applicationCacheDatabase(g_build_filename(applicationCacheDirectory.get(), "ApplicationCache.db", nullptr));
-    unsigned triesCount = 4;
-    while (!g_file_test(applicationCacheDatabase.get(), G_FILE_TEST_IS_REGULAR) && --triesCount)
-        test->wait(0.25);
-    g_assert(triesCount);
-
-
-    GUniquePtr<char> webSQLDirectory(g_build_filename(Test::dataDirectory(), "websql", nullptr));
-    g_assert_cmpstr(webSQLDirectory.get(), ==, webkit_website_data_manager_get_websql_directory(manager));
-    test->runJavaScriptAndWaitUntilFinished("db = openDatabase(\"TestDatabase\", \"1.0\", \"TestDatabase\", 1);", nullptr);
-    g_assert(g_file_test(webSQLDirectory.get(), G_FILE_TEST_IS_DIR));
-
-    GUniquePtr<char> diskCacheDirectory(g_build_filename(Test::dataDirectory(), "disk-cache", nullptr));
-    g_assert_cmpstr(diskCacheDirectory.get(), ==, webkit_website_data_manager_get_disk_cache_directory(manager));
-    g_assert(g_file_test(diskCacheDirectory.get(), G_FILE_TEST_IS_DIR));
-
-    // The default context should have a different manager with different configuration.
-    WebKitWebsiteDataManager* defaultManager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default());
-    g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(defaultManager));
-    g_assert(manager != defaultManager);
-    g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(manager), !=, webkit_website_data_manager_get_local_storage_directory(defaultManager));
-    g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(manager), !=, webkit_website_data_manager_get_indexeddb_directory(defaultManager));
-    g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(manager), !=, webkit_website_data_manager_get_disk_cache_directory(defaultManager));
-    g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(manager), !=, webkit_website_data_manager_get_offline_application_cache_directory(defaultManager));
-    g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(manager), !=, webkit_website_data_manager_get_websql_directory(defaultManager));
-
-    // Using Test::dataDirectory() we get the default configuration but for a differrent prefix.
-    GRefPtr<WebKitWebsiteDataManager> baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(), nullptr));
-    g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(baseDataManager.get()));
-
-    localStorageDirectory.reset(g_build_filename(Test::dataDirectory(), "localstorage", nullptr));
-    g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get());
-
-    indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", "indexeddb", nullptr));
-    g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get());
-
-    applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "applications", nullptr));
-    g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get());
-
-    webSQLDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", nullptr));
-    g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get());
-
-    g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory());
-
-    // Any specific configuration provided takes precedence over base dirs.
-    indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomindexeddb", nullptr));
-    applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomappcache", nullptr));
-    baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(),
-        "indexeddb-directory", indexedDBDirectory.get(), "offline-application-cache-directory", applicationCacheDirectory.get(), nullptr));
-    g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get());
-    g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get());
-    // The resutl should be the same as previous manager.
-    g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get());
-    g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get());
-    g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory());
-}
-
 class PluginsTest: public Test {
 public:
     MAKE_GLIB_TEST_FIXTURE(PluginsTest);
@@ -536,20 +454,6 @@ static void serverCallback(SoupServer* server, SoupMessage* message, const char*
         soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, emptyHTML, strlen(emptyHTML));
         soup_message_body_complete(message->response_body);
         soup_message_set_status(message, SOUP_STATUS_OK);
-    } else if (g_str_equal(path, "/appcache")) {
-        const char* appcacheHTML = "<html manifest=appcache.manifest><body></body></html>";
-        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheHTML, strlen(appcacheHTML));
-        soup_message_body_complete(message->response_body);
-        soup_message_set_status(message, SOUP_STATUS_OK);
-    } else if (g_str_equal(path, "/appcache.manifest")) {
-        const char* appcacheManifest = "CACHE MANIFEST\nCACHE:\nappcache/foo.txt\n";
-        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheManifest, strlen(appcacheManifest));
-        soup_message_body_complete(message->response_body);
-        soup_message_set_status(message, SOUP_STATUS_OK);
-    } else if (g_str_equal(path, "/appcache/foo.txt")) {
-        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "foo", 3);
-        soup_message_body_complete(message->response_body);
-        soup_message_set_status(message, SOUP_STATUS_OK);
     } else if (g_str_equal(path, "/echoPort")) {
         char* port = g_strdup_printf("%u", soup_server_get_port(server));
         soup_message_body_append(message->response_body, SOUP_MEMORY_TAKE, port, strlen(port));
@@ -795,7 +699,6 @@ void beforeAll()
     kServer->run(serverCallback);
 
     Test::add("WebKitWebContext", "default-context", testWebContextDefault);
-    WebViewTest::add("WebKitWebContext", "configuration", testWebContextConfiguration);
     PluginsTest::add("WebKitWebContext", "get-plugins", testWebContextGetPlugins);
     URISchemeTest::add("WebKitWebContext", "uri-scheme", testWebContextURIScheme);
     Test::add("WebKitWebContext", "spell-checker", testWebContextSpellChecker);
diff --git a/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp b/Tools/TestWebKitAPI/Tests/WebKit2Gtk/TestWebsiteData.cpp
new file mode 100644 (file)
index 0000000..16902c0
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2017 Igalia S.L.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2,1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include "WebKitTestServer.h"
+#include "WebViewTest.h"
+
+static WebKitTestServer* kServer;
+
+static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
+{
+    if (message->method != SOUP_METHOD_GET) {
+        soup_message_set_status(message, SOUP_STATUS_NOT_IMPLEMENTED);
+        return;
+    }
+
+    if (g_str_equal(path, "/empty")) {
+        const char* emptyHTML = "<html><body></body></html>";
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, emptyHTML, strlen(emptyHTML));
+        soup_message_body_complete(message->response_body);
+        soup_message_set_status(message, SOUP_STATUS_OK);
+    } else if (g_str_equal(path, "/appcache")) {
+        const char* appcacheHTML = "<html manifest=appcache.manifest><body></body></html>";
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheHTML, strlen(appcacheHTML));
+        soup_message_body_complete(message->response_body);
+        soup_message_set_status(message, SOUP_STATUS_OK);
+    } else if (g_str_equal(path, "/appcache.manifest")) {
+        const char* appcacheManifest = "CACHE MANIFEST\nCACHE:\nappcache/foo.txt\n";
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, appcacheManifest, strlen(appcacheManifest));
+        soup_message_body_complete(message->response_body);
+        soup_message_set_status(message, SOUP_STATUS_OK);
+    } else if (g_str_equal(path, "/appcache/foo.txt")) {
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, "foo", 3);
+        soup_message_body_complete(message->response_body);
+        soup_message_set_status(message, SOUP_STATUS_OK);
+    } else if (g_str_equal(path, "/sessionstorage")) {
+        const char* sessionStorageHTML = "<html><body onload=\"sessionStorage.foo = 'bar';\"></body></html>";
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, sessionStorageHTML, strlen(sessionStorageHTML));
+        soup_message_body_complete(message->response_body);
+        soup_message_set_status(message, SOUP_STATUS_OK);
+    } else if (g_str_equal(path, "/localstorage")) {
+        const char* localStorageHTML = "<html><body onload=\"localStorage.foo = 'bar';\"></body></html>";
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, localStorageHTML, strlen(localStorageHTML));
+        soup_message_body_complete(message->response_body);
+        soup_message_set_status(message, SOUP_STATUS_OK);
+    } else
+        soup_message_set_status(message, SOUP_STATUS_NOT_FOUND);
+}
+
+class WebsiteDataTest : public WebViewTest {
+public:
+    MAKE_GLIB_TEST_FIXTURE(WebsiteDataTest);
+
+
+    WebsiteDataTest()
+        : m_manager(webkit_web_context_get_website_data_manager(webkit_web_view_get_context(m_webView)))
+    {
+        g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(m_manager));
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(m_manager));
+        // WebsiteDataStore creates a new WebProcessPool when used before any secondary process has been created.
+        // Ensure we have a web process by always loading about:blank here.
+        loadURI("about:blank");
+        waitUntilLoadFinished();
+    }
+
+    ~WebsiteDataTest()
+    {
+        g_list_free_full(m_dataList, reinterpret_cast<GDestroyNotify>(webkit_website_data_unref));
+    }
+
+    GList* fetch(WebKitWebsiteDataTypes types)
+    {
+        if (m_dataList) {
+            g_list_free_full(m_dataList, reinterpret_cast<GDestroyNotify>(webkit_website_data_unref));
+            m_dataList = nullptr;
+        }
+        webkit_website_data_manager_fetch(m_manager, types, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) {
+            WebsiteDataTest* test = static_cast<WebsiteDataTest*>(userData);
+            test->m_dataList = webkit_website_data_manager_fetch_finish(test->m_manager, result, nullptr);
+            test->quitMainLoop();
+        }, this);
+        g_main_loop_run(m_mainLoop);
+        return m_dataList;
+    }
+
+    void remove(WebKitWebsiteDataTypes types, GList* dataList)
+    {
+        webkit_website_data_manager_remove(m_manager, types, dataList, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) {
+            WebsiteDataTest* test = static_cast<WebsiteDataTest*>(userData);
+            g_assert(webkit_website_data_manager_remove_finish(test->m_manager, result, nullptr));
+            test->quitMainLoop();
+        }, this);
+        g_main_loop_run(m_mainLoop);
+    }
+
+    void clear(WebKitWebsiteDataTypes types, GTimeSpan timeSpan)
+    {
+        webkit_website_data_manager_clear(m_manager, types, timeSpan, nullptr, [](GObject*, GAsyncResult* result, gpointer userData) {
+            WebsiteDataTest* test = static_cast<WebsiteDataTest*>(userData);
+            g_assert(webkit_website_data_manager_clear_finish(test->m_manager, result, nullptr));
+            test->quitMainLoop();
+        }, this);
+        g_main_loop_run(m_mainLoop);
+    }
+
+    WebKitWebsiteDataManager* m_manager;
+    GList* m_dataList { nullptr };
+};
+
+static void testWebsiteDataConfiguration(WebsiteDataTest* test, gconstpointer)
+{
+    // Base directories are not used by TestMain.
+    g_assert(!webkit_website_data_manager_get_base_data_directory(test->m_manager));
+    g_assert(!webkit_website_data_manager_get_base_cache_directory(test->m_manager));
+
+    GUniquePtr<char> localStorageDirectory(g_build_filename(Test::dataDirectory(), "local-storage", nullptr));
+    g_assert_cmpstr(localStorageDirectory.get(), ==, webkit_website_data_manager_get_local_storage_directory(test->m_manager));
+    g_assert(g_file_test(localStorageDirectory.get(), G_FILE_TEST_IS_DIR));
+
+    test->loadURI(kServer->getURIForPath("/empty").data());
+    test->waitUntilLoadFinished();
+    test->runJavaScriptAndWaitUntilFinished("window.indexedDB.open('TestDatabase');", nullptr);
+    GUniquePtr<char> indexedDBDirectory(g_build_filename(Test::dataDirectory(), "indexeddb", nullptr));
+    g_assert_cmpstr(indexedDBDirectory.get(), ==, webkit_website_data_manager_get_indexeddb_directory(test->m_manager));
+    g_assert(g_file_test(indexedDBDirectory.get(), G_FILE_TEST_IS_DIR));
+
+    GUniquePtr<char> webSQLDirectory(g_build_filename(Test::dataDirectory(), "websql", nullptr));
+    g_assert_cmpstr(webSQLDirectory.get(), ==, webkit_website_data_manager_get_websql_directory(test->m_manager));
+    test->runJavaScriptAndWaitUntilFinished("db = openDatabase(\"TestDatabase\", \"1.0\", \"TestDatabase\", 1);", nullptr);
+    g_assert(g_file_test(webSQLDirectory.get(), G_FILE_TEST_IS_DIR));
+
+    test->loadURI(kServer->getURIForPath("/appcache").data());
+    test->waitUntilLoadFinished();
+    GUniquePtr<char> applicationCacheDirectory(g_build_filename(Test::dataDirectory(), "appcache", nullptr));
+    g_assert_cmpstr(applicationCacheDirectory.get(), ==, webkit_website_data_manager_get_offline_application_cache_directory(test->m_manager));
+    GUniquePtr<char> applicationCacheDatabase(g_build_filename(applicationCacheDirectory.get(), "ApplicationCache.db", nullptr));
+    unsigned triesCount = 4;
+    while (!g_file_test(applicationCacheDatabase.get(), G_FILE_TEST_IS_REGULAR) && --triesCount)
+        test->wait(0.25);
+    g_assert(triesCount);
+
+    GUniquePtr<char> diskCacheDirectory(g_build_filename(Test::dataDirectory(), "disk-cache", nullptr));
+    g_assert_cmpstr(diskCacheDirectory.get(), ==, webkit_website_data_manager_get_disk_cache_directory(test->m_manager));
+    g_assert(g_file_test(diskCacheDirectory.get(), G_FILE_TEST_IS_DIR));
+
+    // Clear all persistent caches, since the data dir is common to all test cases.
+    static const WebKitWebsiteDataTypes persistentCaches = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_DISK_CACHE | WEBKIT_WEBSITE_DATA_LOCAL_STORAGE
+        | WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES | WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES | WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE);
+    test->clear(persistentCaches, 0);
+    g_assert(!test->fetch(persistentCaches));
+
+    // The default context should have a different manager with different configuration.
+    WebKitWebsiteDataManager* defaultManager = webkit_web_context_get_website_data_manager(webkit_web_context_get_default());
+    g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(defaultManager));
+    g_assert(test->m_manager != defaultManager);
+    g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(test->m_manager), !=, webkit_website_data_manager_get_local_storage_directory(defaultManager));
+    g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(test->m_manager), !=, webkit_website_data_manager_get_indexeddb_directory(defaultManager));
+    g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(test->m_manager), !=, webkit_website_data_manager_get_disk_cache_directory(defaultManager));
+    g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(test->m_manager), !=, webkit_website_data_manager_get_offline_application_cache_directory(defaultManager));
+    g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(test->m_manager), !=, webkit_website_data_manager_get_websql_directory(defaultManager));
+
+    // Using Test::dataDirectory() we get the default configuration but for a differrent prefix.
+    GRefPtr<WebKitWebsiteDataManager> baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(), nullptr));
+    g_assert(WEBKIT_IS_WEBSITE_DATA_MANAGER(baseDataManager.get()));
+
+    localStorageDirectory.reset(g_build_filename(Test::dataDirectory(), "localstorage", nullptr));
+    g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get());
+
+    indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", "indexeddb", nullptr));
+    g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get());
+
+    applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "applications", nullptr));
+    g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get());
+
+    webSQLDirectory.reset(g_build_filename(Test::dataDirectory(), "databases", nullptr));
+    g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get());
+
+    g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory());
+
+    // Any specific configuration provided takes precedence over base dirs.
+    indexedDBDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomindexeddb", nullptr));
+    applicationCacheDirectory.reset(g_build_filename(Test::dataDirectory(), "mycustomappcache", nullptr));
+    baseDataManager = adoptGRef(webkit_website_data_manager_new("base-data-directory", Test::dataDirectory(), "base-cache-directory", Test::dataDirectory(),
+        "indexeddb-directory", indexedDBDirectory.get(), "offline-application-cache-directory", applicationCacheDirectory.get(), nullptr));
+    g_assert_cmpstr(webkit_website_data_manager_get_indexeddb_directory(baseDataManager.get()), ==, indexedDBDirectory.get());
+    g_assert_cmpstr(webkit_website_data_manager_get_offline_application_cache_directory(baseDataManager.get()), ==, applicationCacheDirectory.get());
+    // The result should be the same as previous manager.
+    g_assert_cmpstr(webkit_website_data_manager_get_local_storage_directory(baseDataManager.get()), ==, localStorageDirectory.get());
+    g_assert_cmpstr(webkit_website_data_manager_get_websql_directory(baseDataManager.get()), ==, webSQLDirectory.get());
+    g_assert_cmpstr(webkit_website_data_manager_get_disk_cache_directory(baseDataManager.get()), ==, Test::dataDirectory());
+}
+
+static void testWebsiteDataCache(WebsiteDataTest* test, gconstpointer)
+{
+    static const WebKitWebsiteDataTypes cacheTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE);
+    GList* dataList = test->fetch(cacheTypes);
+    g_assert(!dataList);
+
+    test->loadURI(kServer->getURIForPath("/empty").data());
+    test->waitUntilLoadFinished();
+
+    // Disk cache delays the storing of initial resources for 1 second to avoid
+    // affecting early page load. So, wait 1 second here to make sure resources
+    // have already been stored.
+    test->wait(1);
+
+    dataList = test->fetch(cacheTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data());
+    g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin));
+    webkit_security_origin_unref(origin);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, cacheTypes);
+    // Memory cache size is unknown.
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_MEMORY_CACHE), ==, 0);
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_DISK_CACHE), >, 0);
+
+    // Try again but only getting disk cache.
+    dataList = test->fetch(WEBKIT_WEBSITE_DATA_DISK_CACHE);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_DISK_CACHE);
+    g_assert(!(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_MEMORY_CACHE));
+
+    GUniquePtr<char> fileURL(g_strdup_printf("file://%s/simple.html", Test::getResourcesDir(Test::WebKit2Resources).data()));
+    test->loadURI(fileURL.get());
+    test->waitUntilLoadFinished();
+
+    fileURL.reset(g_strdup_printf("file://%s/simple2.html", Test::getResourcesDir(Test::WebKit2Resources).data()));
+    test->loadURI(fileURL.get());
+    test->waitUntilLoadFinished();
+
+    // Local files are grouped.
+    dataList = test->fetch(cacheTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 2);
+    GList* itemList = g_list_find_custom(dataList, nullptr, [](gconstpointer item, gconstpointer) -> int {
+        WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(const_cast<gpointer>(item));
+        return g_strcmp0(webkit_website_data_get_name(data), "Local files");
+    });
+    g_assert(itemList);
+    data = static_cast<WebKitWebsiteData*>(itemList->data);
+    g_assert(data);
+    g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_MEMORY_CACHE);
+    // Local files are never stored in disk cache.
+    g_assert(!(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_DISK_CACHE));
+
+    // Clear data modified since the last microsecond should not clear anything.
+    // Use disk-cache because memory cache ignores the modified since.
+    test->clear(WEBKIT_WEBSITE_DATA_DISK_CACHE, 1);
+    dataList = test->fetch(cacheTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 2);
+
+    // Remove memory cache only for local files.
+    itemList = g_list_find_custom(dataList, nullptr, [](gconstpointer item, gconstpointer) -> int {
+        WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(const_cast<gpointer>(item));
+        return g_strcmp0(webkit_website_data_get_name(data), "Local files");
+    });
+    g_assert(itemList);
+    GList removeList = { itemList->data, nullptr, nullptr };
+    test->remove(WEBKIT_WEBSITE_DATA_MEMORY_CACHE, &removeList);
+    dataList = test->fetch(cacheTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_DISK_CACHE);
+
+    // Clear all.
+    test->clear(cacheTypes, 0);
+    dataList = test->fetch(cacheTypes);
+    g_assert(!dataList);
+}
+
+static void testWebsiteDataStorage(WebsiteDataTest* test, gconstpointer)
+{
+    static const WebKitWebsiteDataTypes storageTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_SESSION_STORAGE | WEBKIT_WEBSITE_DATA_LOCAL_STORAGE);
+    GList* dataList = test->fetch(storageTypes);
+    g_assert(!dataList);
+
+    test->loadURI(kServer->getURIForPath("/sessionstorage").data());
+    test->waitUntilLoadFinished();
+
+    test->loadURI(kServer->getURIForPath("/localstorage").data());
+    test->waitUntilLoadFinished();
+
+    // Local storage uses a 1 second timer to update the database.
+    test->wait(1);
+
+    dataList = test->fetch(storageTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data());
+    g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin));
+    webkit_security_origin_unref(origin);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, storageTypes);
+    // Storage sizes are unknown.
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_SESSION_STORAGE), ==, 0);
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_LOCAL_STORAGE), ==, 0);
+
+    // Get also cached data, and clear it.
+    static const WebKitWebsiteDataTypes cacheAndStorageTypes = static_cast<WebKitWebsiteDataTypes>(storageTypes | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE);
+    dataList = test->fetch(cacheAndStorageTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, cacheAndStorageTypes);
+    test->clear(static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE), 0);
+
+    // Get all types again, but only storage is retrieved now.
+    dataList = test->fetch(cacheAndStorageTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, storageTypes);
+
+    // Remove the session storage.
+    GList removeList = { data, nullptr, nullptr };
+    test->remove(WEBKIT_WEBSITE_DATA_SESSION_STORAGE, &removeList);
+    dataList = test->fetch(storageTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(!(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_SESSION_STORAGE));
+    g_assert(webkit_website_data_get_types(data) & WEBKIT_WEBSITE_DATA_LOCAL_STORAGE);
+
+    // Clear all.
+    test->clear(cacheAndStorageTypes, 0);
+    dataList = test->fetch(cacheAndStorageTypes);
+    g_assert(!dataList);
+}
+
+static void testWebsiteDataDatabases(WebsiteDataTest* test, gconstpointer)
+{
+    static const WebKitWebsiteDataTypes databaseTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES | WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES);
+    GList* dataList = test->fetch(databaseTypes);
+    g_assert(!dataList);
+
+    test->loadURI(kServer->getURIForPath("/empty").data());
+    test->waitUntilLoadFinished();
+    test->runJavaScriptAndWaitUntilFinished("window.indexedDB.open('TestDatabase');", nullptr);
+
+    dataList = test->fetch(databaseTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data());
+    g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin));
+    webkit_security_origin_unref(origin);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES);
+    // Database sizes are unknown.
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES), ==, 0);
+
+    test->runJavaScriptAndWaitUntilFinished("db = openDatabase(\"TestDatabase\", \"1.0\", \"TestDatabase\", 1);", nullptr);
+    dataList = test->fetch(databaseTypes);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, databaseTypes);
+    // Database sizes are unknown.
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES), ==, 0);
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES), ==, 0);
+
+    // Remove all databases at once.
+    GList removeList = { data, nullptr, nullptr };
+    test->remove(databaseTypes, &removeList);
+    dataList = test->fetch(databaseTypes);
+    g_assert(!dataList);
+
+    // Clear all.
+    static const WebKitWebsiteDataTypes cacheAndDatabaseTypes = static_cast<WebKitWebsiteDataTypes>(databaseTypes | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE);
+    test->clear(cacheAndDatabaseTypes, 0);
+    dataList = test->fetch(cacheAndDatabaseTypes);
+    g_assert(!dataList);
+}
+
+static void testWebsiteDataAppcache(WebsiteDataTest* test, gconstpointer)
+{
+    GList* dataList = test->fetch(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE);
+    g_assert(!dataList);
+
+    test->loadURI(kServer->getURIForPath("/appcache").data());
+    test->waitUntilLoadFinished();
+
+    test->wait(1);
+    dataList = test->fetch(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE);
+    g_assert(dataList);
+    g_assert_cmpuint(g_list_length(dataList), ==, 1);
+    WebKitWebsiteData* data = static_cast<WebKitWebsiteData*>(dataList->data);
+    g_assert(data);
+    WebKitSecurityOrigin* origin = webkit_security_origin_new_for_uri(kServer->getURIForPath("/").data());
+    g_assert_cmpstr(webkit_website_data_get_name(data), ==, webkit_security_origin_get_host(origin));
+    webkit_security_origin_unref(origin);
+    g_assert_cmpuint(webkit_website_data_get_types(data), ==, WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE);
+    // Appcache size is unknown.
+    g_assert_cmpuint(webkit_website_data_get_size(data, WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE), ==, 0);
+
+    GList removeList = { data, nullptr, nullptr };
+    test->remove(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE, &removeList);
+    dataList = test->fetch(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE);
+    g_assert(!dataList);
+
+    // Clear all.
+    static const WebKitWebsiteDataTypes cacheAndAppcacheTypes = static_cast<WebKitWebsiteDataTypes>(WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE | WEBKIT_WEBSITE_DATA_MEMORY_CACHE | WEBKIT_WEBSITE_DATA_DISK_CACHE);
+    test->clear(cacheAndAppcacheTypes, 0);
+    dataList = test->fetch(cacheAndAppcacheTypes);
+    g_assert(!dataList);
+}
+
+void beforeAll()
+{
+    kServer = new WebKitTestServer();
+    kServer->run(serverCallback);
+
+    WebsiteDataTest::add("WebKitWebsiteData", "configuration", testWebsiteDataConfiguration);
+    WebsiteDataTest::add("WebKitWebsiteData", "cache", testWebsiteDataCache);
+    WebsiteDataTest::add("WebKitWebsiteData", "storage", testWebsiteDataStorage);
+    WebsiteDataTest::add("WebKitWebsiteData", "databases", testWebsiteDataDatabases);
+    WebsiteDataTest::add("WebKitWebsiteData", "appcache", testWebsiteDataAppcache);
+}
+
+void afterAll()
+{
+    delete kServer;
+}