| 1 | /* |
| 2 | * Copyright (C) 2018-2019 Igalia S.L. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * 1. Redistributions of source code must retain the above copyright |
| 8 | * notice, this list of conditions and the following disclaimer. |
| 9 | * 2. Redistributions in binary form must reproduce the above copyright |
| 10 | * notice, this list of conditions and the following disclaimer in the |
| 11 | * documentation and/or other materials provided with the distribution. |
| 12 | * |
| 13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| 14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| 15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| 17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| 23 | * THE POSSIBILITY OF SUCH DAMAGE. |
| 24 | */ |
| 25 | |
| 26 | #include "config.h" |
| 27 | #include "WebKitUserContentFilterStore.h" |
| 28 | |
| 29 | #include "APIContentRuleList.h" |
| 30 | #include "APIContentRuleListStore.h" |
| 31 | #include "WebKitError.h" |
| 32 | #include "WebKitUserContent.h" |
| 33 | #include "WebKitUserContentPrivate.h" |
| 34 | #include <WebCore/ContentExtensionError.h> |
| 35 | #include <glib/gi18n-lib.h> |
| 36 | #include <wtf/CompletionHandler.h> |
| 37 | #include <wtf/FileSystem.h> |
| 38 | #include <wtf/RefPtr.h> |
| 39 | #include <wtf/glib/GRefPtr.h> |
| 40 | #include <wtf/glib/GUniquePtr.h> |
| 41 | #include <wtf/glib/WTFGType.h> |
| 42 | |
| 43 | /** |
| 44 | * SECTION: WebKitUserContentFilterStore |
| 45 | * @Short_description: Handles storage of user content filters on disk. |
| 46 | * @Title: WebKitUserContentFilterStore |
| 47 | * |
| 48 | * The WebKitUserContentFilterStore provides the means to import and save |
| 49 | * [JSON rule sets](https://webkit.org/blog/3476/content-blockers-first-look/), |
| 50 | * which can be loaded later in an efficient manner. Once filters are stored, |
| 51 | * the #WebKitUserContentFilter objects which represent them can be added to |
| 52 | * a #WebKitUserContentManager with webkit_user_content_manager_add_filter(). |
| 53 | * |
| 54 | * JSON rule sets are imported using webkit_user_content_filter_store_save() and stored |
| 55 | * on disk in an implementation defined format. The contents of a filter store must be |
| 56 | * managed using the #WebKitUserContentFilterStore: a list of all the stored filters |
| 57 | * can be obtained with webkit_user_content_filter_store_fetch_identifiers(), |
| 58 | * webkit_user_content_filter_store_load() can be used to retrieve a previously saved |
| 59 | * filter, and removed from the store with webkit_user_content_filter_store_remove(). |
| 60 | * |
| 61 | * Since: 2.24 |
| 62 | */ |
| 63 | |
| 64 | enum { |
| 65 | PROP_0, |
| 66 | |
| 67 | PROP_PATH, |
| 68 | }; |
| 69 | |
| 70 | static inline GError* toGError(WebKitUserContentFilterError code, const std::error_code error) |
| 71 | { |
| 72 | ASSERT(error); |
| 73 | ASSERT(error.category() == WebCore::ContentExtensions::contentExtensionErrorCategory()); |
| 74 | return g_error_new_literal(WEBKIT_USER_CONTENT_FILTER_ERROR, code, error.message().c_str()); |
| 75 | } |
| 76 | |
| 77 | struct _WebKitUserContentFilterStorePrivate { |
| 78 | GUniquePtr<char> storagePath; |
| 79 | RefPtr<API::ContentRuleListStore> store; |
| 80 | }; |
| 81 | |
| 82 | WEBKIT_DEFINE_TYPE(WebKitUserContentFilterStore, webkit_user_content_filter_store, G_TYPE_OBJECT) |
| 83 | |
| 84 | static void webkitUserContentFilterStoreGetProperty(GObject* object, guint propID, GValue* value, GParamSpec* paramSpec) |
| 85 | { |
| 86 | WebKitUserContentFilterStore* store = WEBKIT_USER_CONTENT_FILTER_STORE(object); |
| 87 | |
| 88 | switch (propID) { |
| 89 | case PROP_PATH: |
| 90 | g_value_set_string(value, webkit_user_content_filter_store_get_path(store)); |
| 91 | break; |
| 92 | default: |
| 93 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec); |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | static void webkitUserContentFilterStoreSetProperty(GObject* object, guint propID, const GValue* value, GParamSpec* paramSpec) |
| 98 | { |
| 99 | WebKitUserContentFilterStore* store = WEBKIT_USER_CONTENT_FILTER_STORE(object); |
| 100 | |
| 101 | switch (propID) { |
| 102 | case PROP_PATH: |
| 103 | store->priv->storagePath.reset(g_value_dup_string(value)); |
| 104 | break; |
| 105 | default: |
| 106 | G_OBJECT_WARN_INVALID_PROPERTY_ID(object, propID, paramSpec); |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | static void webkitUserContentFilterStoreConstructed(GObject* object) |
| 111 | { |
| 112 | G_OBJECT_CLASS(webkit_user_content_filter_store_parent_class)->constructed(object); |
| 113 | |
| 114 | WebKitUserContentFilterStore* store = WEBKIT_USER_CONTENT_FILTER_STORE(object); |
| 115 | store->priv->store = adoptRef(new API::ContentRuleListStore(FileSystem::stringFromFileSystemRepresentation(store->priv->storagePath.get()), false)); |
| 116 | } |
| 117 | |
| 118 | static void webkit_user_content_filter_store_class_init(WebKitUserContentFilterStoreClass* storeClass) |
| 119 | { |
| 120 | GObjectClass* gObjectClass = G_OBJECT_CLASS(storeClass); |
| 121 | |
| 122 | gObjectClass->get_property = webkitUserContentFilterStoreGetProperty; |
| 123 | gObjectClass->set_property = webkitUserContentFilterStoreSetProperty; |
| 124 | gObjectClass->constructed = webkitUserContentFilterStoreConstructed; |
| 125 | |
| 126 | /** |
| 127 | * WebKitUserContentFilterStore:path: |
| 128 | * |
| 129 | * The directory used for filter storage. This path is used as the base |
| 130 | * directory where user content filters are stored on disk. |
| 131 | * |
| 132 | * Since: 2.24 |
| 133 | */ |
| 134 | g_object_class_install_property( |
| 135 | gObjectClass, |
| 136 | PROP_PATH, |
| 137 | g_param_spec_string( |
| 138 | "path" , |
| 139 | _("Storage directory path" ), |
| 140 | _("The directory where user content filters are stored" ), |
| 141 | nullptr, |
| 142 | static_cast<GParamFlags>(WEBKIT_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY))); |
| 143 | } |
| 144 | |
| 145 | /** |
| 146 | * webkit_user_content_filter_store_new: |
| 147 | * @storage_path: path where data for filters will be stored on disk |
| 148 | * |
| 149 | * Create a new #WebKitUserContentFilterStore to manipulate filters stored at @storage_path. |
| 150 | * The path must point to a local filesystem, and will be created if needed. |
| 151 | * |
| 152 | * Returns: (transfer full): a newly created #WebKitUserContentFilterStore |
| 153 | * |
| 154 | * Since: 2.24 |
| 155 | */ |
| 156 | WebKitUserContentFilterStore* webkit_user_content_filter_store_new(const gchar* storagePath) |
| 157 | { |
| 158 | g_return_val_if_fail(storagePath, nullptr); |
| 159 | return WEBKIT_USER_CONTENT_FILTER_STORE(g_object_new(WEBKIT_TYPE_USER_CONTENT_FILTER_STORE, "path" , storagePath, nullptr)); |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * webkit_user_content_filter_store_get_path: |
| 164 | * @store: a #WebKitUserContentFilterStore |
| 165 | * |
| 166 | * Returns: (transfer none): The storage path for user content filters. |
| 167 | * |
| 168 | * Since: 2.24 |
| 169 | */ |
| 170 | const char* webkit_user_content_filter_store_get_path(WebKitUserContentFilterStore* store) |
| 171 | { |
| 172 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr); |
| 173 | return store->priv->storagePath.get(); |
| 174 | } |
| 175 | |
| 176 | static void webkitUserContentFilterStoreSaveBytes(GRefPtr<GTask>&& task, String&& identifier, GRefPtr<GBytes>&& source) |
| 177 | { |
| 178 | size_t sourceSize; |
| 179 | const char* sourceData = static_cast<const char*>(g_bytes_get_data(source.get(), &sourceSize)); |
| 180 | if (!sourceSize) { |
| 181 | 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" )); |
| 182 | return; |
| 183 | } |
| 184 | |
| 185 | auto* store = WEBKIT_USER_CONTENT_FILTER_STORE(g_task_get_source_object(task.get())); |
| 186 | store->priv->store->compileContentRuleList(identifier, String::fromUTF8(sourceData, sourceSize), [task = WTFMove(task)](RefPtr<API::ContentRuleList> contentRuleList, std::error_code error) { |
| 187 | if (g_task_return_error_if_cancelled(task.get())) |
| 188 | return; |
| 189 | |
| 190 | if (error) |
| 191 | g_task_return_error(task.get(), toGError(WEBKIT_USER_CONTENT_FILTER_ERROR_INVALID_SOURCE, error)); |
| 192 | else |
| 193 | g_task_return_pointer(task.get(), webkitUserContentFilterCreate(WTFMove(contentRuleList)), reinterpret_cast<GDestroyNotify>(webkit_user_content_filter_unref)); |
| 194 | }); |
| 195 | } |
| 196 | |
| 197 | /** |
| 198 | * webkit_user_content_filter_store_save: |
| 199 | * @store: a #WebKitUserContentFilterStore |
| 200 | * @identifier: a string used to identify the saved filter |
| 201 | * @source: #GBytes containing the rule set in JSON format |
| 202 | * @cancellable: (allow-none): a #GCancellable or %NULL to ignore |
| 203 | * @callback: (scope async): a #GAsyncReadyCallback to call when saving is completed |
| 204 | * @user_data: (closure): the data to pass to the callback function |
| 205 | * |
| 206 | * Asynchronously save a content filter from a source rule set in the |
| 207 | * [WebKit content extesions JSON format](https://webkit.org/blog/3476/content-blockers-first-look/). |
| 208 | * |
| 209 | * The @identifier can be used afterwards to refer to the filter when using |
| 210 | * webkit_user_content_filter_store_remove() and webkit_user_content_filter_store_load(). |
| 211 | * When the @identifier has been used in the past, the new filter source will replace |
| 212 | * the one saved beforehand for the same identifier. |
| 213 | * |
| 214 | * When the operation is finished, @callback will be invoked, which then can use |
| 215 | * webkit_user_content_filter_store_save_finish() to obtain the resulting filter. |
| 216 | * |
| 217 | * Since: 2.24 |
| 218 | */ |
| 219 | void webkit_user_content_filter_store_save(WebKitUserContentFilterStore* store, const gchar* identifier, GBytes* source, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
| 220 | { |
| 221 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store)); |
| 222 | g_return_if_fail(identifier); |
| 223 | g_return_if_fail(source); |
| 224 | g_return_if_fail(callback); |
| 225 | |
| 226 | GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData)); |
| 227 | webkitUserContentFilterStoreSaveBytes(WTFMove(task), String::fromUTF8(identifier), GRefPtr<GBytes>(source)); |
| 228 | } |
| 229 | |
| 230 | /** |
| 231 | * webkit_user_content_filter_store_save_finish: |
| 232 | * @store: a #WebKitUserContentFilterStore |
| 233 | * @result: a #GAsyncResult |
| 234 | * @error: return location for error or %NULL to ignore |
| 235 | * |
| 236 | * Finishes an asynchronous filter save previously started with |
| 237 | * webkit_user_content_filter_store_save(). |
| 238 | * |
| 239 | * Returns: (transfer full): a #WebKitUserContentFilter, or %NULL if saving failed |
| 240 | * |
| 241 | * Since: 2.24 |
| 242 | */ |
| 243 | WebKitUserContentFilter* webkit_user_content_filter_store_save_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error) |
| 244 | { |
| 245 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr); |
| 246 | g_return_val_if_fail(result, nullptr); |
| 247 | return static_cast<WebKitUserContentFilter*>(g_task_propagate_pointer(G_TASK(result), error)); |
| 248 | } |
| 249 | |
| 250 | struct SaveTaskData { |
| 251 | String identifier; |
| 252 | }; |
| 253 | WEBKIT_DEFINE_ASYNC_DATA_STRUCT(SaveTaskData) |
| 254 | |
| 255 | /** |
| 256 | * webkit_user_content_filter_store_save_from_file: |
| 257 | * @store: a #WebKitUserContentFilterStore |
| 258 | * @identifier: a string used to identify the saved filter |
| 259 | * @file: a #GFile containing the rule set in JSON format |
| 260 | * @cancellable: (allow-none): a #GCancellable or %NULL to ignore |
| 261 | * @callback: (scope async): a #GAsyncReadyCallback to call when saving is completed |
| 262 | * @user_data: (closure): the data to pass to the callback function |
| 263 | * |
| 264 | * Asynchronously save a content filter from the contents of a file, which must be |
| 265 | * native to the platform, as checked by g_file_is_native(). See |
| 266 | * webkit_user_content_filter_store_save() for more details. |
| 267 | * |
| 268 | * When the operation is finished, @callback will be invoked, which then can use |
| 269 | * webkit_user_content_filter_store_save_finish() to obtain the resulting filter. |
| 270 | * |
| 271 | * Since: 2.24 |
| 272 | */ |
| 273 | void webkit_user_content_filter_store_save_from_file(WebKitUserContentFilterStore* store, const gchar* identifier, GFile* file, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
| 274 | { |
| 275 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store)); |
| 276 | g_return_if_fail(identifier); |
| 277 | g_return_if_fail(G_IS_FILE(file)); |
| 278 | g_return_if_fail(callback); |
| 279 | |
| 280 | GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData)); |
| 281 | |
| 282 | // Try mapping the file in memory first, and fall-back to reading the contents if that fails. |
| 283 | if (g_file_is_native(file)) { |
| 284 | GUniquePtr<char> filePath(g_file_get_path(file)); |
| 285 | GRefPtr<GMappedFile> mappedFile = adoptGRef(g_mapped_file_new(filePath.get(), FALSE, nullptr)); |
| 286 | if (mappedFile) { |
| 287 | GRefPtr<GBytes> source = adoptGRef(g_mapped_file_get_bytes(mappedFile.get())); |
| 288 | webkitUserContentFilterStoreSaveBytes(WTFMove(task), String::fromUTF8(identifier), WTFMove(source)); |
| 289 | return; |
| 290 | } |
| 291 | } |
| 292 | |
| 293 | // Pass the identifier as task data to be used in the completion callback once the contents have been loaded. |
| 294 | SaveTaskData* data = createSaveTaskData(); |
| 295 | data->identifier = String::fromUTF8(identifier); |
| 296 | g_task_set_task_data(task.get(), data, reinterpret_cast<GDestroyNotify>(destroySaveTaskData)); |
| 297 | |
| 298 | g_file_load_contents_async(file, cancellable, [](GObject* sourceObject, GAsyncResult* result, void* userData) { |
| 299 | GRefPtr<GTask> task = adoptGRef(G_TASK(userData)); |
| 300 | if (g_task_return_error_if_cancelled(task.get())) |
| 301 | return; |
| 302 | |
| 303 | char* sourceData; |
| 304 | size_t sourceSize; |
| 305 | GUniqueOutPtr<GError> error; |
| 306 | if (g_file_load_contents_finish(G_FILE(sourceObject), result, &sourceData, &sourceSize, nullptr, &error.outPtr())) { |
| 307 | SaveTaskData* data = static_cast<SaveTaskData*>(g_task_get_task_data(task.get())); |
| 308 | webkitUserContentFilterStoreSaveBytes(WTFMove(task), WTFMove(data->identifier), GRefPtr<GBytes>(g_bytes_new_take(sourceData, sourceSize))); |
| 309 | } else |
| 310 | g_task_return_error(task.get(), error.release().release()); |
| 311 | }, task.leakRef()); |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * webkit_user_content_filter_store_save_from_file_finish: |
| 316 | * @store: a #WebKitUserContentFilterStore |
| 317 | * @result: a #GAsyncResult |
| 318 | * @error: return location for error or %NULL to ignore |
| 319 | * |
| 320 | * Finishes and asynchronous filter save previously started with |
| 321 | * webkit_user_content_filter_store_save_from_file(). |
| 322 | * |
| 323 | * Returns: (transfer full): a #WebKitUserContentFilter, or %NULL if saving failed. |
| 324 | * |
| 325 | * Since: 2.24 |
| 326 | */ |
| 327 | WebKitUserContentFilter* webkit_user_content_filter_store_save_from_file_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error) |
| 328 | { |
| 329 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr); |
| 330 | g_return_val_if_fail(result, nullptr); |
| 331 | return static_cast<WebKitUserContentFilter*>(g_task_propagate_pointer(G_TASK(result), error)); |
| 332 | } |
| 333 | |
| 334 | /** |
| 335 | * webkit_user_content_filter_store_remove: |
| 336 | * @store: a #WebKitUserContentFilterStore |
| 337 | * @identifier: a filter identifier |
| 338 | * @cancellable: (allow-none): a #GCancellable or %NULL to ignore |
| 339 | * @callback: (scope async): a #GAsyncReadyCallback to call when the removal is completed |
| 340 | * @user_data: (closure): the data to pass to the callback function |
| 341 | * |
| 342 | * Asynchronously remove a content filter given its @identifier. |
| 343 | * |
| 344 | * When the operation is finished, @callback will be invoked, which then can use |
| 345 | * webkit_user_content_filter_store_remove_finish() to check whether the removal was |
| 346 | * successful. |
| 347 | * |
| 348 | * Since: 2.24 |
| 349 | */ |
| 350 | void webkit_user_content_filter_store_remove(WebKitUserContentFilterStore* store, const gchar* identifier, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
| 351 | { |
| 352 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store)); |
| 353 | g_return_if_fail(identifier); |
| 354 | g_return_if_fail(callback); |
| 355 | |
| 356 | GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData)); |
| 357 | store->priv->store->removeContentRuleList(String::fromUTF8(identifier), [task = WTFMove(task)](std::error_code error) { |
| 358 | if (g_task_return_error_if_cancelled(task.get())) |
| 359 | return; |
| 360 | |
| 361 | if (error) { |
| 362 | ASSERT(static_cast<API::ContentRuleListStore::Error>(error.value()) == API::ContentRuleListStore::Error::RemoveFailed); |
| 363 | g_task_return_error(task.get(), toGError(WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND, error)); |
| 364 | } else |
| 365 | g_task_return_boolean(task.get(), TRUE); |
| 366 | }); |
| 367 | } |
| 368 | |
| 369 | /** |
| 370 | * webkit_user_content_filter_store_remove_finish: |
| 371 | * @store: a #WebKitUserContentFilterStore |
| 372 | * @result: a #GAsyncResult |
| 373 | * @error: return location for error or %NULL to ignore |
| 374 | * |
| 375 | * Finishes an asynchronous filter removal previously started with |
| 376 | * webkit_user_content_filter_store_remove(). |
| 377 | * |
| 378 | * Returns: whether the removal was successful |
| 379 | * |
| 380 | * Since: 2.24 |
| 381 | */ |
| 382 | gboolean webkit_user_content_filter_store_remove_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error) |
| 383 | { |
| 384 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), FALSE); |
| 385 | g_return_val_if_fail(result, FALSE); |
| 386 | return g_task_propagate_boolean(G_TASK(result), error); |
| 387 | } |
| 388 | |
| 389 | /** |
| 390 | * webkit_user_content_filter_store_load: |
| 391 | * @store: a #WebKitUserContentFilterStore |
| 392 | * @identifier: a filter identifier |
| 393 | * @cancellable: (allow-none): a #GCancellable or %NULL to ignore |
| 394 | * @callback: (scope async): a #GAsyncReadyCallback to call when the load is completed |
| 395 | * @user_data: (closure): the data to pass to the callback function |
| 396 | * |
| 397 | * Asynchronously load a content filter given its @identifier. The filter must have been |
| 398 | * previously stored using webkit_user_content_filter_store_save(). |
| 399 | * |
| 400 | * When the operation is finished, @callback will be invoked, which then can use |
| 401 | * webkit_user_content_filter_store_load_finish() to obtain the resulting filter. |
| 402 | * |
| 403 | * Since: 2.24 |
| 404 | */ |
| 405 | void webkit_user_content_filter_store_load(WebKitUserContentFilterStore* store, const gchar* identifier, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
| 406 | { |
| 407 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store)); |
| 408 | g_return_if_fail(identifier); |
| 409 | g_return_if_fail(callback); |
| 410 | |
| 411 | GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData)); |
| 412 | store->priv->store->lookupContentRuleList(String::fromUTF8(identifier), [task = WTFMove(task)](RefPtr<API::ContentRuleList> contentRuleList, std::error_code error) { |
| 413 | if (g_task_return_error_if_cancelled(task.get())) |
| 414 | return; |
| 415 | |
| 416 | if (error) { |
| 417 | ASSERT(static_cast<API::ContentRuleListStore::Error>(error.value()) == API::ContentRuleListStore::Error::LookupFailed |
| 418 | || static_cast<API::ContentRuleListStore::Error>(error.value()) == API::ContentRuleListStore::Error::VersionMismatch); |
| 419 | g_task_return_error(task.get(), toGError(WEBKIT_USER_CONTENT_FILTER_ERROR_NOT_FOUND, error)); |
| 420 | } else |
| 421 | g_task_return_pointer(task.get(), webkitUserContentFilterCreate(WTFMove(contentRuleList)), reinterpret_cast<GDestroyNotify>(webkit_user_content_filter_unref)); |
| 422 | }); |
| 423 | } |
| 424 | |
| 425 | /** |
| 426 | * webkit_user_content_filter_store_load_finish: |
| 427 | * @store: a #WebKitUserContentFilterStore |
| 428 | * @result: a #GAsyncResult |
| 429 | * @error: return location for error or %NULL to ignore |
| 430 | * |
| 431 | * Finishes an asynchronous filter load previously started with |
| 432 | * webkit_user_content_filter_store_load(). |
| 433 | * |
| 434 | * Returns: (transfer full): a #WebKitUserContentFilter, or %NULL if the load failed |
| 435 | * |
| 436 | * Since: 2.24 |
| 437 | */ |
| 438 | WebKitUserContentFilter* webkit_user_content_filter_store_load_finish(WebKitUserContentFilterStore* store, GAsyncResult* result, GError** error) |
| 439 | { |
| 440 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr); |
| 441 | g_return_val_if_fail(result, nullptr); |
| 442 | return static_cast<WebKitUserContentFilter*>(g_task_propagate_pointer(G_TASK(result), error)); |
| 443 | } |
| 444 | |
| 445 | /** |
| 446 | * webkit_user_content_filter_store_fetch_identifiers: |
| 447 | * @store: a #WebKitUserContentFilterStore |
| 448 | * @cancellable: (allow-none): a #GCancellable or %NULL to ignore |
| 449 | * @callback: (scope async): a #GAsyncReadyCallback to call when the removal is completed |
| 450 | * @user_data: (closure): the data to pass to the callback function |
| 451 | * |
| 452 | * Asynchronously retrieve a list of the identifiers for all the stored filters. |
| 453 | * |
| 454 | * When the operation is finished, @callback will be invoked, which then can use |
| 455 | * webkit_user_content_filter_store_fetch_identifiers_finish() to obtain the list of |
| 456 | * filter identifiers. |
| 457 | * |
| 458 | * Since: 2.24 |
| 459 | */ |
| 460 | void webkit_user_content_filter_store_fetch_identifiers(WebKitUserContentFilterStore* store, GCancellable* cancellable, GAsyncReadyCallback callback, gpointer userData) |
| 461 | { |
| 462 | g_return_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store)); |
| 463 | g_return_if_fail(callback); |
| 464 | |
| 465 | GRefPtr<GTask> task = adoptGRef(g_task_new(store, cancellable, callback, userData)); |
| 466 | store->priv->store->getAvailableContentRuleListIdentifiers([task = WTFMove(task)](WTF::Vector<WTF::String> identifiers) { |
| 467 | if (g_task_return_error_if_cancelled(task.get())) |
| 468 | return; |
| 469 | |
| 470 | GStrv result = static_cast<GStrv>(g_new0(gchar*, identifiers.size() + 1)); |
| 471 | for (size_t i = 0; i < identifiers.size(); ++i) |
| 472 | result[i] = g_strdup(identifiers[i].utf8().data()); |
| 473 | g_task_return_pointer(task.get(), result, reinterpret_cast<GDestroyNotify>(g_strfreev)); |
| 474 | }); |
| 475 | } |
| 476 | |
| 477 | /** |
| 478 | * webkit_user_content_filter_store_fetch_identifiers_finish: |
| 479 | * @store: a #WebKitUserContentFilterStore |
| 480 | * @result: a #GAsyncResult |
| 481 | * |
| 482 | * Finishes an asynchronous fetch of the list of identifiers for the stored filters previously |
| 483 | * started with webkit_user_content_filter_store_fetch_identifiers(). |
| 484 | * |
| 485 | * Returns: (transfer full) (array zero-terminated=1) (element-type utf8): a %NULL-terminated list of filter identifiers. |
| 486 | * |
| 487 | * Since: 2.24 |
| 488 | */ |
| 489 | gchar** webkit_user_content_filter_store_fetch_identifiers_finish(WebKitUserContentFilterStore* store, GAsyncResult* result) |
| 490 | { |
| 491 | g_return_val_if_fail(WEBKIT_IS_USER_CONTENT_FILTER_STORE(store), nullptr); |
| 492 | g_return_val_if_fail(result, nullptr); |
| 493 | return static_cast<gchar**>(g_task_propagate_pointer(G_TASK(result), nullptr)); |
| 494 | } |
| 495 | |