[WPE][GTK] Enable support for CONTENT_EXTENSIONS
authoraperez@igalia.com <aperez@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Feb 2019 10:16:00 +0000 (10:16 +0000)
committeraperez@igalia.com <aperez@igalia.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 20 Feb 2019 10:16:00 +0000 (10:16 +0000)
https://bugs.webkit.org/show_bug.cgi?id=167941

Reviewed by Carlos Garcia Campos.

Source/WebCore:

* platform/gtk/po/POTFILES.in: Added WebKitUserContentFilterStore.cpp
to the list of files with translatable strings.

Source/WebKit:

Adds new API to manage a collection of content extensions on disk (including compilation
of new ones) using WebKitUserContentFilterStore; the associated WebKitUserContentFilter
type (which represents a compiled content extension); and the functions needed to enable
and disable them for a given WebKitUserContentManager.

The WebKitUserContentFilterStore public API is expressed in abstract terms of "saving"
filters into the store (which involves compiling the JSON source rule set); and "loading"
them back as main operations. This way we do not disclose to users of the API any detail
about compilation, nor how contents are laid out on disk, and the documentation explicitly
tells about only using the provided functions to manipulate the on-disk contents. This
way we allow ourselves some leeway if the implementation needs changing in the future.

* PlatformGTK.cmake: Added WebKitUserContentFilterStore.h to the list of public API headers.
* PlatformWPE.cmake: Ditto.
* SourcesGTK.txt: Added WebKitUserContentFilterStore.cpp
* SourcesWPE.txt: Ditto.
* UIProcess/API/glib/WebKitError.cpp: Add definition of webkit_user_content_filter_error_quark().
* UIProcess/API/glib/WebKitUserContent.cpp: Added WebKitUserContentFilter.
(_WebKitUserContentFilter::_WebKitUserContentFilter):
(webkit_user_content_filter_ref):
(webkit_user_content_filter_unref):
(webkit_user_content_filter_get_identifier):
(webkitUserContentFilterCreate):
(webkitUserContentFilterGetContentRuleList):
* UIProcess/API/glib/WebKitUserContentFilterStore.cpp: Added.
(toGError): Utility function to convert content extension error codes to GError.
(webkit_user_content_filter_store_class_init):
(webkit_user_content_filter_store_new):
(webkit_user_content_filter_store_get_path):
(webkitUserContentFilterStoreSaveBytes): Common function used as final step for all the
functions which save (compile) JSON rule sets into the store, to avoid duplicating code.
(webkit_user_content_filter_store_save):
(webkit_user_content_filter_store_save_finish):
(webkit_user_content_filter_store_save_from_file):
(webkit_user_content_filter_store_save_from_file_finish):
(webkit_user_content_filter_store_remove):
(webkit_user_content_filter_store_remove_finish):
(webkit_user_content_filter_store_load):
(webkit_user_content_filter_store_lookup_finish):
(webkit_user_content_filter_store_fetch_identifiers):
(webkit_user_content_filter_store_fetch_identifiers_finish):
* UIProcess/API/glib/WebKitUserContentManager.cpp: Added definitions for the new API
functions to add and remove filters from an user content manager.
(webkit_user_content_manager_add_filter):
(webkit_user_content_manager_remove_filter):
(webkit_user_content_manager_remove_all_filters):
* UIProcess/API/glib/WebKitUserContentPrivate.h: Added declarations for
webkitUserContentFilterCreate() and webkitUserContentFilterGetContentRuleList().
* UIProcess/API/gtk/WebKitAutocleanups.h: Added autocleanups for WebKitUserContentFilter
and WebKitUserContentFilterStore.
* UIProcess/API/gtk/WebKitError.h: Added declarations for WEBKIT_USER_CONTENT_FILTER_ERROR
plus the associated webkit_user_content_filter_error_quark() function and
WebKitUserContentFilterErrror enum.
* UIProcess/API/gtk/WebKitUserContent.h: Added declarations for WebKitUserContentFilter
and its associated functions.
* UIProcess/API/gtk/WebKitUserContentFilterStore.h: Added.
* UIProcess/API/gtk/WebKitUserContentManager.h: Added declarations for the functions to
add and remove filters from the user content manager.
* UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt: Added new API functions and types to
be included in the documentation.
* UIProcess/API/gtk/webkit2.h: Added inclusion of WebKitUserContentFilterStore.h
* UIProcess/API/wpe/WebKitError.h: Same as for the GTK port header.
* UIProcess/API/wpe/WebKitUserContent.h: Same as for the GTK port header.
* UIProcess/API/wpe/WebKitUserContentFilterStore.h: Added.
* UIProcess/API/wpe/WebKitUserContentManager.h: Same as for the GTK port header.
* UIProcess/API/wpe/docs/wpe-0.1-sections.txt: Added new API functions and types to be
included in the documentation.
* UIProcess/API/wpe/webkit.h: Added inclusion of WebKitUserContentFilterStore.h

Source/WTF:

Add specialization of the refGPtr() and derefGPtr() templates for GMappedFile.

* wtf/glib/GRefPtr.cpp:
(WTF::refGPtr): Added.
(WTF::derefGPtr): Added.
* wtf/glib/GRefPtr.h: Declare template specializations.

Tools:

* MiniBrowser/gtk/main.c:
(filterSavedCallback): Added.
(main): Support loading a JSON rule set file for content filtering.
* MiniBrowser/wpe/main.cpp:
(filterSavedCallback): Added.
(main): Support loading a JSON rule set file for content filtering.
* TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentFilterStore.cpp: Added.
(WTF::refGPtr): Added locally for WebKitUserContentFilter, as it would not be used anywhere else.
(WTF::derefGPtr): Ditto.
(testEmptyStore):
(testSaveInvalidFilter):
(testSaveLoadFilter):
(testSavedFilterIdentifierMatch):
(testRemoveFilter):
(testSaveMultipleFilters):
(testSaveFilterFromFile):
(testFilterPersistence):
(beforeAll):
(afterAll):
* TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentManager.cpp:
(removeOldInjectedContentAndResetLists): Also reset content filters.
(isCSSBlockedForURLAtPath): Added. Tests whether the test filter, which blocks a
CSS style sheet, has blocked the load of the CSS by looking at the resulting style of
the element affected by the style sheet.
(getUserContentFilter): Added. Stores the test filter in a WebKitUserContentFilterStore
and returns it to be used by tests.
(testUserContentManagerContentFilter): Added. Tests whether adding and removing a filter
from the WebKitUserContentManager results in the a CSS style sheet being blocked.
(serverCallback): Add support for serving a CSS style sheet for testing.
(beforeAll): Add call to testUserContentManagerContentFilter().
* TestWebKitAPI/glib/CMakeLists.txt: Added TestWebKitUserContentFilterStore.

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

34 files changed:
Source/WTF/ChangeLog
Source/WTF/wtf/glib/GRefPtr.cpp
Source/WTF/wtf/glib/GRefPtr.h
Source/WebCore/ChangeLog
Source/WebCore/platform/gtk/po/POTFILES.in
Source/WebKit/ChangeLog
Source/WebKit/PlatformGTK.cmake
Source/WebKit/PlatformWPE.cmake
Source/WebKit/SourcesGTK.txt
Source/WebKit/SourcesWPE.txt
Source/WebKit/UIProcess/API/glib/WebKitError.cpp
Source/WebKit/UIProcess/API/glib/WebKitUserContent.cpp
Source/WebKit/UIProcess/API/glib/WebKitUserContentFilterStore.cpp [new file with mode: 0644]
Source/WebKit/UIProcess/API/glib/WebKitUserContentManager.cpp
Source/WebKit/UIProcess/API/glib/WebKitUserContentPrivate.h
Source/WebKit/UIProcess/API/gtk/WebKitAutocleanups.h
Source/WebKit/UIProcess/API/gtk/WebKitError.h
Source/WebKit/UIProcess/API/gtk/WebKitUserContent.h
Source/WebKit/UIProcess/API/gtk/WebKitUserContentFilterStore.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/gtk/WebKitUserContentManager.h
Source/WebKit/UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt
Source/WebKit/UIProcess/API/gtk/webkit2.h
Source/WebKit/UIProcess/API/wpe/WebKitError.h
Source/WebKit/UIProcess/API/wpe/WebKitUserContent.h
Source/WebKit/UIProcess/API/wpe/WebKitUserContentFilterStore.h [new file with mode: 0644]
Source/WebKit/UIProcess/API/wpe/WebKitUserContentManager.h
Source/WebKit/UIProcess/API/wpe/docs/wpe-0.1-sections.txt
Source/WebKit/UIProcess/API/wpe/webkit.h
Tools/ChangeLog
Tools/MiniBrowser/gtk/main.c
Tools/MiniBrowser/wpe/main.cpp
Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentFilterStore.cpp [new file with mode: 0644]
Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentManager.cpp
Tools/TestWebKitAPI/glib/CMakeLists.txt

index 6b7ff09..0b4014c 100644 (file)
@@ -1,3 +1,17 @@
+2019-02-20  Adrian Perez de Castro  <aperez@igalia.com>
+
+        [WPE][GTK] Enable support for CONTENT_EXTENSIONS
+        https://bugs.webkit.org/show_bug.cgi?id=167941
+
+        Reviewed by Carlos Garcia Campos.
+
+        Add specialization of the refGPtr() and derefGPtr() templates for GMappedFile.
+
+        * wtf/glib/GRefPtr.cpp:
+        (WTF::refGPtr): Added.
+        (WTF::derefGPtr): Added.
+        * wtf/glib/GRefPtr.h: Declare template specializations.
+
 2019-02-19  Commit Queue  <commit-queue@webkit.org>
 
         Unreviewed, rolling out r241770.
index 2dd06fc..c413ed4 100644 (file)
@@ -169,6 +169,19 @@ template <> void derefGPtr(GRegex* ptr)
         g_regex_unref(ptr);
 }
 
+template <> GMappedFile* refGPtr(GMappedFile* ptr)
+{
+    if (ptr)
+        g_mapped_file_ref(ptr);
+    return ptr;
+}
+
+template <> void derefGPtr(GMappedFile* ptr)
+{
+    if (ptr)
+        g_mapped_file_unref(ptr);
+}
+
 } // namespace WTF
 
 #endif // USE(GLIB)
index aaa16de..06133d8 100644 (file)
@@ -239,6 +239,8 @@ template <> WTF_EXPORT_PRIVATE GClosure* refGPtr(GClosure*);
 template <> WTF_EXPORT_PRIVATE void derefGPtr(GClosure*);
 template <> WTF_EXPORT_PRIVATE GRegex* refGPtr(GRegex*);
 template <> WTF_EXPORT_PRIVATE void derefGPtr(GRegex*);
+template <> WTF_EXPORT_PRIVATE GMappedFile* refGPtr(GMappedFile*);
+template <> WTF_EXPORT_PRIVATE void derefGPtr(GMappedFile*);
 
 template <typename T> inline T* refGPtr(T* ptr)
 {
index 05648c5..24a9da3 100644 (file)
@@ -1,3 +1,13 @@
+2019-02-20  Adrian Perez de Castro  <aperez@igalia.com>
+
+        [WPE][GTK] Enable support for CONTENT_EXTENSIONS
+        https://bugs.webkit.org/show_bug.cgi?id=167941
+
+        Reviewed by Carlos Garcia Campos.
+
+        * platform/gtk/po/POTFILES.in: Added WebKitUserContentFilterStore.cpp
+        to the list of files with translatable strings.
+
 2019-02-19  Simon Fraser  <simon.fraser@apple.com>
 
         REGRESSION (r238090): Toggling visibility on the <html> element can result in a blank web view
index 3ec09db..51ab506 100644 (file)
@@ -19,6 +19,7 @@ LocalizedStringsGtk.cpp
 ../../../WebKit/UIProcess/API/glib/WebKitNotification.cpp
 ../../../WebKit/UIProcess/API/glib/WebKitResponsePolicyDecision.cpp
 ../../../WebKit/UIProcess/API/glib/WebKitSettings.cpp
+../../../WebKit/UIProcess/API/glib/WebKitUserContentFilterStore.cpp
 ../../../WebKit/UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp
 ../../../WebKit/UIProcess/API/glib/WebKitWebContext.cpp
 ../../../WebKit/UIProcess/API/glib/WebKitWebResource.cpp
index 91019bb..2ca59b9 100644 (file)
@@ -1,3 +1,79 @@
+2019-02-20  Adrian Perez de Castro  <aperez@igalia.com>
+
+        [WPE][GTK] Enable support for CONTENT_EXTENSIONS
+        https://bugs.webkit.org/show_bug.cgi?id=167941
+
+        Reviewed by Carlos Garcia Campos.
+
+        Adds new API to manage a collection of content extensions on disk (including compilation
+        of new ones) using WebKitUserContentFilterStore; the associated WebKitUserContentFilter
+        type (which represents a compiled content extension); and the functions needed to enable
+        and disable them for a given WebKitUserContentManager.
+
+        The WebKitUserContentFilterStore public API is expressed in abstract terms of "saving"
+        filters into the store (which involves compiling the JSON source rule set); and "loading"
+        them back as main operations. This way we do not disclose to users of the API any detail
+        about compilation, nor how contents are laid out on disk, and the documentation explicitly
+        tells about only using the provided functions to manipulate the on-disk contents. This
+        way we allow ourselves some leeway if the implementation needs changing in the future.
+
+        * PlatformGTK.cmake: Added WebKitUserContentFilterStore.h to the list of public API headers.
+        * PlatformWPE.cmake: Ditto.
+        * SourcesGTK.txt: Added WebKitUserContentFilterStore.cpp
+        * SourcesWPE.txt: Ditto.
+        * UIProcess/API/glib/WebKitError.cpp: Add definition of webkit_user_content_filter_error_quark().
+        * UIProcess/API/glib/WebKitUserContent.cpp: Added WebKitUserContentFilter.
+        (_WebKitUserContentFilter::_WebKitUserContentFilter):
+        (webkit_user_content_filter_ref):
+        (webkit_user_content_filter_unref):
+        (webkit_user_content_filter_get_identifier):
+        (webkitUserContentFilterCreate):
+        (webkitUserContentFilterGetContentRuleList):
+        * UIProcess/API/glib/WebKitUserContentFilterStore.cpp: Added.
+        (toGError): Utility function to convert content extension error codes to GError.
+        (webkit_user_content_filter_store_class_init):
+        (webkit_user_content_filter_store_new):
+        (webkit_user_content_filter_store_get_path):
+        (webkitUserContentFilterStoreSaveBytes): Common function used as final step for all the
+        functions which save (compile) JSON rule sets into the store, to avoid duplicating code.
+        (webkit_user_content_filter_store_save):
+        (webkit_user_content_filter_store_save_finish):
+        (webkit_user_content_filter_store_save_from_file):
+        (webkit_user_content_filter_store_save_from_file_finish):
+        (webkit_user_content_filter_store_remove):
+        (webkit_user_content_filter_store_remove_finish):
+        (webkit_user_content_filter_store_load):
+        (webkit_user_content_filter_store_lookup_finish):
+        (webkit_user_content_filter_store_fetch_identifiers):
+        (webkit_user_content_filter_store_fetch_identifiers_finish):
+        * UIProcess/API/glib/WebKitUserContentManager.cpp: Added definitions for the new API
+        functions to add and remove filters from an user content manager.
+        (webkit_user_content_manager_add_filter):
+        (webkit_user_content_manager_remove_filter):
+        (webkit_user_content_manager_remove_all_filters):
+        * UIProcess/API/glib/WebKitUserContentPrivate.h: Added declarations for
+        webkitUserContentFilterCreate() and webkitUserContentFilterGetContentRuleList().
+        * UIProcess/API/gtk/WebKitAutocleanups.h: Added autocleanups for WebKitUserContentFilter
+        and WebKitUserContentFilterStore.
+        * UIProcess/API/gtk/WebKitError.h: Added declarations for WEBKIT_USER_CONTENT_FILTER_ERROR
+        plus the associated webkit_user_content_filter_error_quark() function and
+        WebKitUserContentFilterErrror enum.
+        * UIProcess/API/gtk/WebKitUserContent.h: Added declarations for WebKitUserContentFilter
+        and its associated functions.
+        * UIProcess/API/gtk/WebKitUserContentFilterStore.h: Added.
+        * UIProcess/API/gtk/WebKitUserContentManager.h: Added declarations for the functions to
+        add and remove filters from the user content manager.
+        * UIProcess/API/gtk/docs/webkit2gtk-4.0-sections.txt: Added new API functions and types to
+        be included in the documentation.
+        * UIProcess/API/gtk/webkit2.h: Added inclusion of WebKitUserContentFilterStore.h
+        * UIProcess/API/wpe/WebKitError.h: Same as for the GTK port header.
+        * UIProcess/API/wpe/WebKitUserContent.h: Same as for the GTK port header.
+        * UIProcess/API/wpe/WebKitUserContentFilterStore.h: Added.
+        * UIProcess/API/wpe/WebKitUserContentManager.h: Same as for the GTK port header.
+        * UIProcess/API/wpe/docs/wpe-0.1-sections.txt: Added new API functions and types to be
+        included in the documentation.
+        * UIProcess/API/wpe/webkit.h: Added inclusion of WebKitUserContentFilterStore.h
+
 2019-02-19  Antti Koivisto  <antti@apple.com>
 
         Pass rootContentsLayer to Mac remote layer tree
index 634b000..16dc61a 100644 (file)
@@ -109,6 +109,7 @@ set(WebKit2GTK_INSTALLED_HEADERS
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitURISchemeRequest.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitURIUtilities.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserContent.h
+    ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserContentFilterStore.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserContentManager.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserMediaPermissionRequest.h
     ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitWebContext.h
index 0fbe5e2..7084904 100644 (file)
@@ -133,6 +133,7 @@ set(WPE_API_INSTALLED_HEADERS
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitURISchemeRequest.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitURIUtilities.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserContent.h
+    ${WEBKIT_DIR}/UIProcess/API/gtk/WebKitUserContentFilterStore.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserContentManager.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitUserMediaPermissionRequest.h
     ${WEBKIT_DIR}/UIProcess/API/wpe/WebKitWebContext.h
index 90d890e..ea1bf97 100644 (file)
@@ -175,6 +175,7 @@ UIProcess/API/glib/WebKitUIClient.cpp @no-unify
 UIProcess/API/glib/WebKitURISchemeRequest.cpp @no-unify
 UIProcess/API/glib/WebKitURIUtilities.cpp @no-unify
 UIProcess/API/glib/WebKitUserContent.cpp @no-unify
+UIProcess/API/glib/WebKitUserContentFilterStore.cpp @no-unify
 UIProcess/API/glib/WebKitUserContentManager.cpp @no-unify
 UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp @no-unify
 UIProcess/API/glib/WebKitVersion.cpp @no-unify
index 765c69d..d39d553 100644 (file)
@@ -157,6 +157,7 @@ UIProcess/API/glib/WebKitUIClient.cpp @no-unify
 UIProcess/API/glib/WebKitURISchemeRequest.cpp @no-unify
 UIProcess/API/glib/WebKitURIUtilities.cpp @no-unify
 UIProcess/API/glib/WebKitUserContent.cpp @no-unify
+UIProcess/API/glib/WebKitUserContentFilterStore.cpp @no-unify
 UIProcess/API/glib/WebKitUserContentManager.cpp @no-unify
 UIProcess/API/glib/WebKitUserMediaPermissionRequest.cpp @no-unify
 UIProcess/API/glib/WebKitVersion.cpp @no-unify
index ae9f74f..3e581c6 100644 (file)
@@ -71,3 +71,5 @@ GQuark webkit_snapshot_error_quark()
 {
     return g_quark_from_static_string("WebKitSnapshotError");
 }
+
+G_DEFINE_QUARK(WebKitUserContentFilterError, webkit_user_content_filter_error)
index acce07f..80ef542 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Igalia S.L.
+ * Copyright (C) 2014, 2018-2019 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
@@ -317,3 +317,85 @@ API::UserScript& webkitUserScriptGetUserScript(WebKitUserScript* userScript)
 {
     return *userScript->userScript;
 }
+
+
+struct _WebKitUserContentFilter {
+    _WebKitUserContentFilter(RefPtr<API::ContentRuleList>&& contentRuleList)
+        : identifier(contentRuleList->name().utf8())
+        , contentRuleList(WTFMove(contentRuleList))
+        , referenceCount(1)
+    {
+    }
+
+    CString identifier;
+    RefPtr<API::ContentRuleList> contentRuleList;
+    int referenceCount;
+};
+
+G_DEFINE_BOXED_TYPE(WebKitUserContentFilter, webkit_user_content_filter, webkit_user_content_filter_ref, webkit_user_content_filter_unref)
+
+/**
+ * webkit_user_content_filter_ref:
+ * @user_content_filter: A #WebKitUserContentFilter
+ *
+ * Atomically increments the reference count of @user_content_filter by one.
+ * This function is MT-safe and may be called from any thread.
+ *
+ * Since: 2.24
+ */
+WebKitUserContentFilter* webkit_user_content_filter_ref(WebKitUserContentFilter* userContentFilter)
+{
+    g_return_val_if_fail(userContentFilter, nullptr);
+    g_atomic_int_inc(&userContentFilter->referenceCount);
+    return userContentFilter;
+}
+
+/**
+ * webkit_user_content_filter_unref:
+ * @user_content_filter: A #WebKitUserContentFilter
+ *
+ * Atomically decrements the reference count of @user_content_filter by one.
+ * If the reference count drops to 0, all the memory allocated by the
+ * #WebKitUserContentFilter is released. This function is MT-safe and may
+ * be called from any thread.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_filter_unref(WebKitUserContentFilter* userContentFilter)
+{
+    g_return_if_fail(userContentFilter);
+    if (g_atomic_int_dec_and_test(&userContentFilter->referenceCount)) {
+        userContentFilter->~WebKitUserContentFilter();
+        fastFree(userContentFilter);
+    }
+}
+
+/**
+ * webkit_user_content_filter_get_identifier:
+ * @user_content_filter: A #WebKitUserContentFilter
+ *
+ * Obtain the identifier previously used to save the @user_content_filter in the
+ * #WebKitUserContentFilterStore.
+ *
+ * Returns: (transfer none): the identifier for the filter
+ *
+ * Since: 2.24
+ */
+const char* webkit_user_content_filter_get_identifier(WebKitUserContentFilter* userContentFilter)
+{
+    g_return_val_if_fail(userContentFilter, nullptr);
+    return userContentFilter->identifier.data();
+}
+
+WebKitUserContentFilter* webkitUserContentFilterCreate(RefPtr<API::ContentRuleList>&& contentRuleList)
+{
+    WebKitUserContentFilter* userContentFilter = static_cast<WebKitUserContentFilter*>(fastMalloc(sizeof(WebKitUserContentFilter)));
+    new (userContentFilter) WebKitUserContentFilter(WTFMove(contentRuleList));
+    return userContentFilter;
+}
+
+API::ContentRuleList& webkitUserContentFilterGetContentRuleList(WebKitUserContentFilter* userContentFilter)
+{
+    ASSERT(userContentFilter);
+    return *userContentFilter->contentRuleList;
+}
diff --git a/Source/WebKit/UIProcess/API/glib/WebKitUserContentFilterStore.cpp b/Source/WebKit/UIProcess/API/glib/WebKitUserContentFilterStore.cpp
new file mode 100644 (file)
index 0000000..53e0269
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * Copyright (C) 2018-2019 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WebKitUserContentFilterStore.h"
+
+#include "APIContentRuleList.h"
+#include "APIContentRuleListStore.h"
+#include "ContentExtensionError.h"
+#include "WebKitError.h"
+#include "WebKitUserContent.h"
+#include "WebKitUserContentPrivate.h"
+#include <glib/gi18n-lib.h>
+#include <wtf/CompletionHandler.h>
+#include <wtf/FileSystem.h>
+#include <wtf/RefPtr.h>
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+#include <wtf/glib/WTFGType.h>
+
+/**
+ * SECTION: WebKitUserContentFilterStore
+ * @Short_description: Handles storage of user content filters on disk.
+ * @Title: WebKitUserContentFilterStore
+ *
+ * The WebKitUserContentFilterStore provides the means to import and save
+ * [JSON rule sets](https://webkit.org/blog/3476/content-blockers-first-look/),
+ * which can be loaded later in an efficient manner. Once filters are stored,
+ * the #WebKitUserContentFilter objects which represent them can be added to
+ * a #WebKitUserContentManager with webkit_user_content_manager_add_filter().
+ *
+ * JSON rule sets are imported using webkit_user_content_filter_store_save() and stored
+ * on disk in an implementation defined format. The contents of a filter store must be
+ * managed using the #WebKitUserContentFilterStore: a list of all the stored filters
+ * can be obtained with webkit_user_content_filter_store_fetch_identifiers(),
+ * webkit_user_content_filter_store_load() can be used to retrieve a previously saved
+ * filter, and removed from the store with webkit_user_content_filter_store_remove().
+ *
+ * Since: 2.24
+ */
+
+enum {
+    PROP_0,
+
+    PROP_PATH,
+};
+
+static inline GError* toGError(WebKitUserContentFilterError code, const std::error_code error)
+{
+    ASSERT(error);
+    ASSERT(error.category() == WebCore::ContentExtensions::contentExtensionErrorCategory());
+    return g_error_new_literal(WEBKIT_USER_CONTENT_FILTER_ERROR, code, error.message().c_str());
+}
+
+struct _WebKitUserContentFilterStorePrivate {
+    GUniquePtr<char> storagePath;
+    RefPtr<API::ContentRuleListStore> store;
+};
+
+WEBKIT_DEFINE_TYPE(WebKitUserContentFilterStore, webkit_user_content_filter_store, G_TYPE_OBJECT)
+
+static void webkitUserContentFilterStoreGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec)
+{
+    WebKitUserContentFilterStore* store = WEBKIT_USER_CONTENT_FILTER_STORE(object);
+
+    switch (propID) {
+    case PROP_PATH:
+        g_value_set_string(value, webkit_user_content_filter_store_get_path(store));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
+    }
+}
+
+static void webkitUserContentFilterStoreSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec)
+{
+    WebKitUserContentFilterStore* store = WEBKIT_USER_CONTENT_FILTER_STORE(object);
+
+    switch (propID) {
+    case PROP_PATH:
+        store->priv->storagePath.reset(g_value_dup_string(value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec);
+    }
+}
+
+static void webkitUserContentFilterStoreConstructed(GObject* object)
+{
+    G_OBJECT_CLASS(webkit_user_content_filter_store_parent_class)->constructed(object);
+
+    WebKitUserContentFilterStore* store = WEBKIT_USER_CONTENT_FILTER_STORE(object);
+    store->priv->store = adoptRef(new API::ContentRuleListStore(FileSystem::stringFromFileSystemRepresentation(store->priv->storagePath.get()), false));
+}
+
+static void webkit_user_content_filter_store_class_init(WebKitUserContentFilterStoreClass* storeClass)
+{
+    GObjectClass* gObjectClass = G_OBJECT_CLASS(storeClass);
+
+    gObjectClass->get_property = webkitUserContentFilterStoreGetProperty;
+    gObjectClass->set_property = webkitUserContentFilterStoreSetProperty;
+    gObjectClass->constructed = webkitUserContentFilterStoreConstructed;
+
+    /**
+     * WebKitUserContentFilterStore:path:
+     *
+     * The directory used for filter storage. This path is used as the base
+     * directory where user content filters are stored on disk.
+     *
+     * Since: 2.24
+     */
+    g_object_class_install_property(
+        gObjectClass,
+        PROP_PATH,
+        g_param_spec_string(
+            "path",
+            _("Storage directory path"),
+            _("The directory where user content filters are stored"),
+            nullptr,
+            static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)));
+}
+
+/**
+ * webkit_user_content_filter_store_new:
+ * @storage_path: path where data for filters will be stored on disk
+ *
+ * Create a new #WebKitUserContentFilterStore to manipulate filters stored at @storage_path.
+ * The path must point to a local filesystem, and will be created if needed.
+ *
+ * Returns: (transfer full): a newly created #WebKitUserContentFilterStore
+ *
+ * Since: 2.24
+ */
+WebKitUserContentFilterStore* webkit_user_content_filter_store_new(const gchar* storagePath)
+{
+    g_return_val_if_fail(storagePath, nullptr);
+    return WEBKIT_USER_CONTENT_FILTER_STORE(g_object_new(WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, "path", storagePath, nullptr));
+}
+
+/**
+ * webkit_user_content_filter_store_get_path:
+ * @store: a #WebKitUserContentFilterStore
+ *
+ * Returns: (transfer none): The storage path for user content filters.
+ *
+ * Since: 2.24
+ */
+const char* webkit_user_content_filter_store_get_path(WebKitUserContentFilterStore* store)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr);
+    return store->priv->storagePath.get();
+}
+
+static void webkitUserContentFilterStoreSaveBytes(GRefPtr<GTask>&& task, String&& identifier, GRefPtr<GBytes>&& source)
+{
+    size_t sourceSize;
+    const char* sourceData = static_cast<const char*>(g_bytes_get_data(source.get(), &sourceSize));
+    if (!sourceSize) {
+        g_task_return_error(task.get(), g_error_new_literal(WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE, "Source JSON rule set cannot be empty"));
+        return;
+    }
+
+    auto* store = WEBKIT_USER_CONTENT_FILTER_STORE(g_task_get_source_object(task.get()));
+    store->priv->store->compileContentRuleList(identifier, String::fromUTF8(sourceData, sourceSize), [task = WTFMove(task)](RefPtr<API::ContentRuleList> contentRuleList, std::error_code error) {
+        if (g_task_return_error_if_cancelled(task.get()))
+            return;
+
+        if (error)
+            g_task_return_error(task.get(), toGError(WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE, error));
+        else
+            g_task_return_pointer(task.get(), webkitUserContentFilterCreate(WTFMove(contentRuleList)), reinterpret_cast<GDestroyNotify>(webkit_user_content_filter_unref));
+    });
+}
+
+/**
+ * webkit_user_content_filter_store_save:
+ * @store: a #WebKitUserContentFilterStore
+ * @identifier: a string used to identify the saved filter
+ * @source: #GBytes containing the rule set in JSON format
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when saving is completed
+ * @user_data: (closure): the data to pass to the callback function
+ *
+ * Asynchronously save a content filter from a source rule set in the
+ * [WebKit content extesions JSON format](https://webkit.org/blog/3476/content-blockers-first-look/).
+ *
+ * The @identifier can be used afterwards to refer to the filter when using
+ * webkit_user_content_filter_store_remove() and webkit_user_content_filter_store_load().
+ * When the @identifier has been used in the past, the new filter source will replace
+ * the one saved beforehand for the same identifier.
+ *
+ * When the operation is finished, @callback will be invoked, which then can use
+ * webkit_user_content_filter_store_save_finish() to obtain the resulting filter.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_filter_store_save(WebKitUserContentFilterStore* store, const gchar* identifier, GBytes* source, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store));
+    g_return_if_fail(identifier);
+    g_return_if_fail(source);
+    g_return_if_fail(callback);
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData));
+    webkitUserContentFilterStoreSaveBytes(WTFMove(task), String::fromUTF8(identifier), GRefPtr<GBytes>(source));
+}
+
+/**
+ * webkit_user_content_filter_store_save_finish:
+ * @store: a #WebKitUserContentFilterStore
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finishes an asynchronous filter save previously started with
+ * webkit_user_content_filter_store_save().
+ *
+ * Returns: (transfer full): a #WebKitUserContentFilter, or %NULL if saving failed
+ *
+ * Since: 2.24
+ */
+WebKitUserContentFilter* webkit_user_content_filter_store_save_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr);
+    g_return_val_if_fail(result, nullptr);
+    return static_cast<WebKitUserContentFilter*>(g_task_propagate_pointer(G_TASK(result), error));
+}
+
+struct SaveTaskData {
+    String identifier;
+};
+WEBKIT_DEFINE_ASYNC_DATA_STRUCT(SaveTaskData)
+
+/**
+ * webkit_user_content_filter_store_save_from_file:
+ * @store: a #WebKitUserContentFilterStore
+ * @identifier: a string used to identify the saved filter
+ * @file: a #GFile containing the rule set in JSON format
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when saving is completed
+ * @user_data: (closure): the data to pass to the callback function
+ *
+ * Asynchronously save a content filter from the contents of a file, which must be
+ * native to the platform, as checked by g_file_is_native(). See
+ * webkit_user_content_filter_store_save() for more details.
+ *
+ * When the operation is finished, @callback will be invoked, which then can use
+ * webkit_user_content_filter_store_save_finish() to obtain the resulting filter.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_filter_store_save_from_file(WebKitUserContentFilterStore* store, const gchar* identifier, GFile* file, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store));
+    g_return_if_fail(identifier);
+    g_return_if_fail(G_IS_FILE(file));
+    g_return_if_fail(callback);
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData));
+
+    // Try mapping the file in memory first, and fall-back to reading the contents if that fails.
+    if (g_file_is_native(file)) {
+        GUniquePtr<char> filePath(g_file_get_path(file));
+        GRefPtr<GMappedFile> mappedFile = adoptGRef(g_mapped_file_new(filePath.get(), FALSE, nullptr));
+        if (mappedFile) {
+            GRefPtr<GBytes> source = adoptGRef(g_mapped_file_get_bytes(mappedFile.get()));
+            webkitUserContentFilterStoreSaveBytes(WTFMove(task), String::fromUTF8(identifier), WTFMove(source));
+            return;
+        }
+    }
+
+    // Pass the identifier as task data to be used in the completion callback once the contents have been loaded.
+    SaveTaskData* data = createSaveTaskData();
+    data->identifier = String::fromUTF8(identifier);
+    g_task_set_task_data(task.get(), data, reinterpret_cast<GDestroyNotify>(destroySaveTaskData));
+
+    g_file_load_contents_async(file, cancellable, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+        GRefPtr<GTask> task = adoptGRef(G_TASK(userData));
+        if (g_task_return_error_if_cancelled(task.get()))
+            return;
+
+        char* sourceData;
+        size_t sourceSize;
+        GUniqueOutPtr<GError> error;
+        if (g_file_load_contents_finish(G_FILE(sourceObject), result, &sourceData, &sourceSize, nullptr, &error.outPtr())) {
+            SaveTaskData* data = static_cast<SaveTaskData*>(g_task_get_task_data(task.get()));
+            webkitUserContentFilterStoreSaveBytes(WTFMove(task), WTFMove(data->identifier), GRefPtr<GBytes>(g_bytes_new_take(sourceData, sourceSize)));
+        } else
+            g_task_return_error(task.get(), error.release().release());
+    }, task.leakRef());
+}
+
+/**
+ * webkit_user_content_filter_store_save_from_file_finish:
+ * @store: a #WebKitUserContentFilterStore
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finishes and asynchronous filter save previously started with
+ * webkit_user_content_filter_store_save_from_file().
+ *
+ * Returns: (transfer full): a #WebKitUserContentFilter, or %NULL if saving failed.
+ *
+ * Since: 2.24
+ */
+WebKitUserContentFilter* webkit_user_content_filter_store_save_from_file_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr);
+    g_return_val_if_fail(result, nullptr);
+    return static_cast<WebKitUserContentFilter*>(g_task_propagate_pointer(G_TASK(result), error));
+}
+
+/**
+ * webkit_user_content_filter_store_remove:
+ * @store: a #WebKitUserContentFilterStore
+ * @identifier: a filter identifier
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the removal is completed
+ * @user_data: (closure): the data to pass to the callback function
+ *
+ * Asynchronously remove a content filter given its @identifier.
+ *
+ * When the operation is finished, @callback will be invoked, which then can use
+ * webkit_user_content_filter_store_remove_finish() to check whether the removal was
+ * successful.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_filter_store_remove(WebKitUserContentFilterStore* store, const gchar* identifier, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store));
+    g_return_if_fail(identifier);
+    g_return_if_fail(callback);
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData));
+    store->priv->store->removeContentRuleList(String::fromUTF8(identifier), [task = WTFMove(task)](std::error_code error) {
+        if (g_task_return_error_if_cancelled(task.get()))
+            return;
+
+        if (error) {
+            ASSERT(static_cast<API::ContentRuleListStore::Error>(error.value()) == API::ContentRuleListStore::Error::RemoveFailed);
+            g_task_return_error(task.get(), toGError(WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND, error));
+        } else
+            g_task_return_boolean(task.get(), TRUE);
+    });
+}
+
+/**
+ * webkit_user_content_filter_store_remove_finish:
+ * @store: a #WebKitUserContentFilterStore
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finishes an asynchronous filter removal previously started with
+ * webkit_user_content_filter_store_remove().
+ *
+ * Returns: whether the removal was successful
+ *
+ * Since: 2.24
+ */
+gboolean webkit_user_content_filter_store_remove_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), FALSE);
+    g_return_val_if_fail(result, FALSE);
+    return g_task_propagate_boolean(G_TASK(result), error);
+}
+
+/**
+ * webkit_user_content_filter_store_load:
+ * @store: a #WebKitUserContentFilterStore
+ * @identifier: a filter identifier
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the load is completed
+ * @user_data: (closure): the data to pass to the callback function
+ *
+ * Asynchronously load a content filter given its @identifier. The filter must have been
+ * previously stored using webkit_user_content_filter_store_save().
+ *
+ * When the operation is finished, @callback will be invoked, which then can use
+ * webkit_user_content_filter_store_load_finish() to obtain the resulting filter.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_filter_store_load(WebKitUserContentFilterStore* store, const gchar* identifier, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store));
+    g_return_if_fail(identifier);
+    g_return_if_fail(callback);
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData));
+    store->priv->store->lookupContentRuleList(String::fromUTF8(identifier), [task = WTFMove(task)](RefPtr<API::ContentRuleList> contentRuleList, std::error_code error) {
+        if (g_task_return_error_if_cancelled(task.get()))
+            return;
+
+        if (error) {
+            ASSERT(static_cast<API::ContentRuleListStore::Error>(error.value()) == API::ContentRuleListStore::Error::LookupFailed
+                || static_cast<API::ContentRuleListStore::Error>(error.value()) == API::ContentRuleListStore::Error::VersionMismatch);
+            g_task_return_error(task.get(), toGError(WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND, error));
+        } else
+            g_task_return_pointer(task.get(), webkitUserContentFilterCreate(WTFMove(contentRuleList)), reinterpret_cast<GDestroyNotify>(webkit_user_content_filter_unref));
+    });
+}
+
+/**
+ * webkit_user_content_filter_store_load_finish:
+ * @store: a #WebKitUserContentFilterStore
+ * @result: a #GAsyncResult
+ * @error: return location for error or %NULL to ignore
+ *
+ * Finishes an asynchronous filter load previously started with
+ * webkit_user_content_filter_store_load().
+ *
+ * Returns: (transfer full): a #WebKitUserContentFilter, or %NULL if the load failed
+ *
+ * Since: 2.24
+ */
+WebKitUserContentFilter* webkit_user_content_filter_store_load_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr);
+    g_return_val_if_fail(result, nullptr);
+    return static_cast<WebKitUserContentFilter*>(g_task_propagate_pointer(G_TASK(result), error));
+}
+
+/**
+ * webkit_user_content_filter_store_fetch_identifiers:
+ * @store: a #WebKitUserContentFilterStore
+ * @cancellable: (allow-none): a #GCancellable or %NULL to ignore
+ * @callback: (scope async): a #GAsyncReadyCallback to call when the removal is completed
+ * @user_data: (closure): the data to pass to the callback function
+ *
+ * Asynchronously retrieve a list of the identifiers for all the stored filters.
+ *
+ * When the operation is finished, @callback will be invoked, which then can use
+ * webkit_user_content_filter_store_fetch_identifiers_finish() to obtain the list of
+ * filter identifiers.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_filter_store_fetch_identifiers(WebKitUserContentFilterStore* store, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store));
+    g_return_if_fail(callback);
+
+    GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData));
+    store->priv->store->getAvailableContentRuleListIdentifiers([task = WTFMove(task)](WTF::Vector<WTF::String> identifiers) {
+        if (g_task_return_error_if_cancelled(task.get()))
+            return;
+
+        GStrv result = static_cast<GStrv>(g_new0(gchar*, identifiers.size() + 1));
+        for (size_t i = 0; i < identifiers.size(); ++i)
+            result[i] = g_strdup(identifiers[i].utf8().data());
+        g_task_return_pointer(task.get(), result, reinterpret_cast<GDestroyNotify>(g_strfreev));
+    });
+}
+
+/**
+ * webkit_user_content_filter_store_fetch_identifiers_finish:
+ * @store: a #WebKitUserContentFilterStore
+ * @result: a #GAsyncResult
+ *
+ * Finishes an asynchronous fetch of the list of identifiers for the stored filters previously
+ * started with webkit_user_content_filter_store_fetch_identifiers().
+ *
+ * Returns: (transfer full) (array zero-terminated=1) (element-type utf8): a %NULL-terminated list of filter identifiers.
+ *
+ * Since: 2.24
+ */
+gchar** webkit_user_content_filter_store_fetch_identifiers_finish(WebKitUserContentFilterStore* store, GAsyncResult* result)
+{
+    g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr);
+    g_return_val_if_fail(result, nullptr);
+    return static_cast<gchar**>(g_task_propagate_pointer(G_TASK(result), nullptr));
+}
index 7b07694..48b38ea 100644 (file)
@@ -318,6 +318,56 @@ void webkit_user_content_manager_unregister_script_message_handler_in_world(WebK
     manager->priv->userContentController->removeUserMessageHandlerForName(String::fromUTF8(name), webkitUserContentWorld(worldName));
 }
 
+/**
+ * webkit_user_content_manager_add_filter:
+ * @manager: A #WebKitUserContentManager
+ * @filter: A #WebKitUserContentFilter
+ *
+ * Adds a #WebKitUserContentFilter to the given #WebKitUserContentManager.
+ * The same #WebKitUserContentFilter can be reused with multiple
+ * #WebKitUserContentManager instances.
+ *
+ * Filters need to be saved and loaded from #WebKitUserContentFilterStore.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_manager_add_filter(WebKitUserContentManager* manager, WebKitUserContentFilter* filter)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
+    g_return_if_fail(filter);
+    manager->priv->userContentController->addContentRuleList(webkitUserContentFilterGetContentRuleList(filter));
+}
+
+/**
+ * webkit_user_content_manager_remove_filter:
+ * @manager: A #WebKitUserContentManager
+ * @filter: A #WebKitUserContentFilter
+ *
+ * Removes a filter from the given #WebKitUserContentManager.
+ *
+ * Since 2.24
+ */
+void webkit_user_content_manager_remove_filter(WebKitUserContentManager* manager, WebKitUserContentFilter* filter)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
+    g_return_if_fail(filter);
+    manager->priv->userContentController->removeContentRuleList(webkitUserContentFilterGetContentRuleList(filter).name());
+}
+
+/**
+ * webkit_user_content_manager_remove_all_filters:
+ * @manager: A #WebKitUserContentManager
+ *
+ * Removes all content filters from the given #WebKitUserContentManager.
+ *
+ * Since: 2.24
+ */
+void webkit_user_content_manager_remove_all_filters(WebKitUserContentManager* manager)
+{
+    g_return_if_fail(WEBKIT_IS_USER_CONTENT_MANAGER(manager));
+    manager->priv->userContentController->removeAllContentRuleLists();
+}
+
 WebUserContentControllerProxy* webkitUserContentManagerGetUserContentControllerProxy(WebKitUserContentManager* manager)
 {
     return manager->priv->userContentController.get();
index f181a29..ad3495b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Igalia S.L.
+ * Copyright (C) 2014, 2018 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
@@ -20,6 +20,7 @@
 #ifndef WebKitUserContentPrivate_h
 #define WebKitUserContentPrivate_h
 
+#include "APIContentRuleList.h"
 #include "APIUserContentWorld.h"
 #include "APIUserScript.h"
 #include "APIUserStyleSheet.h"
@@ -30,5 +31,7 @@
 API::UserScript& webkitUserScriptGetUserScript(WebKitUserScript*);
 API::UserStyleSheet& webkitUserStyleSheetGetUserStyleSheet(WebKitUserStyleSheet*);
 API::UserContentWorld& webkitUserContentWorld(const char*);
+API::ContentRuleList& webkitUserContentFilterGetContentRuleList(WebKitUserContentFilter*);
+WebKitUserContentFilter* webkitUserContentFilterCreate(RefPtr<API::ContentRuleList>&&);
 
 #endif // WebKitUserContentPrivate_h
index 8cc9f0f..ed7eda7 100644 (file)
@@ -65,6 +65,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitSettings, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitURIRequest, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitURIResponse, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitURISchemeRequest, g_object_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentFilterStore, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentManager, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserMediaPermissionRequest, g_object_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebContext, g_object_unref)
@@ -83,6 +84,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitNetworkProxySettings, webkit_network_proxy_
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitSecurityOrigin, webkit_security_origin_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserScript, webkit_user_script_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserStyleSheet, webkit_user_style_sheet_unref)
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitUserContentFilter, webkit_user_content_filter_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebsiteData, webkit_website_data_unref)
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (WebKitWebViewSessionState, webkit_web_view_session_state_unref)
 
index 78b8ddc..29d4f7c 100644 (file)
 
 G_BEGIN_DECLS
 
-#define WEBKIT_NETWORK_ERROR    webkit_network_error_quark ()
-#define WEBKIT_POLICY_ERROR     webkit_policy_error_quark ()
-#define WEBKIT_PLUGIN_ERROR     webkit_plugin_error_quark ()
-#define WEBKIT_DOWNLOAD_ERROR   webkit_download_error_quark ()
-#define WEBKIT_PRINT_ERROR      webkit_print_error_quark ()
-#define WEBKIT_JAVASCRIPT_ERROR webkit_javascript_error_quark ()
-#define WEBKIT_SNAPSHOT_ERROR   webkit_snapshot_error_quark ()
+#define WEBKIT_NETWORK_ERROR             webkit_network_error_quark ()
+#define WEBKIT_POLICY_ERROR              webkit_policy_error_quark ()
+#define WEBKIT_PLUGIN_ERROR              webkit_plugin_error_quark ()
+#define WEBKIT_DOWNLOAD_ERROR            webkit_download_error_quark ()
+#define WEBKIT_PRINT_ERROR               webkit_print_error_quark ()
+#define WEBKIT_JAVASCRIPT_ERROR          webkit_javascript_error_quark ()
+#define WEBKIT_SNAPSHOT_ERROR            webkit_snapshot_error_quark ()
+#define WEBKIT_USER_CONTENT_FILTER_ERROR webkit_user_content_filter_error_quark ()
 
 /**
  * WebKitNetworkError:
@@ -141,26 +142,41 @@ typedef enum {
     WEBKIT_SNAPSHOT_ERROR_FAILED_TO_CREATE = 799
 } WebKitSnapshotError;
 
+/**
+ * WebKitUserContentFilterError:
+ * @WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE: The JSON source for a content filter is invalid.
+ * @WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND: The requested content filter could not be found.
+ *
+ * Since: 2.24
+ */
+typedef enum {
+    WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE,
+    WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND,
+} WebKitUserContentFilterError;
+
+WEBKIT_API GQuark
+webkit_network_error_quark             (void);
+
 WEBKIT_API GQuark
-webkit_network_error_quark    (void);
+webkit_policy_error_quark              (void);
 
 WEBKIT_API GQuark
-webkit_policy_error_quark     (void);
+webkit_plugin_error_quark              (void);
 
 WEBKIT_API GQuark
-webkit_plugin_error_quark     (void);
+webkit_download_error_quark            (void);
 
 WEBKIT_API GQuark
-webkit_download_error_quark   (void);
+webkit_print_error_quark               (void);
 
 WEBKIT_API GQuark
-webkit_print_error_quark      (void);
+webkit_javascript_error_quark          (void);
 
 WEBKIT_API GQuark
-webkit_javascript_error_quark (void);
+webkit_snapshot_error_quark            (void);
 
 WEBKIT_API GQuark
-webkit_snapshot_error_quark   (void);
+webkit_user_content_filter_error_quark (void);
 
 G_END_DECLS
 
index 1dd450d..2746e76 100644 (file)
@@ -136,6 +136,22 @@ webkit_user_script_new_for_world (const gchar                    *source,
                                   const gchar* const             *whitelist,
                                   const gchar* const             *blacklist);
 
+#define WEBKIT_TYPE_USER_CONTENT_FILTER   (webkit_user_content_filter_get_type())
+
+typedef struct _WebKitUserContentFilter WebKitUserContentFilter;
+
+WEBKIT_API GType
+webkit_user_content_filter_get_type       (void);
+
+WEBKIT_API const char*
+webkit_user_content_filter_get_identifier (WebKitUserContentFilter     *user_content_filter);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_ref            (WebKitUserContentFilter     *user_content_filter);
+
+WEBKIT_API void
+webkit_user_content_filter_unref          (WebKitUserContentFilter     *user_content_filter);
+
 G_END_DECLS
 
 #endif
diff --git a/Source/WebKit/UIProcess/API/gtk/WebKitUserContentFilterStore.h b/Source/WebKit/UIProcess/API/gtk/WebKitUserContentFilterStore.h
new file mode 100644 (file)
index 0000000..3cab886
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(__WEBKIT2_H_INSIDE__) && !defined(WEBKIT2_COMPILATION)
+#error "Only <webkit2/webkit2.h> can be included directly."
+#endif
+
+#ifndef WebKitUserContentFilterStore_h
+#define WebKitUserContentFilterStore_h
+
+#include <gio/gio.h>
+#include <webkit2/WebKitDefines.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_USER_CONTENT_FILTER_STORE            (webkit_user_content_filter_store_get_type())
+#define WEBKIT_USER_CONTENT_FILTER_STORE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, WebKitUserContentFilterStore))
+#define WEBKIT_IS_USER_CONTENT_FILTER_STORE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_USER_CONTENT_FILTER_STORE))
+#define WEBKIT_USER_CONTENT_FILTER_STORE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, WebKitUserContentFilterStoreClass))
+#define WEBKIT_IS_USER_CONTENT_FILTER_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_USER_CONTENT_FILTER_STORE))
+#define WEBKIT_USER_CONTENT_FILTER_STORE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, WebKitUserContentFilterStoreClass))
+
+typedef struct _WebKitUserContentFilterStore        WebKitUserContentFilterStore;
+typedef struct _WebKitUserContentFilterStoreClass   WebKitUserContentFilterStoreClass;
+typedef struct _WebKitUserContentFilterStorePrivate WebKitUserContentFilterStorePrivate;
+
+typedef struct _WebKitUserContentFilter             WebKitUserContentFilter;
+
+struct _WebKitUserContentFilterStore {
+    GObject parent;
+
+    /*< private >*/
+    WebKitUserContentFilterStorePrivate *priv;
+};
+
+struct _WebKitUserContentFilterStoreClass {
+    GObjectClass parent_class;
+
+    void (*_webkit_reserved0) (void);
+    void (*_webkit_reserved1) (void);
+    void (*_webkit_reserved2) (void);
+    void (*_webkit_reserved3) (void);
+};
+
+
+WEBKIT_API GType
+webkit_user_content_filter_store_get_type                 (void);
+
+WEBKIT_API WebKitUserContentFilterStore *
+webkit_user_content_filter_store_new                      (const gchar                  *storage_path);
+
+WEBKIT_API const gchar *
+webkit_user_content_filter_store_get_path                 (WebKitUserContentFilterStore *store);
+
+WEBKIT_API void
+webkit_user_content_filter_store_save                     (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GBytes                       *source,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_store_save_finish              (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_save_from_file           (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GFile                        *file,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_store_save_from_file_finish    (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_remove                   (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API gboolean
+webkit_user_content_filter_store_remove_finish            (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_load                     (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_store_load_finish              (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_fetch_identifiers        (WebKitUserContentFilterStore *store,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API gchar**
+webkit_user_content_filter_store_fetch_identifiers_finish (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result);
+
+G_END_DECLS
+
+#endif /* !WebKitUserContentFilterStore_h */
index ff50568..1f41d11 100644 (file)
@@ -93,6 +93,17 @@ webkit_user_content_manager_add_script                                 (WebKitUs
 WEBKIT_API void
 webkit_user_content_manager_remove_all_scripts                         (WebKitUserContentManager *manager);
 
+WEBKIT_API void
+webkit_user_content_manager_add_filter                                 (WebKitUserContentManager *manager,
+                                                                        WebKitUserContentFilter  *filter);
+
+WEBKIT_API void
+webkit_user_content_manager_remove_filter                              (WebKitUserContentManager *manager,
+                                                                        WebKitUserContentFilter  *filter);
+
+WEBKIT_API void
+webkit_user_content_manager_remove_all_filters                         (WebKitUserContentManager *manager);
+
 G_END_DECLS
 
 #endif
index 4efb952..06d7450 100644 (file)
@@ -96,6 +96,7 @@ WebKitUserScript
 WebKitUserContentInjectedFrames
 WebKitUserStyleLevel
 WebKitUserScriptInjectionTime
+WebKitUserContentFilter
 webkit_user_style_sheet_ref
 webkit_user_style_sheet_unref
 webkit_user_style_sheet_new
@@ -104,14 +105,19 @@ webkit_user_script_ref
 webkit_user_script_unref
 webkit_user_script_new
 webkit_user_script_new_for_world
+webkit_user_content_filter_ref
+webkit_user_content_filter_unref
+webkit_user_content_filter_get_identifier
 
 <SUBSECTION Standard>
 WEBKIT_TYPE_USER_STYLE_SHEET
 WEBKIT_TYPE_USER_SCRIPT
+WEBKIT_TYPE_USER_CONTENT_FILTER
 
 <SUBSECTION Private>
 webkit_user_style_sheet_get_type
 webkit_user_script_get_type
+webkit_user_content_filter_get_type
 </SECTION>
 
 <SECTION>
@@ -127,6 +133,9 @@ webkit_user_content_manager_register_script_message_handler
 webkit_user_content_manager_unregister_script_message_handler
 webkit_user_content_manager_register_script_message_handler_in_world
 webkit_user_content_manager_unregister_script_message_handler_in_world
+webkit_user_content_manager_add_filter
+webkit_user_content_manager_remove_filter
+webkit_user_content_manager_remove_all_filters
 
 <SUBSECTION Standard>
 WEBKIT_IS_USER_CONTENT_MANAGER
@@ -143,6 +152,37 @@ webkit_user_content_manager_get_type
 </SECTION>
 
 <SECTION>
+<FILE>WebKitUserContentFilterStore</FILE>
+<TITLE>WebKitUserContentFilterStore</TITLE>
+WebKitUserContentFilterStore
+webkit_user_content_filter_store_new
+webkit_user_content_filter_store_get_path
+webkit_user_content_filter_store_save
+webkit_user_content_filter_store_save_finish
+webkit_user_content_filter_store_save_from_file
+webkit_user_content_filter_store_save_from_file_finish
+webkit_user_content_filter_store_load
+webkit_user_content_filter_store_load_finish
+webkit_user_content_filter_store_remove
+webkit_user_content_filter_store_remove_finish
+webkit_user_content_filter_store_fetch_identifiers
+webkit_user_content_filter_store_fetch_identifiers_finish
+
+<SUBSECTION Standard>
+WEBKIT_IS_USER_CONTENT_FILTER_STORE
+WEBKIT_IS_USER_CONTENT_FILTER_STORE_CLASS
+WEBKIT_TYPE_USER_CONTENT_FILTER_STORE
+WEBKIT_USER_CONTENT_FILTER_STORE
+WEBKIT_USER_CONTENT_FILTER_STORE_CLASS
+WEBKIT_USER_CONTENT_FILTER_STORE_GET_CLASS
+WebKitUserContentFilterStoreClass
+
+<SUBSECTION Private>
+WebKitUserContentFilterStorePrivate
+webkit_user_content_filter_store_get_type
+</SECTION>
+
+<SECTION>
 <FILE>WebKitWebView</FILE>
 <TITLE>WebKitWebView</TITLE>
 WebKitWebView
@@ -944,6 +984,7 @@ WEBKIT_DOWNLOAD_ERROR
 WEBKIT_PRINT_ERROR
 WEBKIT_JAVASCRIPT_ERROR
 WEBKIT_SNAPSHOT_ERROR
+WEBKIT_USER_CONTENT_FILTER_ERROR
 WebKitNetworkError
 WebKitPluginError
 WebKitPolicyError
@@ -951,6 +992,7 @@ WebKitDownloadError
 WebKitPrintError
 WebKitJavascriptError
 WebKitSnapshotError
+WebKitUserContentFilterError
 webkit_network_error_quark
 webkit_plugin_error_quark
 webkit_policy_error_quark
@@ -958,6 +1000,7 @@ webkit_download_error_quark
 webkit_print_error_quark
 webkit_javascript_error_quark
 webkit_snapshot_error_quark
+webkit_user_content_filter_error_quark
 </SECTION>
 
 <SECTION>
index 5c6e4cc..1470b27 100644 (file)
@@ -75,6 +75,7 @@
 #include <webkit2/WebKitURISchemeRequest.h>
 #include <webkit2/WebKitURIUtilities.h>
 #include <webkit2/WebKitUserContent.h>
+#include <webkit2/WebKitUserContentFilterStore.h>
 #include <webkit2/WebKitUserContentManager.h>
 #include <webkit2/WebKitUserMediaPermissionRequest.h>
 #include <webkit2/WebKitVersion.h>
index 4dad4ef..5e4e8fa 100644 (file)
 
 G_BEGIN_DECLS
 
-#define WEBKIT_NETWORK_ERROR    webkit_network_error_quark ()
-#define WEBKIT_POLICY_ERROR     webkit_policy_error_quark ()
-#define WEBKIT_PLUGIN_ERROR     webkit_plugin_error_quark ()
-#define WEBKIT_DOWNLOAD_ERROR   webkit_download_error_quark ()
-#define WEBKIT_JAVASCRIPT_ERROR webkit_javascript_error_quark ()
-#define WEBKIT_SNAPSHOT_ERROR   webkit_snapshot_error_quark ()
+#define WEBKIT_NETWORK_ERROR             webkit_network_error_quark ()
+#define WEBKIT_POLICY_ERROR              webkit_policy_error_quark ()
+#define WEBKIT_PLUGIN_ERROR              webkit_plugin_error_quark ()
+#define WEBKIT_DOWNLOAD_ERROR            webkit_download_error_quark ()
+#define WEBKIT_JAVASCRIPT_ERROR          webkit_javascript_error_quark ()
+#define WEBKIT_SNAPSHOT_ERROR            webkit_snapshot_error_quark ()
+#define WEBKIT_USER_CONTENT_FILTER_ERROR webkit_user_content_filter_error_quark ()
 
 /**
  * WebKitNetworkError:
@@ -126,23 +127,38 @@ typedef enum {
     WEBKIT_SNAPSHOT_ERROR_FAILED_TO_CREATE = 799
 } WebKitSnapshotError;
 
+/**
+ * WebKitUserContentFilterError:
+ * @WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE: The JSON source for a content filter is invalid.
+ * @WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND: The requested content filter could not be found.
+ *
+ * Since: 2.24
+ */
+typedef enum {
+    WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE,
+    WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND,
+} WebKitUserContentFilterError;
+
+WEBKIT_API GQuark
+webkit_network_error_quark             (void);
+
 WEBKIT_API GQuark
-webkit_network_error_quark    (void);
+webkit_policy_error_quark              (void);
 
 WEBKIT_API GQuark
-webkit_policy_error_quark     (void);
+webkit_plugin_error_quark              (void);
 
 WEBKIT_API GQuark
-webkit_plugin_error_quark     (void);
+webkit_download_error_quark            (void);
 
 WEBKIT_API GQuark
-webkit_download_error_quark   (void);
+webkit_javascript_error_quark          (void);
 
 WEBKIT_API GQuark
-webkit_javascript_error_quark (void);
+webkit_snapshot_error_quark            (void);
 
 WEBKIT_API GQuark
-webkit_snapshot_error_quark   (void);
+webkit_user_content_filter_error_quark (void);
 
 G_END_DECLS
 
index 926f98d..f28b99a 100644 (file)
@@ -136,6 +136,22 @@ webkit_user_script_new_for_world (const gchar                    *source,
                                   const gchar* const             *whitelist,
                                   const gchar* const             *blacklist);
 
+#define WEBKIT_TYPE_USER_CONTENT_FILTER   (webkit_user_content_filter_get_type())
+
+typedef struct _WebKitUserContentFilter WebKitUserContentFilter;
+
+WEBKIT_API GType
+webkit_user_content_filter_get_type       (void);
+
+WEBKIT_API const char*
+webkit_user_content_filter_get_identifier (WebKitUserContentFilter     *user_content_filter);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_ref            (WebKitUserContentFilter     *user_content_filter);
+
+WEBKIT_API void
+webkit_user_content_filter_unref          (WebKitUserContentFilter     *user_content_filter);
+
 G_END_DECLS
 
 #endif
diff --git a/Source/WebKit/UIProcess/API/wpe/WebKitUserContentFilterStore.h b/Source/WebKit/UIProcess/API/wpe/WebKitUserContentFilterStore.h
new file mode 100644 (file)
index 0000000..9b760fe
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 Igalia S.L.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if !defined(__WEBKIT_H_INSIDE__) && !defined(WEBKIT2_COMPILATION)
+#error "Only <wpe/webkit.h> can be included directly."
+#endif
+
+#ifndef WebKitUserContentFilterStore_h
+#define WebKitUserContentFilterStore_h
+
+#include <gio/gio.h>
+#include <wpe/WebKitDefines.h>
+
+G_BEGIN_DECLS
+
+#define WEBKIT_TYPE_USER_CONTENT_FILTER_STORE            (webkit_user_content_filter_store_get_type())
+#define WEBKIT_USER_CONTENT_FILTER_STORE(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, WebKitUserContentFilterStore))
+#define WEBKIT_IS_USER_CONTENT_FILTER_STORE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), WEBKIT_TYPE_USER_CONTENT_FILTER_STORE))
+#define WEBKIT_USER_CONTENT_FILTER_STORE_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),  WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, WebKitUserContentFilterStoreClass))
+#define WEBKIT_IS_USER_CONTENT_FILTER_STORE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),  WEBKIT_TYPE_USER_CONTENT_FILTER_STORE))
+#define WEBKIT_USER_CONTENT_FILTER_STORE_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj),  WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, WebKitUserContentFilterStoreClass))
+
+typedef struct _WebKitUserContentFilterStore        WebKitUserContentFilterStore;
+typedef struct _WebKitUserContentFilterStoreClass   WebKitUserContentFilterStoreClass;
+typedef struct _WebKitUserContentFilterStorePrivate WebKitUserContentFilterStorePrivate;
+
+typedef struct _WebKitUserContentFilter             WebKitUserContentFilter;
+
+struct _WebKitUserContentFilterStore {
+    GObject parent;
+
+    /*< private >*/
+    WebKitUserContentFilterStorePrivate *priv;
+};
+
+struct _WebKitUserContentFilterStoreClass {
+    GObjectClass parent_class;
+
+    void (*_webkit_reserved0) (void);
+    void (*_webkit_reserved1) (void);
+    void (*_webkit_reserved2) (void);
+    void (*_webkit_reserved3) (void);
+};
+
+
+WEBKIT_API GType
+webkit_user_content_filter_store_get_type                 (void);
+
+WEBKIT_API WebKitUserContentFilterStore *
+webkit_user_content_filter_store_new                      (const gchar                  *storage_path);
+
+WEBKIT_API const gchar *
+webkit_user_content_filter_store_get_path                 (WebKitUserContentFilterStore *store);
+
+WEBKIT_API void
+webkit_user_content_filter_store_save                     (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GBytes                       *source,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_store_save_finish              (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_save_from_file           (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GFile                        *file,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_store_save_from_file_finish    (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_remove                   (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API gboolean
+webkit_user_content_filter_store_remove_finish            (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_load                     (WebKitUserContentFilterStore *store,
+                                                           const gchar                  *identifier,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API WebKitUserContentFilter *
+webkit_user_content_filter_store_load_finish              (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result,
+                                                           GError                      **error);
+
+WEBKIT_API void
+webkit_user_content_filter_store_fetch_identifiers        (WebKitUserContentFilterStore *store,
+                                                           GCancellable                 *cancellable,
+                                                           GAsyncReadyCallback           callback,
+                                                           gpointer                      user_data);
+
+WEBKIT_API gchar**
+webkit_user_content_filter_store_fetch_identifiers_finish (WebKitUserContentFilterStore *store,
+                                                           GAsyncResult                 *result);
+
+G_END_DECLS
+
+#endif /* !WebKitUserContentFilterStore_h */
index 6513fa7..12d7ede 100644 (file)
@@ -93,6 +93,17 @@ webkit_user_content_manager_add_script                                 (WebKitUs
 WEBKIT_API void
 webkit_user_content_manager_remove_all_scripts                         (WebKitUserContentManager *manager);
 
+WEBKIT_API void
+webkit_user_content_manager_add_filter                                 (WebKitUserContentManager *manager,
+                                                                        WebKitUserContentFilter  *filter);
+
+WEBKIT_API void
+webkit_user_content_manager_remove_filter                              (WebKitUserContentManager *manager,
+                                                                        WebKitUserContentFilter  *filter);
+
+WEBKIT_API void
+webkit_user_content_manager_remove_all_filters                         (WebKitUserContentManager *manager);
+
 G_END_DECLS
 
 #endif
index a5ecfe4..eae29cb 100644 (file)
@@ -73,6 +73,7 @@ WebKitUserScript
 WebKitUserContentInjectedFrames
 WebKitUserStyleLevel
 WebKitUserScriptInjectionTime
+WebKitUserContentFilter
 webkit_user_style_sheet_ref
 webkit_user_style_sheet_unref
 webkit_user_style_sheet_new
@@ -81,14 +82,19 @@ webkit_user_script_ref
 webkit_user_script_unref
 webkit_user_script_new
 webkit_user_script_new_for_world
+webkit_user_content_filter_ref
+webkit_user_content_filter_unref
+webkit_user_content_filter_get_identifier
 
 <SUBSECTION Standard>
 WEBKIT_TYPE_USER_STYLE_SHEET
 WEBKIT_TYPE_USER_SCRIPT
+WEBKIT_TYPE_USER_CONTENT_FILTER
 
 <SUBSECTION Private>
 webkit_user_style_sheet_get_type
 webkit_user_script_get_type
+webkit_user_content_filter_get_type
 </SECTION>
 
 <SECTION>
@@ -104,6 +110,9 @@ webkit_user_content_manager_register_script_message_handler
 webkit_user_content_manager_unregister_script_message_handler
 webkit_user_content_manager_register_script_message_handler_in_world
 webkit_user_content_manager_unregister_script_message_handler_in_world
+webkit_user_content_manager_add_filter
+webkit_user_content_manager_remove_filter
+webkit_user_content_manager_remove_all_filters
 
 <SUBSECTION Standard>
 WEBKIT_IS_USER_CONTENT_MANAGER
@@ -120,6 +129,37 @@ webkit_user_content_manager_get_type
 </SECTION>
 
 <SECTION>
+<FILE>WebKitUserContentFilterStore</FILE>
+<TITLE>WebKitUserContentFilterStore</TITLE>
+WebKitUserContentFilterStore
+webkit_user_content_filter_store_new
+webkit_user_content_filter_store_get_path
+webkit_user_content_filter_store_save
+webkit_user_content_filter_store_save_finish
+webkit_user_content_filter_store_save_from_file
+webkit_user_content_filter_store_save_from_file_finish
+webkit_user_content_filter_store_load
+webkit_user_content_filter_store_load_finish
+webkit_user_content_filter_store_remove
+webkit_user_content_filter_store_remove_finish
+webkit_user_content_filter_store_fetch_identifiers
+webkit_user_content_filter_store_fetch_identifiers_finish
+
+<SUBSECTION Standard>
+WEBKIT_IS_USER_CONTENT_FILTER_STORE
+WEBKIT_IS_USER_CONTENT_FILTER_STORE_CLASS
+WEBKIT_TYPE_USER_CONTENT_FILTER_STORE
+WEBKIT_USER_CONTENT_FILTER_STORE
+WEBKIT_USER_CONTENT_FILTER_STORE_CLASS
+WEBKIT_USER_CONTENT_FILTER_STORE_GET_CLASS
+WebKitUserContentFilterStoreClass
+
+<SUBSECTION Private>
+WebKitUserContentFilterStorePrivate
+webkit_user_content_filter_store_get_type
+</SECTION>
+
+<SECTION>
 <FILE>WebKitWebView</FILE>
 <TITLE>WebKitWebView</TITLE>
 WebKitWebView
@@ -887,18 +927,21 @@ WEBKIT_POLICY_ERROR
 WEBKIT_DOWNLOAD_ERROR
 WEBKIT_JAVASCRIPT_ERROR
 WEBKIT_SNAPSHOT_ERROR
+WEBKIT_USER_CONTENT_FILTER_ERROR
 WebKitNetworkError
 WebKitPluginError
 WebKitPolicyError
 WebKitDownloadError
 WebKitJavascriptError
 WebKitSnapshotError
+WebKitUserContentFilterError
 webkit_network_error_quark
 webkit_plugin_error_quark
 webkit_policy_error_quark
 webkit_download_error_quark
 webkit_javascript_error_quark
 webkit_snapshot_error_quark
+webkit_user_content_filter_error_quark
 </SECTION>
 
 <SECTION>
index 9955a40..94448a0 100644 (file)
@@ -69,6 +69,7 @@
 #include <wpe/WebKitURISchemeRequest.h>
 #include <wpe/WebKitURIUtilities.h>
 #include <wpe/WebKitUserContent.h>
+#include <wpe/WebKitUserContentFilterStore.h>
 #include <wpe/WebKitUserContentManager.h>
 #include <wpe/WebKitUserMediaPermissionRequest.h>
 #include <wpe/WebKitVersion.h>
index cd6b717..96708e7 100644 (file)
@@ -1,3 +1,42 @@
+2019-02-20  Adrian Perez de Castro  <aperez@igalia.com>
+
+        [WPE][GTK] Enable support for CONTENT_EXTENSIONS
+        https://bugs.webkit.org/show_bug.cgi?id=167941
+
+        Reviewed by Carlos Garcia Campos.
+
+        * MiniBrowser/gtk/main.c:
+        (filterSavedCallback): Added.
+        (main): Support loading a JSON rule set file for content filtering.
+        * MiniBrowser/wpe/main.cpp:
+        (filterSavedCallback): Added.
+        (main): Support loading a JSON rule set file for content filtering.
+        * TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentFilterStore.cpp: Added.
+        (WTF::refGPtr): Added locally for WebKitUserContentFilter, as it would not be used anywhere else.
+        (WTF::derefGPtr): Ditto.
+        (testEmptyStore):
+        (testSaveInvalidFilter):
+        (testSaveLoadFilter):
+        (testSavedFilterIdentifierMatch):
+        (testRemoveFilter):
+        (testSaveMultipleFilters):
+        (testSaveFilterFromFile):
+        (testFilterPersistence):
+        (beforeAll):
+        (afterAll):
+        * TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentManager.cpp:
+        (removeOldInjectedContentAndResetLists): Also reset content filters.
+        (isCSSBlockedForURLAtPath): Added. Tests whether the test filter, which blocks a
+        CSS style sheet, has blocked the load of the CSS by looking at the resulting style of
+        the element affected by the style sheet.
+        (getUserContentFilter): Added. Stores the test filter in a WebKitUserContentFilterStore
+        and returns it to be used by tests.
+        (testUserContentManagerContentFilter): Added. Tests whether adding and removing a filter
+        from the WebKitUserContentManager results in the a CSS style sheet being blocked.
+        (serverCallback): Add support for serving a CSS style sheet for testing.
+        (beforeAll): Add call to testUserContentManagerContentFilter().
+        * TestWebKitAPI/glib/CMakeLists.txt: Added TestWebKitUserContentFilterStore.
+
 2019-02-19  Keith Rollin  <krollin@apple.com>
 
         Add timing information to build output
index afbe555..133284f 100644 (file)
@@ -48,6 +48,7 @@ static gboolean privateMode;
 static gboolean automationMode;
 static gboolean fullScreen;
 static gboolean ignoreTLSErrors;
+static const char *contentFilter;
 static const char *cookiesFile;
 static const char *cookiesPolicy;
 static const char *proxy;
@@ -112,6 +113,7 @@ static const GOptionEntry commandLineOptions[] =
     { "proxy", 0, 0, G_OPTION_ARG_STRING, &proxy, "Set proxy", "PROXY" },
     { "ignore-host", 0, 0, G_OPTION_ARG_STRING_ARRAY, &ignoreHosts, "Set proxy ignore hosts", "HOSTS" },
     { "ignore-tls-errors", 0, 0, G_OPTION_ARG_NONE, &ignoreTLSErrors, "Ignore TLS errors", NULL },
+    { "content-filter", 0, 0, G_OPTION_ARG_FILENAME, &contentFilter, "JSON with content filtering rules", "FILE" },
     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, 0, "[URL…]" },
     { 0, 0, 0, 0, 0, 0, 0 }
 };
@@ -471,6 +473,18 @@ static void automationStartedCallback(WebKitWebContext *webContext, WebKitAutoma
     g_signal_connect(session, "create-web-view", G_CALLBACK(createWebViewForAutomationCallback), NULL);
 }
 
+typedef struct {
+    GMainLoop *mainLoop;
+    WebKitUserContentFilter *filter;
+    GError *error;
+} FilterSaveData;
+
+static void filterSavedCallback(WebKitUserContentFilterStore *store, GAsyncResult *result, FilterSaveData *data)
+{
+    data->filter = webkit_user_content_filter_store_save_finish(store, result, &data->error);
+    g_main_loop_quit(data->mainLoop);
+}
+
 int main(int argc, char *argv[])
 {
 #if ENABLE_DEVELOPER_MODE
@@ -539,6 +553,30 @@ int main(int argc, char *argv[])
     webkit_user_content_manager_register_script_message_handler(userContentManager, "aboutData");
     g_signal_connect(userContentManager, "script-message-received::aboutData", G_CALLBACK(aboutDataScriptMessageReceivedCallback), webContext);
 
+    if (contentFilter) {
+        GFile *contentFilterFile = g_file_new_for_commandline_arg(contentFilter);
+
+        FilterSaveData saveData = { NULL, NULL, NULL };
+        gchar *filtersPath = g_build_filename(g_get_user_cache_dir(), g_get_prgname(), "filters", NULL);
+        WebKitUserContentFilterStore *store = webkit_user_content_filter_store_new(filtersPath);
+        g_free(filtersPath);
+
+        webkit_user_content_filter_store_save_from_file(store, "GTKMiniBrowserFilter", contentFilterFile, NULL, (GAsyncReadyCallback)filterSavedCallback, &saveData);
+        saveData.mainLoop = g_main_loop_new(NULL, FALSE);
+        g_main_loop_run(saveData.mainLoop);
+        g_object_unref(store);
+
+        if (saveData.filter)
+            webkit_user_content_manager_add_filter(userContentManager, saveData.filter);
+        else
+            g_printerr("Cannot save filter '%s': %s\n", contentFilter, saveData.error->message);
+
+        g_clear_pointer(&saveData.error, g_error_free);
+        g_clear_pointer(&saveData.filter, webkit_user_content_filter_unref);
+        g_main_loop_unref(saveData.mainLoop);
+        g_object_unref(contentFilterFile);
+    }
+
     webkit_web_context_set_automation_allowed(webContext, automationMode);
     g_signal_connect(webContext, "automation-started", G_CALLBACK(automationStartedCallback), NULL);
 
index 131a0f3..863c4c0 100644 (file)
@@ -39,6 +39,7 @@ static gboolean headlessMode;
 static gboolean privateMode;
 static gboolean automationMode;
 static gboolean ignoreTLSErrors;
+static const char* contentFilter;
 static const char* cookiesFile;
 static const char* cookiesPolicy;
 static const char* proxy;
@@ -53,6 +54,7 @@ static const GOptionEntry commandLineOptions[] =
     { "proxy", 0, 0, G_OPTION_ARG_STRING, &proxy, "Set proxy", "PROXY" },
     { "ignore-host", 0, 0, G_OPTION_ARG_STRING_ARRAY, &ignoreHosts, "Set proxy ignore hosts", "HOSTS" },
     { "ignore-tls-errors", 0, 0, G_OPTION_ARG_NONE, &ignoreTLSErrors, "Ignore TLS errors", nullptr },
+    { "content-filter", 0, 0, G_OPTION_ARG_FILENAME, &contentFilter, "JSON with content filtering rules", "FILE" },
     { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &uriArguments, nullptr, "[URL]" },
     { nullptr, 0, 0, G_OPTION_ARG_NONE, nullptr, nullptr, nullptr }
 };
@@ -125,6 +127,18 @@ static std::unique_ptr<WPEToolingBackends::ViewBackend> createViewBackend(uint32
     return std::make_unique<WPEToolingBackends::WindowViewBackend>(width, height);
 }
 
+typedef struct {
+    GMainLoop* mainLoop { nullptr };
+    WebKitUserContentFilter* filter { nullptr };
+    GError* error { nullptr };
+} FilterSaveData;
+
+static void filterSavedCallback(WebKitUserContentFilterStore *store, GAsyncResult *result, FilterSaveData *data)
+{
+    data->filter = webkit_user_content_filter_store_save_finish(store, result, &data->error);
+    g_main_loop_quit(data->mainLoop);
+}
+
 int main(int argc, char *argv[])
 {
 #if ENABLE_DEVELOPER_MODE
@@ -184,6 +198,32 @@ int main(int argc, char *argv[])
     webkit_web_context_set_process_model(webContext, (singleprocess && *singleprocess) ?
         WEBKIT_PROCESS_MODEL_SHARED_SECONDARY_PROCESS : WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
 
+    WebKitUserContentManager* userContentManager = nullptr;
+    if (contentFilter) {
+        GFile* contentFilterFile = g_file_new_for_commandline_arg(contentFilter);
+
+        FilterSaveData saveData = { nullptr, };
+        gchar* filtersPath = g_build_filename(g_get_user_cache_dir(), g_get_prgname(), "filters", nullptr);
+        WebKitUserContentFilterStore* store = webkit_user_content_filter_store_new(filtersPath);
+        g_free(filtersPath);
+
+        webkit_user_content_filter_store_save_from_file(store, "WPEMiniBrowserFilter", contentFilterFile, nullptr, (GAsyncReadyCallback)filterSavedCallback, &saveData);
+        saveData.mainLoop = g_main_loop_new(nullptr, FALSE);
+        g_main_loop_run(saveData.mainLoop);
+        g_object_unref(store);
+
+        if (saveData.filter) {
+            userContentManager = webkit_user_content_manager_new();
+            webkit_user_content_manager_add_filter(userContentManager, saveData.filter);
+        } else
+            g_printerr("Cannot save filter '%s': %s\n", contentFilter, saveData.error->message);
+
+        g_clear_pointer(&saveData.error, g_error_free);
+        g_clear_pointer(&saveData.filter, webkit_user_content_filter_unref);
+        g_main_loop_unref(saveData.mainLoop);
+        g_object_unref(contentFilterFile);
+    }
+
     auto* settings = webkit_settings_new_with_settings(
         "enable-developer-extras", TRUE,
         "enable-webgl", TRUE,
@@ -200,6 +240,7 @@ int main(int argc, char *argv[])
         "backend", viewBackend,
         "web-context", webContext,
         "settings", settings,
+        "user-content-manager", userContentManager,
         "is-controlled-by-automation", automationMode,
         nullptr));
     g_object_unref(settings);
diff --git a/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentFilterStore.cpp b/Tools/TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentFilterStore.cpp
new file mode 100644 (file)
index 0000000..e3243d6
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2019 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 "TestMain.h"
+#include <wtf/glib/GRefPtr.h>
+#include <wtf/glib/GUniquePtr.h>
+
+static const char* kInvalidJSONSources[] = {
+    // Empty input.
+    "",
+    // Incorrect JSON input.
+    "[",
+    "]",
+    "[{]",
+    "[{ \"trigger: {} }]",
+    "[{ 'trigger': { 'url-filter': '.*' }, 'action': { 'type': 'block' } }]",
+    // Oddball JSON objects.
+    "{}",
+    "[]",
+    "[{}]",
+    // Empty trigger/action.
+    "[{ \"trigger\": {} }]",
+    "[{ \"action\": {} }]",
+    "[{ \"trigger\": {}, \"action\": {} }]",
+    "[{ \"trigger\": { \"url-filter\": \".*\" } }]",
+    "[{ \"trigger\": { \"url-filter\": \".*\" }, \"action\": {} }]",
+    // Empty action type.
+    "[{ \"trigger\": { \"url-filter\": \".*\" }, \"action\": { \"type\": \"\" } }]",
+    // Empty URL filter.
+    "[{ \"trigger\": { \"url-filter\": \"\" }, \"action\": { \"type\": \"block\" } }]",
+};
+
+static const char* kSimpleJSONSource =
+    "[{\n"
+    "  \"trigger\": {\n"
+    "    \"url-filter\": \".*\"\n"
+    "  },\n"
+    "  \"action\": {\n"
+    "    \"type\": \"block\"\n"
+    "  }\n"
+    "}]\n";
+
+namespace WTF {
+
+template <> WebKitUserContentFilter* refGPtr(WebKitUserContentFilter* ptr)
+{
+    if (ptr)
+        webkit_user_content_filter_ref(ptr);
+    return ptr;
+}
+
+template <> void derefGPtr(WebKitUserContentFilter* ptr)
+{
+    if (ptr)
+        webkit_user_content_filter_unref(ptr);
+}
+
+}
+
+class UserContentFilterStoreTest: public Test {
+public:
+    MAKE_GLIB_TEST_FIXTURE(UserContentFilterStoreTest);
+
+    GError* lastError() const
+    {
+        return m_error.get();
+    }
+
+    void resetStore()
+    {
+        auto* oldStore = m_filterStore.get();
+        m_filterStore = newStore();
+        g_assert_true(oldStore != m_filterStore.get());
+    }
+
+    char** fetchIdentifiers()
+    {
+        webkit_user_content_filter_store_fetch_identifiers(m_filterStore.get(), nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+            auto* test = static_cast<UserContentFilterStoreTest*>(userData);
+            test->m_filterIds.reset(webkit_user_content_filter_store_fetch_identifiers_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result));
+            g_main_loop_quit(test->m_mainLoop.get());
+        }, this);
+        g_main_loop_run(m_mainLoop.get());
+        g_assert_nonnull(m_filterIds.get());
+        return m_filterIds.get();
+    }
+
+    WebKitUserContentFilter* saveFilter(const char* filterId, const char* source)
+    {
+        GRefPtr<GBytes> sourceBytes = adoptGRef(g_bytes_new_static(source, strlen(source)));
+        webkit_user_content_filter_store_save(m_filterStore.get(), filterId, sourceBytes.get(), nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+            auto* test = static_cast<UserContentFilterStoreTest*>(userData);
+            test->m_filter = adoptGRef(webkit_user_content_filter_store_save_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr()));
+            g_main_loop_quit(test->m_mainLoop.get());
+        }, this);
+        g_main_loop_run(m_mainLoop.get());
+        return m_filter.get();
+    }
+
+    WebKitUserContentFilter* saveFilterFromFile(const char* filterId, GFile* file)
+    {
+        webkit_user_content_filter_store_save_from_file(m_filterStore.get(), filterId, file, nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+            auto* test = static_cast<UserContentFilterStoreTest*>(userData);
+            test->m_filter = adoptGRef(webkit_user_content_filter_store_save_from_file_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr()));
+            g_main_loop_quit(test->m_mainLoop.get());
+        }, this);
+        g_main_loop_run(m_mainLoop.get());
+        return m_filter.get();
+    }
+
+    WebKitUserContentFilter* loadFilter(const char* filterId)
+    {
+        webkit_user_content_filter_store_load(m_filterStore.get(), filterId, nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+            auto* test = static_cast<UserContentFilterStoreTest*>(userData);
+            test->m_filter = adoptGRef(webkit_user_content_filter_store_load_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr()));
+            g_main_loop_quit(test->m_mainLoop.get());
+        }, this);
+        g_main_loop_run(m_mainLoop.get());
+        return m_filter.get();
+    }
+
+    bool removeFilter(const char* filterId)
+    {
+        webkit_user_content_filter_store_remove(m_filterStore.get(), filterId, nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+            auto* test = static_cast<UserContentFilterStoreTest*>(userData);
+            test->m_completedOk = !!webkit_user_content_filter_store_remove_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &test->m_error.outPtr());
+            g_main_loop_quit(test->m_mainLoop.get());
+        }, this);
+        g_main_loop_run(m_mainLoop.get());
+        return m_completedOk;
+    }
+
+private:
+    GRefPtr<WebKitUserContentFilterStore> newStore()
+    {
+        GUniquePtr<char> storagePath(g_build_filename(dataDirectory(), "content-filters", nullptr));
+        auto store = adoptGRef(webkit_user_content_filter_store_new(storagePath.get()));
+        g_assert_nonnull(store.get());
+        assertObjectIsDeletedWhenTestFinishes(G_OBJECT(store.get()));
+        g_assert_cmpstr(storagePath.get(), ==, webkit_user_content_filter_store_get_path(store.get()));
+        return store;
+    }
+
+    GRefPtr<WebKitUserContentFilterStore> m_filterStore { newStore() };
+    GRefPtr<GMainLoop> m_mainLoop { adoptGRef(g_main_loop_new(nullptr, FALSE)) };
+    GRefPtr<WebKitUserContentFilter> m_filter;
+    GUniquePtr<char*> m_filterIds;
+    GUniqueOutPtr<GError> m_error;
+    bool m_completedOk;
+};
+
+static void testEmptyStore(UserContentFilterStoreTest* test, gconstpointer)
+{
+    g_assert_cmpuint(0, ==, g_strv_length(test->fetchIdentifiers()));
+}
+
+static void testSaveInvalidFilter(UserContentFilterStoreTest* test, gconstpointer)
+{
+    for (unsigned i = 0; i < G_N_ELEMENTS(kInvalidJSONSources); i++) {
+        GUniquePtr<char> filterId(g_strdup_printf("Filter-%u", i));
+        g_assert_null(test->saveFilter(filterId.get(), kInvalidJSONSources[i]));
+        g_assert_error(test->lastError(), WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE);
+    }
+}
+
+static void testSaveLoadFilter(UserContentFilterStoreTest* test, gconstpointer)
+{
+    static const char* kFilterId = "SimpleFilter";
+
+    // Trying to load a filter before saving must fail.
+    g_assert_null(test->loadFilter(kFilterId));
+    g_assert_error(test->lastError(), WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND);
+
+    // Then save a filter. Which must succed.
+    g_assert_nonnull(test->saveFilter(kFilterId, kSimpleJSONSource));
+    g_assert_no_error(test->lastError());
+
+    // Now it should be possible to actually load the filter.
+    g_assert_nonnull(test->loadFilter(kFilterId));
+    g_assert_no_error(test->lastError());
+}
+
+static void testSavedFilterIdentifierMatch(UserContentFilterStoreTest* test, gconstpointer)
+{
+    static const char* kFilterId = "SimpleFilter";
+
+    auto* filter = test->saveFilter(kFilterId, kSimpleJSONSource);
+    g_assert_nonnull(filter);
+    g_assert_no_error(test->lastError());
+    g_assert_cmpstr(kFilterId, ==, webkit_user_content_filter_get_identifier(filter));
+}
+
+static void testRemoveFilter(UserContentFilterStoreTest* test, gconstpointer)
+{
+    static const char* kFilterId = "SimpleFilter";
+
+    // Save a filter.
+    g_assert_nonnull(test->saveFilter(kFilterId, kSimpleJSONSource));
+    g_assert_no_error(test->lastError());
+
+    // Now remove it.
+    g_assert_true(test->removeFilter(kFilterId));
+    g_assert_no_error(test->lastError());
+
+    // Load must fail, and should not be listed.
+    g_assert_null(test->loadFilter(kFilterId));
+    g_assert_error(test->lastError(), WEBKIT_USER_CONTENT_FILTER_ERROR, WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND);
+    g_assert_cmpuint(0, ==, g_strv_length(test->fetchIdentifiers()));
+}
+
+static void testSaveMultipleFilters(UserContentFilterStoreTest* test, gconstpointer)
+{
+    static const char* kFilterIdSpam = "Filter-Spam";
+    static const char* kFilterIdEggs = "Filter-Eggs";
+
+    g_assert_nonnull(test->saveFilter(kFilterIdSpam, kSimpleJSONSource));
+    g_assert_no_error(test->lastError());
+
+    g_assert_nonnull(test->saveFilter(kFilterIdEggs, kSimpleJSONSource));
+    g_assert_no_error(test->lastError());
+
+    auto filterIds = test->fetchIdentifiers();
+    g_assert_true(g_strv_contains(filterIds, kFilterIdSpam));
+    g_assert_true(g_strv_contains(filterIds, kFilterIdEggs));
+    g_assert_cmpuint(2, ==, g_strv_length(filterIds));
+
+    g_assert_nonnull(test->saveFilter(kFilterIdEggs, kSimpleJSONSource));
+    g_assert_no_error(test->lastError());
+
+    filterIds = test->fetchIdentifiers();
+    g_assert_true(g_strv_contains(filterIds, kFilterIdSpam));
+    g_assert_true(g_strv_contains(filterIds, kFilterIdEggs));
+    g_assert_cmpuint(2, ==, g_strv_length(filterIds));
+}
+
+static void testSaveFilterFromFile(UserContentFilterStoreTest* test, gconstpointer)
+{
+    static const char* kFilterId = "SimpleFilter";
+
+    GUniqueOutPtr<GError> error;
+    GUniquePtr<char> filePath(g_build_filename(test->dataDirectory(), "SimpleFilter.json", nullptr));
+    g_assert_true(g_file_set_contents(filePath.get(), kSimpleJSONSource, strlen(kSimpleJSONSource), &error.outPtr()));
+    g_assert_no_error(error.get());
+
+    GRefPtr<GFile> file = adoptGRef(g_file_new_for_path(filePath.get()));
+    g_assert_nonnull(test->saveFilterFromFile(kFilterId, file.get()));
+    g_assert_no_error(test->lastError());
+
+    g_assert_true(g_strv_contains(test->fetchIdentifiers(), kFilterId));
+}
+
+static void testFilterPersistence(UserContentFilterStoreTest* test, gconstpointer)
+{
+    static const char* kFilterId = "PersistedFilter";
+
+    g_assert_nonnull(test->saveFilter(kFilterId, kSimpleJSONSource));
+    g_assert_no_error(test->lastError());
+
+    test->resetStore();
+
+    g_assert_nonnull(test->loadFilter(kFilterId));
+    g_assert_no_error(test->lastError());
+}
+
+void beforeAll()
+{
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "empty-store", testEmptyStore);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "invalid-filter-source", testSaveInvalidFilter);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "filter-save-load", testSaveLoadFilter);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "saved-filter-identifier-match", testSavedFilterIdentifierMatch);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "remove-filter", testRemoveFilter);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "save-multiple-filters", testSaveMultipleFilters);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "save-from-file", testSaveFilterFromFile);
+    UserContentFilterStoreTest::add("WebKitUserContentFilterStore", "filter-persistence", testFilterPersistence);
+}
+
+void afterAll()
+{
+}
index 20ecca8..01db4f1 100644 (file)
 static WebKitTestServer* kServer;
 
 // These are all here so that they can be changed easily, if necessary.
-static const char* kStyleSheetHTML = "<html><div id=\"styledElement\">Sweet stylez!</div></html>";
+static const char* kTestHTML =
+"<html>\n"
+"  <head><link rel=\"stylesheet\" type=\"text/css\" href=\"/extra.css\"></head>\n"
+"  <body>\n"
+"    <div id=\"styledElement\">Sweet stylez!</div>\n"
+"    <div id=\"otherElement\">Blocked stylez!</div>\n"
+"  </body>\n"
+"</html>";
+
+static const char* kTestCSS =
+"div#otherElement {\n"
+"  font-weight: bold;\n"
+"}\n";
+
 static const char* kInjectedStyleSheet = "#styledElement { font-weight: bold; }";
 static const char* kStyleSheetTestScript = "getComputedStyle(document.getElementById('styledElement'))['font-weight']";
 static const char* kStyleSheetTestScriptResult = "bold";
 static const char* kInjectedScript = "document.write('<div id=\"item\">Generated by a script</div>')";
 static const char* kScriptTestScript = "document.getElementById('item').innerText";
 static const char* kScriptTestScriptResult = "Generated by a script";
+static const char* kScriptTestCSSBlocked = "getComputedStyle(document.getElementById('otherElement'))['font-weight']";
+static const char* kScriptTestCSSBlockedResult = "normal";
+static const char* kJSONFilter =
+"[\n"
+"  {\n"
+"    \"trigger\": {\n"
+"      \"url-filter\": \".*\",\n"
+"      \"resource-type\": [\"style-sheet\"]\n"
+"    },\n"
+"    \"action\": {\n"
+"      \"type\": \"block\"\n"
+"    }\n"
+"  }\n"
+"]\n";
 
 static void testWebViewNewWithUserContentManager(Test* test, gconstpointer)
 {
@@ -97,6 +124,7 @@ static void removeOldInjectedContentAndResetLists(WebKitUserContentManager* user
 {
     webkit_user_content_manager_remove_all_style_sheets(userContentManager);
     webkit_user_content_manager_remove_all_scripts(userContentManager);
+    webkit_user_content_manager_remove_all_filters(userContentManager);
 
     while (*whitelist) {
         g_free(*whitelist);
@@ -404,10 +432,76 @@ static void testUserContentManagerScriptMessageFromDOMBindings(UserScriptMessage
 }
 #endif
 
+static bool isCSSBlockedForURLAtPath(WebViewTest* test, const char* path)
+{
+    test->loadURI(kServer->getURIForPath(path).data());
+    test->waitUntilLoadFinished();
+
+    GUniqueOutPtr<GError> error;
+    WebKitJavascriptResult* javascriptResult = test->runJavaScriptAndWaitUntilFinished(kScriptTestCSSBlocked, &error.outPtr());
+    g_assert_nonnull(javascriptResult);
+    g_assert_no_error(error.get());
+
+    GUniquePtr<char> valueString(WebViewTest::javascriptResultToCString(javascriptResult));
+    return !g_strcmp0(valueString.get(), kScriptTestCSSBlockedResult);
+}
+
+static WebKitUserContentFilter* getUserContentFilter(WebViewTest* test)
+{
+    GUniquePtr<char> filtersPath(g_build_filename(test->dataDirectory(), "filters", nullptr));
+    WebKitUserContentFilterStore* store = webkit_user_content_filter_store_new(filtersPath.get());
+    test->assertObjectIsDeletedWhenTestFinishes(G_OBJECT(store));
+
+    struct Data {
+        GMainLoop* mainLoop;
+        WebKitUserContentFilter* filter;
+    };
+    Data data { test->m_mainLoop, nullptr, };
+
+    GRefPtr<GBytes> source = adoptGRef(g_bytes_new_static(kJSONFilter, strlen(kJSONFilter)));
+    webkit_user_content_filter_store_save(store, "TestFilter", source.get(), nullptr, [](GObject* sourceObject, GAsyncResult* result, void* userData) {
+        auto* data = static_cast<Data*>(userData);
+        GUniqueOutPtr<GError> error;
+        data->filter = webkit_user_content_filter_store_save_finish(WEBKIT_USER_CONTENT_FILTER_STORE(sourceObject), result, &error.outPtr());
+        g_assert_nonnull(data->filter);
+        g_assert_no_error(error.get());
+        g_main_loop_quit(data->mainLoop);
+    }, &data);
+    g_main_loop_run(data.mainLoop);
+
+    g_object_unref(store);
+
+    g_assert_nonnull(data.filter);
+    return data.filter;
+}
+
+static void testUserContentManagerContentFilter(WebViewTest* test, gconstpointer)
+{
+    char* whitelist[] = { nullptr, nullptr, nullptr };
+    char* blacklist[] = { nullptr, nullptr, nullptr };
+
+    removeOldInjectedContentAndResetLists(test->m_userContentManager.get(), whitelist, blacklist);
+
+    static const char* somePath = "somepath";
+    g_assert_false(isCSSBlockedForURLAtPath(test, somePath));
+
+    WebKitUserContentFilter* filter = getUserContentFilter(test);
+    webkit_user_content_manager_add_filter(test->m_userContentManager.get(), filter);
+    g_assert_true(isCSSBlockedForURLAtPath(test, somePath));
+
+    webkit_user_content_manager_remove_filter(test->m_userContentManager.get(), filter);
+    g_assert_false(isCSSBlockedForURLAtPath(test, somePath));
+
+    webkit_user_content_filter_unref(filter);
+}
+
 static void serverCallback(SoupServer* server, SoupMessage* message, const char* path, GHashTable*, SoupClientContext*, gpointer)
 {
     soup_message_set_status(message, SOUP_STATUS_OK);
-    soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kStyleSheetHTML, strlen(kStyleSheetHTML));
+    if (!g_strcmp0(path, "/extra.css"))
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kTestCSS, strlen(kTestCSS));
+    else
+        soup_message_body_append(message->response_body, SOUP_MEMORY_STATIC, kTestHTML, strlen(kTestHTML));
     soup_message_body_complete(message->response_body);
 }
 
@@ -424,6 +518,7 @@ void beforeAll()
 #if PLATFORM(GTK)
     UserScriptMessageTest::add("WebKitUserContentManager", "script-message-from-dom-bindings", testUserContentManagerScriptMessageFromDOMBindings);
 #endif
+    WebViewTest::add("WebKitUserContentManager", "content-filter", testUserContentManagerContentFilter);
 }
 
 void afterAll()
index 056634c..65a0078 100644 (file)
@@ -141,6 +141,7 @@ ADD_WK2_TEST(TestWebKitSettings ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/Test
 ADD_WK2_TEST(TestWebKitURIUtilities ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestWebKitURIUtilities.cpp)
 ADD_WK2_TEST(TestWebKitWebContext ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebContext.cpp)
 ADD_WK2_TEST(TestWebKitWebView ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestWebKitWebView.cpp)
+ADD_WK2_TEST(TestWebKitUserContentFilterStore ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentFilterStore.cpp)
 ADD_WK2_TEST(TestWebKitUserContentManager ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestWebKitUserContentManager.cpp)
 ADD_WK2_TEST(TestWebsiteData ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestWebsiteData.cpp)
 ADD_WK2_TEST(TestConsoleMessage ${TOOLS_DIR}/TestWebKitAPI/Tests/WebKitGLib/TestConsoleMessage.cpp)