[epiphany] Add support for Google Search Suggestions



commit a334cad703988767b60222138e3fc5d2461ee988
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Sun Jan 10 16:44:31 2021 +0100

    Add support for Google Search Suggestions

 data/org.gnome.epiphany.gschema.xml     |   5 +
 lib/ephy-prefs.h                        |   1 +
 src/ephy-suggestion-model.c             | 214 +++++++++++++++++++++++++-------
 src/preferences/prefs-privacy-page.c    |  15 +++
 src/resources/gtk/prefs-privacy-page.ui |  21 ++++
 5 files changed, 209 insertions(+), 47 deletions(-)
---
diff --git a/data/org.gnome.epiphany.gschema.xml b/data/org.gnome.epiphany.gschema.xml
index 5badc3006..99800b3f2 100644
--- a/data/org.gnome.epiphany.gschema.xml
+++ b/data/org.gnome.epiphany.gschema.xml
@@ -33,6 +33,11 @@
                        <summary>Default search engines.</summary>
                        <description>List of the default search engines. It is an array in which each search 
engine is described by a name, an address, and a bang (shortcut).</description>
                </key>
+               <key type="b" name="use-google-search-suggestions">
+                       <default>false</default>
+                       <summary>Enable Google Search Suggestions</summary>
+                       <description>Whether to show Google Search Suggestion in url entry 
popdown.</description>
+               </key>
                <key type="b" name="new-windows-in-tabs">
                        <default>true</default>
                        <summary>Force new windows to be opened in tabs</summary>
diff --git a/lib/ephy-prefs.h b/lib/ephy-prefs.h
index 9b1a8545c..fb0f37ef8 100644
--- a/lib/ephy-prefs.h
+++ b/lib/ephy-prefs.h
@@ -161,6 +161,7 @@ static const char * const ephy_prefs_web_schema[] = {
 #define EPHY_PREFS_ASK_FOR_DEFAULT                    "ask-for-default"
 #define EPHY_PREFS_START_IN_INCOGNITO_MODE            "start-in-incognito-mode"
 #define EPHY_PREFS_ACTIVE_CLEAR_DATA_ITEMS            "active-clear-data-items"
+#define EPHY_PREFS_USE_GOOGLE_SEARCH_SUGGESTIONS      "use-google-search-suggestions"
 
 #define EPHY_PREFS_LOCKDOWN_SCHEMA            "org.gnome.Epiphany.lockdown"
 #define EPHY_PREFS_LOCKDOWN_FULLSCREEN        "disable-fullscreen"
diff --git a/src/ephy-suggestion-model.c b/src/ephy-suggestion-model.c
index 106b39c8b..1c5fb6793 100644
--- a/src/ephy-suggestion-model.c
+++ b/src/ephy-suggestion-model.c
@@ -20,8 +20,11 @@
 #include "ephy-suggestion-model.h"
 
 #include "ephy-embed-shell.h"
+#include "ephy-prefs.h"
 #include "ephy-search-engine-manager.h"
+#include "ephy-settings.h"
 #include "ephy-suggestion.h"
+#include "ephy-user-agent.h"
 #include "ephy-window.h"
 
 #include <dazzle.h>
@@ -37,6 +40,7 @@ struct _EphySuggestionModel {
   GSequence *items;
   GCancellable *icon_cancellable;
   guint num_custom_entries;
+  SoupSession *session;
 };
 
 enum {
@@ -62,6 +66,7 @@ ephy_suggestion_model_finalize (GObject *object)
   g_clear_object (&self->history_service);
   g_clear_pointer (&self->urls, g_sequence_free);
   g_clear_pointer (&self->items, g_sequence_free);
+  g_clear_object (&self->session);
 
   g_cancellable_cancel (self->icon_cancellable);
   g_clear_object (&self->icon_cancellable);
@@ -139,6 +144,7 @@ static void
 ephy_suggestion_model_init (EphySuggestionModel *self)
 {
   self->items = g_sequence_new (g_object_unref);
+  self->session = soup_session_new_with_options ("user-agent", ephy_user_agent_get (), NULL);
 }
 
 static GType
@@ -283,8 +289,12 @@ static gboolean
 append_suggestion (EphySuggestionModel *self,
                    EphySuggestion      *suggestion)
 {
+  if (g_sequence_lookup (self->urls, (char *)ephy_suggestion_get_uri (suggestion), 
(GCompareDataFunc)g_strcmp0, NULL))
+    return FALSE;
+
   if (self->num_custom_entries < MAX_URL_ENTRIES) {
-    g_sequence_append (self->items, suggestion);
+    g_sequence_append (self->items, g_object_ref (suggestion));
+    load_favicon (self, suggestion, ephy_suggestion_get_uri (suggestion));
     self->num_custom_entries++;
 
     return TRUE;
@@ -348,42 +358,6 @@ add_bookmarks (EphySuggestionModel *self,
   return added;
 }
 
-static guint
-add_history (EphySuggestionModel *self,
-             GList               *urls,
-             const char          *query)
-{
-  guint added = 0;
-
-  for (const GList *p = urls; p != NULL; p = p->next) {
-    EphyHistoryURL *url = (EphyHistoryURL *)p->data;
-    EphySuggestion *suggestion;
-    g_autofree gchar *escaped_title = NULL;
-    g_autofree gchar *markup = NULL;
-    const gchar *title = url->title;
-
-    if (g_sequence_lookup (self->urls, url->url, (GCompareDataFunc)g_strcmp0,
-                           NULL))
-      continue;
-
-    if (strlen (url->title) == 0)
-      title = url->url;
-
-    escaped_title = g_markup_escape_text (title, -1);
-
-    markup = dzl_fuzzy_highlight (escaped_title, query, FALSE);
-    suggestion = ephy_suggestion_new (markup, title, url->url);
-    load_favicon (self, suggestion, url->url);
-
-    if (append_suggestion (self, suggestion))
-      added++;
-    else
-      break;
-  }
-
-  return added;
-}
-
 static guint
 add_search_engines (EphySuggestionModel *self,
                     const char          *query)
@@ -505,6 +479,9 @@ add_tabs (EphySuggestionModel *self,
 typedef struct {
   char *query;
   gboolean include_search_engines;
+  GSequence *history;
+  GSequence *google_suggestions;
+  int active_sources;
 } QueryData;
 
 static QueryData *
@@ -516,6 +493,8 @@ query_data_new (const char *query,
   data = g_malloc0 (sizeof (QueryData));
   data->query = g_strdup (query);
   data->include_search_engines = include_search_engines;
+  data->history = g_sequence_new (g_object_unref);
+  data->google_suggestions = g_sequence_new (g_object_unref);
 
   return data;
 }
@@ -524,26 +503,27 @@ static void
 query_data_free (QueryData *data)
 {
   g_assert (data != NULL);
+  g_clear_pointer (&data->history, g_sequence_free);
+  g_clear_pointer (&data->google_suggestions, g_sequence_free);
   g_free (data->query);
   g_free (data);
 }
 
 static void
-query_completed_cb (EphyHistoryService *service,
-                    gboolean            success,
-                    gpointer            result_data,
-                    gpointer            user_data)
+query_collection_done (EphySuggestionModel *self,
+                       GTask               *task)
 {
-  GTask *task = user_data;
-  EphySuggestionModel *self;
   QueryData *data;
-  GList *urls;
   guint removed;
   guint added = 0;
 
   self = g_task_get_source_object (task);
   data = g_task_get_task_data (task);
-  urls = (GList *)result_data;
+
+  if (--data->active_sources) {
+    g_object_unref (task);
+    return;
+  }
 
   g_cancellable_cancel (self->icon_cancellable);
   g_clear_object (&self->icon_cancellable);
@@ -560,8 +540,26 @@ query_completed_cb (EphyHistoryService *service,
 
   if (strlen (data->query) > 0) {
     added = add_tabs (self, data->query);
+
+    for (GSequenceIter *iter = g_sequence_get_begin_iter (data->google_suggestions); !g_sequence_iter_is_end 
(iter); iter = g_sequence_iter_next (iter)) {
+      EphySuggestion *tmp = g_sequence_get (iter);
+
+      if (append_suggestion (self, tmp))
+        added++;
+      else
+        break;
+    }
+
     added += add_bookmarks (self, data->query);
-    added += add_history (self, urls, data->query);
+
+    for (GSequenceIter *iter = g_sequence_get_begin_iter (data->history); !g_sequence_iter_is_end (iter); 
iter = g_sequence_iter_next (iter)) {
+      EphySuggestion *tmp = g_sequence_get (iter);
+
+      if (append_suggestion (self, tmp))
+        added++;
+      else
+        break;
+    }
 
     if (data->include_search_engines)
       added += add_search_engines (self, data->query);
@@ -573,6 +571,119 @@ query_completed_cb (EphyHistoryService *service,
   g_object_unref (task);
 }
 
+static void
+history_query_completed_cb (EphyHistoryService *service,
+                            gboolean            success,
+                            gpointer            result_data,
+                            gpointer            user_data)
+{
+  GTask *task = user_data;
+  EphySuggestionModel *self;
+  QueryData *data;
+  GList *urls;
+
+  self = g_task_get_source_object (task);
+  data = g_task_get_task_data (task);
+  urls = (GList *)result_data;
+
+  if (strlen (data->query) > 0) {
+    for (const GList *p = urls; p != NULL; p = p->next) {
+      EphyHistoryURL *url = (EphyHistoryURL *)p->data;
+      EphySuggestion *suggestion;
+      g_autofree gchar *escaped_title = NULL;
+      g_autofree gchar *markup = NULL;
+      const gchar *title = url->title;
+
+      if (strlen (url->title) == 0)
+        title = url->url;
+
+      escaped_title = g_markup_escape_text (title, -1);
+
+      markup = dzl_fuzzy_highlight (escaped_title, data->query, FALSE);
+      suggestion = ephy_suggestion_new (markup, title, url->url);
+
+      g_sequence_append (data->history, g_steal_pointer (&suggestion));
+    }
+  }
+
+  query_collection_done (self, g_steal_pointer (&task));
+}
+
+static void
+google_search_suggestions_cb (SoupSession *session,
+                              SoupMessage *msg,
+                              gpointer     user_data)
+{
+  GTask *task = G_TASK (user_data);
+  EphySuggestionModel *self = g_task_get_source_object (task);
+  EphyEmbedShell *shell;
+  EphySearchEngineManager *manager;
+  QueryData *data;
+  JsonNode *node;
+  JsonArray *array;
+  JsonArray *suggestions;
+  char *engine;
+  int added = 0;
+
+  if (msg->status_code != 200)
+    goto out;
+
+  shell = ephy_embed_shell_get_default ();
+  manager = ephy_embed_shell_get_search_engine_manager (shell);
+  engine = ephy_search_engine_manager_get_default_engine (manager);
+
+  node = json_from_string (msg->response_body->data, NULL);
+  if (!node || !JSON_NODE_HOLDS_ARRAY (node)) {
+    g_warning ("Google search suggestion response is not a valid JSON object: %s", msg->response_body->data);
+    goto out;
+  }
+
+  array = json_node_get_array (node);
+  suggestions = json_array_get_array_element (array, 1);
+  data = g_task_get_task_data (task);
+
+  for (guint i = 0; i < json_array_get_length (suggestions) && added < 5; i++) {
+    EphySuggestion *suggestion;
+    const char *str = json_array_get_string_element (suggestions, i);
+    g_autofree char *address = NULL;
+    g_autofree char *escaped_title = NULL;
+    g_autofree char *markup = NULL;
+
+    address = ephy_search_engine_manager_build_search_address (manager, engine, str);
+    escaped_title = g_markup_escape_text (str, -1);
+    markup = dzl_fuzzy_highlight (escaped_title, str, FALSE);
+    suggestion = ephy_suggestion_new (markup, engine, address);
+
+    g_sequence_append (data->google_suggestions, g_steal_pointer (&suggestion));
+    added++;
+  }
+
+out:
+  query_collection_done (self, g_steal_pointer (&task));
+}
+
+static void
+google_search_suggestions_query (EphySuggestionModel *self,
+                                 const gchar         *query,
+                                 GTask               *task)
+{
+  SoupMessage *msg;
+  g_auto (GStrv) split = g_strsplit (query, " ", -1);
+  g_autofree char *url = NULL;
+  g_autofree char *escaped_query = NULL;
+
+  if (g_strv_length (split) < 2) {
+    query_collection_done (self, g_steal_pointer (&task));
+    return;
+  }
+
+  escaped_query = g_markup_escape_text (query, -1);
+  url = g_strdup_printf ("http://suggestqueries.google.com/complete/search?client=firefox&q=%s";, 
escaped_query);
+  msg = soup_message_new (SOUP_METHOD_GET, url);
+
+  soup_session_queue_message (self->session, g_steal_pointer (&msg), google_search_suggestions_cb, 
g_steal_pointer (&task));
+}
+
 void
 ephy_suggestion_model_query_async (EphySuggestionModel *self,
                                    const gchar         *query,
@@ -601,13 +712,22 @@ ephy_suggestion_model_query_async (EphySuggestionModel *self,
   for (guint i = 0; strings[i]; i++)
     qlist = g_list_append (qlist, g_strdup (strings[i]));
 
+  /* History */
+  data->active_sources = 1;
+
+  /* Google Search Suggestions */
+  if (g_settings_get_boolean (EPHY_SETTINGS_MAIN, EPHY_PREFS_USE_GOOGLE_SEARCH_SUGGESTIONS)) {
+    data->active_sources++;
+    google_search_suggestions_query (self, query, g_object_ref (task));
+  }
+
   ephy_history_service_find_urls (self->history_service,
                                   0, 0,
                                   MAX_URL_ENTRIES, 0,
                                   qlist,
                                   EPHY_HISTORY_SORT_MOST_VISITED,
                                   cancellable,
-                                  (EphyHistoryJobCallback)query_completed_cb,
+                                  (EphyHistoryJobCallback)history_query_completed_cb,
                                   task);
 
   g_strfreev (strings);
diff --git a/src/preferences/prefs-privacy-page.c b/src/preferences/prefs-privacy-page.c
index a5fc7d98c..d8caea0e9 100644
--- a/src/preferences/prefs-privacy-page.c
+++ b/src/preferences/prefs-privacy-page.c
@@ -44,6 +44,9 @@ struct _PrefsPrivacyPage {
   GtkWidget *enable_itp_switch;
   GtkWidget *enable_website_data_storage_switch;
 
+  /* Search Suggestions */
+  GtkWidget *enable_google_search_suggestions_switch;
+
   /* Passwords */
   GtkWidget *remember_passwords_switch;
 };
@@ -107,6 +110,15 @@ setup_privacy_page (PrefsPrivacyPage *privacy_page)
                    privacy_page->remember_passwords_switch,
                    "active",
                    G_SETTINGS_BIND_DEFAULT);
+
+  /* ======================================================================== */
+  /* ========================== Search Suggestions ========================== */
+  /* ======================================================================== */
+  g_settings_bind (EPHY_SETTINGS_MAIN,
+                   EPHY_PREFS_USE_GOOGLE_SEARCH_SUGGESTIONS,
+                   privacy_page->enable_google_search_suggestions_switch,
+                   "active",
+                   G_SETTINGS_BIND_DEFAULT);
 }
 
 static void
@@ -139,6 +151,9 @@ prefs_privacy_page_class_init (PrefsPrivacyPageClass *klass)
   gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, enable_itp_switch);
   gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, enable_website_data_storage_switch);
 
+  /* Search Suggestions */
+  gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, 
enable_google_search_suggestions_switch);
+
   /* Passwords */
   gtk_widget_class_bind_template_child (widget_class, PrefsPrivacyPage, remember_passwords_switch);
 
diff --git a/src/resources/gtk/prefs-privacy-page.ui b/src/resources/gtk/prefs-privacy-page.ui
index 8be4e548b..bf61d6b91 100644
--- a/src/resources/gtk/prefs-privacy-page.ui
+++ b/src/resources/gtk/prefs-privacy-page.ui
@@ -60,6 +60,27 @@
         </child>
       </object>
     </child>
+    <child>
+      <object class="HdyPreferencesGroup">
+        <property name="title" translatable="yes">Search Suggestions</property>
+        <property name="visible">True</property>
+        <child>
+          <object class="HdyActionRow">
+            <property name="activatable_widget">enable_google_search_suggestions_switch</property>
+            <property name="subtitle" translatable="yes">Enable search suggestions in the URL 
entry.</property>
+            <property name="title" translatable="yes">_Google Search Suggestions</property>
+            <property name="use_underline">True</property>
+            <property name="visible">True</property>
+            <child>
+              <object class="GtkSwitch" id="enable_google_search_suggestions_switch">
+                <property name="valign">center</property>
+                <property name="visible">True</property>
+              </object>
+            </child>
+          </object>
+        </child>
+      </object>
+    </child>
     <child>
       <object class="HdyPreferencesGroup">
         <property name="title" translatable="yes">Personal Data</property>


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