[epiphany] permissions-manager: Cache permissions when enumerating them



commit ddcc22e62a7067c850c2208d2f4900fc0ca43fef
Author: Michael Catanzaro <mcatanzaro gnome org>
Date:   Mon Jan 30 19:42:07 2017 -0600

    permissions-manager: Cache permissions when enumerating them
    
    GKeyFile performs synchronous file I/O to read permissions from the
    permissions store, so we'd better do it as little as possible. This
    commit ensures we read from the notification permissions file just twice
    ever, instead of twice each time a new tab is created.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=777835

 embed/ephy-embed-shell.c       |    3 -
 lib/ephy-permissions-manager.c |  138 ++++++++++++++++++++++++++++++++++++++--
 2 files changed, 132 insertions(+), 9 deletions(-)
---
diff --git a/embed/ephy-embed-shell.c b/embed/ephy-embed-shell.c
index 80e1f3f..555c2ff 100644
--- a/embed/ephy-embed-shell.c
+++ b/embed/ephy-embed-shell.c
@@ -686,9 +686,6 @@ initialize_notification_permissions (WebKitWebContext *web_context,
   denied_origins = ephy_permissions_manager_get_denied_origins (priv->permissions_manager,
                                                                 EPHY_PERMISSION_TYPE_SHOW_NOTIFICATIONS);
   webkit_web_context_initialize_notification_permissions (web_context, permitted_origins, denied_origins);
-
-  g_list_free_full (permitted_origins, (GDestroyNotify)webkit_security_origin_unref);
-  g_list_free_full (denied_origins, (GDestroyNotify)webkit_security_origin_unref);
 }
 
 static void
diff --git a/lib/ephy-permissions-manager.c b/lib/ephy-permissions-manager.c
index d03b86f..c2a74c1 100644
--- a/lib/ephy-permissions-manager.c
+++ b/lib/ephy-permissions-manager.c
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /*
  *  Copyright © 2015 Gustavo Noronha Silva <gns gnome org>
- *  Copyright © 2016 Igalia S.L.
+ *  Copyright © 2016-2017 Igalia S.L.
  *
  *  This file is part of Epiphany.
  *
@@ -37,6 +37,9 @@ struct _EphyPermissionsManager
 
   GHashTable *origins_mapping;
   GHashTable *settings_mapping;
+
+  GHashTable *permission_type_permitted_origins;
+  GHashTable *permission_type_denied_origins;
 };
 
 G_DEFINE_TYPE (EphyPermissionsManager, ephy_permissions_manager, G_TYPE_OBJECT)
@@ -48,6 +51,19 @@ ephy_permissions_manager_init (EphyPermissionsManager *manager)
 {
   manager->origins_mapping = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
   manager->settings_mapping = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
+
+  /* We cannot use a key_destroy_func here because we need to be able to update
+   * the GList keys without destroying the contents of the lists. */
+  manager->permission_type_permitted_origins = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
NULL);
+  manager->permission_type_denied_origins = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, 
NULL);
+}
+
+static void
+free_cached_origin_list (gpointer key,
+                         gpointer value,
+                         gpointer user_data)
+{
+  g_list_free_full ((GList *)value, (GDestroyNotify)webkit_security_origin_unref);
 }
 
 static void
@@ -58,6 +74,18 @@ ephy_permissions_manager_dispose (GObject *object)
   g_clear_pointer (&manager->origins_mapping, g_hash_table_destroy);
   g_clear_pointer (&manager->settings_mapping, g_hash_table_destroy);
 
+  if (manager->permission_type_permitted_origins != NULL) {
+    g_hash_table_foreach (manager->permission_type_permitted_origins, free_cached_origin_list, NULL);
+    g_hash_table_destroy (manager->permission_type_permitted_origins);
+    manager->permission_type_permitted_origins = NULL;
+  }
+
+  if (manager->permission_type_denied_origins != NULL) {
+    g_hash_table_foreach (manager->permission_type_denied_origins, free_cached_origin_list, NULL);
+    g_hash_table_destroy (manager->permission_type_denied_origins);
+    manager->permission_type_denied_origins = NULL;
+  }
+
   G_OBJECT_CLASS (ephy_permissions_manager_parent_class)->dispose (object);
 }
 
@@ -150,14 +178,95 @@ ephy_permissions_manager_get_permission (EphyPermissionsManager *manager,
   return g_settings_get_enum (settings, permission_type_to_string (type));
 }
 
+static gint
+webkit_security_origin_compare (WebKitSecurityOrigin *a,
+                                WebKitSecurityOrigin *b)
+{
+  if (webkit_security_origin_is_opaque (a))
+    return -1;
+  if (webkit_security_origin_is_opaque (b))
+    return 1;
+
+  return g_strcmp0 (webkit_security_origin_get_protocol (a), webkit_security_origin_get_protocol (b)) * 100 
||
+         g_strcmp0 (webkit_security_origin_get_host (a), webkit_security_origin_get_host (b)) * 10 ||
+         webkit_security_origin_get_port (b) - webkit_security_origin_get_port (a);
+}
+
+static void
+maybe_add_origin_to_permission_type_cache (GHashTable           *permissions,
+                                           EphyPermissionType    type,
+                                           WebKitSecurityOrigin *origin)
+{
+  GList *origins;
+  GList *l;
+
+  /* Add origin to the appropriate permissions cache if (a) the cache already
+   * exists, and (b) it does not already contain origin. */
+  origins = g_hash_table_lookup (permissions, GINT_TO_POINTER (type));
+  if (origins != NULL) {
+    l = g_list_find_custom (origins, origin, (GCompareFunc)webkit_security_origin_compare);
+    if (l == NULL) {
+      origins = g_list_prepend (origins, webkit_security_origin_ref (origin));
+      g_hash_table_replace (permissions, GINT_TO_POINTER (type), origins);
+    }
+  }
+}
+
+static void
+maybe_remove_origin_from_permission_type_cache (GHashTable           *permissions,
+                                                EphyPermissionType    type,
+                                                WebKitSecurityOrigin *origin)
+{
+  GList *origins;
+  GList *l;
+
+  /* Remove origin from the appropriate permissions cache if (a) the cache
+   * exists, and (b) it contains origin. */
+  origins = g_hash_table_lookup (permissions, GINT_TO_POINTER (type));
+  if (origins != NULL) {
+    l = g_list_find_custom (origins, origin, (GCompareFunc)webkit_security_origin_compare);
+    if (l != NULL) {
+      webkit_security_origin_unref (l->data);
+      origins = g_list_remove_link (origins, l);
+      g_hash_table_replace (permissions, GINT_TO_POINTER (type), origins);
+    }
+  }
+}
+
 void
 ephy_permissions_manager_set_permission (EphyPermissionsManager *manager,
                                          EphyPermissionType      type,
                                          const char             *origin,
                                          EphyPermission          permission)
 {
-  GSettings *settings = ephy_permissions_manager_get_settings_for_origin (manager, origin);
+  WebKitSecurityOrigin *webkit_origin;
+  GSettings *settings;
+
+  webkit_origin = webkit_security_origin_new_for_uri (origin);
+  if (webkit_origin == NULL)
+    return;
+
+  settings = ephy_permissions_manager_get_settings_for_origin (manager, origin);
   g_settings_set_enum (settings, permission_type_to_string (type), permission);
+
+  switch (permission) {
+    case EPHY_PERMISSION_UNDECIDED:
+      maybe_remove_origin_from_permission_type_cache (manager->permission_type_permitted_origins, type, 
webkit_origin);
+      maybe_remove_origin_from_permission_type_cache (manager->permission_type_denied_origins, type, 
webkit_origin);
+      break;
+    case EPHY_PERMISSION_DENY:
+      maybe_remove_origin_from_permission_type_cache (manager->permission_type_permitted_origins, type, 
webkit_origin);
+      maybe_add_origin_to_permission_type_cache (manager->permission_type_denied_origins, type, 
webkit_origin);
+      break;
+    case EPHY_PERMISSION_PERMIT:
+      maybe_add_origin_to_permission_type_cache (manager->permission_type_permitted_origins, type, 
webkit_origin);
+      maybe_remove_origin_from_permission_type_cache (manager->permission_type_denied_origins, type, 
webkit_origin);
+      break;
+    default:
+      g_assert_not_reached ();
+  }
+
+  webkit_security_origin_unref (webkit_origin);
 }
 
 static WebKitSecurityOrigin *
@@ -238,10 +347,6 @@ origins_for_keyfile_group (GKeyFile           *file,
   return origins;
 }
 
-/* TODO: Consider caching this in memory. This gets called twice when
- * starting each web process. It could be dozens or hundreds of file
- * reads when starting the browser. It's silly to cache individual
- * settings but not this. */
 static GList *
 ephy_permissions_manager_get_matching_origins (EphyPermissionsManager *manager,
                                                EphyPermissionType      type,
@@ -254,6 +359,19 @@ ephy_permissions_manager_get_matching_origins (EphyPermissionsManager *manager,
   GList *origins = NULL;
   GError *error = NULL;
 
+  /* Return results from cache, if they exist. */
+  if (permit) {
+    origins = g_hash_table_lookup (manager->permission_type_permitted_origins, GINT_TO_POINTER (type));
+    if (origins != NULL)
+      return origins;
+  } else {
+    origins = g_hash_table_lookup (manager->permission_type_denied_origins, GINT_TO_POINTER (type));
+    if (origins != NULL)
+      return origins;
+  }
+
+  /* Not cached. Load results from GSettings keyfile. Do it manually because the
+   * GSettings API is not designed to be used for enumerating settings. */
   file = g_key_file_new ();
   filename = g_build_filename (ephy_dot_dir (), PERMISSIONS_FILENAME, NULL);
 
@@ -274,6 +392,14 @@ ephy_permissions_manager_get_matching_origins (EphyPermissionsManager *manager,
   g_strfreev (groups);
   g_free (filename);
 
+  /* Cache the results. */
+  if (origins != NULL) {
+    g_hash_table_insert (permit ? manager->permission_type_permitted_origins
+                                : manager->permission_type_denied_origins,
+                         GINT_TO_POINTER (type),
+                         origins);
+  }
+
   return origins;
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]