diff --git a/embed/web-process-extension/resources/js/webextensions-common.js b/embed/web-process-extension/resources/js/webextensions-common.js index d3f24cc9857791e3c2caa0be35ef6c7210b49b9b..f4417b3e3fc5258777ff9c7e65dc87879ba2340f 100644 --- a/embed/web-process-extension/resources/js/webextensions-common.js +++ b/embed/web-process-extension/resources/js/webextensions-common.js @@ -7,8 +7,28 @@ window.browser = {}; const promises = []; let last_promise = 0; -let runtime_onmessage_listeners = []; -let runtime_onconnect_listeners = []; +class EphyEventListener { + constructor () { + this._listeners = []; + } + + addListener (cb) { + this._listeners.push({callback: cb}); + } + + removeListener (cb) { + this._listeners = this._listeners.filter(l => l.callback !== cb); + } + + hasListener (cb) { + return !!this._listeners.find(l => l.callback === cb); + } + + _emit (data) { + for (const listener of this._listeners) + listener.callback (data); + } +} const ephy_message = function (fn, args, cb) { const promise = new Promise (function (resolve, reject) { @@ -18,44 +38,32 @@ const ephy_message = function (fn, args, cb) { return promise; }; -const runtimeSendMessage = function(x) { - for (const listener of runtime_onmessage_listeners) - listener.callback(x); -}; - -const runtimeOnConnect = function(x) { - for (const listener of runtime_onconnect_listeners) - listener.callback(x); -}; - window.browser.runtime = { getURL: function (args, cb) { return window.browser.extension.getURL(args, cb); }, getManifest: function (args, cb) { return '[]'; }, - onMessage: { - addListener: function (cb) { - runtime_onmessage_listeners.push({callback: cb}); - }, - removeListener: function (cb) { - runtime_onmessage_listeners = runtime_onmessage_listeners.filter(l => l.callback !== cb); - }, - hasListener: function (cb) { - return !!runtime_onmessage_listeners.find(l => l.callback === cb); - } + onMessage: new EphyEventListener (), + onConnect: new EphyEventListener (), + sendMessage: function (args, cb) { + return ephy_message ('runtime.sendMessage', args, cb); }, - onConnect: { - addListener: function (cb) { - runtime_onconnect_listeners.push({callback: cb}); +}; + + +window.browser.storage = { + local: { + get: function (keys) { + return ephy_message ('storage.local.get', keys); }, - removeListener: function (cb) { - runtime_onconnect_listeners = runtime_onconnect_listeners.filter(l => l.callback !== cb); + set: function (keys) { + return ephy_message ('storage.local.set', keys); }, - hasListener: function (cb) { - return !!runtime_onconnect_listeners.find(l => l.callback === cb); + remove: function (keys) { + return ephy_message ('storage.local.remove', keys); + }, + clear: function () { + return ephy_message ('storage.local.clear'); } - }, - sendMessage: function (args, cb) { - return ephy_message ('runtime.sendMessage', args, cb); - }, + } }; // Compatibility with Chrome diff --git a/embed/web-process-extension/resources/js/webextensions.js b/embed/web-process-extension/resources/js/webextensions.js index 7fafff9030491d383900f2c0787ff3fd17e0df85..33985f9226572bb36383f3ed144ee6235db97457 100644 --- a/embed/web-process-extension/resources/js/webextensions.js +++ b/embed/web-process-extension/resources/js/webextensions.js @@ -1,29 +1,7 @@ 'use strict'; /* exported pageActionOnClicked, browserActionClicked, browserActionClicked, tabsOnUpdated */ -/* global ephy_message */ - -const tabs_listeners = []; -const page_listeners = []; -const browser_listeners = []; -const runtime_listeners = []; -const runtime_onmessageexternal_listeners = []; -const windows_onremoved_listeners = []; - -const pageActionOnClicked = function(x) { - for (const listener of page_listeners) - listener.callback(x); -}; - -const browserActionClicked = function(x) { - for (const listener of browser_listeners) - listener.callback(x); -}; - -const tabsOnUpdated = function(x) { - for (const listener of tabs_listeners) - listener.callback(x); -}; +/* global ephy_message EphyEventListener */ // Browser async API window.browser.alarms = { @@ -31,9 +9,7 @@ window.browser.alarms = { }; window.browser.windows = { - onRemoved: { - addListener: function (cb) { windows_onremoved_listeners.push({callback: cb}); } - } + onRemoved: new EphyEventListener (), }; window.browser.tabs = { @@ -43,9 +19,7 @@ window.browser.tabs = { get: function (args, cb) { return ephy_message ('tabs.get', args, cb); }, insertCSS: function (...args) { return ephy_message ('tabs.insertCSS', args, null); }, removeCSS: function (...args) { return ephy_message ('tabs.removeCSS', args, null); }, - onUpdated: { - addListener: function (cb) { tabs_listeners.push({callback: cb}); } - }, + onUpdated: new EphyEventListener (), sendMessage: function (...args) { return ephy_message ('tabs.sendMessage', args, null); }, TAB_ID_NONE: -1, }; @@ -54,16 +28,18 @@ window.browser.notifications = { create: function (args, cb) { return ephy_message ('notifications.create', args, cb); }, }; +// browser.extension is defined in ephy-webextension-common.c +window.browser.extension.getViews = function (...args) { return []; }; + // browser.runtime is defined in webextensions-common.js window.browser.runtime.getBrowserInfo = function (args, cb) { return ephy_message ('runtime.getBrowserInfo', args, cb); }; window.browser.runtime.connectNative = function (args, cb) { return ephy_message ('runtime.connectNative', args, cb); }; window.browser.runtime.openOptionsPage = function (args, cb) { return ephy_message ('runtime.openOptionsPage', args, cb); }; window.browser.runtime.setUninstallURL = function (args, cb) { return ephy_message ('runtime.setUninstallURL', args, cb); }; -window.browser.runtime.onInstalled = { - addListener: function (cb) { runtime_listeners.push({callback: cb}); } -}; -window.browser.runtime.onMessageExternal = { - addListener: function (cb) { runtime_onmessageexternal_listeners.push({callback: cb}); } +window.browser.runtime.onInstalled = new EphyEventListener (); +window.browser.runtime.onMessageExternal = new EphyEventListener (); +window.browser.runtime.sendNativeMessage = function (args) { + return new Promise ((resolve, reject) => { reject ('Unsupported API'); }); }; window.browser.pageAction = { @@ -72,15 +48,11 @@ window.browser.pageAction = { getTitle: function (args, cb) { return ephy_message ('pageAction.getTitle', args, cb); }, show: function (args, cb) { return ephy_message ('pageAction.show', args, cb); }, hide: function (args, cb) { return ephy_message ('pageAction.hide', args, cb); }, - onClicked: { - addListener: function (cb) { page_listeners.push({callback: cb}); } - } + onClicked: new EphyEventListener (), }; window.browser.browserAction = { - onClicked: { - addListener: function (cb) { browser_listeners.push({callback: cb}); } - } + onClicked: new EphyEventListener (), }; window.browser.windows = { diff --git a/src/webextension/api/runtime.c b/src/webextension/api/runtime.c index 156390461431a51a0656abd510aa409788f96ed2..303edbade3918a81723ede0cd8ef72f9ef891663 100644 --- a/src/webextension/api/runtime.c +++ b/src/webextension/api/runtime.c @@ -55,7 +55,7 @@ runtime_handler_send_message (EphyWebExtension *self, WebKitWebView *view = WEBKIT_WEB_VIEW (ephy_web_extension_manager_get_background_web_view (manager, self)); g_autofree char *script = NULL; - script = g_strdup_printf ("runtimeSendMessage(%s);", jsc_value_to_json (args, 2)); + script = g_strdup_printf ("window.browser.runtime.onMessage._emit(%s);", jsc_value_to_json (args, 2)); webkit_web_view_run_javascript (view, script, NULL, NULL, NULL); return NULL; diff --git a/src/webextension/api/storage.c b/src/webextension/api/storage.c new file mode 100644 index 0000000000000000000000000000000000000000..ff31c8764efc3c12d5d9ff9f1e7e03b6357bb268 --- /dev/null +++ b/src/webextension/api/storage.c @@ -0,0 +1,209 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * Copyright © 2019-2020 Jan-Michael Brummer + * Copyright © 2022 Igalia S.L. + * + * This file is part of Epiphany. + * + * Epiphany is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Epiphany 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Epiphany. If not, see . + */ + +#include "config.h" + +#include "ephy-embed-utils.h" +#include "ephy-shell.h" +#include "ephy-window.h" + +#include "storage.h" + +static JsonNode * +node_from_value_property (JSCValue *object, + const char *property) +{ + g_autoptr (JSCValue) property_value = jsc_value_object_get_property (object, property); + g_autofree char *value_json = jsc_value_to_json (property_value, 0); + JsonNode *json = json_from_string (value_json, NULL); + g_assert (json); + return json; +} + +static GStrv +strv_from_value (JSCValue *array) +{ + g_autoptr (GStrvBuilder) builder = g_strv_builder_new (); + + for (guint i = 0;; i++) { + g_autoptr (JSCValue) value = jsc_value_object_get_property_at_index (array, i); + g_autofree char *str = NULL; + + if (jsc_value_is_undefined (value)) + break; + + str = jsc_value_to_string (value); + g_strv_builder_add (builder, str); + } + + return g_strv_builder_end (builder); +} + +static char * +storage_handler_local_set (EphyWebExtension *self, + char *name, + JSCValue *args) +{ + JsonNode *local_storage = ephy_web_extension_get_local_storage (self); + JsonObject *local_storage_obj = json_node_get_object (local_storage); + g_auto (GStrv) keys = NULL; + + if (!jsc_value_is_object (args)) + return NULL; + + keys = jsc_value_object_enumerate_properties (args); + + for (guint i = 0; keys[i]; i++) + json_object_set_member (local_storage_obj, keys[i], node_from_value_property (args, keys[i])); + + /* FIXME: Implement storage.onChanged */ + /* FIXME: Async IO */ + ephy_web_extension_save_local_storage (self); + + return NULL; +} + +static char * +storage_handler_local_get (EphyWebExtension *self, + char *name, + JSCValue *args) +{ + JsonNode *local_storage = ephy_web_extension_get_local_storage (self); + JsonObject *local_storage_obj = json_node_get_object (local_storage); + g_autoptr (JsonBuilder) builder = NULL; + g_autoptr (JsonNode) root = NULL; + g_auto (GStrv) keys = NULL; + + if (jsc_value_is_null (args)) + return json_to_string (local_storage, FALSE); + + builder = json_builder_new (); + json_builder_begin_object (builder); + + if (jsc_value_is_string (args)) { + g_autofree char *key = jsc_value_to_string (args); + JsonNode *member = json_object_get_member (local_storage_obj, key); + if (member) { + json_builder_set_member_name (builder, key); + json_builder_add_value (builder, member); + } + goto end_get; + } + + if (jsc_value_is_array (args)) { + keys = strv_from_value (args); + for (guint i = 0; keys[i]; i++) { + const char *key = keys[i]; + JsonNode *member = json_object_get_member (local_storage_obj, key); + if (member) { + json_builder_set_member_name (builder, key); + json_builder_add_value (builder, member); + } + } + goto end_get; + } + + if (jsc_value_is_object (args)) { + keys = jsc_value_object_enumerate_properties (args); + for (guint i = 0; keys[i]; i++) { + const char *key = keys[i]; + JsonNode *member = json_object_get_member (local_storage_obj, key); + json_builder_set_member_name (builder, key); + if (!member) + member = node_from_value_property (args, key); + json_builder_add_value (builder, member); + } + goto end_get; + } + +end_get: + json_builder_end_object (builder); + root = json_builder_get_root (builder); + return json_to_string (root, FALSE); +} + +static char * +storage_handler_local_remove (EphyWebExtension *self, + char *name, + JSCValue *args) +{ + JsonNode *local_storage = ephy_web_extension_get_local_storage (self); + JsonObject *local_storage_obj = json_node_get_object (local_storage); + + if (jsc_value_is_string (args)) { + g_autofree char *key = jsc_value_to_string (args); + json_object_remove_member (local_storage_obj, key); + goto end_remove; + } + + if (jsc_value_is_array (args)) { + g_auto (GStrv) keys = strv_from_value (args); + for (guint i = 0; keys[i]; i++) { + json_object_remove_member (local_storage_obj, keys[i]); + } + goto end_remove; + } + +end_remove: + ephy_web_extension_save_local_storage (self); + return NULL; +} + +static char * +storage_handler_local_clear (EphyWebExtension *self, + char *name, + JSCValue *args) +{ + ephy_web_extension_clear_local_storage (self); + ephy_web_extension_save_local_storage (self); + return NULL; +} + +static EphyWebExtensionApiHandler storage_handlers[] = { + {"local.set", storage_handler_local_set}, + {"local.get", storage_handler_local_get}, + {"local.remove", storage_handler_local_remove}, + {"local.clear", storage_handler_local_clear}, +}; + +char * +ephy_web_extension_api_storage_handler (EphyWebExtension *self, + char *name, + JSCValue *args) +{ + guint idx; + + if (!ephy_web_extension_has_permission (self, "storage")) { + g_warning ("Extension %s tried to use storage without permission.", ephy_web_extension_get_name (self)); + return NULL; + } + + for (idx = 0; idx < G_N_ELEMENTS (storage_handlers); idx++) { + EphyWebExtensionApiHandler handler = storage_handlers[idx]; + + if (g_strcmp0 (handler.name, name) == 0) + return handler.execute (self, name, args); + } + + g_warning ("%s(): '%s' not implemented by Epiphany!", __FUNCTION__, name); + + return NULL; +} diff --git a/src/webextension/api/storage.h b/src/webextension/api/storage.h new file mode 100644 index 0000000000000000000000000000000000000000..18ff87de963c2ee11a88af9298600462c67e933d --- /dev/null +++ b/src/webextension/api/storage.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* + * Copyright © 2019-2020 Jan-Michael Brummer + * Copyright © 2022 Igalia S.L. + * + * This file is part of Epiphany. + * + * Epiphany is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Epiphany 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Epiphany. If not, see . + */ + + +#pragma once + +#include "ephy-web-extension.h" + +#include + +G_BEGIN_DECLS + +char *ephy_web_extension_api_storage_handler (EphyWebExtension *self, + char *name, + JSCValue *value); + +G_END_DECLS diff --git a/src/webextension/api/tabs.c b/src/webextension/api/tabs.c index aa69e41e87e68714090d853d57c3d9776f57d52c..47e0fb1c9a951947bc4feab01ffb2a5bdfd10ce8 100644 --- a/src/webextension/api/tabs.c +++ b/src/webextension/api/tabs.c @@ -420,7 +420,7 @@ tabs_handler_send_message (EphyWebExtension *self, return NULL; serialized_message = jsc_value_to_json (message_value, 0); - code = g_strdup_printf ("runtimeSendMessage(JSON.parse('%s'));", serialized_message); + code = g_strdup_printf ("window.browser.runtime.onMessage._emit(JSON.parse('%s'));", serialized_message); target_web_view = get_web_view_for_tab_id (shell, jsc_value_to_int32 (tab_id_value), NULL); diff --git a/src/webextension/ephy-web-extension-manager.c b/src/webextension/ephy-web-extension-manager.c index d01cbe2284a62ce3b9f681287d534174de4f7fde..a00d66faf38819147cb71f16447fd7de444adada 100644 --- a/src/webextension/ephy-web-extension-manager.c +++ b/src/webextension/ephy-web-extension-manager.c @@ -38,6 +38,7 @@ #include "api/notifications.h" #include "api/pageaction.h" #include "api/runtime.h" +#include "api/storage.h" #include "api/tabs.h" #include @@ -58,6 +59,7 @@ EphyWebExtensionApiHandler api_handlers[] = { {"notifications", ephy_web_extension_api_notifications_handler}, {"pageAction", ephy_web_extension_api_pageaction_handler}, {"runtime", ephy_web_extension_api_runtime_handler}, + {"storage", ephy_web_extension_api_storage_handler}, {"tabs", ephy_web_extension_api_tabs_handler}, {NULL, NULL}, }; @@ -199,7 +201,7 @@ ephy_web_extension_manager_constructed (GObject *object) EphyWebExtensionManager *self = EPHY_WEB_EXTENSION_MANAGER (object); g_autofree char *dir = g_build_filename (ephy_default_profile_dir (), "web_extensions", NULL); - self->background_web_views = g_hash_table_new (NULL, NULL); + self->background_web_views = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)gtk_widget_destroy); self->page_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_hash_table_destroy); self->browser_action_map = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)destroy_widget_list); self->web_extensions = NULL; @@ -374,7 +376,7 @@ ephy_web_extension_manager_get_background_web_view (EphyWebExtensionManager *sel static void ephy_web_extension_manager_set_background_web_view (EphyWebExtensionManager *self, EphyWebExtension *web_extension, - EphyWebView *web_view) + WebKitWebView *web_view) { g_hash_table_insert (self->background_web_views, web_extension, web_view); } @@ -405,7 +407,7 @@ page_action_clicked (GtkWidget *event_box, json = json_to_string (root, FALSE); - script = g_strdup_printf ("pageActionOnClicked(%s);", json); + script = g_strdup_printf ("window.browser.pageAction.onClicked._emit(%s);", json); webkit_web_view_run_javascript (web_view, script, NULL, @@ -787,7 +789,7 @@ on_browser_action_clicked (GtkWidget *event_box, g_autofree char *script = NULL; WebKitWebView *web_view = WEBKIT_WEB_VIEW (ephy_web_extension_manager_get_background_web_view (self, web_extension)); - script = g_strdup_printf ("browserActionClicked();"); + script = g_strdup_printf ("window.browser.browserAction.onClicked._emit();"); webkit_web_view_run_javascript (web_view, script, @@ -968,7 +970,7 @@ run_background_script (EphyWebExtensionManager *self, /* Create new background web_view */ background = create_web_extensions_webview (web_extension); - ephy_web_extension_manager_set_background_web_view (self, web_extension, EPHY_WEB_VIEW (background)); + ephy_web_extension_manager_set_background_web_view (self, web_extension, WEBKIT_WEB_VIEW (background)); base_uri = create_base_uri_for_resource_path (web_extension, page); @@ -1066,6 +1068,7 @@ ephy_web_extension_manager_set_active (EphyWebExtensionManager *self, run_background_script (self, web_extension); } else { g_hash_table_remove (self->browser_action_map, web_extension); + g_hash_table_remove (self->background_web_views, web_extension); } } diff --git a/src/webextension/ephy-web-extension.c b/src/webextension/ephy-web-extension.c index 82eb061fed434c04e1dbbdcaf52628afbd01a31b..1b9e5c47938923066616a00ed00450c8caacb7fa 100644 --- a/src/webextension/ephy-web-extension.c +++ b/src/webextension/ephy-web-extension.c @@ -108,6 +108,8 @@ struct _EphyWebExtension { GList *custom_css; GPtrArray *permissions; GCancellable *cancellable; + char *local_storage_path; + JsonNode *local_storage; }; G_DEFINE_TYPE (EphyWebExtension, ephy_web_extension, G_TYPE_OBJECT) @@ -728,6 +730,7 @@ ephy_web_extension_dispose (GObject *object) g_clear_pointer (&self->name, g_free); g_clear_pointer (&self->version, g_free); g_clear_pointer (&self->homepage_url, g_free); + g_clear_pointer (&self->local_storage_path, g_free); g_clear_list (&self->icons, (GDestroyNotify)web_extension_icon_free); g_clear_list (&self->content_scripts, (GDestroyNotify)web_extension_content_script_free); @@ -735,6 +738,7 @@ ephy_web_extension_dispose (GObject *object) g_clear_pointer (&self->background, web_extension_background_free); g_clear_pointer (&self->options_ui, web_extension_options_ui_free); g_clear_pointer (&self->permissions, g_ptr_array_unref); + g_clear_pointer (&self->local_storage, json_node_unref); g_clear_pointer (&self->page_action, web_extension_page_action_free); g_clear_pointer (&self->browser_action, web_extension_browser_action_free); @@ -884,6 +888,7 @@ ephy_web_extension_load (GFile *target) GFileType type; gsize length = 0; const unsigned char *manifest; + g_autofree char *local_storage_contents = NULL; type = g_file_query_file_type (source, G_FILE_QUERY_INFO_NONE, NULL); if (type == G_FILE_TYPE_DIRECTORY) { @@ -927,6 +932,20 @@ ephy_web_extension_load (GFile *target) self->homepage_url = ephy_web_extension_manifest_get_key (self, root_object, "homepage_url"); self->author = ephy_web_extension_manifest_get_key (self, root_object, "author"); + self->local_storage_path = g_build_filename (ephy_config_dir (), "web_extensions", + g_path_get_basename (self->base_location), "local-storage.json", NULL); + + if (g_file_get_contents (self->local_storage_path, &local_storage_contents, NULL, NULL)) { + self->local_storage = json_from_string (local_storage_contents, &error); + if (error) { + g_warning ("Failed to load extension's local storage JSON: %s", error->message); + g_clear_error (&error); + } + } + + if (!self->local_storage) + self->local_storage = json_node_init_object (json_node_alloc (), json_object_new ()); + if (json_object_has_member (root_object, "icons")) { icons_object = json_object_get_object_member (root_object, "icons"); @@ -1346,7 +1365,7 @@ ephy_web_extension_has_permission_internal (EphyWebExtension *self, /* https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Match_patterns */ - for (uint i = 0; i < self->permissions->len; i++) { + for (guint i = 0; i < self->permissions->len; i++) { const char *permission = g_ptr_array_index (self->permissions, i); if (is_user_interaction && is_active_tab && strcmp (permission, "activeTab") == 0) @@ -1377,3 +1396,42 @@ ephy_web_extension_has_tab_or_host_permission (EphyWebExtension *self, { return ephy_web_extension_has_permission_internal (self, web_view, is_user_interaction, TRUE); } + +gboolean +ephy_web_extension_has_permission (EphyWebExtension *self, + const char *permission) +{ + for (guint i = 0; i < self->permissions->len; i++) { + if (strcmp (g_ptr_array_index (self->permissions, i), permission) == 0) + return TRUE; + } + + return FALSE; +} + +JsonNode * +ephy_web_extension_get_local_storage (EphyWebExtension *self) +{ + return self->local_storage; +} + +void +ephy_web_extension_save_local_storage (EphyWebExtension *self) +{ + g_autoptr (GError) error = NULL; + g_autofree char *json = NULL; + g_autofree char *parent_dir = NULL; + + parent_dir = g_path_get_dirname (self->local_storage_path); + g_mkdir_with_parents (parent_dir, 0755); + + json = json_to_string (self->local_storage, TRUE); + if (!g_file_set_contents (self->local_storage_path, json, -1, &error)) + g_warning ("Failed to write %s: %s", self->local_storage_path, error->message); +} + +void +ephy_web_extension_clear_local_storage (EphyWebExtension *self) +{ + self->local_storage = json_node_init_object (self->local_storage, json_object_new ()); +} diff --git a/src/webextension/ephy-web-extension.h b/src/webextension/ephy-web-extension.h index 1a7584c46e0f31f9deb4b47355f3a1284bea5b70..625488c16d4e17aae3a483d06c79d040a65f9b7f 100644 --- a/src/webextension/ephy-web-extension.h +++ b/src/webextension/ephy-web-extension.h @@ -133,5 +133,14 @@ gboolean ephy_web_extension_has_host_permission (EphyW EphyWebView *web_view, gboolean is_user_interaction); +gboolean ephy_web_extension_has_permission (EphyWebExtension *self, + const char *permission); + +JsonNode *ephy_web_extension_get_local_storage (EphyWebExtension *self); + +void ephy_web_extension_save_local_storage (EphyWebExtension *self); + +void ephy_web_extension_clear_local_storage (EphyWebExtension *self); + G_END_DECLS diff --git a/src/webextension/meson.build b/src/webextension/meson.build index 921cc68bc8ff6475fb8777a43d3498fb38924680..5656e0b6e256eb4452be3ae189d1e655206c8c06 100644 --- a/src/webextension/meson.build +++ b/src/webextension/meson.build @@ -2,6 +2,7 @@ ephywebextension_src = [ 'webextension/api/notifications.c', 'webextension/api/pageaction.c', 'webextension/api/runtime.c', + 'webextension/api/storage.c', 'webextension/api/tabs.c', 'webextension/ephy-web-extension-manager.c', 'webextension/ephy-web-extension.c',