[epiphany] URL entry: Replacing GtkEntry with DzlSuggestionEntry



commit fccdc31d7b02dcb0c8c2f9aca86179694ab071dd
Author: Jan-Michael Brummer <jan brummer tabos org>
Date:   Sat Jun 23 23:51:02 2018 +0200

    URL entry: Replacing GtkEntry with DzlSuggestionEntry
    
    Make use of dazzles suggestion entry widget to make url entry more fancy.

 embed/meson.build                           |   2 +-
 lib/ephy-suggestion.c                       | 194 +++++++++
 lib/ephy-suggestion.h                       |  39 ++
 lib/meson.build                             |   2 +
 lib/widgets/contrib/gd-two-lines-renderer.c | 619 ---------------------------
 lib/widgets/contrib/gd-two-lines-renderer.h |  36 --
 lib/widgets/ephy-location-entry.c           | 375 ++---------------
 lib/widgets/ephy-location-entry.h           |  17 +-
 lib/widgets/meson.build                     |   4 +-
 meson.build                                 |   2 +-
 src/ephy-completion-model.c                 | 625 ----------------------------
 src/ephy-completion-model.h                 |  53 ---
 src/ephy-location-controller.c              | 135 +-----
 src/ephy-suggestion-model.c                 | 521 +++++++++++++++++++++++
 src/ephy-suggestion-model.h                 |  46 ++
 src/ephy-window.c                           |   2 +-
 src/meson.build                             |   2 +-
 src/search-provider/ephy-search-provider.c  | 119 ++----
 subprojects/libdazzle.wrap                  |   2 +-
 tests/ephy-completion-model-test.c          | 101 -----
 tests/ephy-location-entry-test.c            |  21 +-
 tests/meson.build                           |   9 -
 22 files changed, 910 insertions(+), 2016 deletions(-)
---
diff --git a/embed/meson.build b/embed/meson.build
index df5edd6e5..e3a217c34 100644
--- a/embed/meson.build
+++ b/embed/meson.build
@@ -32,7 +32,7 @@ libephyembed_sources = [
 ]
 
 libephyembed_deps = [
-  dazzle_dep,
+  libdazzle_dep,
   ephymisc_dep,
   gio_dep,
   glib_dep,
diff --git a/lib/ephy-suggestion.c b/lib/ephy-suggestion.c
new file mode 100644
index 000000000..9c7beec36
--- /dev/null
+++ b/lib/ephy-suggestion.c
@@ -0,0 +1,194 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Igalia S.L.
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-suggestion.h"
+
+#include "ephy-uri-helpers.h"
+
+#include <dazzle.h>
+#include <glib.h>
+
+struct _EphySuggestion {
+  DzlSuggestion parent;
+
+  char *unescaped_title;
+  cairo_surface_t *favicon;
+};
+
+G_DEFINE_TYPE (EphySuggestion, ephy_suggestion, DZL_TYPE_SUGGESTION)
+
+enum {
+  PROP_0,
+  PROP_UNESCAPED_TITLE,
+  LAST_PROP
+};
+
+static GParamSpec *obj_properties[LAST_PROP];
+
+static void
+ephy_suggestion_set_property (GObject      *object,
+                              guint         prop_id,
+                              const GValue *value,
+                              GParamSpec   *pspec)
+{
+  EphySuggestion *self = EPHY_SUGGESTION (object);
+
+  switch (prop_id) {
+    case PROP_UNESCAPED_TITLE:
+      self->unescaped_title = g_strdup (g_value_get_string (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_suggestion_get_property (GObject    *object,
+                              guint       prop_id,
+                              GValue     *value,
+                              GParamSpec *pspec)
+{
+  EphySuggestion *self = EPHY_SUGGESTION (object);
+
+  switch (prop_id) {
+    case PROP_UNESCAPED_TITLE:
+      g_value_set_string (value, self->unescaped_title);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+char *
+ephy_suggestion_replace_typed_text (DzlSuggestion *self,
+                                    const char    *typed_text)
+{
+  const char *url;
+
+  g_assert (EPHY_IS_SUGGESTION (self));
+
+  url = ephy_suggestion_get_uri (EPHY_SUGGESTION (self));
+
+  return g_strdup (url);
+}
+
+cairo_surface_t *
+ephy_suggestion_get_icon_surface (DzlSuggestion *self,
+                                  GtkWidget     *widget)
+{
+  EphySuggestion *suggestion = EPHY_SUGGESTION (self);
+
+  return suggestion->favicon;
+}
+
+
+static void
+ephy_suggestion_class_init (EphySuggestionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  DzlSuggestionClass *dzl_suggestion_class = DZL_SUGGESTION_CLASS (klass);
+
+  object_class->get_property = ephy_suggestion_get_property;
+  object_class->set_property = ephy_suggestion_set_property;
+
+  dzl_suggestion_class->replace_typed_text = ephy_suggestion_replace_typed_text;
+  dzl_suggestion_class->get_icon_surface = ephy_suggestion_get_icon_surface;
+
+  obj_properties[PROP_UNESCAPED_TITLE] =
+    g_param_spec_string ("unescaped-title",
+                         "Unescaped title",
+                         "The title of the suggestion, not XML-escaped",
+                         "",
+                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+}
+
+static void
+ephy_suggestion_init (EphySuggestion *self)
+{
+}
+
+EphySuggestion *
+ephy_suggestion_new (const char *title,
+                     const char *uri)
+{
+  EphySuggestion *suggestion;
+  char *escaped_title = g_markup_escape_text (title, -1);
+  char *decoded_uri = ephy_uri_decode (uri);
+  char *escaped_uri = g_markup_escape_text (decoded_uri, -1);
+
+  suggestion = g_object_new (EPHY_TYPE_SUGGESTION,
+                             "icon-name", "web-browser-symbolic",
+                             "id", uri,
+                             "subtitle", escaped_uri,
+                             "title", escaped_title,
+                             "unescaped-title", title,
+                             NULL);
+
+  g_free (escaped_title);
+  g_free (decoded_uri);
+  g_free (escaped_uri);
+
+  return suggestion;
+}
+
+EphySuggestion *
+ephy_suggestion_new_without_subtitle (const char *title,
+                                      const char *uri)
+{
+  EphySuggestion *suggestion;
+  char *escaped_title;
+
+  escaped_title = g_markup_escape_text (title, -1);
+  suggestion = g_object_new (EPHY_TYPE_SUGGESTION,
+                             "icon-name", "web-browser-symbolic",
+                             "id", uri,
+                             "title", escaped_title,
+                             "unescaped-title", title,
+                             NULL);
+
+  g_free (escaped_title);
+
+  return suggestion;
+}
+
+const char *
+ephy_suggestion_get_unescaped_title (EphySuggestion *self)
+{
+  g_assert (EPHY_IS_SUGGESTION (self));
+
+  return self->unescaped_title;
+}
+
+const char *
+ephy_suggestion_get_uri (EphySuggestion *self)
+{
+  g_assert (EPHY_IS_SUGGESTION (self));
+
+  return dzl_suggestion_get_id (DZL_SUGGESTION (self));
+}
+
+void
+ephy_suggestion_set_favicon (EphySuggestion  *self,
+                             cairo_surface_t *favicon)
+{
+  self->favicon = favicon;
+  g_object_notify (G_OBJECT (self), "icon");
+}
diff --git a/lib/ephy-suggestion.h b/lib/ephy-suggestion.h
new file mode 100644
index 000000000..ca461120b
--- /dev/null
+++ b/lib/ephy-suggestion.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Igalia S.L.
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <dazzle.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_SUGGESTION (ephy_suggestion_get_type())
+
+G_DECLARE_FINAL_TYPE (EphySuggestion, ephy_suggestion, EPHY, SUGGESTION, DzlSuggestion)
+
+EphySuggestion *ephy_suggestion_new                  (const char *title,
+                                                      const char *uri);
+EphySuggestion *ephy_suggestion_new_without_subtitle (const char *title,
+                                                      const char *uri);
+const char     *ephy_suggestion_get_unescaped_title  (EphySuggestion *self);
+const char     *ephy_suggestion_get_uri              (EphySuggestion *self);
+
+void            ephy_suggestion_set_favicon          (EphySuggestion  *self,
+                                                      cairo_surface_t *favicon);
+
+G_END_DECLS
diff --git a/lib/meson.build b/lib/meson.build
index 5b2affa49..8196d1f68 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -34,6 +34,7 @@ libephymisc_sources = [
   'ephy-sqlite-connection.c',
   'ephy-sqlite-statement.c',
   'ephy-string.c',
+  'ephy-suggestion.c',
   'ephy-sync-utils.c',
   'ephy-time-helpers.c',
   'ephy-uri-helpers.c',
@@ -61,6 +62,7 @@ libephymisc_deps = [
   gtk_dep,
   icu_uc_dep,
   json_glib_dep,
+  libdazzle_dep,
   libsecret_dep,
   libsoup_dep,
   libxml_dep,
diff --git a/lib/widgets/ephy-location-entry.c b/lib/widgets/ephy-location-entry.c
index b86ff1524..5e6c7de4e 100644
--- a/lib/widgets/ephy-location-entry.c
+++ b/lib/widgets/ephy-location-entry.c
@@ -31,10 +31,11 @@
 #include "ephy-gui.h"
 #include "ephy-lib-type-builtins.h"
 #include "ephy-signal-accumulator.h"
+#include "ephy-suggestion.h"
 #include "ephy-title-widget.h"
 #include "ephy-uri-helpers.h"
-#include "gd-two-lines-renderer.h"
 
+#include <dazzle.h>
 #include <gdk/gdkkeysyms.h>
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
@@ -103,12 +104,6 @@ struct _EphyLocationEntry {
 
 static gboolean ephy_location_entry_reset_internal (EphyLocationEntry *, gboolean);
 
-static void extracell_data_func (GtkCellLayout   *cell_layout,
-                                 GtkCellRenderer *cell,
-                                 GtkTreeModel    *tree_model,
-                                 GtkTreeIter     *iter,
-                                 gpointer         data);
-
 enum {
   PROP_0,
   PROP_ADDRESS,
@@ -131,6 +126,12 @@ G_DEFINE_TYPE_WITH_CODE (EphyLocationEntry, ephy_location_entry, GTK_TYPE_OVERLA
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_TITLE_WIDGET,
                                                 ephy_location_entry_title_widget_interface_init))
 
+static void
+ephy_location_entry_activate (EphyLocationEntry *entry)
+{
+  g_signal_emit_by_name (entry->url_entry, "activate");
+}
+
 static void
 update_address_state (EphyLocationEntry *entry)
 {
@@ -523,99 +524,14 @@ entry_key_press_cb (GtkEntry          *entry,
     ephy_location_entry_activate (location_entry);
   }
 
-  return FALSE;
-}
-
-static gboolean
-entry_key_press_after_cb (GtkEntry          *entry,
-                          GdkEventKey       *event,
-                          EphyLocationEntry *lentry)
-{
-  guint state = event->state & gtk_accelerator_get_default_mod_mask ();
-
-  if ((event->keyval == GDK_KEY_Return ||
-       event->keyval == GDK_KEY_KP_Enter ||
-       event->keyval == GDK_KEY_ISO_Enter) &&
-      (state == GDK_CONTROL_MASK ||
-       state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) {
-    /* gtk_im_context_reset (entry->im_context); */
-
-    lentry->needs_reset = TRUE;
-    g_signal_emit_by_name (entry, "activate");
-
-    return TRUE;
-  }
-
-  if ((event->keyval == GDK_KEY_Down || event->keyval == GDK_KEY_KP_Down)
-      && state == 0) {
-    /* If we are focusing the entry, with the cursor at the end of it
-     * we emit the changed signal, so that the completion popup appears */
-    const char *string;
-
-    string = gtk_entry_get_text (entry);
-    if (gtk_editable_get_position (GTK_EDITABLE (entry)) == (int)strlen (string)) {
-      g_signal_emit_by_name (entry, "changed", 0);
-      return TRUE;
-    }
-  }
+  if (event->keyval == GDK_KEY_Return ||
+      event->keyval == GDK_KEY_KP_Enter ||
+      event->keyval == GDK_KEY_ISO_Enter)
+    ephy_location_entry_activate (location_entry);
 
   return FALSE;
 }
 
-static void
-entry_activate_after_cb (GtkEntry          *entry,
-                         EphyLocationEntry *lentry)
-{
-  lentry->user_changed = FALSE;
-
-  if (lentry->needs_reset) {
-    ephy_location_entry_reset_internal (lentry, TRUE);
-    lentry->needs_reset = FALSE;
-  }
-}
-
-static gboolean
-match_selected_cb (GtkEntryCompletion *completion,
-                   GtkTreeModel       *model,
-                   GtkTreeIter        *iter,
-                   EphyLocationEntry  *entry)
-{
-  char *item = NULL;
-  guint state;
-
-  gtk_tree_model_get (model, iter,
-                      entry->action_col, &item, -1);
-  if (item == NULL) return FALSE;
-
-  ephy_gui_get_current_event (NULL, &state, NULL);
-
-  entry->needs_reset = (state == GDK_CONTROL_MASK ||
-                        state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
-
-  ephy_title_widget_set_address (EPHY_TITLE_WIDGET (entry), item);
-  /* gtk_im_context_reset (GTK_ENTRY (entry)->im_context); */
-  g_signal_emit_by_name (entry->url_entry, "activate");
-
-  g_free (item);
-
-  return TRUE;
-}
-
-static void
-action_activated_after_cb (GtkEntryCompletion *completion,
-                           gint                index,
-                           EphyLocationEntry  *lentry)
-{
-  guint state, button;
-
-  ephy_gui_get_current_event (NULL, &state, &button);
-  if ((state == GDK_CONTROL_MASK ||
-       state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) ||
-      button == 2) {
-    ephy_location_entry_reset_internal (lentry, TRUE);
-  }
-}
-
 static void
 entry_clear_activate_cb (GtkMenuItem       *item,
                          EphyLocationEntry *entry)
@@ -633,7 +549,7 @@ paste_received (GtkClipboard      *clipboard,
 {
   if (text) {
     gtk_entry_set_text (GTK_ENTRY (entry->url_entry), text);
-    g_signal_emit_by_name (entry->url_entry, "activate");
+    ephy_location_entry_activate (entry);
   }
 }
 
@@ -814,6 +730,19 @@ button_box_size_allocated_cb (GtkWidget    *widget,
   g_free (css);
 }
 
+static void
+ephy_location_entry_suggestion_activated (DzlSuggestionEntry *entry,
+                                          DzlSuggestion      *arg1,
+                                          gpointer            user_data)
+{
+  EphyLocationEntry *lentry = EPHY_LOCATION_ENTRY (user_data);
+  DzlSuggestion *suggestion = dzl_suggestion_entry_get_suggestion (entry);
+  gtk_entry_set_text (GTK_ENTRY (entry), ephy_suggestion_get_uri (EPHY_SUGGESTION (suggestion)));
+
+  /* Now trigger the load.... */
+  ephy_location_entry_activate (EPHY_LOCATION_ENTRY (lentry));
+}
+
 static void
 ephy_location_entry_construct_contents (EphyLocationEntry *entry)
 {
@@ -823,18 +752,18 @@ ephy_location_entry_construct_contents (EphyLocationEntry *entry)
   LOG ("EphyLocationEntry constructing contents %p", entry);
 
   /* URL entry */
-  entry->url_entry = gtk_entry_new ();
+  entry->url_entry = dzl_suggestion_entry_new ();
 
   /* Add special widget css provider */
   context = gtk_widget_get_style_context (GTK_WIDGET (entry->url_entry));
   entry->css_provider = gtk_css_provider_new ();
   gtk_style_context_add_provider (context, GTK_STYLE_PROVIDER (entry->css_provider), 
GTK_STYLE_PROVIDER_PRIORITY_USER);
 
-  gtk_style_context_add_class (gtk_widget_get_style_context (entry->url_entry), "url_entry");
+  gtk_style_context_add_class (gtk_widget_get_style_context (GTK_WIDGET (entry->url_entry)), "url_entry");
   g_signal_connect (G_OBJECT (entry->url_entry), "copy-clipboard", G_CALLBACK 
(ephy_location_entry_copy_clipboard), NULL);
   g_signal_connect (G_OBJECT (entry->url_entry), "cut-clipboard", G_CALLBACK 
(ephy_location_entry_cut_clipboard), NULL);
-  gtk_widget_show (entry->url_entry);
-  gtk_overlay_add_overlay (GTK_OVERLAY (entry), entry->url_entry);
+  gtk_widget_show (GTK_WIDGET (entry->url_entry));
+  gtk_overlay_add_overlay (GTK_OVERLAY (entry), GTK_WIDGET (entry->url_entry));
 
   /* Button Box */
   button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
@@ -874,10 +803,8 @@ ephy_location_entry_construct_contents (EphyLocationEntry *entry)
                     "signal::changed", G_CALLBACK (editable_changed_cb), entry,
                     NULL);
 
-  g_signal_connect_after (entry->url_entry, "key-press-event",
-                          G_CALLBACK (entry_key_press_after_cb), entry);
-  g_signal_connect_after (entry->url_entry, "activate",
-                          G_CALLBACK (entry_activate_after_cb), entry);
+  g_signal_connect (entry->url_entry, "suggestion-activated",
+                          G_CALLBACK (ephy_location_entry_suggestion_activated), entry);
 }
 
 static void
@@ -954,238 +881,6 @@ schedule_dns_prefetch (EphyLocationEntry *entry, guint interval, const gchar *ur
 }
 #endif
 
-static gboolean
-cursor_on_match_cb (GtkEntryCompletion *completion,
-                    GtkTreeModel       *model,
-                    GtkTreeIter        *iter,
-                    EphyLocationEntry  *le)
-{
-  char *url = NULL;
-  GtkWidget *entry;
-
-  gtk_tree_model_get (model, iter,
-                      le->url_col,
-                      &url, -1);
-  entry = gtk_entry_completion_get_entry (completion);
-
-  /* Prevent the update so we keep the highlight from our input.
-   * See textcell_data_func().
-   */
-  le->block_update = TRUE;
-  gtk_entry_set_text (GTK_ENTRY (entry), url);
-  gtk_editable_set_position (GTK_EDITABLE (entry), -1);
-  le->block_update = FALSE;
-
-#if 0
-/* FIXME: Refactor the DNS prefetch, this is a layering violation */
-  schedule_dns_prefetch (le, 250, (const gchar *)url);
-#endif
-
-  g_free (url);
-
-  return TRUE;
-}
-
-static void
-extracell_data_func (GtkCellLayout   *cell_layout,
-                     GtkCellRenderer *cell,
-                     GtkTreeModel    *tree_model,
-                     GtkTreeIter     *iter,
-                     gpointer         data)
-{
-  EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (data);
-  gboolean is_bookmark = FALSE;
-  GValue visible = { 0, };
-
-  gtk_tree_model_get (tree_model, iter,
-                      entry->extra_col, &is_bookmark,
-                      -1);
-
-  if (is_bookmark)
-    g_object_set (cell,
-                  "icon-name", "starred-symbolic",
-                  NULL);
-
-  g_value_init (&visible, G_TYPE_BOOLEAN);
-  g_value_set_boolean (&visible, is_bookmark);
-  g_object_set_property (G_OBJECT (cell), "visible", &visible);
-  g_value_unset (&visible);
-}
-
-/**
- * ephy_location_entry_set_match_func:
- * @entry: an #EphyLocationEntry widget
- * @match_func: a #GtkEntryCompletionMatchFunc
- * @user_data: user_data to pass to the @match_func
- * @notify: a #GDestroyNotify, like the one given to
- * gtk_entry_completion_set_match_func
- *
- * Sets the match_func for the internal #GtkEntryCompletion to @match_func.
- *
- **/
-void
-ephy_location_entry_set_match_func (EphyLocationEntry          *entry,
-                                    GtkEntryCompletionMatchFunc match_func,
-                                    gpointer                    user_data,
-                                    GDestroyNotify              notify)
-{
-  GtkEntryCompletion *completion;
-
-  completion = gtk_entry_get_completion (GTK_ENTRY (entry->url_entry));
-  gtk_entry_completion_set_match_func (completion, match_func, user_data, notify);
-}
-
-/**
- * ephy_location_entry_set_completion:
- * @entry: an #EphyLocationEntry widget
- * @model: the #GtkModel for the completion
- * @text_col: column id to access #GtkModel relevant data
- * @action_col: column id to access #GtkModel relevant data
- * @keywords_col: column id to access #GtkModel relevant data
- * @relevance_col: column id to access #GtkModel relevant data
- * @url_col: column id to access #GtkModel relevant data
- * @extra_col: column id to access #GtkModel relevant data
- * @favicon_col: column id to access #GtkModel relevant data
- *
- * Initializes @entry to have a #GtkEntryCompletion using @model as the
- * internal #GtkModel. The *_col arguments are for internal data retrieval from
- * @model, like when setting the text property of one of the #GtkCellRenderer
- * of the completion.
- *
- **/
-void
-ephy_location_entry_set_completion (EphyLocationEntry *entry,
-                                    GtkTreeModel      *model,
-                                    guint              text_col,
-                                    guint              action_col,
-                                    guint              keywords_col,
-                                    guint              relevance_col,
-                                    guint              url_col,
-                                    guint              extra_col,
-                                    guint              favicon_col)
-{
-  GtkEntryCompletion *completion;
-  GtkCellRenderer *cell;
-
-  entry->text_col = text_col;
-  entry->action_col = action_col;
-  entry->keywords_col = keywords_col;
-  entry->relevance_col = relevance_col;
-  entry->url_col = url_col;
-  entry->extra_col = extra_col;
-  entry->favicon_col = favicon_col;
-
-  completion = gtk_entry_completion_new ();
-  gtk_entry_completion_set_model (completion, model);
-  g_signal_connect (completion, "match-selected",
-                    G_CALLBACK (match_selected_cb), entry);
-  g_signal_connect_after (completion, "action-activated",
-                          G_CALLBACK (action_activated_after_cb), entry);
-
-  cell = gtk_cell_renderer_pixbuf_new ();
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
-                              cell, FALSE);
-  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
-                                 cell, "pixbuf", favicon_col);
-
-  /* Pixel-perfect aligment with the location entry favicon
-   * (16x16). Consider that this /might/ depend on the theme.
-   *
-   * The GtkEntryCompletion can not be themed so we work-around
-   * that with padding and fixed sizes.
-   * For the first cell, this is:
-   *
-   * ___+++++iiiiiiiiiiiiiiii++__ttt...bbb++++++__
-   *
-   * _ = widget spacing, can not be handled (3 px)
-   * + = padding (5 px) (ICON_PADDING_LEFT)
-   * i = the icon (16 px) (ICON_CONTENT_WIDTH)
-   * + = padding (2 px) (ICON_PADDING_RIGHT) (cut by the fixed_size)
-   * _ = spacing between cells, can not be handled (2 px)
-   * t = the text (expands)
-   * b = bookmark icon (16 px)
-   * + = padding (6 px) (BKMK_PADDING_RIGHT)
-   * _ = widget spacing, can not be handled (2 px)
-   *
-   * Each character is a pixel.
-   *
-   * The text cell and the bookmark icon cell are much more
-   * flexible in its aligment, because they do not have to align
-   * with anything in the entry.
-   */
-
-#define ROW_PADDING_VERT 4
-
-#define ICON_PADDING_LEFT 5
-#define ICON_CONTENT_WIDTH 16
-#define ICON_PADDING_RIGHT 9
-
-#define ICON_CONTENT_HEIGHT 16
-
-#define TEXT_PADDING_LEFT 0
-
-#define BKMK_PADDING_RIGHT 6
-
-  gtk_cell_renderer_set_padding
-    (cell, ICON_PADDING_LEFT, ROW_PADDING_VERT);
-  gtk_cell_renderer_set_fixed_size
-    (cell,
-    (ICON_PADDING_LEFT + ICON_CONTENT_WIDTH + ICON_PADDING_RIGHT),
-    ICON_CONTENT_HEIGHT);
-  gtk_cell_renderer_set_alignment (cell, 0.0, 0.5);
-
-  cell = gd_two_lines_renderer_new ();
-  g_object_set (cell,
-                "ellipsize", PANGO_ELLIPSIZE_END,
-                "text-lines", 2,
-                NULL);
-  gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
-                              cell, TRUE);
-  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
-                                 cell, "text", text_col);
-  gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
-                                 cell, "line-two", url_col);
-
-  /* Pixel-perfect aligment with the text in the location entry.
-   * See above.
-   */
-  gtk_cell_renderer_set_padding
-    (cell, TEXT_PADDING_LEFT, ROW_PADDING_VERT);
-  gtk_cell_renderer_set_alignment (cell, 0.0, 0.5);
-
-  /*
-   * As the width of the entry completion is known in advance
-   * (as big as the entry you are completing on), we can set
-   * any fixed width (the 1 is just this random number here)
-   * Since the height is known too, we avoid computing the actual
-   * sizes of the cells, which takes a lot of CPU time and does
-   * not get used anyway.
-   */
-  gtk_cell_renderer_set_fixed_size (cell, 1, -1);
-  gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (cell), 2);
-
-  cell = gtk_cell_renderer_pixbuf_new ();
-  g_object_set (cell, "follow-state", TRUE, NULL);
-  gtk_cell_layout_pack_end (GTK_CELL_LAYOUT (completion),
-                            cell, FALSE);
-  gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (completion),
-                                      cell, extracell_data_func,
-                                      entry,
-                                      NULL);
-
-  /* Pixel-perfect aligment. This just keeps the same margin from
-   * the border than the favicon on the other side. See above. */
-  gtk_cell_renderer_set_padding
-    (cell, BKMK_PADDING_RIGHT, ROW_PADDING_VERT);
-
-  g_object_set (completion, "inline-selection", TRUE, NULL);
-  g_signal_connect (completion, "cursor-on-match",
-                    G_CALLBACK (cursor_on_match_cb), entry);
-
-  gtk_entry_set_completion (GTK_ENTRY (entry->url_entry), completion);
-  g_object_unref (completion);
-}
-
 /**
  * ephy_location_entry_get_can_undo:
  * @entry: an #EphyLocationEntry widget
@@ -1282,7 +977,7 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
 }
 
 /**
- * ephy_location_entry_activate:
+ * ephy_location_entry_focus:
  * @entry: an #EphyLocationEntry widget
  *
  * Set focus on @entry and select the text whithin. This is called when the
@@ -1290,7 +985,7 @@ ephy_location_entry_reset (EphyLocationEntry *entry)
  *
  **/
 void
-ephy_location_entry_activate (EphyLocationEntry *entry)
+ephy_location_entry_focus (EphyLocationEntry *entry)
 {
   GtkWidget *toplevel, *widget = GTK_WIDGET (entry->url_entry);
 
@@ -1391,7 +1086,7 @@ ephy_location_entry_get_search_terms (EphyLocationEntry *entry)
 GtkWidget *
 ephy_location_entry_get_entry (EphyLocationEntry *entry)
 {
-  return entry->url_entry;
+  return GTK_WIDGET (entry->url_entry);
 }
 
 GtkWidget *
diff --git a/lib/widgets/ephy-location-entry.h b/lib/widgets/ephy-location-entry.h
index 7056df801..65960ebea 100644
--- a/lib/widgets/ephy-location-entry.h
+++ b/lib/widgets/ephy-location-entry.h
@@ -41,21 +41,6 @@ typedef enum {
 
 GtkWidget      *ephy_location_entry_new                        (void);
 
-void            ephy_location_entry_set_completion             (EphyLocationEntry *entry,
-                                                                GtkTreeModel      *model,
-                                                                guint              text_col,
-                                                                guint              action_col,
-                                                                guint              keywords_col,
-                                                                guint              relevance_col,
-                                                                guint              url_col,
-                                                                guint              extra_col,
-                                                                guint              favicon_col);
-
-void            ephy_location_entry_set_match_func             (EphyLocationEntry           *entry,
-                                                                GtkEntryCompletionMatchFunc  match_func,
-                                                                gpointer                     user_data,
-                                                                GDestroyNotify               notify);
-
 gboolean        ephy_location_entry_get_can_undo               (EphyLocationEntry *entry);
 
 gboolean        ephy_location_entry_get_can_redo               (EphyLocationEntry *entry);
@@ -66,7 +51,7 @@ gboolean        ephy_location_entry_reset                      (EphyLocationEntr
 
 void            ephy_location_entry_undo_reset                 (EphyLocationEntry *entry);
 
-void            ephy_location_entry_activate                   (EphyLocationEntry *entry);
+void            ephy_location_entry_focus                      (EphyLocationEntry *entry);
 
 void            ephy_location_entry_set_bookmark_icon_state    (EphyLocationEntry                  *entry,
                                                                 EphyLocationEntryBookmarkIconState  state);
diff --git a/lib/widgets/meson.build b/lib/widgets/meson.build
index afdb75293..cea7788a7 100644
--- a/lib/widgets/meson.build
+++ b/lib/widgets/meson.build
@@ -7,7 +7,6 @@ enums = gnome.mkenums_simple('ephy-widgets-type-builtins',
 )
 
 libephywidgets_sources = [
-  'contrib/gd-two-lines-renderer.c',
   'contrib/nautilus-floating-bar.c',
   'ephy-certificate-dialog.c',
   'ephy-downloads-popover.c',
@@ -23,7 +22,7 @@ libephywidgets_sources = [
 ]
 
 libephywidgets_deps = [
-  dazzle_dep,
+  libdazzle_dep,
   ephymisc_dep,
   gcr_dep,
   gdk_dep,
@@ -31,6 +30,7 @@ libephywidgets_deps = [
   gio_dep,
   glib_dep,
   gtk_dep,
+  libdazzle_dep,
   libsoup_dep,
   webkit2gtk_dep,
 ]
diff --git a/meson.build b/meson.build
index 8ba6782dd..7e1ed02a3 100644
--- a/meson.build
+++ b/meson.build
@@ -71,7 +71,6 @@ nettle_requirement = '>= 3.2'
 webkitgtk_requirement = '>= 2.21.92'
 
 cairo_dep = dependency('cairo', version: '>= 1.2')
-dazzle_dep = dependency('libdazzle-1.0', version: '>= 3.29.91', fallback : ['libdazzle', 'libdazzle_dep'])
 gcr_dep = dependency('gcr-3', version: '>= 3.5.5')
 gdk_dep = dependency('gdk-3.0', version: gtk_requirement)
 gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0', version: '>= 2.36.5')
@@ -84,6 +83,7 @@ hogweed_dep = dependency('hogweed', version: nettle_requirement)
 icu_uc_dep = dependency('icu-uc', version: '>= 4.6')
 iso_codes_dep = dependency('iso-codes', version: '>= 0.35')
 json_glib_dep = dependency('json-glib-1.0', version: '>= 1.2.4')
+libdazzle_dep = dependency('libdazzle-1.0', version: '>= 3.29.4', fallback : ['libdazzle', 'libdazzle_dep'])
 libnotify_dep = dependency('libnotify', version: '>= 0.5.1')
 libsecret_dep = dependency('libsecret-1', version: '>= 0.14')
 libsoup_dep = dependency('libsoup-2.4', version: '>= 2.48.0')
diff --git a/src/ephy-location-controller.c b/src/ephy-location-controller.c
index cb9440097..a3d4cb18f 100644
--- a/src/ephy-location-controller.c
+++ b/src/ephy-location-controller.c
@@ -22,8 +22,6 @@
 #include "config.h"
 #include "ephy-location-controller.h"
 
-
-#include "ephy-completion-model.h"
 #include "ephy-debug.h"
 #include "ephy-dnd.h"
 #include "ephy-embed-container.h"
@@ -31,10 +29,12 @@
 #include "ephy-link.h"
 #include "ephy-location-entry.h"
 #include "ephy-shell.h"
+#include "ephy-suggestion-model.h"
 #include "ephy-title-widget.h"
 #include "ephy-uri-helpers.h"
 #include "ephy-widgets-type-builtins.h"
 
+#include <dazzle.h>
 #include <gdk/gdkkeysyms.h>
 #include <gtk/gtk.h>
 #include <string.h>
@@ -80,16 +80,6 @@ G_DEFINE_TYPE_WITH_CODE (EphyLocationController, ephy_location_controller, G_TYP
                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_LINK,
                                                 NULL))
 
-static gboolean
-match_func (GtkEntryCompletion *completion,
-            const char         *key,
-            GtkTreeIter        *iter,
-            gpointer            data)
-{
-  /* We want every row in the model to show up. */
-  return TRUE;
-}
-
 static void
 entry_drag_data_received_cb (GtkWidget *widget,
                              GdkDragContext *context,
@@ -182,36 +172,22 @@ entry_activate_cb (GtkEntry               *entry,
   g_free (effective_address);
 }
 
-static void
-update_done_cb (EphyHistoryService *service,
-                gboolean            success,
-                gpointer            result_data,
-                gpointer            user_data)
-{
-  /* FIXME: this hack is needed for the completion entry popup
-   * to resize smoothly. See:
-   * https://bugzilla.gnome.org/show_bug.cgi?id=671074 */
-  gtk_entry_completion_complete (GTK_ENTRY_COMPLETION (user_data));
-}
 
 static void
 user_changed_cb (GtkWidget *widget, EphyLocationController *controller)
 {
   const char *address;
-  GtkTreeModel *model;
-  GtkEntryCompletion *completion;
   GtkWidget *entry;
+  GListModel *model;
 
   address = ephy_title_widget_get_address (EPHY_TITLE_WIDGET (widget));
 
   LOG ("user_changed_cb, address %s", address);
 
   entry = ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY (widget));
-  completion = gtk_entry_get_completion (GTK_ENTRY (entry));
-  model = gtk_entry_completion_get_model (completion);
+  model = dzl_suggestion_entry_get_model (DZL_SUGGESTION_ENTRY (entry));
 
-  ephy_completion_model_update_for_string (EPHY_COMPLETION_MODEL (model), address,
-                                           update_done_cb, completion);
+  ephy_suggestion_model_query_async (EPHY_SUGGESTION_MODEL (model), address, NULL, NULL, NULL);
 }
 
 static void
@@ -294,83 +270,6 @@ switch_page_cb (GtkNotebook            *notebook,
   }
 }
 
-static void
-action_activated_cb (GtkEntryCompletion     *completion,
-                     int                     index,
-                     EphyLocationController *controller)
-{
-  GtkWidget *entry;
-  char *url = NULL;
-  char *content;
-  char **engine_names;
-
-  entry = gtk_entry_completion_get_entry (completion);
-  content = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
-  if (content == NULL)
-    return;
-
-  engine_names = ephy_search_engine_manager_get_names (controller->search_engine_manager);
-  if (index < g_strv_length (engine_names)) {
-    url = ephy_search_engine_manager_build_search_address (controller->search_engine_manager,
-                                                           engine_names[index],
-                                                           content);
-    ephy_link_open (EPHY_LINK (controller), url, NULL,
-                    ephy_link_flags_from_current_event ());
-  }
-
-  g_strfreev (engine_names);
-  g_free (content);
-  g_free (url);
-}
-
-static void
-fill_entry_completion_with_actions (GtkEntryCompletion     *completion,
-                                    EphyLocationController *controller)
-{
-  char **engine_names;
-
-  engine_names = ephy_search_engine_manager_get_names (controller->search_engine_manager);
-
-  controller->num_search_engines_actions = 0;
-
-  for (guint i = 0; engine_names[i] != NULL; i++) {
-    gtk_entry_completion_insert_action_text (completion, i, engine_names[i]);
-    controller->num_search_engines_actions++;
-  }
-
-  g_strfreev (engine_names);
-}
-
-static void
-add_completion_actions (EphyLocationController *controller,
-                        EphyLocationEntry      *lentry)
-{
-  GtkWidget *entry = ephy_location_entry_get_entry (lentry);
-  GtkEntryCompletion *completion = gtk_entry_get_completion (GTK_ENTRY (entry));
-
-  fill_entry_completion_with_actions (completion, controller);
-  g_signal_connect (completion, "action_activated",
-                    G_CALLBACK (action_activated_cb), controller);
-}
-
-static void
-search_engines_changed_cb (EphySearchEngineManager *manager,
-                           gpointer                 data)
-{
-  EphyLocationController *controller;
-  GtkEntryCompletion *completion;
-  GtkWidget *entry;
-
-  controller = EPHY_LOCATION_CONTROLLER (data);
-  entry = ephy_location_entry_get_entry (EPHY_LOCATION_ENTRY (controller->title_widget));
-  completion = gtk_entry_get_completion (GTK_ENTRY (entry));
-
-  for (guint i = 0; i < controller->num_search_engines_actions; i++)
-    gtk_entry_completion_delete_action (completion, 0);
-
-  fill_entry_completion_with_actions (completion, controller);
-}
-
 static void
 longpress_gesture_cb (GtkGestureLongPress *gesture,
                       gdouble              x,
@@ -408,7 +307,7 @@ ephy_location_controller_constructed (GObject *object)
   EphyLocationController *controller = EPHY_LOCATION_CONTROLLER (object);
   EphyHistoryService *history_service;
   EphyBookmarksManager *bookmarks_manager;
-  EphyCompletionModel *model;
+  EphySuggestionModel *model;
   GtkWidget *notebook, *widget, *reader_mode, *entry;
 
   G_OBJECT_CLASS (ephy_location_controller_parent_class)->constructed (object);
@@ -433,28 +332,10 @@ ephy_location_controller_constructed (GObject *object)
 
   history_service = ephy_embed_shell_get_global_history_service (ephy_embed_shell_get_default ());
   bookmarks_manager = ephy_shell_get_bookmarks_manager (ephy_shell_get_default ());
-  model = ephy_completion_model_new (history_service, bookmarks_manager);
-  ephy_location_entry_set_completion (EPHY_LOCATION_ENTRY (controller->title_widget),
-                                      GTK_TREE_MODEL (model),
-                                      EPHY_COMPLETION_TEXT_COL,
-                                      EPHY_COMPLETION_ACTION_COL,
-                                      EPHY_COMPLETION_KEYWORDS_COL,
-                                      EPHY_COMPLETION_RELEVANCE_COL,
-                                      EPHY_COMPLETION_URL_COL,
-                                      EPHY_COMPLETION_EXTRA_COL,
-                                      EPHY_COMPLETION_FAVICON_COL);
+  model = ephy_suggestion_model_new (history_service, bookmarks_manager);
+  dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (entry), G_LIST_MODEL (model));
   g_object_unref (model);
 
-  ephy_location_entry_set_match_func (EPHY_LOCATION_ENTRY (controller->title_widget),
-                                      match_func,
-                                      controller->title_widget,
-                                      NULL);
-
-  add_completion_actions (controller, EPHY_LOCATION_ENTRY (controller->title_widget));
-
-  g_signal_connect_object (controller->search_engine_manager, "changed",
-                           G_CALLBACK (search_engines_changed_cb), controller, 0);
-
   reader_mode = ephy_location_entry_get_reader_mode_widget (EPHY_LOCATION_ENTRY (controller->title_widget));
   g_signal_connect (G_OBJECT (reader_mode), "button-press-event", G_CALLBACK 
(reader_mode_button_press_event_cb), controller);
 
diff --git a/src/ephy-suggestion-model.c b/src/ephy-suggestion-model.c
new file mode 100644
index 000000000..f9c82a81b
--- /dev/null
+++ b/src/ephy-suggestion-model.c
@@ -0,0 +1,521 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "ephy-suggestion-model.h"
+
+#include "ephy-embed-shell.h"
+#include "ephy-search-engine-manager.h"
+#include "ephy-suggestion.h"
+
+#include <dazzle.h>
+#include <glib/gi18n.h>
+
+#define MAX_COMPLETION_HISTORY_URLS 8
+
+struct _EphySuggestionModel {
+  GObject               parent;
+  EphyHistoryService   *history_service;
+  EphyBookmarksManager *bookmarks_manager;
+  GSequence            *items;
+  GSList               *search_terms;
+};
+
+enum {
+  PROP_0,
+  PROP_BOOKMARKS_MANAGER,
+  PROP_HISTORY_SERVICE,
+  N_PROPS
+};
+
+static void list_model_iface_init (GListModelInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphySuggestionModel, ephy_suggestion_model, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static GParamSpec *properties[N_PROPS];
+
+static void
+ephy_suggestion_model_finalize (GObject *object)
+{
+  EphySuggestionModel *self = (EphySuggestionModel *)object;
+
+  g_clear_object (&self->bookmarks_manager);
+  g_clear_object (&self->history_service);
+  g_clear_pointer (&self->items, g_sequence_free);
+
+  g_slist_free_full (self->search_terms, (GDestroyNotify)g_regex_unref);
+
+  G_OBJECT_CLASS (ephy_suggestion_model_parent_class)->finalize (object);
+}
+
+static void
+ephy_suggestion_model_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (object);
+
+  switch (prop_id) {
+    case PROP_HISTORY_SERVICE:
+      g_value_set_object (value, self->history_service);
+      break;
+    case PROP_BOOKMARKS_MANAGER:
+      g_value_set_object (value, self->bookmarks_manager);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_suggestion_model_set_property (GObject      *object,
+                                    guint         prop_id,
+                                    const GValue *value,
+                                    GParamSpec   *pspec)
+{
+  EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (object);
+
+  switch (prop_id) {
+    case PROP_HISTORY_SERVICE:
+      self->history_service = g_value_dup_object (value);
+      break;
+    case PROP_BOOKMARKS_MANAGER:
+      self->bookmarks_manager = g_value_dup_object (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  }
+}
+
+static void
+ephy_suggestion_model_class_init (EphySuggestionModelClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ephy_suggestion_model_finalize;
+  object_class->get_property = ephy_suggestion_model_get_property;
+  object_class->set_property = ephy_suggestion_model_set_property;
+
+  properties [PROP_BOOKMARKS_MANAGER] =
+    g_param_spec_object ("bookmarks-manager",
+                         "Bookmarks Manager",
+                         "The bookmarks manager for suggestions",
+                         EPHY_TYPE_BOOKMARKS_MANAGER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_HISTORY_SERVICE] =
+    g_param_spec_object ("history-service",
+                         "History Service",
+                         "The history service for suggestions",
+                         EPHY_TYPE_HISTORY_SERVICE,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ephy_suggestion_model_init (EphySuggestionModel *self)
+{
+  self->items = g_sequence_new (g_object_unref);
+}
+
+static GType
+ephy_suggestion_model_get_item_type (GListModel *model)
+{
+  return EPHY_TYPE_SUGGESTION;
+}
+
+static guint
+ephy_suggestion_model_get_n_items (GListModel *model)
+{
+  EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (model);
+
+  return g_sequence_get_length (self->items);
+}
+
+static gpointer
+ephy_suggestion_model_get_item (GListModel *model,
+                                guint       position)
+{
+  EphySuggestionModel *self = EPHY_SUGGESTION_MODEL (model);
+  GSequenceIter *iter;
+  DzlSuggestion *suggestion;
+
+  iter = g_sequence_get_iter_at_pos (self->items, position);
+  suggestion = g_sequence_get (iter);
+
+  return g_object_ref (suggestion);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+  iface->get_item_type = ephy_suggestion_model_get_item_type;
+  iface->get_item = ephy_suggestion_model_get_item;
+  iface->get_n_items = ephy_suggestion_model_get_n_items;
+}
+
+EphySuggestionModel *
+ephy_suggestion_model_new (EphyHistoryService   *history_service,
+                           EphyBookmarksManager *bookmarks_manager)
+{
+  g_assert (EPHY_IS_HISTORY_SERVICE (history_service));
+  g_assert (EPHY_IS_BOOKMARKS_MANAGER (bookmarks_manager));
+
+  return g_object_new (EPHY_TYPE_SUGGESTION_MODEL,
+                       "history-service", history_service,
+                       "bookmarks-manager", bookmarks_manager,
+                       NULL);
+}
+
+static void
+update_search_terms (EphySuggestionModel *self,
+                     const char          *text)
+{
+  const char *current;
+  const char *ptr;
+  char *tmp;
+  char *term;
+  GRegex *term_regex;
+  GRegex *quote_regex;
+  gint count;
+  gboolean inside_quotes = FALSE;
+
+  g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+
+  if (self->search_terms) {
+    g_slist_free_full (self->search_terms, (GDestroyNotify)g_regex_unref);
+    self->search_terms = NULL;
+  }
+
+  quote_regex = g_regex_new ("\"", G_REGEX_OPTIMIZE,
+                             G_REGEX_MATCH_NOTEMPTY, NULL);
+
+  /*
+   * This code loops through the string using pointer arythmetics.
+   * Although the string we are handling may contain UTF-8 chars
+   * this works because only ASCII chars affect what is actually
+   * copied from the string as a search term.
+   */
+  for (count = 0, current = ptr = text; ptr[0] != '\0'; ptr++, count++) {
+    /*
+     * If we found a double quote character; we will
+     * consume bytes up until the next quote, or
+     * end of line;
+     */
+    if (ptr[0] == '"')
+      inside_quotes = !inside_quotes;
+
+    /*
+     * If we found a space, and we are not looking for a
+     * closing double quote, or if the next char is the
+     * end of the string, append what we have already as
+     * a search term.
+     */
+    if (((ptr[0] == ' ') && (!inside_quotes)) || ptr[1] == '\0') {
+      /*
+       * We special-case the end of the line because
+       * we would otherwise not copy the last character
+       * of the search string, since the for loop will
+       * stop before that.
+       */
+      if (ptr[1] == '\0')
+        count++;
+
+      /*
+       * remove quotes, and quote any regex-sensitive
+       * characters
+       */
+      tmp = g_regex_escape_string (current, count);
+      term = g_regex_replace (quote_regex, tmp, -1, 0,
+                              "", G_REGEX_MATCH_NOTEMPTY, NULL);
+      g_strstrip (term);
+      g_free (tmp);
+
+      /* we don't want empty search terms */
+      if (term[0] != '\0') {
+        term_regex = g_regex_new (term,
+                                  G_REGEX_CASELESS | G_REGEX_OPTIMIZE,
+                                  G_REGEX_MATCH_NOTEMPTY, NULL);
+        self->search_terms = g_slist_append (self->search_terms, term_regex);
+      }
+      g_free (term);
+
+      /* count will be incremented by the for loop */
+      count = -1;
+      current = ptr + 1;
+    }
+  }
+
+  g_regex_unref (quote_regex);
+}
+
+static gboolean
+should_add_bookmark_to_model (EphySuggestionModel *self,
+                              const char          *search_string,
+                              const char          *title,
+                              const char          *location)
+{
+  gboolean ret = TRUE;
+
+  if (self->search_terms) {
+    GSList *iter;
+    GRegex *current = NULL;
+
+    for (iter = self->search_terms; iter != NULL; iter = iter->next) {
+      current = (GRegex *)iter->data;
+      if ((!g_regex_match (current, title ? title : "", G_REGEX_MATCH_NOTEMPTY, NULL)) &&
+          (!g_regex_match (current, location ? location : "", G_REGEX_MATCH_NOTEMPTY, NULL))) {
+        ret = FALSE;
+        break;
+      }
+    }
+  }
+
+  return ret;
+}
+
+static void
+icon_loaded_cb (GObject      *source,
+                GAsyncResult *result,
+                gpointer      user_data)
+{
+  WebKitFaviconDatabase *database = WEBKIT_FAVICON_DATABASE (source);
+  EphySuggestion *suggestion = EPHY_SUGGESTION (user_data);
+  cairo_surface_t *favicon = webkit_favicon_database_get_favicon_finish (database, result, NULL);
+
+  if (favicon != NULL) {
+    gdouble x_scale, y_scale;
+    int x, y;
+
+    x = cairo_image_surface_get_width (favicon);
+    y = cairo_image_surface_get_height (favicon);
+    x_scale = (gdouble)x / 16;
+    y_scale = (gdouble)y / 16;
+
+    cairo_surface_set_device_scale (favicon, x_scale, y_scale);
+
+    ephy_suggestion_set_favicon (suggestion, favicon);
+  }
+
+  g_object_unref (suggestion);
+}
+
+static void
+load_favicon (EphySuggestion *suggestion,
+              const char     *url)
+{
+  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
+  WebKitWebContext *context = ephy_embed_shell_get_web_context (shell);
+  WebKitFaviconDatabase *database = webkit_web_context_get_favicon_database (context);
+
+  webkit_favicon_database_get_favicon (database, url, NULL, icon_loaded_cb, g_object_ref (suggestion));
+}
+
+static guint
+add_bookmarks (EphySuggestionModel *self,
+               const char          *query)
+{
+  GSequence *bookmarks;
+  guint added = 0;
+
+  bookmarks = ephy_bookmarks_manager_get_bookmarks (self->bookmarks_manager);
+
+  for (GSequenceIter *iter = g_sequence_get_begin_iter (bookmarks);
+       !g_sequence_iter_is_end (iter);
+       iter = g_sequence_iter_next (iter)) {
+    EphyBookmark *bookmark;
+    const char *url, *title;
+
+    bookmark = g_sequence_get (iter);
+
+    url = ephy_bookmark_get_url (bookmark);
+    title = ephy_bookmark_get_title (bookmark);
+
+    if (should_add_bookmark_to_model (self, query, title, url)) {
+      EphySuggestion *suggestion;
+
+      suggestion = ephy_suggestion_new (title, url);
+      load_favicon (suggestion, url);
+
+      g_sequence_append (self->items, suggestion);
+      added++;
+    }
+  }
+
+  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;
+
+    suggestion = ephy_suggestion_new (url->title, url->url);
+    load_favicon (suggestion, url->url);
+
+    g_sequence_append (self->items, suggestion);
+    added++;
+  }
+
+  return added;
+}
+
+static guint
+add_search_engines (EphySuggestionModel *self,
+                    const char          *query)
+{
+  EphyEmbedShell *shell;
+  EphySearchEngineManager *manager;
+  char **engines;
+  guint added = 0;
+
+  shell = ephy_embed_shell_get_default ();
+  manager = ephy_embed_shell_get_search_engine_manager (shell);
+  engines = ephy_search_engine_manager_get_names (manager);
+
+  for (guint i = 0; engines[i] != NULL; i++) {
+    EphySuggestion *suggestion;
+    char *address;
+
+    address = ephy_search_engine_manager_build_search_address (manager, engines[i], query);
+    suggestion = ephy_suggestion_new_without_subtitle (engines[i], address);
+    load_favicon (suggestion, address);
+
+    g_sequence_append (self->items, suggestion);
+    added++;
+
+    g_free (address);
+  }
+
+  g_strfreev (engines);
+
+  return added;
+}
+
+static void
+query_completed_cb (EphyHistoryService *service,
+                    gboolean            success,
+                    gpointer            result_data,
+                    gpointer            user_data)
+{
+  GTask *task = user_data;
+  EphySuggestionModel *self;
+  const gchar *query;
+  GList *urls;
+  guint removed;
+  guint added = 0;
+
+  self = g_task_get_source_object (task);
+  query = g_task_get_task_data (task);
+  urls = (GList *)result_data;
+
+  removed = g_sequence_get_length (self->items);
+
+  g_clear_pointer (&self->items, g_sequence_free);
+  self->items = g_sequence_new (g_object_unref);
+
+  if (strlen (query) > 0) {
+    added = add_bookmarks (self, query);
+    added += add_history (self, urls, query);
+    added += add_search_engines (self, query);
+  }
+
+  g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
+
+  g_task_return_boolean (task, TRUE);
+  g_object_unref (task);
+}
+
+void
+ephy_suggestion_model_query_async (EphySuggestionModel *self,
+                                   const gchar         *query,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
+{
+  GTask *task = NULL;
+  char **strings;
+  GList *qlist = NULL;
+
+  g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+  g_assert (query != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ephy_suggestion_model_query_async);
+  g_task_set_task_data (task, g_strdup (query), g_free);
+
+  /* Split the search string. */
+  strings = g_strsplit (query, " ", -1);
+  for (guint i = 0; strings[i]; i++)
+    qlist = g_list_append (qlist, g_strdup (strings[i]));
+
+  update_search_terms (self, query);
+
+  ephy_history_service_find_urls (self->history_service,
+                                  0, 0,
+                                  MAX_COMPLETION_HISTORY_URLS, 0,
+                                  qlist,
+                                  EPHY_HISTORY_SORT_MOST_VISITED,
+                                  cancellable,
+                                  (EphyHistoryJobCallback)query_completed_cb,
+                                  task);
+
+  g_strfreev (strings);
+}
+
+gboolean
+ephy_suggestion_model_query_finish (EphySuggestionModel  *self,
+                                    GAsyncResult         *result,
+                                    GError              **error)
+{
+  g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+EphySuggestion *
+ephy_suggestion_model_get_suggestion_with_uri (EphySuggestionModel *self,
+                                               const char          *uri)
+{
+  GSequenceIter *iter;
+
+  g_assert (EPHY_IS_SUGGESTION_MODEL (self));
+  g_assert (uri != NULL && *uri != '\0');
+
+  for (iter = g_sequence_get_begin_iter (self->items);
+       !g_sequence_iter_is_end (iter); iter = g_sequence_iter_next (iter)) {
+    EphySuggestion *suggestion;
+
+    suggestion = g_sequence_get (iter);
+    if (strcmp (ephy_suggestion_get_uri (suggestion), uri) == 0)
+      return suggestion;
+  }
+
+  return NULL;
+}
diff --git a/src/ephy-suggestion-model.h b/src/ephy-suggestion-model.h
new file mode 100644
index 000000000..ba1ff06d1
--- /dev/null
+++ b/src/ephy-suggestion-model.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright © 2017 Christian Hergert <chergert redhat com>
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <gio/gio.h>
+
+#include "ephy-bookmarks-manager.h"
+#include "ephy-history-service.h"
+#include "ephy-suggestion.h"
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_SUGGESTION_MODEL (ephy_suggestion_model_get_type())
+
+G_DECLARE_FINAL_TYPE (EphySuggestionModel, ephy_suggestion_model, EPHY, SUGGESTION_MODEL, GObject)
+
+EphySuggestionModel *ephy_suggestion_model_new                     (EphyHistoryService    *history_service,
+                                                                    EphyBookmarksManager  
*bookmarks_manager);
+void                 ephy_suggestion_model_query_async             (EphySuggestionModel   *self,
+                                                                    const gchar           *query,
+                                                                    GCancellable          *cancellable,
+                                                                    GAsyncReadyCallback    callback,
+                                                                    gpointer               user_data);
+gboolean             ephy_suggestion_model_query_finish            (EphySuggestionModel   *self,
+                                                                    GAsyncResult          *result,
+                                                                    GError               **error);
+EphySuggestion      *ephy_suggestion_model_get_suggestion_with_uri (EphySuggestionModel *self,
+                                                                    const char          *uri);
+
+G_END_DECLS
diff --git a/src/ephy-window.c b/src/ephy-window.c
index d86c59d07..deeedaf3d 100644
--- a/src/ephy-window.c
+++ b/src/ephy-window.c
@@ -3434,7 +3434,7 @@ ephy_window_activate_location (EphyWindow *window)
   title_widget = ephy_header_bar_get_title_widget (EPHY_HEADER_BAR (window->header_bar));
 
   if (EPHY_IS_LOCATION_ENTRY (title_widget))
-    ephy_location_entry_activate (EPHY_LOCATION_ENTRY (title_widget));
+    ephy_location_entry_focus (EPHY_LOCATION_ENTRY (title_widget));
 }
 
 /**
diff --git a/src/meson.build b/src/meson.build
index c45a50c24..3d4c6d137 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -25,7 +25,6 @@ libephymain_sources = [
   'ephy-action-bar-end.c',
   'ephy-action-bar-start.c',
   'ephy-action-helper.c',
-  'ephy-completion-model.c',
   'ephy-encoding-dialog.c',
   'ephy-encoding-row.c',
   'ephy-header-bar.c',
@@ -37,6 +36,7 @@ libephymain_sources = [
   'ephy-search-engine-dialog.c',
   'ephy-session.c',
   'ephy-shell.c',
+  'ephy-suggestion-model.c',
   'ephy-window.c',
   'passwords-dialog.c',
   'popup-commands.c',
diff --git a/src/search-provider/ephy-search-provider.c b/src/search-provider/ephy-search-provider.c
index daa04f36b..97be78448 100644
--- a/src/search-provider/ephy-search-provider.c
+++ b/src/search-provider/ephy-search-provider.c
@@ -23,11 +23,12 @@
 #include "ephy-search-provider.h"
 
 #include "ephy-bookmarks-manager.h"
-#include "ephy-completion-model.h"
 #include "ephy-file-helpers.h"
 #include "ephy-prefs.h"
 #include "ephy-profile-utils.h"
 #include "ephy-shell.h"
+#include "ephy-suggestion-model.h"
+#include "ephy-uri-helpers.h"
 
 #include <gio/gio.h>
 #include <gio/gdesktopappinfo.h>
@@ -43,7 +44,7 @@ struct _EphySearchProvider {
 
   GSettings                *settings;
   EphyBookmarksManager     *bookmarks_manager;
-  EphyCompletionModel      *model;
+  EphySuggestionModel      *model;
 };
 
 struct _EphySearchProviderClass {
@@ -55,29 +56,29 @@ G_DEFINE_TYPE (EphySearchProvider, ephy_search_provider, G_TYPE_APPLICATION)
 #define INACTIVITY_TIMEOUT 60 * 1000 /* One minute, in milliseconds */
 
 static void
-on_model_updated (EphyHistoryService *service,
-                  gboolean            success,
-                  gpointer            result_data,
-                  gpointer            user_data)
+on_model_updated (GObject      *source_object,
+                  GAsyncResult *result,
+                  GTask        *task)
 {
-  GTask *task = user_data;
   EphySearchProvider *self = g_task_get_source_object (task);
-  GtkTreeModel *model = GTK_TREE_MODEL (self->model);
-  GtkTreeIter iter;
+  EphySuggestion *suggestion;
   GPtrArray *results;
   const char *search_string;
-  gboolean ok;
-
+  guint n_items;
+  GError *error = NULL;
   results = g_ptr_array_new ();
 
-  ok = gtk_tree_model_get_iter_first (model, &iter);
-  while (ok) {
-    char *result;
-
-    result = gtk_tree_model_get_string_from_iter (model, &iter);
-    g_ptr_array_add (results, result);
-
-    ok = gtk_tree_model_iter_next (model, &iter);
+  if (ephy_suggestion_model_query_finish (self->model,
+                                          result,
+                                          &error)) {
+    n_items = g_list_model_get_n_items (G_LIST_MODEL (self->model));
+    for (guint i = 0; i < n_items; i++) {
+      suggestion = g_list_model_get_item (G_LIST_MODEL (self->model), i);
+      g_ptr_array_add (results, g_strdup (ephy_suggestion_get_uri (suggestion)));
+    }
+  } else {
+    g_warning ("Failed to query suggestion model: %s", error->message);
+    g_error_free (error);
   }
 
   search_string = g_task_get_task_data (task);
@@ -112,10 +113,11 @@ gather_results_async (EphySearchProvider *self,
   search_string = g_strjoinv (" ", terms);
   g_task_set_task_data (task, search_string, g_free);
 
-  ephy_completion_model_update_for_string (self->model,
-                                           search_string,
-                                           on_model_updated,
-                                           task);
+  ephy_suggestion_model_query_async (self->model,
+                                     search_string,
+                                     cancellable,
+                                     (GAsyncReadyCallback)on_model_updated,
+                                     task);
 }
 
 static void
@@ -126,7 +128,6 @@ complete_request (GObject      *object,
   EphySearchProvider *self = EPHY_SEARCH_PROVIDER (object);
   char **results;
   GError *error;
-
   error = NULL;
   results = gather_results_finish (self, result, &error);
 
@@ -177,14 +178,8 @@ handle_get_result_metas (EphyShellSearchProvider2 *skeleton,
                          char                    **results,
                          EphySearchProvider       *self)
 {
-  GtkTreeModel *model = GTK_TREE_MODEL (self->model);
-  GtkTreeIter iter;
   int i;
   GVariantBuilder builder;
-  GIcon *favicon;
-  char *name, *url;
-  gboolean is_bookmark;
-
   g_application_hold (G_APPLICATION (self));
   g_cancellable_cancel (self->cancellable);
 
@@ -201,54 +196,28 @@ handle_get_result_metas (EphyShellSearchProvider2 *skeleton,
       g_variant_builder_add (&builder, "{sv}",
                              "gicon", g_variant_new_string ("org.gnome.Epiphany"));
       g_variant_builder_close (&builder);
-      continue;
-    }
-
-    if (!gtk_tree_model_get_iter_from_string (model, &iter, results[i]))
-      continue;
-
-    gtk_tree_model_get (model, &iter,
-                        EPHY_COMPLETION_TEXT_COL, &name,
-                        EPHY_COMPLETION_URL_COL, &url,
-                        EPHY_COMPLETION_FAVICON_COL, &favicon,
-                        EPHY_COMPLETION_EXTRA_COL, &is_bookmark,
-                        -1);
+    } else {
+      EphySuggestion *suggestion;
+      const char *title;
+      const char *uri;
+      char *decoded_uri;
 
-    g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
-    g_variant_builder_add (&builder, "{sv}",
-                           "id", g_variant_new_string (url));
-    g_variant_builder_add (&builder, "{sv}",
-                           "name", g_variant_new_string (name));
+      suggestion = ephy_suggestion_model_get_suggestion_with_uri (self->model, results[i]);
+      title = ephy_suggestion_get_unescaped_title (suggestion);
+      uri = ephy_suggestion_get_uri (suggestion);
+      decoded_uri = ephy_uri_decode (uri);
 
-    if (favicon == NULL) {
-      char *type;
-
-      type = g_content_type_from_mime_type ("text/html");
-      favicon = g_content_type_get_icon (type);
-
-      if (is_bookmark) {
-        GEmblem *emblem;
-        GIcon *emblem_icon, *emblemed;
-
-        emblem_icon = g_themed_icon_new ("emblem-favorite");
-        emblem = g_emblem_new (emblem_icon);
-
-        emblemed = g_emblemed_icon_new (favicon, emblem);
+      g_variant_builder_open (&builder, G_VARIANT_TYPE ("a{sv}"));
+      g_variant_builder_add (&builder, "{sv}",
+                             "id", g_variant_new_string (decoded_uri));
+      g_variant_builder_add (&builder, "{sv}",
+                             "name", g_variant_new_string (title));
+      g_variant_builder_add (&builder, "{sv}",
+                             "gicon", g_variant_new_string ("text-html"));
+      g_variant_builder_close (&builder);
 
-        g_object_unref (emblem);
-        g_object_unref (emblem_icon);
-        g_object_unref (favicon);
-        favicon = emblemed;
-      }
+      g_free (decoded_uri);
     }
-
-    g_variant_builder_add (&builder, "{sv}",
-                           "icon", g_icon_serialize (favicon));
-    g_variant_builder_close (&builder);
-
-    g_object_unref (favicon);
-    g_free (name);
-    g_free (url);
   }
 
   ephy_shell_search_provider2_complete_get_result_metas (skeleton,
@@ -370,7 +339,7 @@ ephy_search_provider_init (EphySearchProvider *self)
 
   filename = g_build_filename (ephy_dot_dir (), EPHY_HISTORY_FILE, NULL);
   self->bookmarks_manager = ephy_bookmarks_manager_new ();
-  self->model = ephy_completion_model_new (ephy_embed_shell_get_global_history_service (shell),
+  self->model = ephy_suggestion_model_new (ephy_embed_shell_get_global_history_service (shell),
                                            self->bookmarks_manager);
   g_free (filename);
 
diff --git a/subprojects/libdazzle.wrap b/subprojects/libdazzle.wrap
index c021f807e..15ac93ad2 100644
--- a/subprojects/libdazzle.wrap
+++ b/subprojects/libdazzle.wrap
@@ -1,4 +1,4 @@
 [wrap-git]
 directory=libdazzle
 url=https://gitlab.gnome.org/GNOME/libdazzle.git
-revision=ace2d7a10388117b8178d253ca3a59754c5924dc
+revision=cac52ec1804c44b0c27faa8032969a60ab77cff8
diff --git a/tests/ephy-location-entry-test.c b/tests/ephy-location-entry-test.c
index c378187a5..8013e2a49 100644
--- a/tests/ephy-location-entry-test.c
+++ b/tests/ephy-location-entry-test.c
@@ -38,11 +38,13 @@ test_entry_new (void)
 static void
 test_entry_get_entry (void)
 {
-  EphyLocationEntry *entry;
+  EphyLocationEntry *lentry;
+  GtkWidget *entry;
 
-  entry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
+  lentry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
+  entry = ephy_location_entry_get_entry (lentry);
 
-  g_assert (GTK_IS_ENTRY (ephy_location_entry_get_entry (entry)));
+  g_assert (GTK_IS_ENTRY (entry));
 }
 
 static void
@@ -104,16 +106,19 @@ test_entry_can_undo (void)
 {
   const char *test = "test";
 
-  EphyLocationEntry *entry;
-  entry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
+  EphyLocationEntry *lentry;
+  GtkWidget *entry;
+
+  lentry = EPHY_LOCATION_ENTRY (ephy_location_entry_new ());
 
-  g_assert_cmpint (ephy_location_entry_get_can_undo (entry), ==, FALSE);
+  g_assert_cmpint (ephy_location_entry_get_can_undo (lentry), ==, FALSE);
 
   /* Use gtk_* function or otherwise user_changed won't be correctly handled
    * internally by the location entry (see editable_changed_cb and
    * block_update) */
-  gtk_entry_set_text (GTK_ENTRY (ephy_location_entry_get_entry (entry)), test);
-  g_assert_cmpint (ephy_location_entry_get_can_undo (entry), ==, TRUE);
+  entry = ephy_location_entry_get_entry (lentry);
+  gtk_entry_set_text (GTK_ENTRY (entry), test);
+  g_assert_cmpint (ephy_location_entry_get_can_undo (lentry), ==, TRUE);
 }
 
 static void
diff --git a/tests/meson.build b/tests/meson.build
index 7bb567cef..6e8cc3fd8 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -17,15 +17,6 @@ if get_option('unit_tests')
   #   link_with: libephytestutils
   # )
 
-  completion_model_test = executable('test-ephy-completion-model',
-    'ephy-completion-model-test.c',
-    dependencies: ephymain_dep
-  )
-  test('Completion model test',
-       completion_model_test,
-       env: envs
-  )
-
   # FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=778153
   # download_test = executable('test-ephy-download',
   #   'ephy-download-test.c',


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